//Help method to build a triangle and add it to a mesh
        //v1-v2-v3 should be sorted clock-wise
        //v1-v2 should be the cut edge (if we have a cut edge), and we know this triangle has a cut edge if newEdges != null
        private static void AddTriangleToMesh(MyMeshVertex v1, MyMeshVertex v2, MyMeshVertex v3, HalfEdgeData3 mesh, HashSet <HalfEdge3> newEdges)
        {
            //Create three new vertices
            HalfEdgeVertex3 half_v1 = new HalfEdgeVertex3(v1.position, v1.normal);
            HalfEdgeVertex3 half_v2 = new HalfEdgeVertex3(v2.position, v2.normal);
            HalfEdgeVertex3 half_v3 = new HalfEdgeVertex3(v3.position, v3.normal);

            //Create three new half-edges that points TO these vertices
            HalfEdge3 e_to_v1 = new HalfEdge3(half_v1);
            HalfEdge3 e_to_v2 = new HalfEdge3(half_v2);
            HalfEdge3 e_to_v3 = new HalfEdge3(half_v3);

            //Create the face (which is a triangle) which needs a reference to one of the edges
            HalfEdgeFace3 f = new HalfEdgeFace3(e_to_v1);


            //Connect the data:

            //Connect the edges clock-wise
            e_to_v1.nextEdge = e_to_v2;
            e_to_v2.nextEdge = e_to_v3;
            e_to_v3.nextEdge = e_to_v1;

            e_to_v1.prevEdge = e_to_v3;
            e_to_v2.prevEdge = e_to_v1;
            e_to_v3.prevEdge = e_to_v2;

            //Each vertex needs a reference to an edge going FROM that vertex
            half_v1.edge = e_to_v2;
            half_v2.edge = e_to_v3;
            half_v3.edge = e_to_v1;

            //Each edge needs a reference to the face
            e_to_v1.face = f;
            e_to_v2.face = f;
            e_to_v3.face = f;

            //Each edge needs an opposite edge
            //This is slow process but we need it to be able to split meshes which are not connected
            //You could do this afterwards when all triangles have been generate, but Im not sure which is the fastest...

            //Save the data
            mesh.verts.Add(half_v1);
            mesh.verts.Add(half_v2);
            mesh.verts.Add(half_v3);

            mesh.edges.Add(e_to_v1);
            mesh.edges.Add(e_to_v2);
            mesh.edges.Add(e_to_v3);

            mesh.faces.Add(f);


            //Save the new edge
            if (newEdges != null)
            {
                //We know the knew edge goes from v1 to v2, so we should save the half-edge that points to v2
                newEdges.Add(e_to_v2);
            }
        }
        //Convert to Unity mesh (if we know we have stored triangles in the data structure)
        //shareVertices means that we want a smooth surface where some vertices are shared between triangles
        public Mesh ConvertToUnityMesh(string name, bool shareVertices, bool generateNormals)
        {
            MyMesh myMesh = new MyMesh();

            //Loop through each triangle
            foreach (HalfEdgeFace3 f in faces)
            {
                //These should have been stored clock-wise
                HalfEdgeVertex3 v1 = f.edge.v;
                HalfEdgeVertex3 v2 = f.edge.nextEdge.v;
                HalfEdgeVertex3 v3 = f.edge.nextEdge.nextEdge.v;

                //Standardize
                MyMeshVertex my_v1 = new MyMeshVertex(v1.position, v1.normal);
                MyMeshVertex my_v2 = new MyMeshVertex(v2.position, v2.normal);
                MyMeshVertex my_v3 = new MyMeshVertex(v3.position, v3.normal);

                myMesh.AddTriangle(my_v1, my_v2, my_v3, shareVertices: true);
            }


            Mesh unityMesh = myMesh.ConvertToUnityMesh(name);

            return(unityMesh);
        }
        //HashSet<HalfEdgeFace3>
        public HashSet <HalfEdgeFace3> UnNormalize(HashSet <HalfEdgeFace3> data)
        {
            foreach (HalfEdgeFace3 f in data)
            {
                //TODO: This will generate a new list for each face, so maybe better to put the code from the method here
                List <HalfEdge3> edges = f.GetEdges();

                if (edges == null)
                {
                    continue;
                }

                foreach (HalfEdge3 e in edges)
                {
                    HalfEdgeVertex3 v = e.v;

                    v.position = UnNormalize(v.position);
                }
            }

            return(data);
        }
