// 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); }
// 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); }
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); }
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); }
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); }
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); } }
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; } }
/** * 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."); } }