private int TraverseCut(Cut cut, EdgeLoop loop, List <Vertex> perimeter, int currentVertexIndex) { cut.traversed = true; // add all vertices of the egress's cut to the loop loop.vertices.AddRange(cut); perimeter[currentVertexIndex].loops.Add(loop); // find the index of the last vertex in the cut (which is always an egress) and get its index in the perimeter currentVertexIndex = perimeter.IndexOf(cut[cut.Count - 1]); perimeter[currentVertexIndex].loops.Add(loop); string cutString = ""; foreach (Vertex vertex in cut) { cutString += " "; if (perimeter.Contains(vertex)) { cutString += perimeter.IndexOf(vertex) + ": "; } cutString += vertex; } //Debug.Log("Traversing egress: " + cutString); //Debug.Log("Egress traversed. Arrived at " + currentVertexIndex); return(currentVertexIndex); }
private List <Triangle> TriangulateEdgeLoop(EdgeLoop loop, bool debug = false) { List <Triangle> triangles = new List <Triangle>(); for (int i = 1; i < loop.vertices.Count - 1; i++) { triangles.Add(new Triangle(loop.vertices[0], loop.vertices[i], loop.vertices[(i + 1) % loop.vertices.Count])); } return(triangles); }
public bool LiesWithinLoop(EdgeLoop loop) { //Debug.Log("kjshfd"); // collect intersection points Vector3 castDirection = (loop.vertices[0].value - loop.vertices[1].value).normalized; List <Vector3> positiveIntersections = new List <Vector3>(); List <Vector3> negativeIntersections = new List <Vector3>(); for (int i = 0; i < loop.vertices.Count; i++) { Point3 intersection = IntersectLineWithEdge( this.value, castDirection, loop.vertices[i].value, loop.vertices[(i + 1) % loop.vertices.Count].value); if (intersection != null) { Vector3 directionToIntersection = (intersection.value - this.value).normalized; //Debug.Log("found intersection"); if (Vector3.Dot(directionToIntersection, castDirection) == 1) { positiveIntersections.Add(intersection.value); } else { negativeIntersections.Add(intersection.value); } } } // remove duplicates RemoveDuplicates(positiveIntersections); RemoveDuplicates(negativeIntersections); // count # above, below // if both odd, return true, else return false return(positiveIntersections.Count % 2 == 1 && negativeIntersections.Count % 2 == 1); }
private EdgeLoop DiscoverInternalLoop(Vertex initialVertex, List <Vertex> unusedVertices, List <EdgeLoop> loops) { //TODO: discovery of loops Queue <EdgeLoop> potentialLoops = new Queue <EdgeLoop>(); List <EdgeLoop> completedLoops = new List <EdgeLoop>(); potentialLoops.Enqueue(new EdgeLoop()); potentialLoops.Peek().vertices.Add(initialVertex); int k = 0; Debug.Log("before"); while (potentialLoops.Count > 0) { Debug.Log("as"); Vertex nextVertex; do { nextVertex = null; // foundNext = false; //Debug.Log(potentialLoops.Count); List <Vertex> loopVertices = potentialLoops.Peek().vertices; for (int i = unusedVertices.Count - 1; i >= 0; i--) { if (unusedVertices[i].SharesTriangle(loopVertices[loopVertices.Count - 1]) && !loopVertices.Contains(unusedVertices[i])) { if (nextVertex == null) { Debug.Log("found next in path"); nextVertex = unusedVertices[i]; } else { Debug.Log("found additional possible path"); EdgeLoop newLoop = new EdgeLoop(potentialLoops.Peek().vertices); //Debug.Log(newLoop.vertices); potentialLoops.Enqueue(newLoop); foreach (EdgeLoop vertex in potentialLoops) { //Debug.Log(vertex); } } k++; if (k > 100) { throw new System.Exception(); } //unusedVertices.RemoveAt(i); //initialVertex = unusedVertices[i]; //foundNext = true; } } if (nextVertex != null) { Debug.Log(nextVertex.value); loopVertices.Add(nextVertex); Debug.Log(loopVertices.Count); Debug.Log(potentialLoops.Peek().vertices.Count); } } while (nextVertex != null); completedLoops.Add(potentialLoops.Dequeue()); } // Debug.Log(potentialLoops.Peek().vertices.Count); // the loop with the greatest number of vertices is the correct loop EdgeLoop finalLoop = completedLoops[0]; foreach (EdgeLoop loop in completedLoops) { if (loop.vertices.Count > finalLoop.vertices.Count) { finalLoop = loop; } } Debug.Log(finalLoop.vertices.Count); // determine which external loop contains this new loop foreach (EdgeLoop loop in loops) { if (initialVertex.LiesWithinLoop(loop)) { if (loop.nestedLoop == null) { loop.nestedLoop = finalLoop; } else { finalLoop.nestedLoop = loop.nestedLoop; EdgeLoop previousLoop = loop; do { if (initialVertex.LiesWithinLoop(finalLoop.nestedLoop)) { previousLoop.nestedLoop = finalLoop.nestedLoop; finalLoop.nestedLoop = finalLoop.nestedLoop.nestedLoop; previousLoop.nestedLoop.nestedLoop = finalLoop; } else { break; } } while (finalLoop.nestedLoop != null); } // we've found the loop we're contained by, break out break; } } return(finalLoop); }
// classify existing verticies // perform new cut operation on each face of the bounded shape // add back to mesh public Mesh ClipAToB(GameObject toClip, GameObject bounds, GameObject result, bool flipNormals = false) { //Debug.Log("hello"); // get a list of all triangles of the bounding mesh List <Triangle> boundsTriangles = GetBoundsTriangles(toClip, bounds); // get a list of all vertices of the clipping target List <Vertex> vertices = ClassifyVertices(toClip, bounds, boundsTriangles); // get a list of all triangles of the clipping target List <Triangle> meshTriangles = GetAllTriangles(toClip, vertices); // here is where the final triangles will be stored List <Triangle> triangles = new List <Triangle>(); // to create the triangles, we'll need a list of edge loops to triangluate List <EdgeLoop> edgeLoops = new List <EdgeLoop>(); //TODO: this is debug code that I need for now // fill the edge loops that need to be filled, add the result to the list of triangles if (triangleCount > -1 && triangleCount < meshTriangles.Count) { edgeLoops.AddRange(ClipTriangleToBound(meshTriangles[triangleCount], boundsTriangles, vertices, true)); foreach (EdgeLoop loop in edgeLoops) { if (loop.filled) { triangles.AddRange(TriangulateEdgeLoop(loop)); } EdgeLoop nestedLoop = loop.nestedLoop; while (nestedLoop != null) { if (nestedLoop.filled) { triangles.AddRange(TriangulateEdgeLoop(nestedLoop)); } nestedLoop = nestedLoop.nestedLoop; } } } else if (triangleCount < meshTriangles.Count) { foreach (Triangle triangle in meshTriangles) { edgeLoops.AddRange(ClipTriangleToBound(triangle, boundsTriangles, vertices)); } foreach (EdgeLoop loop in edgeLoops) { if (loop.filled) { triangles.AddRange(TriangulateEdgeLoop(loop)); } EdgeLoop nestedLoop = loop.nestedLoop; while (nestedLoop != null) { if (nestedLoop.filled) { triangles.AddRange(TriangulateEdgeLoop(nestedLoop)); } nestedLoop = nestedLoop.nestedLoop; } } } else { return(null); } if (flipNormals) { foreach (Triangle triangle in triangles) { triangle.FlipNormal(); } } Mesh completedMesh = new Mesh(); result.GetComponent <MeshFilter>().mesh = completedMesh; // reindex vertices and add them to the mesh List <Vector3> createdVertices = new List <Vector3>(); // add vertices to mesh for (int i = 0; i < vertices.Count; i++) { vertices[i].index = i; createdVertices.Add(vertices[i].value); } completedMesh.SetVertices(createdVertices); // add triangles to mesh int[] newTriangles = new int[triangles.Count * 3]; int index = 0; foreach (Triangle triangle in triangles) { foreach (Vertex vertex in triangle.vertices) { if (!vertices.Contains(vertex)) { Debug.Log("Vertex " + vertex + " not contained in vertices, :: " + vertex.containedByBound); } } } //Debug.Log(triangles.Count); foreach (Triangle triangle in triangles) { newTriangles[index] = triangle.vertices[0].index; index++; newTriangles[index] = triangle.vertices[1].index; index++; newTriangles[index] = triangle.vertices[2].index; index++; } completedMesh.SetTriangles(newTriangles, 0); completedMesh.RecalculateTangents(); completedMesh.RecalculateNormals(); return(completedMesh); }
private List <EdgeLoop> ClipTriangleToBound(Triangle triangle, List <Triangle> boundsTriangles, List <Vertex> vertices, bool debug = false) { List <Egress> aToBEgresses = new List <Egress>(); List <Egress> bToCEgresses = new List <Egress>(); List <Egress> cToAEgresses = new List <Egress>(); List <Vertex> internalIntersections = new List <Vertex>(); // find all intersections between the triangle and the bound foreach (Triangle boundsTriangle in boundsTriangles) { IntersectTriangleEdge(triangle.vertices[0].value, triangle.vertices[1].value, aToBEgresses, boundsTriangle, debug); IntersectTriangleEdge(triangle.vertices[1].value, triangle.vertices[2].value, bToCEgresses, boundsTriangle, debug); IntersectTriangleEdge(triangle.vertices[2].value, triangle.vertices[0].value, cToAEgresses, boundsTriangle, debug); internalIntersections.AddRange(FindTriangleIntersections(boundsTriangle, triangle)); if (debug) { foreach (Vertex intersection in internalIntersections) { Debug.DrawRay(transform.localToWorldMatrix.MultiplyPoint3x4(intersection.value), Vector3.back * 0.1f, Color.green, Time.deltaTime); } } } if (debug) { foreach (Vertex vertex in triangle.vertices) { Color color = Color.blue; if (vertex.containedByBound) { color = Color.cyan; } Debug.DrawRay(transform.localToWorldMatrix.MultiplyPoint3x4(vertex.value), Vector3.back * 0.1f, color, Time.deltaTime); } } List <Egress> allEgresses = new List <Egress>(); allEgresses.AddRange(aToBEgresses); allEgresses.AddRange(bToCEgresses); allEgresses.AddRange(cToAEgresses); // combine duplicate internal intersections for (int i = internalIntersections.Count - 1; i > 0; i--) { for (int k = i - 1; k >= 0; k--) { if (Vector3.Distance(internalIntersections[i].value, internalIntersections[k].value) < intersectionError) { internalIntersections[k].triangles.AddRange(internalIntersections[i].triangles); internalIntersections.RemoveAt(i); break; } } } // store the resulting intersections in the greater vertex list vertices.AddRange(aToBEgresses); vertices.AddRange(bToCEgresses); vertices.AddRange(cToAEgresses); vertices.AddRange(internalIntersections); // organize the intersections into cuts CreateCuts(allEgresses, internalIntersections); //TODO: soon. deal with floating islands by collecting up loops without egresses // (they won't be a part of any cuts) // organize all triangle vertices and egress intersections in an ordered list around the perimeter List <Vertex> perimeter = new List <Vertex>(); aToBEgresses.Sort((a, b) => (int)Mathf.Sign(Vector3.Distance(a.value, triangle.vertices[0].value) - Vector3.Distance(b.value, triangle.vertices[0].value))); bToCEgresses.Sort((a, b) => (int)Mathf.Sign(Vector3.Distance(a.value, triangle.vertices[1].value) - Vector3.Distance(b.value, triangle.vertices[1].value))); cToAEgresses.Sort((a, b) => (int)Mathf.Sign(Vector3.Distance(a.value, triangle.vertices[2].value) - Vector3.Distance(b.value, triangle.vertices[2].value))); perimeter.Add(triangle.vertices[0]); perimeter.AddRange(aToBEgresses); perimeter.Add(triangle.vertices[1]); perimeter.AddRange(bToCEgresses); perimeter.Add(triangle.vertices[2]); perimeter.AddRange(cToAEgresses); perimeter.Add(triangle.vertices[0]);// a duplicate of the first vertex as a sentinel // find all edge loops and classify whether they should be retopologized or not List <EdgeLoop> loops = new List <EdgeLoop>(); // while there are still entries in that list for which we haven't identified all loops (1 for triangle vertices, 2 for egresses) //Debug.Log("Beginning classification of edge loops"); foreach (Vertex vertex in triangle.vertices) { vertex.loops = new List <EdgeLoop>(); } while (true) { // find the earliest entry whose loops aren't satisfied int currentVertexIndex = 0; //Debug.Log("~~FINDING NEW STARTING POINT~~"); for (currentVertexIndex = 0; currentVertexIndex < perimeter.Count - 2; currentVertexIndex++) { //Debug.Log("Index: " + currentVertexIndex + ", Is Egress = " + (perimeter[currentVertexIndex] is Egress) + ", loop count: " + perimeter[currentVertexIndex].loops.Count); // for egresses: if (perimeter[currentVertexIndex] is Egress) { //Debug.Log(((Egress)perimeter[currentVertexIndex]).cuts.Count); // if this one is unsatisfied, break. We've found the earliest if (perimeter[currentVertexIndex].loops.Count < ((Egress)perimeter[currentVertexIndex]).cuts.Count + 1) { break; } } else // for original vertices { // if this one is unsatisfied, break. We've found the earliest if (perimeter[currentVertexIndex].loops.Count < 1) { break; } } } string parString = "perimeter:"; foreach (Vertex vertex in perimeter) { parString += " " + perimeter.IndexOf(vertex) + ": (" + (vertex is Egress) + ")" + vertex.value; } //Debug.Log(parString); //Debug.Log("finished with: " + currentVertexIndex); //Debug.Log("perimeter size: " + perimeter.Count); // we've satisfied all loops, which is our exit condition if (currentVertexIndex == perimeter.Count - 2) { break; } // now that we've found the beginning of a loop, it's time to find the rest Vertex initialVertex = perimeter[currentVertexIndex]; // determine whether this loop should be filled or not EdgeLoop loop = new EdgeLoop(); // the loop defines either a surface or a hole, and we can determine that by looking at our starting entry // the starting entry can be one of two cases: // 1. an egress which is already part of one loop if (initialVertex is Egress) { // in this case, the new loop is the opposite of whatever the previous loop is loop.filled = !initialVertex.loops[initialVertex.loops.Count - 1].filled; // also, if the initial vertex is an egress, we should add it and move on to the next perimeter value, // so as not to traverse backwards along the cut /*Cut furthestCut = ((Egress)initialVertex).GetFurthestCut(perimeter); * * loop.vertices.Add(perimeter[currentVertexIndex]); * perimeter[currentVertexIndex].loops.Add(loop); * currentVertexIndex++;*/ } else // 2. a vertex of the original triangle { // in this case, if the vertex is contained by the bound, the loop is a surface, otherwise it's a hole loop.filled = initialVertex.containedByBound; } // traverse forward in the ordered list, following each cut, until we reach the initial point // once the inital entry is reached again, we have identified a loop, and can add it to the pile int tooMuch = 0; //Debug.Log("starting new loop"); do { // if the current vertex is an egress tooMuch++; if (tooMuch > 100) { Debug.Log("too long"); throw new System.ApplicationException(); break; } //Debug.Log(currentVertexIndex); Cut furthestCut = null; if (perimeter[currentVertexIndex] is Egress) { furthestCut = ((Egress)perimeter[currentVertexIndex]).GetFurthestCut(perimeter); } if (furthestCut != null) { currentVertexIndex = TraverseCut(furthestCut, loop, perimeter, currentVertexIndex); if (perimeter[currentVertexIndex] == initialVertex)// if we arrived at the initial vertex { loop.vertices.RemoveAt(loop.vertices.Count - 1); break; } // if there's more than one cut on the vertex, we need to potentially while (((Egress)perimeter[currentVertexIndex]).cuts.Count > 1) { //Debug.Log("Double traversal"); // select cut Cut toIgnore = null; foreach (Cut cut in ((Egress)perimeter[currentVertexIndex]).cuts) { if (cut[cut.Count - 1] == furthestCut[0]) { toIgnore = cut; break; } } furthestCut = ((Egress)perimeter[currentVertexIndex]).GetFurthestCut(perimeter, toIgnore, currentVertexIndex, perimeter.IndexOf(initialVertex)); if (furthestCut != null) { currentVertexIndex = TraverseCut(furthestCut, loop, perimeter, currentVertexIndex); } else { //Debug.Log("Couldn't find double traversal"); break; } if (perimeter[currentVertexIndex] == initialVertex)// if we arrived at the initial vertex { loop.vertices.RemoveAt(loop.vertices.Count - 1); goto EndOfDoWhile; } } } else { // add current vertex to loop loop.vertices.Add(perimeter[currentVertexIndex]); perimeter[currentVertexIndex].loops.Add(loop); } // increment the current index by 1 currentVertexIndex++; if (currentVertexIndex >= perimeter.Count) { string listString = ""; foreach (Vertex vertex in loop.vertices) { listString += " " + perimeter.IndexOf(vertex); } Debug.Log("Failed to create loop, (Filled: " + loop.filled + ") :: " + listString); } // once the current vertex loops around to the start, we're done } while (perimeter[currentVertexIndex] != initialVertex); EndOfDoWhile: //Debug.Log("terminated"); loops.Add(loop); } if (debug) { foreach (EdgeLoop loop in loops) { string listString = ""; foreach (Vertex vertex in loop.vertices) { listString += " " + perimeter.IndexOf(vertex); } Debug.Log("Created loop, (Filled: " + loop.filled + ") :: " + listString); } } // now that we've created all loops that intersect the edge of the triangle, we can start on loops that float as islands List <Vertex> unusedVertices = new List <Vertex>(); foreach (Vertex intersection in internalIntersections) { if (!intersection.usedInLoop) { unusedVertices.Add(intersection); } } Debug.Log(unusedVertices.Count); while (unusedVertices.Count > 0) { Vertex currentVertex = unusedVertices[unusedVertices.Count - 1]; unusedVertices.RemoveAt(unusedVertices.Count - 1); DiscoverInternalLoop(currentVertex, unusedVertices, loops); } foreach (EdgeLoop loop in loops) { EdgeLoop nestedLoop = loop.nestedLoop; EdgeLoop previousLoop = loop; while (nestedLoop != null) { nestedLoop.filled = !previousLoop.filled; previousLoop = nestedLoop; nestedLoop = nestedLoop.nestedLoop; } } return(loops); }