예제 #4
0
        //We have just the faces (which we know are triangles)
        public static MyMesh ConvertToMyMesh(string meshName, HashSet <HalfEdgeFace3> faces, MyMesh.MeshStyle meshStyle)
        {
            MyMesh myMesh = new MyMesh(meshName);

            //Loop through each triangle
            foreach (HalfEdgeFace3 f in faces)
            {
                //These should have been stored clock-wise
                HalfEdgeVertex3 v1 = f.edge.v;
                HalfEdgeVertex3 v2 = f.edge.nextEdge.v;
                HalfEdgeVertex3 v3 = f.edge.nextEdge.nextEdge.v;

                //Standardize
                MyMeshVertex my_v1 = new MyMeshVertex(v1.position, v1.normal);
                MyMeshVertex my_v2 = new MyMeshVertex(v2.position, v2.normal);
                MyMeshVertex my_v3 = new MyMeshVertex(v3.position, v3.normal);

                myMesh.AddTriangle(my_v1, my_v2, my_v3, meshStyle);
            }

            return(myMesh);
        }
 public HalfEdge3(HalfEdgeVertex3 v)
 {
     this.v = v;
 }
        //Remove flat tetrahedrons (a vertex in a triangle)
        private static bool RemoveFlatTetrahedrons(HalfEdgeData3 meshData, Normalizer3 normalizer = null)
        {
            HashSet <HalfEdgeVertex3> vertices = meshData.verts;

            bool foundFlatTetrahedron = false;

            foreach (HalfEdgeVertex3 vertex in vertices)
            {
                HashSet <HalfEdge3> edgesGoingToVertex = vertex.GetEdgesPointingToVertex(meshData);

                if (edgesGoingToVertex.Count == 3)
                {
                    //Find the vertices of the triangle covering this vertex clock-wise
                    HalfEdgeVertex3 v1 = vertex.edge.v;
                    HalfEdgeVertex3 v2 = vertex.edge.prevEdge.oppositeEdge.v;
                    HalfEdgeVertex3 v3 = vertex.edge.oppositeEdge.nextEdge.v;

                    //Build a plane
                    MyVector3 normal = MyVector3.Normalize(MyVector3.Cross(v3.position - v2.position, v1.position - v2.position));

                    Plane3 plane = new Plane3(v1.position, normal);

                    //Find the distance from the vertex to the plane
                    float distance = _Geometry.GetSignedDistanceFromPointToPlane(vertex.position, plane);

                    distance = Mathf.Abs(distance);

                    if (distance < FLAT_TETRAHEDRON_DISTANCE)
                    {
                        //Debug.Log("Found flat tetrahedron");

                        Vector3 p1 = normalizer.UnNormalize(v1.position).ToVector3();
                        Vector3 p2 = normalizer.UnNormalize(v2.position).ToVector3();
                        Vector3 p3 = normalizer.UnNormalize(v3.position).ToVector3();

                        TestAlgorithmsHelpMethods.DebugDrawTriangle(p1, p2, p3, normal.ToVector3(), Color.blue, Color.red);

                        foundFlatTetrahedron = true;

                        //Save the opposite edges
                        HashSet <HalfEdge3> oppositeEdges = new HashSet <HalfEdge3>();

                        oppositeEdges.Add(v1.edge.oppositeEdge);
                        oppositeEdges.Add(v2.edge.oppositeEdge);
                        oppositeEdges.Add(v3.edge.oppositeEdge);

                        //Remove the three triangles
                        foreach (HalfEdge3 e in edgesGoingToVertex)
                        {
                            meshData.DeleteFace(e.face);
                        }

                        //Add the new triangle (could maybe connect it ourselves)
                        HalfEdgeFace3 newTriangle = meshData.AddTriangle(v1.position, v2.position, v3.position, findOppositeEdge: false);

                        meshData.TryFindOppositeEdge(newTriangle.edge, oppositeEdges);
                        meshData.TryFindOppositeEdge(newTriangle.edge.nextEdge, oppositeEdges);
                        meshData.TryFindOppositeEdge(newTriangle.edge.nextEdge.nextEdge, oppositeEdges);

                        break;
                    }
                }
            }

            return(foundFlatTetrahedron);
        }
