예제 #1
0
    // h0 and h1 are halfedges in face.
    // Creates an halfedge between the vertices of h0 and h1
    // And creates a new face.
    private void SplitFace(HEFace face, HalfEdge h0, HalfEdge h1)
    {
        HEFace newFace = new HEFace();

        newFace.normal = face.normal;
        // walk from h0 to h1, this will be the vertices of the new face
        List <short> newFaceEdges = new List <short>();
        HalfEdge     h1TOh0       = new HalfEdge(); // part of face when walking from h0 to h1
        HalfEdge     h0TOh1       = new HalfEdge();

        // Update face halfedge indices
        newFace.heIndex = h0.index;
        // Update old face half edge index to h1
        faces[h1.faceIndex].heIndex = h1.index;
        face.heIndex = h1.index;
        // Set new half edge data
        h1TOh0.index         = (short)halfEdges.Count;
        h0TOh1.index         = (short)(halfEdges.Count + 1);
        h1TOh0.faceIndex     = (short)faces.Count; // part of new face
        h0TOh1.faceIndex     = h1.faceIndex;       // old face
        h1TOh0.verIndex      = h1.verIndex;
        h0TOh1.verIndex      = h0.verIndex;
        h1TOh0.nextIndex     = h0.index;
        h0TOh1.nextIndex     = h1.index;
        h1TOh0.oppositeIndex = h0TOh1.index;
        h0TOh1.oppositeIndex = h1TOh0.index;
        // Update the next of the edges before h0 and h1
        short b4h1 = h0.index; // Bless thy names

        while (halfEdges[b4h1].nextIndex != h1.index)
        {
            b4h1 = halfEdges[b4h1].nextIndex;
        }
        halfEdges[b4h1].nextIndex = h1TOh0.index;
        short b4h0 = h1.index; // Bless thy names

        while (halfEdges[b4h0].nextIndex != h0.index)
        {
            b4h0 = halfEdges[b4h0].nextIndex;
        }
        halfEdges[b4h0].nextIndex = h0TOh1.index;
        // Update the face index of edges in the new face h0-..-..-h1toh0
        h0.faceIndex = (short)faces.Count;
        short next = h0.index;

        // Walk the next index until we return to the first.
        while (next != h1TOh0.index)
        {
            halfEdges[next].faceIndex = (short)faces.Count;
            next = halfEdges[next].nextIndex;
        }
        halfEdges.Add(h1TOh0);
        halfEdges.Add(h0TOh1);
        faces.Add(newFace);
    }
예제 #2
0
    // Return indices of HalfEdges with vertices that have the given config
    List <short> GetConfigEdges(HEFace face, PlaneConfig config)
    {
        List <short> he  = HalfEdge.FaceHalfEdges(face, halfEdges);
        List <short> res = new List <short>();

        foreach (short s in he)
        {
            if (vertices[halfEdges[s].verIndex].config == config)
            {
                res.Add(s);
            }
        }
        return(res);
    }
예제 #3
0
    private string PrintFaceConfig(HEFace face)
    {
        List <short> he   = HalfEdge.FaceHalfEdges(face, halfEdges);
        string       res  = "";
        string       res1 = "face idx: ";

        foreach (short s in he)
        {
            res  += " " + vertices[halfEdges[s].verIndex].config.ToString() + "(" + s + "," + halfEdges[s].nextIndex + ")" + " ";
            res1 += s + ": " + halfEdges[s].faceIndex + ".   ";
        }
        res1 += ".  with face pointing at = " + faces[halfEdges[he[0]].faceIndex].heIndex;
        return(res + "\n" + res1);
    }
예제 #4
0
    private List <short> GetOnVertices(HEFace face)
    {
        List <short> res = new List <short>();
        List <short> he  = HalfEdge.FaceHalfEdges(face, halfEdges);

        foreach (short s in he)
        {
            if (vertices[halfEdges[s].verIndex].config == PlaneConfig.On)
            {
                res.Add(halfEdges[s].verIndex);
            }
        }
        return(res);
    }
