/** * Calculate the Barycentric coordinate weight values u-v-w for Point p in respect to the provided * triangle. This is useful for computing new UV coordinates for arbitrary points. */ public Vector3 Barycentric(Vector3 p) { Vector3 a = m_pos_a; Vector3 b = m_pos_b; Vector3 c = m_pos_c; Vector3 m = Vector3.Cross(b - a, c - a); float nu; float nv; float ood; float x = Mathf.Abs(m.x); float y = Mathf.Abs(m.y); float z = Mathf.Abs(m.z); // compute areas of plane with largest projections if (x >= y && x >= z) { // area of PBC in yz plane nu = Intersector.TriArea2D(p.y, p.z, b.y, b.z, c.y, c.z); // area of PCA in yz plane nv = Intersector.TriArea2D(p.y, p.z, c.y, c.z, a.y, a.z); // 1/2*area of ABC in yz plane ood = 1.0f / m.x; } else if (y >= x && y >= z) { // project in xz plane nu = Intersector.TriArea2D(p.x, p.z, b.x, b.z, c.x, c.z); nv = Intersector.TriArea2D(p.x, p.z, c.x, c.z, a.x, a.z); ood = 1.0f / -m.y; } else { // project in xy plane nu = Intersector.TriArea2D(p.x, p.y, b.x, b.y, c.x, c.y); nv = Intersector.TriArea2D(p.x, p.y, c.x, c.y, a.x, a.y); ood = 1.0f / m.z; } float u = nu * ood; float v = nv * ood; float w = 1.0f - u - v; return(new Vector3(u, v, w)); }
/** * O(n log n) Convex Hull Algorithm. * Accepts a list of vertices as Vector3 and triangulates them according to a projection * plane defined as planeNormal. Algorithm will output vertices, indices and UV coordinates * as arrays */ public static bool MonotoneChain(List <Vector3> vertices, Vector3 normal, out List <Triangle> tri, TextureRegion texRegion) { int count = vertices.Count; // we cannot triangulate less than 3 points. Use minimum of 3 points if (count < 3) { tri = null; return(false); } // first, we map from 3D points into a 2D plane represented by the provided normal Vector3 u = Vector3.Normalize(Vector3.Cross(normal, Vector3.up)); if (Vector3.zero == u) { u = Vector3.Normalize(Vector3.Cross(normal, Vector3.forward)); } Vector3 v = Vector3.Cross(u, normal); // generate an array of mapped values Mapped2D[] mapped = new Mapped2D[count]; // these values will be used to generate new UV coordinates later on float maxDivX = float.MinValue; float maxDivY = float.MinValue; float minDivX = float.MaxValue; float minDivY = float.MaxValue; // map the 3D vertices into the 2D mapped values for (int i = 0; i < count; i++) { Vector3 vertToAdd = vertices[i]; Mapped2D newMappedValue = new Mapped2D(vertToAdd, u, v); Vector2 mapVal = newMappedValue.mappedValue; // grab our maximal values so we can map UV's in a proper range maxDivX = Mathf.Max(maxDivX, mapVal.x); maxDivY = Mathf.Max(maxDivY, mapVal.y); minDivX = Mathf.Min(minDivX, mapVal.x); minDivY = Mathf.Min(minDivY, mapVal.y); mapped[i] = newMappedValue; } // sort our newly generated array values Array.Sort <Mapped2D>(mapped, (a, b) => { Vector2 x = a.mappedValue; Vector2 p = b.mappedValue; return((x.x < p.x || (x.x == p.x && x.y < p.y)) ? -1 : 1); }); // our final hull mappings will end up in here Mapped2D[] hulls = new Mapped2D[count + 1]; int k = 0; // build the lower hull of the chain for (int i = 0; i < count; i++) { while (k >= 2) { Vector2 mA = hulls[k - 2].mappedValue; Vector2 mB = hulls[k - 1].mappedValue; Vector2 mC = mapped[i].mappedValue; if (Intersector.TriArea2D(mA.x, mA.y, mB.x, mB.y, mC.x, mC.y) > 0.0f) { break; } k--; } hulls[k++] = mapped[i]; } // build the upper hull of the chain for (int i = count - 2, t = k + 1; i >= 0; i--) { while (k >= t) { Vector2 mA = hulls[k - 2].mappedValue; Vector2 mB = hulls[k - 1].mappedValue; Vector2 mC = mapped[i].mappedValue; if (Intersector.TriArea2D(mA.x, mA.y, mB.x, mB.y, mC.x, mC.y) > 0.0f) { break; } k--; } hulls[k++] = mapped[i]; } // finally we can build our mesh, generate all the variables // and fill them up int vertCount = k - 1; int triCount = (vertCount - 2) * 3; // this should not happen, but here just in case if (vertCount < 3) { tri = null; return(false); } // ensure List does not dynamically grow, performing copy ops each time! tri = new List <Triangle>(triCount / 3); float width = maxDivX - minDivX; float height = maxDivY - minDivY; int indexCount = 1; // generate both the vertices and uv's in this loop for (int i = 0; i < triCount; i += 3) { // the Vertices in our triangle Mapped2D posA = hulls[0]; Mapped2D posB = hulls[indexCount]; Mapped2D posC = hulls[indexCount + 1]; // generate UV Maps Vector2 uvA = posA.mappedValue; Vector2 uvB = posB.mappedValue; Vector2 uvC = posC.mappedValue; uvA.x = (uvA.x - minDivX) / width; uvA.y = (uvA.y - minDivY) / height; uvB.x = (uvB.x - minDivX) / width; uvB.y = (uvB.y - minDivY) / height; uvC.x = (uvC.x - minDivX) / width; uvC.y = (uvC.y - minDivY) / height; Triangle newTriangle = new Triangle(posA.originalValue, posB.originalValue, posC.originalValue); // ensure our UV coordinates are mapped into the requested TextureRegion newTriangle.SetUV(texRegion.Map(uvA), texRegion.Map(uvB), texRegion.Map(uvC)); // the normals is the same for all vertices since the final mesh is completly flat newTriangle.SetNormal(normal, normal, normal); newTriangle.ComputeTangents(); tri.Add(newTriangle); indexCount++; } return(true); }
/** * Perform an intersection between Plane and Triangle. This is a comprehensive function * which alwo builds a HULL Hirearchy useful for decimation projects. This obviously * comes at the cost of more complex code and runtime checks, but the returned results * are much more flexible. * Results will be filled into the IntersectionResult reference. Check result.isValid() * for the final results. */ public static void Intersect(Plane pl, Triangle tri, ref IntersectionResult result) { // clear the previous results from the IntersectionResult result.Clear(); // grab local variables for easier access Vector3 a = tri.positionA; Vector3 b = tri.positionB; Vector3 c = tri.positionC; // check to see which side of the plane the points all // lay in. SideOf operation is a simple dot product and some comparison // operations, so these are a very quick checks SideOfPlane sa = pl.SideOf(a); SideOfPlane sb = pl.SideOf(b); SideOfPlane sc = pl.SideOf(c); // we cannot intersect if the triangle points all fall on the same side // of the plane. This is an easy early out test as no intersections are possible. if (sa == sb && sb == sc) { return; } // detect cases where two points lay straight on the plane, meaning // that the plane is actually parralel with one of the edges of the triangle else if ((sa == SideOfPlane.ON && sa == sb) || (sa == SideOfPlane.ON && sa == sc) || (sb == SideOfPlane.ON && sb == sc)) { return; } // keep in mind that intersection points are shared by both // the upper HULL and lower HULL hence they lie perfectly // on the plane that cut them Vector3 qa; Vector3 qb; // check the cases where the points of the triangle actually lie on the plane itself // in these cases, there is only going to be 2 triangles, one for the upper HULL and // the other on the lower HULL // we just need to figure out which points to accept into the upper or lower hulls. if (sa == SideOfPlane.ON) { // if the point a is on the plane, test line b-c if (Intersector.Intersect(pl, b, c, out qa)) { // line b-c intersected, construct out triangles and return approprietly result.AddIntersectionPoint(qa); result.AddIntersectionPoint(a); // our two generated triangles, we need to figure out which // triangle goes into the UPPER hull and which goes into the LOWER hull Triangle ta = new Triangle(a, b, qa); Triangle tb = new Triangle(a, qa, c); // generate UV coordinates if there is any if (tri.hasUV) { // the computed UV coordinate if the intersection point Vector2 pq = tri.GenerateUV(qa); Vector2 pa = tri.uvA; Vector2 pb = tri.uvB; Vector2 pc = tri.uvC; ta.SetUV(pa, pb, pq); tb.SetUV(pa, pq, pc); } // generate Normal coordinates if there is any if (tri.hasNormal) { // the computed Normal coordinate if the intersection point Vector3 pq = tri.GenerateNormal(qa); Vector3 pa = tri.normalA; Vector3 pb = tri.normalB; Vector3 pc = tri.normalC; ta.SetNormal(pa, pb, pq); tb.SetNormal(pa, pq, pc); } // generate Tangent coordinates if there is any if (tri.hasTangent) { // the computed Tangent coordinate if the intersection point Vector4 pq = tri.GenerateTangent(qa); Vector4 pa = tri.tangentA; Vector4 pb = tri.tangentB; Vector4 pc = tri.tangentC; ta.SetTangent(pa, pb, pq); tb.SetTangent(pa, pq, pc); } // b point lies on the upside of the plane if (sb == SideOfPlane.UP) { result.AddUpperHull(ta).AddLowerHull(tb); } // b point lies on the downside of the plane else if (sb == SideOfPlane.DOWN) { result.AddUpperHull(tb).AddLowerHull(ta); } } } // test the case where the b point lies on the plane itself else if (sb == SideOfPlane.ON) { // if the point b is on the plane, test line a-c if (Intersector.Intersect(pl, a, c, out qa)) { // line a-c intersected, construct out triangles and return approprietly result.AddIntersectionPoint(qa); result.AddIntersectionPoint(b); // our two generated triangles, we need to figure out which // triangle goes into the UPPER hull and which goes into the LOWER hull Triangle ta = new Triangle(a, b, qa); Triangle tb = new Triangle(qa, b, c); // generate UV coordinates if there is any if (tri.hasUV) { // the computed UV coordinate if the intersection point Vector2 pq = tri.GenerateUV(qa); Vector2 pa = tri.uvA; Vector2 pb = tri.uvB; Vector2 pc = tri.uvC; ta.SetUV(pa, pb, pq); tb.SetUV(pq, pb, pc); } // generate Normal coordinates if there is any if (tri.hasNormal) { // the computed Normal coordinate if the intersection point Vector3 pq = tri.GenerateNormal(qa); Vector3 pa = tri.normalA; Vector3 pb = tri.normalB; Vector3 pc = tri.normalC; ta.SetNormal(pa, pb, pq); tb.SetNormal(pq, pb, pc); } // generate Tangent coordinates if there is any if (tri.hasTangent) { // the computed Tangent coordinate if the intersection point Vector4 pq = tri.GenerateTangent(qa); Vector4 pa = tri.tangentA; Vector4 pb = tri.tangentB; Vector4 pc = tri.tangentC; ta.SetTangent(pa, pb, pq); tb.SetTangent(pq, pb, pc); } // a point lies on the upside of the plane if (sa == SideOfPlane.UP) { result.AddUpperHull(ta).AddLowerHull(tb); } // a point lies on the downside of the plane else if (sa == SideOfPlane.DOWN) { result.AddUpperHull(tb).AddLowerHull(ta); } } } // test the case where the c point lies on the plane itself else if (sc == SideOfPlane.ON) { // if the point c is on the plane, test line a-b if (Intersector.Intersect(pl, a, b, out qa)) { // line a-c intersected, construct out triangles and return approprietly result.AddIntersectionPoint(qa); result.AddIntersectionPoint(c); // our two generated triangles, we need to figure out which // triangle goes into the UPPER hull and which goes into the LOWER hull Triangle ta = new Triangle(a, qa, c); Triangle tb = new Triangle(qa, b, c); // generate UV coordinates if there is any if (tri.hasUV) { // the computed UV coordinate if the intersection point Vector2 pq = tri.GenerateUV(qa); Vector2 pa = tri.uvA; Vector2 pb = tri.uvB; Vector2 pc = tri.uvC; ta.SetUV(pa, pq, pc); tb.SetUV(pq, pb, pc); } // generate Normal coordinates if there is any if (tri.hasNormal) { // the computed Normal coordinate if the intersection point Vector3 pq = tri.GenerateNormal(qa); Vector3 pa = tri.normalA; Vector3 pb = tri.normalB; Vector3 pc = tri.normalC; ta.SetNormal(pa, pq, pc); tb.SetNormal(pq, pb, pc); } // generate Tangent coordinates if there is any if (tri.hasTangent) { // the computed Tangent coordinate if the intersection point Vector4 pq = tri.GenerateTangent(qa); Vector4 pa = tri.tangentA; Vector4 pb = tri.tangentB; Vector4 pc = tri.tangentC; ta.SetTangent(pa, pq, pc); tb.SetTangent(pq, pb, pc); } // a point lies on the upside of the plane if (sa == SideOfPlane.UP) { result.AddUpperHull(ta).AddLowerHull(tb); } // a point lies on the downside of the plane else if (sa == SideOfPlane.DOWN) { result.AddUpperHull(tb).AddLowerHull(ta); } } } // at this point, all edge cases have been tested and failed, we need to perform // full intersection tests against the lines. From this point onwards we will generate // 3 triangles else if (sa != sb && Intersector.Intersect(pl, a, b, out qa)) { // intersection found against a - b result.AddIntersectionPoint(qa); // since intersection was found against a - b, we need to check which other // lines to check (we only need to check one more line) for intersection. // the line we check against will be the line against the point which lies on // the other side of the plane. if (sa == sc) { // we likely have an intersection against line b-c which will complete this loop if (Intersector.Intersect(pl, b, c, out qb)) { result.AddIntersectionPoint(qb); // our three generated triangles. Two of these triangles will end // up on either the UPPER or LOWER hulls. Triangle ta = new Triangle(qa, b, qb); Triangle tb = new Triangle(a, qa, qb); Triangle tc = new Triangle(a, qb, c); // generate UV coordinates if there is any if (tri.hasUV) { // the computed UV coordinate if the intersection point Vector2 pqa = tri.GenerateUV(qa); Vector2 pqb = tri.GenerateUV(qb); Vector2 pa = tri.uvA; Vector2 pb = tri.uvB; Vector2 pc = tri.uvC; ta.SetUV(pqa, pb, pqb); tb.SetUV(pa, pqa, pqb); tc.SetUV(pa, pqb, pc); } // generate Normal coordinates if there is any if (tri.hasNormal) { // the computed Normal coordinate if the intersection point Vector3 pqa = tri.GenerateNormal(qa); Vector3 pqb = tri.GenerateNormal(qb); Vector3 pa = tri.normalA; Vector3 pb = tri.normalB; Vector3 pc = tri.normalC; ta.SetNormal(pqa, pb, pqb); tb.SetNormal(pa, pqa, pqb); tc.SetNormal(pa, pqb, pc); } // generate Tangent coordinates if there is any if (tri.hasTangent) { // the computed Tangent coordinate if the intersection point Vector4 pqa = tri.GenerateTangent(qa); Vector4 pqb = tri.GenerateTangent(qb); Vector4 pa = tri.tangentA; Vector4 pb = tri.tangentB; Vector4 pc = tri.tangentC; ta.SetTangent(pqa, pb, pqb); tb.SetTangent(pa, pqa, pqb); tc.SetTangent(pa, pqb, pc); } if (sa == SideOfPlane.UP) { result.AddUpperHull(tb).AddUpperHull(tc).AddLowerHull(ta); } else { result.AddLowerHull(tb).AddLowerHull(tc).AddUpperHull(ta); } } } else { // in this scenario, the point a is a "lone" point which lies in either upper // or lower HULL. We need to perform another intersection to find the last point if (Intersector.Intersect(pl, a, c, out qb)) { result.AddIntersectionPoint(qb); // our three generated triangles. Two of these triangles will end // up on either the UPPER or LOWER hulls. Triangle ta = new Triangle(a, qa, qb); Triangle tb = new Triangle(qa, b, c); Triangle tc = new Triangle(qb, qa, c); // generate UV coordinates if there is any if (tri.hasUV) { // the computed UV coordinate if the intersection point Vector2 pqa = tri.GenerateUV(qa); Vector2 pqb = tri.GenerateUV(qb); Vector2 pa = tri.uvA; Vector2 pb = tri.uvB; Vector2 pc = tri.uvC; ta.SetUV(pa, pqa, pqb); tb.SetUV(pqa, pb, pc); tc.SetUV(pqb, pqa, pc); } // generate Normal coordinates if there is any if (tri.hasNormal) { // the computed Normal coordinate if the intersection point Vector3 pqa = tri.GenerateNormal(qa); Vector3 pqb = tri.GenerateNormal(qb); Vector3 pa = tri.normalA; Vector3 pb = tri.normalB; Vector3 pc = tri.normalC; ta.SetNormal(pa, pqa, pqb); tb.SetNormal(pqa, pb, pc); tc.SetNormal(pqb, pqa, pc); } // generate Tangent coordinates if there is any if (tri.hasTangent) { // the computed Tangent coordinate if the intersection point Vector4 pqa = tri.GenerateTangent(qa); Vector4 pqb = tri.GenerateTangent(qb); Vector4 pa = tri.tangentA; Vector4 pb = tri.tangentB; Vector4 pc = tri.tangentC; ta.SetTangent(pa, pqa, pqb); tb.SetTangent(pqa, pb, pc); tc.SetTangent(pqb, pqa, pc); } if (sa == SideOfPlane.UP) { result.AddUpperHull(ta).AddLowerHull(tb).AddLowerHull(tc); } else { result.AddLowerHull(ta).AddUpperHull(tb).AddUpperHull(tc); } } } } // if line a-b did not intersect (or the lie on the same side of the plane) // this simplifies the problem a fair bit. This means we have an intersection // in line a-c and b-c, which we can use to build a new UPPER and LOWER hulls // we are expecting both of these intersection tests to pass, otherwise something // went wrong (float errors? missed a checked case?) else if (Intersector.Intersect(pl, c, a, out qa) && Intersector.Intersect(pl, c, b, out qb)) { // in here we know that line a-b actually lie on the same side of the plane, this will // simplify the rest of the logic. We also have our intersection points // the computed UV coordinate of the intersection point result.AddIntersectionPoint(qa); result.AddIntersectionPoint(qb); // our three generated triangles. Two of these triangles will end // up on either the UPPER or LOWER hulls. Triangle ta = new Triangle(qa, qb, c); Triangle tb = new Triangle(a, qb, qa); Triangle tc = new Triangle(a, b, qb); // generate UV coordinates if there is any if (tri.hasUV) { // the computed UV coordinate if the intersection point Vector2 pqa = tri.GenerateUV(qa); Vector2 pqb = tri.GenerateUV(qb); Vector2 pa = tri.uvA; Vector2 pb = tri.uvB; Vector2 pc = tri.uvC; ta.SetUV(pqa, pqb, pc); tb.SetUV(pa, pqb, pqa); tc.SetUV(pa, pb, pqb); } // generate Normal coordinates if there is any if (tri.hasNormal) { // the computed Normal coordinate if the intersection point Vector3 pqa = tri.GenerateNormal(qa); Vector3 pqb = tri.GenerateNormal(qb); Vector3 pa = tri.normalA; Vector3 pb = tri.normalB; Vector3 pc = tri.normalC; ta.SetNormal(pqa, pqb, pc); tb.SetNormal(pa, pqb, pqa); tc.SetNormal(pa, pb, pqb); } // generate Tangent coordinates if there is any if (tri.hasTangent) { // the computed Tangent coordinate if the intersection point Vector4 pqa = tri.GenerateTangent(qa); Vector4 pqb = tri.GenerateTangent(qb); Vector4 pa = tri.tangentA; Vector4 pb = tri.tangentB; Vector4 pc = tri.tangentC; ta.SetTangent(pqa, pqb, pc); tb.SetTangent(pa, pqb, pqa); tc.SetTangent(pa, pb, pqb); } if (sa == SideOfPlane.UP) { result.AddUpperHull(tb).AddUpperHull(tc).AddLowerHull(ta); } else { result.AddLowerHull(tb).AddLowerHull(tc).AddUpperHull(ta); } } }
/** * Perform an intersection between Plane and Line, storing intersection point * in reference q. Function returns true if intersection has been found or * false otherwise. */ public static bool Intersect(Plane pl, Line ln, out Vector3 q) { return(Intersector.Intersect(pl, ln.positionA, ln.positionB, out q)); }
/** * Helper function to split this triangle by the provided plane and store * the results inside the IntersectionResult structure. * Returns true on success or false otherwise */ public bool Split(Plane pl, IntersectionResult result) { Intersector.Intersect(pl, this, ref result); return(result.isValid); }
/** * O(n log n) Convex Hull Algorithm. * Accepts a list of vertices as Vector3 and triangulates them according to a projection * plane defined as planeNormal. Algorithm will output vertices, indices and UV coordinates * as arrays */ public static bool MonotoneChain(List <Vector3> vertices, Vector3 normal, out Vector3[] verts, out int[] indices, out Vector2[] uv) { int count = vertices.Count; // we cannot triangulate less than 3 points. Use minimum of 3 points if (count < 3) { verts = null; indices = null; uv = null; return(false); } // first, we map from 3D points into a 2D plane represented by the provided normal Vector3 r = Mathf.Abs(normal.x) > Mathf.Abs(normal.y) ? new Vector3(0, 1, 0) : new Vector3(1, 0, 0); Vector3 v = Vector3.Normalize(Vector3.Cross(r, normal)); Vector3 u = Vector3.Cross(normal, v); // generate an array of mapped values Mapped2D[] mapped = new Mapped2D[count]; // these values will be used to generate new UV coordinates later on float maxDivX = 0.0f; float maxDivY = 0.0f; // map the 3D vertices into the 2D mapped values for (int i = 0; i < count; i++) { Vector3 vertToAdd = vertices[i]; Mapped2D newMappedValue = new Mapped2D(vertToAdd, u, v); Vector2 mapVal = newMappedValue.mappedValue; // grab our maximal values so we can map UV's in a proper range maxDivX = Mathf.Max(maxDivX, mapVal.x); maxDivY = Mathf.Max(maxDivY, mapVal.y); mapped[i] = newMappedValue; } // sort our newly generated array values Array.Sort <Mapped2D>(mapped, (a, b) => { Vector2 x = a.mappedValue; Vector2 p = b.mappedValue; return((x.x < p.x || (x.x == p.x && x.y < p.y)) ? -1 : 1); }); // our final hull mappings will end up in here Mapped2D[] hulls = new Mapped2D[count + 1]; int k = 0; // build the lower hull of the chain for (int i = 0; i < count; i++) { while (k >= 2) { Vector2 mA = hulls[k - 2].mappedValue; Vector2 mB = hulls[k - 1].mappedValue; Vector2 mC = mapped[i].mappedValue; if (Intersector.TriArea2D(mA.x, mA.y, mB.x, mB.y, mC.x, mC.y) > 0.0f) { break; } k--; } hulls[k++] = mapped[i]; } // build the upper hull of the chain for (int i = count - 2, t = k + 1; i >= 0; i--) { while (k >= t) { Vector2 mA = hulls[k - 2].mappedValue; Vector2 mB = hulls[k - 1].mappedValue; Vector2 mC = mapped[i].mappedValue; if (Intersector.TriArea2D(mA.x, mA.y, mB.x, mB.y, mC.x, mC.y) > 0.0f) { break; } k--; } hulls[k++] = mapped[i]; } // finally we can build our mesh, generate all the variables // and fill them up int vertCount = k - 1; // this should not happen, but here just in case if (vertCount < 3) { verts = null; indices = null; uv = null; return(false); } int triCount = (vertCount - 2) * 3; verts = new Vector3[vertCount]; indices = new int[triCount]; uv = new Vector2[vertCount]; // generate both the vertices and uv's in this loop for (int i = 0; i < vertCount; i++) { Mapped2D val = hulls[i]; // place the vertex verts[i] = val.originalValue; // generate and place the UV Vector2 mappedValue = val.mappedValue; mappedValue.x = (mappedValue.x / maxDivX) * 0.5f; mappedValue.y = (mappedValue.y / maxDivY) * 0.5f; uv[i] = mappedValue; } int indexCount = 1; // generate the triangles/indices for (int i = 0; i < triCount; i += 3) { indices[i + 0] = 0; indices[i + 1] = indexCount; indices[i + 2] = indexCount + 1; indexCount++; } return(true); }