예제 #7
0
        //
        // Contract an edge if we know we are dealing only with triangles
        //

        //Returns all edge pointing to the new vertex
        public HashSet <HalfEdge3> ContractTriangleHalfEdge(HalfEdge3 e, Vector3 mergePos, System.Diagnostics.Stopwatch timer = null)
        {
            //Step 1. Get all edges pointing to the vertices we will merge
            //And edge is going TO a vertex, so this edge goes from v1 to v2
            HalfEdgeVertex3 v1 = e.prevEdge.v;
            HalfEdgeVertex3 v2 = e.v;

            //timer.Start();
            //It's better to get these before we remove triangles because then we will get a messed up half-edge system?
            HashSet <HalfEdge3> edgesGoingToVertex_v1 = v1.GetEdgesPointingToVertex(this);
            HashSet <HalfEdge3> edgesGoingToVertex_v2 = v2.GetEdgesPointingToVertex(this);

            //timer.Stop();

            //Step 2. Remove the triangles, which will create a hole,
            //and the edges on the opposite sides of the hole are connected
            RemoveTriangleAndConnectOppositeSides(e);

            //We might also have an opposite triangle, so we may have to delete a total of two triangles
            if (e.oppositeEdge != null)
            {
                RemoveTriangleAndConnectOppositeSides(e.oppositeEdge);
            }


            //Step 3. Move the vertices to the merge position
            //Some of these edges belong to the triangles we removed, but it doesnt matter because this operation is fast

            //We can at the same time find the edges pointing to the new vertex
            HashSet <HalfEdge3> edgesPointingToVertex = new HashSet <HalfEdge3>();

            if (edgesGoingToVertex_v1 != null)
            {
                foreach (HalfEdge3 edgeToV in edgesGoingToVertex_v1)
                {
                    //This edge belonged to one of the faces we removed
                    if (edgeToV.face == null)
                    {
                        continue;
                    }

                    edgeToV.v.position = mergePos;

                    edgesPointingToVertex.Add(edgeToV);
                }
            }
            if (edgesGoingToVertex_v2 != null)
            {
                foreach (HalfEdge3 edgeToV in edgesGoingToVertex_v2)
                {
                    //This edge belonged to one of the faces we removed
                    if (edgeToV.face == null)
                    {
                        continue;
                    }

                    edgeToV.v.position = mergePos;

                    edgesPointingToVertex.Add(edgeToV);
                }
            }


            return(edgesPointingToVertex);
        }
예제 #8
0
        //v1-v2-v3 should be clock-wise which is Unity standard
        public HalfEdgeFace3 AddTriangle(MyMeshVertex v1, MyMeshVertex v2, MyMeshVertex v3, bool findOppositeEdge = false)
        {
            //Create three new vertices
            HalfEdgeVertex3 half_v1 = new HalfEdgeVertex3(v1.position, v1.normal);
            HalfEdgeVertex3 half_v2 = new HalfEdgeVertex3(v2.position, v2.normal);
            HalfEdgeVertex3 half_v3 = new HalfEdgeVertex3(v3.position, v3.normal);

            //Create three new half-edges that points TO these vertices
            HalfEdge3 e_to_v1 = new HalfEdge3(half_v1);
            HalfEdge3 e_to_v2 = new HalfEdge3(half_v2);
            HalfEdge3 e_to_v3 = new HalfEdge3(half_v3);

            //Create the face (which is a triangle) which needs a reference to one of the edges
            HalfEdgeFace3 f = new HalfEdgeFace3(e_to_v1);


            //Connect the data:

            //Connect the edges clock-wise
            e_to_v1.nextEdge = e_to_v2;
            e_to_v2.nextEdge = e_to_v3;
            e_to_v3.nextEdge = e_to_v1;

            e_to_v1.prevEdge = e_to_v3;
            e_to_v2.prevEdge = e_to_v1;
            e_to_v3.prevEdge = e_to_v2;

            //Each vertex needs a reference to an edge going FROM that vertex
            half_v1.edge = e_to_v2;
            half_v2.edge = e_to_v3;
            half_v3.edge = e_to_v1;

            //Each edge needs a reference to the face
            e_to_v1.face = f;
            e_to_v2.face = f;
            e_to_v3.face = f;

            //Each edge needs an opposite edge
            //This is slow process
            //You could do this afterwards when all triangles have been generate
            //Doing it in this method takes 2.7 seconds for the bunny
            //Doing it afterwards takes 0.1 seconds by using the fast method and 1.6 seconds for the slow method
            //The reason is that we keep searching the list for an opposite which doesnt exist yet, so we get more searches even though
            //the list is shorter as we build up the mesh
            //But you could maybe do it here if you just add a new triangle?
            if (findOppositeEdge)
            {
                TryFindOppositeEdge(e_to_v1);
                TryFindOppositeEdge(e_to_v2);
                TryFindOppositeEdge(e_to_v3);
            }


            //Save the data
            this.verts.Add(half_v1);
            this.verts.Add(half_v2);
            this.verts.Add(half_v3);

            this.edges.Add(e_to_v1);
            this.edges.Add(e_to_v2);
            this.edges.Add(e_to_v3);

            this.faces.Add(f);

            return(f);
        }