private IEnumerator GenerateHull(HashSet <MyVector3> points, HalfEdgeData3 convexHull) { //PAUSE FOR VISUALIZATION //Display what we have so far controller.DisplayMeshMain(convexHull.faces); controller.HideAllVisiblePoints(convexHull.verts); yield return(new WaitForSeconds(10f)); //Add all other points one-by-one List <MyVector3> pointsToAdd = new List <MyVector3>(points); foreach (MyVector3 p in pointsToAdd) { //Is this point within the tetrahedron bool isWithinHull = _Intersections.PointWithinConvexHull(p, convexHull); if (isWithinHull) { points.Remove(p); controller.HideVisiblePoint(p); continue; } //PAUSE FOR VISUALIZATION //Display active point controller.DisplayActivePoint(p); //Rotate camera to this point //Important to turn this vector to 2d Vector3 unity_pos = controller.normalizer.UnNormalize(p).ToVector3(); controller.cameraScript.SetWantedHeight(unity_pos.y); unity_pos.y = 0f; controller.cameraScript.SetWantedDirection((Vector3.zero - unity_pos).normalized); yield return(new WaitForSeconds(2f)); //Find visible triangles and edges on the border between the visible and invisible triangles HashSet <HalfEdgeFace3> visibleTriangles = null; HashSet <HalfEdge3> borderEdges = null; IterativeHullAlgorithm3D.FindVisibleTrianglesAndBorderEdgesFromPoint(p, convexHull, out visibleTriangles, out borderEdges); //Remove all visible triangles foreach (HalfEdgeFace3 triangle in visibleTriangles) { convexHull.DeleteFace(triangle); } //PAUSE FOR VISUALIZATION //For visualization purposes we now need to create two meshes and then remove the triangles again controller.DisplayMeshMain(convexHull.faces); controller.DisplayMeshOther(visibleTriangles); controller.HideAllVisiblePoints(convexHull.verts); yield return(new WaitForSeconds(2f)); //PAUSE FOR VISUALIZATION //Remove all now visible triangles that forms the hole List <HalfEdgeFace3> visibleTrianglesList = new List <HalfEdgeFace3>(visibleTriangles); for (int i = 0; i < visibleTrianglesList.Count; i++) { visibleTriangles.Remove(visibleTrianglesList[i]); controller.DisplayMeshOther(visibleTriangles); yield return(new WaitForSeconds(0.5f)); } //Save all ned edges so we can connect them with an opposite edge //To make it faster you can use the ideas in the Valve paper to get a sorted list of newEdges HashSet <HalfEdge3> newEdges = new HashSet <HalfEdge3>(); foreach (HalfEdge3 borderEdge in borderEdges) { //Each edge is point TO a vertex MyVector3 p1 = borderEdge.prevEdge.v.position; MyVector3 p2 = borderEdge.v.position; //The border edge belongs to a triangle which is invisible //Because triangles are oriented clockwise, we have to add the vertices in the other direction //to build a new triangle with the point HalfEdgeFace3 newTriangle = convexHull.AddTriangle(p2, p1, p); //PAUSE FOR VISUALIZATION controller.DisplayMeshMain(convexHull.faces); yield return(new WaitForSeconds(0.5f)); //Connect the new triangle with the opposite edge on the border //When we create the face we give it a reference edge which goes to p2 //So the edge we want to connect is the next edge HalfEdge3 edgeToConnect = newTriangle.edge.nextEdge; edgeToConnect.oppositeEdge = borderEdge; borderEdge.oppositeEdge = edgeToConnect; //Two edges are still not connected, so save those HalfEdge3 e1 = newTriangle.edge; //HalfEdge3 e2 = newTriangle.edge.nextEdge; HalfEdge3 e3 = newTriangle.edge.nextEdge.nextEdge; newEdges.Add(e1); //newEdges.Add(e2); newEdges.Add(e3); } //Two edges in each triangle are still not connected with an opposite edge foreach (HalfEdge3 e in newEdges) { if (e.oppositeEdge != null) { continue; } convexHull.TryFindOppositeEdge(e, newEdges); } //PAUSE FOR VISUALIZATION //controller.DisplayMeshMain(convexHull.faces); //yield return new WaitForSeconds(2f); controller.HideVisiblePoint(p); } controller.HideActivePoint(); controller.cameraScript.SetWantedDirection(Vector3.zero); controller.cameraScript.SetWantedHeight(0f); //controller.DisplayMeshMain(convexHull.faces); //yield return new WaitForSeconds(5f); yield return(null); }
private IEnumerator QEMLoop(HalfEdgeData3 halfEdgeMeshData, Heap <QEM_Edge> sorted_QEM_edges, Dictionary <MyVector3, Matrix4x4> qMatrices, Dictionary <HalfEdge3, QEM_Edge> halfEdge_QEM_Lookup, int maxEdgesToContract, float maxError, bool normalizeTriangles = false) { //PAUSE FOR VISUALIZATION //Display what we have so far controller.DisplayMeshMain(halfEdgeMeshData.faces); controller.displayStuffUI.text = "Triangles: " + halfEdgeMeshData.faces.Count.ToString(); yield return(new WaitForSeconds(5f)); // // Start contracting edges // //For each edge we want to remove for (int i = 0; i < maxEdgesToContract; i++) { //Check that we can simplify the mesh //The smallest mesh we can have is a tetrahedron with 4 faces, itherwise we get a flat triangle if (halfEdgeMeshData.faces.Count <= 4) { Debug.Log($"Cant contract more than {i} edges"); break; } // // Remove the pair (v1,v2) of the least cost and contract the pair // //timer.Start(); QEM_Edge smallestErrorEdge = sorted_QEM_edges.RemoveFirst(); //This means an edge in this face has already been contracted //We are never removing edges from the heap after contracting and edges, //so we do it this way for now, which is maybe better? if (smallestErrorEdge.halfEdge.face == null) { //This edge wasn't contracted so don't add it to iteration i -= 1; continue; } if (smallestErrorEdge.qem > maxError) { Debug.Log($"Cant contract more than {i} edges because reached max error"); break; } //timer.Stop(); //timer.Start(); //Get the half-edge we want to contract HalfEdge3 edgeToContract = smallestErrorEdge.halfEdge; //Need to save the endpoints so we can remove the old Q matrices from the pos-matrix lookup table Edge3 contractedEdgeEndpoints = new Edge3(edgeToContract.prevEdge.v.position, edgeToContract.v.position); //Contract edge HashSet <HalfEdge3> edgesPointingToNewVertex = halfEdgeMeshData.ContractTriangleHalfEdge(edgeToContract, smallestErrorEdge.mergePosition); //timer.Stop(); // // Remove all QEM_edges that belonged to the faces we contracted // //This is not needed if we check if an edge in the triangle has already been contracted /* * //timer.Start(); * * //This edge doesnt exist anymore, so remove it from the lookup * halfEdge_QEM_Lookup.Remove(edgeToContract); * * //Remove the two edges that were a part of the triangle of the edge we contracted * RemoveHalfEdgeFromQEMEdges(edgeToContract.nextEdge, QEM_edges, halfEdge_QEM_Lookup); * RemoveHalfEdgeFromQEMEdges(edgeToContract.nextEdge.nextEdge, QEM_edges, halfEdge_QEM_Lookup); * * //Remove the three edges belonging to the triangle on the opposite side of the edge we contracted * //If there was an opposite side... * if (edgeToContract.oppositeEdge != null) * { * HalfEdge3 oppositeEdge = edgeToContract.oppositeEdge; * * RemoveHalfEdgeFromQEMEdges(oppositeEdge, QEM_edges, halfEdge_QEM_Lookup); * RemoveHalfEdgeFromQEMEdges(oppositeEdge.nextEdge, QEM_edges, halfEdge_QEM_Lookup); * RemoveHalfEdgeFromQEMEdges(oppositeEdge.nextEdge.nextEdge, QEM_edges, halfEdge_QEM_Lookup); * } * //timer.Stop(); */ //Remove the edges start and end vertices from the pos-matrix lookup table qMatrices.Remove(contractedEdgeEndpoints.p1); qMatrices.Remove(contractedEdgeEndpoints.p2); //timer.Stop(); // // Update all QEM_edges that is now connected with the new contracted vertex because their errors have changed // //The contracted position has a new Q matrix Matrix4x4 QNew = MeshSimplification_QEM.CalculateQMatrix(edgesPointingToNewVertex, normalizeTriangles); //Add the Q matrix to the pos-matrix lookup table qMatrices.Add(smallestErrorEdge.mergePosition, QNew); //Update the error of the QEM_edges of the edges that pointed to and from one of the two old Q matrices //Those edges are the same edges that points to the new vertex and goes from the new vertex //timer.Start(); foreach (HalfEdge3 edgeToV in edgesPointingToNewVertex) { //The edge going from the new vertex is the next edge of the edge going to the vertex HalfEdge3 edgeFromV = edgeToV.nextEdge; //To QEM_Edge QEM_edgeToV = halfEdge_QEM_Lookup[edgeToV]; Edge3 edgeToV_endPoints = QEM_edgeToV.GetEdgeEndPoints(); Matrix4x4 Q1_edgeToV = qMatrices[edgeToV_endPoints.p1]; Matrix4x4 Q2_edgeToV = QNew; QEM_edgeToV.UpdateEdge(edgeToV, Q1_edgeToV, Q2_edgeToV); sorted_QEM_edges.UpdateItem(QEM_edgeToV); //From QEM_Edge QEM_edgeFromV = halfEdge_QEM_Lookup[edgeFromV]; Edge3 edgeFromV_endPoints = QEM_edgeFromV.GetEdgeEndPoints(); Matrix4x4 Q1_edgeFromV = QNew; Matrix4x4 Q2_edgeFromV = qMatrices[edgeFromV_endPoints.p2]; QEM_edgeFromV.UpdateEdge(edgeFromV, Q1_edgeFromV, Q2_edgeFromV); sorted_QEM_edges.UpdateItem(QEM_edgeFromV); } //timer.Stop(); //PAUSE FOR VISUALIZATION //Display what we have so far controller.DisplayMeshMain(halfEdgeMeshData.faces); controller.displayStuffUI.text = "Triangles: " + halfEdgeMeshData.faces.Count.ToString(); yield return(new WaitForSeconds(0.02f)); } }