public void AddVertices(Vertex position, params VertexKeyPair[] vertexPairs) // Add a single position and several corresponding vertices { int pi = mesh.AddPosition(position); foreach (VertexKeyPair p in vertexPairs) { Vertex v = p.vertex; v.posIndex = pi; int vi = mesh.AddVertex(v, addPosition: false); vertexIndices[p.key] = vi; } }
public static MeshX Subdivide(MeshX mesh, Options options) { if (!mesh.helpersInited) { mesh.InitHelpers(); } if (!mesh.normalPerPosition) { mesh.MakeNormalPerPosition(); } MeshX newMesh = new MeshX { name = mesh.name + "/s", content = mesh.content, normalPerPosition = true, }; newMesh.StartBuilding(); CheckForVertexKeys(mesh); var newVertices = new MeshVertices { mesh = newMesh }; var edgeMidPositions = new Dictionary <VertexKey, Vertex>(); // position & normal // reserve indices for new control-points: they keep indices and we need no special keys for them. // later for control-points we set position & normal only: tangent & uv stay the same. for (int pi = 0; pi < mesh.positions.Count; ++pi) { newMesh.AddPosition(default(Vertex)); } for (int vi = 0; vi < mesh.vertexCount; ++vi) { Vertex v = mesh.GetVertex(vi, maskVertex); newMesh.AddVertex(v, addPosition: false); } // add face-points // "for each face, a face point is created which is the average of all the points of the face." foreach (Face f in mesh.IterAllFaces()) { Vertex[] vs = mesh.GetVertices(mesh.GetFaceVertexIndices(f)); Vertex v = mesh.Average(vs); VertexKey keyF = GetFacePointKey(f); newVertices.AddVertices(v, Pair(v, keyF)); } // add edge-points foreach (Edge e in mesh.IterAllEdges()) { EdgeType type = mesh.GetEdgeType(e); if (type == EdgeType.back) { continue; } if (type == EdgeType.boundary) { // "for the edges that are on the border of a hole, the edge point is just the middle of the edge." Vertex midE = mesh.Average( mesh.GetVertices(mesh.GetEdgeVertexIndices(e)) ); VertexKey keyE = GetEdgePointKey(e); edgeMidPositions[keyE] = midE; newVertices.AddVertices(midE, Pair(midE, keyE)); } else if (type == EdgeType.solid) { // "for each edge, an edge point is created which is the average between the center of the edge // and the center of the segment made with the face points of the two adjacent faces." Edge n = mesh.GetNeighbor(e); Vertex midE = mesh.Average( mesh.GetVertices(mesh.GetEdgeVertexIndices(e)) ); VertexKey keyE = GetEdgePointKey(e); edgeMidPositions[keyE] = midE; Vertex v = mesh.Average( new[] { midE, newVertices.GetVertex(GetFacePointKey(e.face)), newVertices.GetVertex(GetFacePointKey(n.face)), }, weights: new[] { 2f, 1f, 1f } ); newVertices.AddVertices(v, Pair(v, keyE)); } else // seam edge { Edge n = mesh.GetNeighbor(e); Vertex midE = mesh.Average( mesh.GetVertices(mesh.GetEdgeVertexIndices(e)) ); Vertex midN = mesh.Average( mesh.GetVertices(mesh.GetEdgeVertexIndices(n)), maskVertex // pos & normal already got in midE ); VertexKey keyE = GetEdgePointKey(e); VertexKey keyN = GetEdgePointKey(n); edgeMidPositions[keyE] = midE; Vertex p = mesh.Average( // pos & normal only new[] { midE, newVertices.GetVertex(GetFacePointKey(e.face), maskPosition), newVertices.GetVertex(GetFacePointKey(n.face), maskPosition), }, maskPosition, new[] { 2f, 1f, 1f } ); newVertices.AddVertices(p, Pair(midE, keyE), Pair(midN, keyN)); } } // move control-points for (int pi = 0; pi < mesh.positions.Count; ++pi) { // count edges var edges = new List <Edge>(); // edges outcoming from the position var boundaries = new List <Edge>(); var front = new List <Edge>(); foreach (int vi in mesh.positionVertices[pi]) { foreach (Edge e in mesh.vertexEdges[vi]) { edges.Add(e); foreach (Edge edge in new[] { e, mesh.GetNextInFace(e, -1) }) { EdgeType type = mesh.GetEdgeType(edge); if (type == EdgeType.boundary) { boundaries.Add(edge); } else if (type != EdgeType.back) { front.Add(edge); } } } } Debug.AssertFormat(boundaries.Count > 0 || (edges.Count == front.Count), "Counting edges error: boundaries: {0}, edges {1}, front: {2}", boundaries.Count, edges.Count, front.Count); Vertex controlPoint; if (boundaries.Count > 0) { bool isCorner = edges.Count == 1; if (options.boundaryInterpolation == Options.BoundaryInterpolation.fixBoundaries || options.boundaryInterpolation == Options.BoundaryInterpolation.fixCorners && isCorner) { controlPoint = mesh.GetPosition(pi); // keep same position } else { // "for the vertex points that are on the border of a hole, the new coordinates are calculated as follows: // 1. in all the edges the point belongs to, only take in account the middles of the edges that are on the border of the hole // 2. calculate the average between these points (on the hole boundary) and the old coordinates (also on the hole boundary)." Vertex[] vs = new Vertex[boundaries.Count + 1]; vs[0] = mesh.GetPosition(pi); for (int e = 0; e < boundaries.Count; ++e) { vs[e + 1] = edgeMidPositions[GetEdgePointKey(boundaries[e])]; } controlPoint = mesh.Average(vs, maskPosition); } } else { // "for each vertex point, its coordinates are updated from (new_coords): // the old coordinates (P), // the average of the face points of the faces the point belongs to (F), // the average of the centers of edges the point belongs to (R), // how many faces a point belongs to (n), then use this formula: // (F + 2R + (n-3)P) / n" // edge-midpoints Vertex[] ms = new Vertex[front.Count]; for (int e = 0; e < front.Count; ++e) { ms[e] = edgeMidPositions[GetEdgePointKey(front[e])]; } Vertex edgeMidAverage = mesh.Average(ms, maskPosition); // face-points Vertex[] fs = new Vertex[edges.Count]; for (int e = 0; e < edges.Count; ++e) { fs[e] = newVertices.GetVertex(GetFacePointKey(edges[e].face), maskPosition); } Vertex faceAverage = mesh.Average(fs, maskPosition); // new control-point controlPoint = mesh.Average( new[] { faceAverage, edgeMidAverage, mesh.GetPosition(pi) }, maskPosition, new[] { 1f, 2f, edges.Count - 3f } ); } // set moved control-point position to reserved index newMesh.SetPosition(pi, controlPoint); } // add 4 new quads per face // eis[i] // fis[i].----.----.fis[i+1] // | | // eis[i-1]. ci. . // | | // .____.____. newMesh.submeshes = new Submesh[mesh.submeshes.Length]; for (int si = 0; si < mesh.submeshes.Length; ++si) { int[][] faces = mesh.submeshes[si].faces; // get new face count int faceCount = 0; foreach (int[] face in faces) { faceCount += face.Length; } newMesh.submeshes[si].faces = new int[faceCount][]; // fill faces int faceIndex = 0; for (int fi = 0; fi < faces.Length; ++fi) { int[] fis = faces[fi]; int edgeCount = fis.Length; // 3 or 4 // Face f = new Face { submesh = si, index = fi }; int ci = newVertices.vertexIndices[GetFacePointKey(f)]; // face-point index // int[] eis = new int[edgeCount]; // edge-point indices for (int i = 0; i < edgeCount; ++i) { Edge e = new Edge { face = f, index = i }; // get neighbor for solid back edges if (mesh.GetEdgeType(e) == EdgeType.back) //!!! here should be some EdgeType.backOfSeam or EdgeType.backOfSolid { Edge n = mesh.GetNeighbor(e); if (mesh.GetEdgeType(n) == EdgeType.solid) { e = n; } } eis[i] = newVertices.vertexIndices[GetEdgePointKey(e)]; } // for (int i = 0; i < edgeCount; ++i) { int[] q = new int[4]; // new faces are always quads int s = edgeCount == 4 ? i : 0; // shift indices (+ i) to keep quad orientation q[(0 + s) % 4] = fis[i]; q[(1 + s) % 4] = eis[i]; q[(2 + s) % 4] = ci; q[(3 + s) % 4] = eis[(i - 1 + edgeCount) % edgeCount]; newMesh.submeshes[si].faces[faceIndex++] = q; } } } newMesh.FinishBuilding(); return(newMesh); }