예제 #5
0
    private PlaneConfig GetFaceConfig(HEFace face)
    {
        // If any vertex is left / right the whole face is it
        List <short> heIdx = HalfEdge.FaceHalfEdges(face, halfEdges);

        foreach (short s in heIdx)
        {
            if (vertices[halfEdges[s].verIndex].config == PlaneConfig.Left)
            {
                return(PlaneConfig.Left);
            }
            else if (vertices[halfEdges[s].verIndex].config == PlaneConfig.Right)
            {
                return(PlaneConfig.Right);
            }
        }
        return(PlaneConfig.On);
    }
예제 #6
0
    public void CapClipPlane(Vector3 planeNormal, Vector3 newVertCoord)
    {
        // VERY IMPORTANT: Changes the half-edge structure but it is not a complete structure!
        // No oppositeIndices are added. Can be made complete by generating the mesh and then turn the mesh
        // into a half-edge structure. Major hack and major performance loss.


        // Idea:
        // Cap the clip by generating a new vertex at the center of the clipping area.
        // Then take all faces with two ON vertices and create a new face :- )
        // Then do MeshFromHalfedge -> and back again for a good half-edge structure....

        // Calculate new vertex pos
        Vector3 centerVert = new Vector3(0.0f, 0.0f, 0.0f);

        foreach (HEVertex v in vertices)
        {
            if (v.config == PlaneConfig.On)
            {
                centerVert += v.v;
            }
        }
        centerVert = centerVert / (float)vertices.Count;
        HEVertex newVert = new HEVertex
        {
            v      = newVertCoord,
            config = PlaneConfig.On
        };

        vertices.Add(newVert);
        short verIdx = (short)(vertices.Count - 1);

        // Get all faces with two ON vertices
        Dictionary <short, List <short> > onFaces = GetTwoOnFaces();

        foreach (KeyValuePair <short, List <short> > kvp in onFaces)
        {
            // Make new face
            HalfEdge h0    = new HalfEdge();
            HalfEdge h1    = new HalfEdge();
            HalfEdge h2    = new HalfEdge();
            short    heIdx = (short)(halfEdges.Count);
            h0.index     = heIdx;
            h1.index     = (short)(heIdx + 1);
            h2.index     = (short)(heIdx + 2);
            h0.nextIndex = h1.index;
            h1.nextIndex = h2.index;
            h2.nextIndex = h0.index;
            h0.verIndex  = kvp.Value[0];
            h1.verIndex  = verIdx;
            h2.verIndex  = kvp.Value[1];
            // check correct winding order
            Vector3 v1     = (vertices[h1.verIndex].v - vertices[h0.verIndex].v).normalized;
            Vector3 v2     = (vertices[h2.verIndex].v - vertices[h0.verIndex].v).normalized;
            Vector3 c      = Vector3.Cross(v1, v2).normalized;
            Vector3 diff   = c - planeNormal;
            float   diff_m = diff.magnitude;

            // Debug.Log("Cross = " + (Vector3.Cross(v1.normalized, v2.normalized) - planeNormal.normalized).magnitude + ". Normal = " + planeNormal + ". " + (Vector3.Cross(v1.normalized, v2.normalized)));
            if (diff_m >= 0.01f)
            {
                h0.nextIndex = h2.index;
                h1.nextIndex = h0.index;
                h2.nextIndex = h1.index;
            }
            HEFace newFace = new HEFace
            {
                heIndex = h0.index,
                normal  = planeNormal
            };
            short faceIdx = (short)faces.Count;
            h0.faceIndex = faceIdx;
            h1.faceIndex = faceIdx;
            h2.faceIndex = faceIdx;
            halfEdges.Add(h0);
            halfEdges.Add(h1);
            halfEdges.Add(h2);
            faces.Add(newFace);
        }
    }
예제 #7
0
    public void CreateStructureFromMesh(Mesh mesh)
    {
        faces.Clear();
        vertices.Clear();
        halfEdges.Clear();
        int[]     meshTriangles = mesh.triangles;
        Vector3[] meshVertices  = mesh.vertices;
        Vector3[] meshNormals   = mesh.normals;

        // One face per triangle. Add normals
        for (int i = 0; i < meshTriangles.Length; i += 3)
        {
            HEFace f = new HEFace();
            // Take the mean of the triangle normals
            Vector3 n = meshNormals[meshTriangles[i]] + meshNormals[meshTriangles[i + 1]] + meshNormals[meshTriangles[i + 2]];
            n        = n / 3.0f;
            f.normal = n.normalized;
            faces.Add(f);
        }

        // Simplify mesh vertices i.e. remove duplicates.
        // There are duplicate vertices since they have different normals
        // Use a dictionary to store the vertices and their new indices
        Dictionary <Vector3, int> d        = new Dictionary <Vector3, int>();
        List <Vector3>            newVerts = new List <Vector3>();
        List <int> newTris = new List <int>();
        int        newIdx  = 0;

        for (int i = 0; i < meshVertices.Length; i++)
        {
            if (!d.ContainsKey(meshVertices[i]))
            {
                d[meshVertices[i]] = newIdx;
                newVerts.Add(meshVertices[i]);
                newIdx++;
            }
        }
        // Update the triangle indices to the new vertex indices.
        for (int i = 0; i < meshTriangles.Length; i++)
        {
            newTris.Add(d[meshVertices[meshTriangles[i]]]);
        }
        // Set to updated values
        meshVertices  = newVerts.ToArray();
        meshTriangles = newTris.ToArray();

        // Init the vertices with vertex data, but not half-edge
        for (int i = 0; i < meshVertices.Length; i++)
        {
            HEVertex vert = new HEVertex
            {
                v = meshVertices[i]
            };
            vertices.Add(vert);
        }

        // Dictionary for opposite lookup. Build in first loop over triangles, used in the second.
        // key is v0v1
        Dictionary <string, List <short> > edgeMap = new Dictionary <string, List <short> >();

        // Start with creating the halfedges and filling in everything except oppositite
        for (int i = 0; i < meshTriangles.Length; i += 3)
        {
            // Stored 3-by-3 indices. e.g. 0,1,2 forms the first triangle.
            short i0 = (short)i;
            short i1 = (short)(i + 1);
            short i2 = (short)(i + 2);

            // Triangles are in clockwise order in Unity
            HalfEdge h0 = new HalfEdge();
            HalfEdge h1 = new HalfEdge();
            HalfEdge h2 = new HalfEdge();

            h0.index = i0;
            h1.index = i1;
            h2.index = i2;

            h0.faceIndex = (short)(i0 / 3);
            h1.faceIndex = (short)(i0 / 3);
            h2.faceIndex = (short)(i0 / 3);

            h0.verIndex = (short)meshTriangles[i];
            h1.verIndex = (short)meshTriangles[i + 1];
            h2.verIndex = (short)meshTriangles[i + 2];

            // Set the vertices halfedge index. It doesn't matter if we overwrite previously set values.
            vertices[meshTriangles[i]].heIndex     = i0;
            vertices[meshTriangles[i + 1]].heIndex = i1;
            vertices[meshTriangles[i + 2]].heIndex = i2;
            // next entries. Unity wants clockwise order of vertices
            h0.nextIndex = h1.index;
            h1.nextIndex = h2.index;
            h2.nextIndex = h0.index;

            // Add them!
            halfEdges.Add(h0);
            halfEdges.Add(h1);
            halfEdges.Add(h2);

            // set face HalfEdge index. i0 is a multiple of 3
            faces[i0 / 3].heIndex = i0;
            // Fill edgemap, three times :)
            string sum;
            // This check is required so the ordering is consistent
            if (h0.verIndex >= h1.verIndex)
            {
                sum = h0.verIndex.ToString() + h1.verIndex.ToString();
            }
            else
            {
                sum = h1.verIndex.ToString() + h0.verIndex.ToString();
            }
            if (!edgeMap.ContainsKey(sum))
            {
                List <short> l = new List <short>();
                // add the face index
                l.Add(i0);
                edgeMap.Add(sum, l);
            }
            else
            {
                edgeMap[sum].Add(i0);
            }

            if (h1.verIndex >= h2.verIndex)
            {
                sum = h1.verIndex.ToString() + h2.verIndex.ToString();
            }
            else
            {
                sum = h2.verIndex.ToString() + h1.verIndex.ToString();
            }
            if (!edgeMap.ContainsKey(sum))
            {
                List <short> l = new List <short>();
                // add the face index
                l.Add(i1);
                edgeMap.Add(sum, l);
            }
            else
            {
                edgeMap[sum].Add(i1);
            }

            if (h0.verIndex >= h2.verIndex)
            {
                sum = h0.verIndex.ToString() + h2.verIndex.ToString();
            }
            else
            {
                sum = h2.verIndex.ToString() + h0.verIndex.ToString();
            }
            if (!edgeMap.ContainsKey(sum))
            {
                List <short> l = new List <short>();
                // add the face index
                l.Add(i2);
                edgeMap.Add(sum, l);
            }
            else
            {
                edgeMap[sum].Add(i2);
            }
        }

        // Fill the opposite entries
        for (int i = 0; i < meshTriangles.Length; i += 3)
        {
            // each edge is shared between exactly two faces
            // Stored 3-by-3 indices. e.g. 0,1,2 forms the first triangle.
            short    i0 = (short)i;
            short    i1 = (short)(i + 1);
            short    i2 = (short)(i + 2);
            HalfEdge h0 = halfEdges[i0];
            HalfEdge h1 = halfEdges[i1];
            HalfEdge h2 = halfEdges[i2];
            // First edge
            // Use our edgeMap to find opposites
            string sum;
            if (h0.verIndex >= h1.verIndex)
            {
                sum = h0.verIndex.ToString() + h1.verIndex.ToString();
            }
            else
            {
                sum = h1.verIndex.ToString() + h0.verIndex.ToString();
            }
            List <short> l = edgeMap[sum];

            short h0Idx = l[0];
            short h1Idx = l[1];
            halfEdges[h0Idx].oppositeIndex = h1Idx;
            halfEdges[h1Idx].oppositeIndex = h0Idx;

            // Second edge
            if (h1.verIndex >= h2.verIndex)
            {
                sum = h1.verIndex.ToString() + h2.verIndex.ToString();
            }
            else
            {
                sum = h2.verIndex.ToString() + h1.verIndex.ToString();
            }
            l = edgeMap[sum];

            h0Idx = l[0];
            h1Idx = l[1];
            halfEdges[h0Idx].oppositeIndex = h1Idx;
            halfEdges[h1Idx].oppositeIndex = h0Idx;

            // Third edge
            if (h0.verIndex >= h2.verIndex)
            {
                sum = h0.verIndex.ToString() + h2.verIndex.ToString();
            }
            else
            {
                sum = h2.verIndex.ToString() + h0.verIndex.ToString();
            }
            l = edgeMap[sum];

            h0Idx = l[0];
            h1Idx = l[1];
            halfEdges[h0Idx].oppositeIndex = h1Idx;
            halfEdges[h1Idx].oppositeIndex = h0Idx;
        }
    }
예제 #8
0
        /**
         * Analyzes the input mesh and populates the half-edge structure. Can be called as many times you want (for examples if the original mesh is modified).
         */
        public IEnumerator Generate()
        {
            if (input != null){

            heFaces.Clear();
            heVertices.Clear();
            heEdges.Clear();
            _area = 0;
            _modified = false;

            Dictionary<Vector3, HEVertex> vertexBuffer = new Dictionary<Vector3, HEVertex>();
            Dictionary<KeyValuePair<int,int>,HEEdge> edgeBuffer = new Dictionary<KeyValuePair<int,int>,HEEdge>();

            // Get copies of vertex and triangle buffers:
            Vector3[] vertices = input.vertices;
            int[] triangles = input.triangles;

            // first, create vertices:
            for(int i = 0; i < vertices.Length; i++){

                //if the vertex already exists, add physical vertex index to it.
                HEVertex vertex;
                if (vertexBuffer.TryGetValue(vertices[i], out vertex)){
                    vertex.physicalIDs.Add(i);
                }else{
                    vertex = new HEVertex(i);
                }

                vertexBuffer[vertices[i]] = vertex;

                if (i % 200 == 0)
                    yield return new CoroutineJob.ProgressInfo("Half-edge: analyzing vertices...",i/(float)vertices.Length);
            }

            // assign unique indices to vertices:
            int index = 0;
            foreach(KeyValuePair<Vector3,HEVertex> pair in vertexBuffer){
                ((HEVertex)pair.Value).index = index;
                heVertices.Add(pair.Value);
                if (index % 200 == 0)
                    yield return new CoroutineJob.ProgressInfo("Half-edge: assigning indices...",index/(float)vertices.Length);
                index++;
            }

            // build half edge structure:
            for(int i = 0; i<triangles.Length;i+=3){

                Vector3 pos1 = vertices[triangles[i]];
                Vector3 pos2 = vertices[triangles[i+1]];
                Vector3 pos3 = vertices[triangles[i+2]];

                HEVertex v1 = vertexBuffer[pos1];
                HEVertex v2 = vertexBuffer[pos2];
                HEVertex v3 = vertexBuffer[pos3];

                // create half edges:
                HEEdge e1 = new HEEdge();
                e1.index = heEdges.Count;
                e1.indexOnFace = 0;
                e1.faceIndex = heFaces.Count;
                e1.endVertex = v1.index;
                e1.startVertex = v2.index;

                HEEdge e2 = new HEEdge();
                e2.index = heEdges.Count+1;
                e2.indexOnFace = 1;
                e2.faceIndex = heFaces.Count;
                e2.endVertex = v2.index;
                e2.startVertex = v3.index;

                HEEdge e3 = new HEEdge();
                e3.index = heEdges.Count+2;
                e3.indexOnFace = 2;
                e3.faceIndex = heFaces.Count;
                e3.endVertex = v3.index;
                e3.startVertex = v1.index;

                // link half edges together:
                e1.nextEdgeIndex = e3.index;
                e2.nextEdgeIndex = e1.index;
                e3.nextEdgeIndex = e2.index;

                // vertex outgoing half edge indices:
                v1.halfEdgeIndex = e3.index;
                v2.halfEdgeIndex = e1.index;
                v3.halfEdgeIndex = e2.index;

                // add edges:
                heEdges.Add(e1);
                heEdges.Add(e2);
                heEdges.Add(e3);

                // populate and add face:
                HEFace face = new HEFace();
                face.edges[0] = e1.index;
                face.edges[1] = e2.index;
                face.edges[2] = e3.index;
                face.area = ObiUtils.TriangleArea(pos1,pos2,pos3);
                _area += face.area;
                _volume += Vector3.Dot(Vector3.Cross(pos1,pos2),pos3)/6f;
                face.index = heFaces.Count;
                heFaces.Add(face);

                try{
                    edgeBuffer.Add(new KeyValuePair<int,int>(v1.index,v2.index),e1);
                    edgeBuffer.Add(new KeyValuePair<int,int>(v2.index,v3.index),e2);
                    edgeBuffer.Add(new KeyValuePair<int,int>(v3.index,v1.index),e3);
                }catch{
                    Debug.LogError("Your mesh is non manifold, and thus cannot be processed by Obi: more than 1 edge joining the same pair of vertices.");
                    heFaces.Clear();
                    heVertices.Clear();
                    heEdges.Clear();
                    _area = 0;
                    yield break;
                }

                if (i % 500 == 0)
                    yield return new CoroutineJob.ProgressInfo("Half-edge: generating edges and faces...",i/(float)triangles.Length);

            }

            List<HEEdge> borderEdges = new List<HEEdge>();		//edges belonging to a mesh border.

            // stitch half edge pairs together:
            index = 0;
            foreach(KeyValuePair<KeyValuePair<int,int>,HEEdge> pair in edgeBuffer){

                KeyValuePair<int,int> edgeKey = new KeyValuePair<int,int>(pair.Key.Value,pair.Key.Key);

                HEEdge edge = null;
                if (edgeBuffer.TryGetValue(edgeKey, out edge)){
                    ((HEEdge)pair.Value).pair = edge.index;
                }else{

                    //create border edge:
                    HEEdge e = new HEEdge();
                    e.index = heEdges.Count;
                    e.endVertex = ((HEEdge)pair.Value).startVertex;
                    e.startVertex = ((HEEdge)pair.Value).endVertex;
                    heVertices[e.startVertex].halfEdgeIndex = e.index;
                    e.pair = ((HEEdge)pair.Value).index;
                    ((HEEdge)pair.Value).pair = e.index;
                    heEdges.Add(e);

                    borderEdges.Add(e);
                }

                if (index % 1000 == 0)
                    yield return new CoroutineJob.ProgressInfo("Half-edge: stitching half-edges...",index/(float)edgeBuffer.Count);

                index++;

            }

            _closed = borderEdges.Count == 0;

            // link together border edges:
            foreach(HEEdge edge in borderEdges){
                edge.nextEdgeIndex = heVertices[edge.endVertex].halfEdgeIndex;
            }

            }else{
            Debug.LogWarning("Tried to generate adjacency info for an empty mesh.");
            }
        }