public static int GetEdgePointKey(int vi, int vj, MeshStruct meshStruct) { int qi = meshStruct.FindQuad(vi, vj); int qj = meshStruct.FindQuad(vj, vi); if (qi > qj) // quad index may be -1 (if hole) { return((qi << 3) | (meshStruct.GetIndexInQuad(qi, vi) << 1)); } else { return((qj << 3) | (meshStruct.GetIndexInQuad(qj, vj) << 1)); } }
public MeshData Subdivide() { // based on: // http://www.rorydriscoll.com/2008/08/01/catmull-clark-subdivision-the-basics/ // https://rosettacode.org/wiki/Catmull%E2%80%93Clark_subdivision_surface var newVertices = new Utils.OrderedDictionary <int, Vertex>(); // reserve indices for new control-points (we need no special unique keys for them) for (int vi = 0; vi < vertices.Length; ++vi) { newVertices.items.Add(default(Vertex)); } // face-point for (int qi = 0; qi < meshStruct.quads.Length; ++qi) { int[] q = meshStruct.quads[qi]; Vertex facePoint = Average(q.Select(vi => vertices[vi]).ToArray()); int facePointKey = GetFacePointKey(qi); newVertices.Add(facePointKey, facePoint); } // edge-point var edgeMidPoints = new Dictionary <int, Vertex>(); for (int qi = 0; qi < meshStruct.quads.Length; ++qi) { int[] q = meshStruct.quads[qi]; foreach (int vi in q) { int vj = meshStruct.GetNextInQuad(qi, vi); int edgePointKey = GetEdgePointKey(vi, vj, meshStruct); if (!newVertices.HasKey(edgePointKey)) { // edge-midpoint (for control-point calculation) Vertex midPoint = Average(new[] { vertices[vi], vertices[vj], }); edgeMidPoints[edgePointKey] = midPoint; // edge-point int qj = meshStruct.FindQuad(vj, vi); if (qj == -1) // for the edges that are on the border of a hole, the edge point is just the middle of the edge. { newVertices.Add(edgePointKey, midPoint); } else { Vertex edgePoint = Average(new[] { vertices[vi], vertices[vj], newVertices.GetItem(GetFacePointKey(qi)), newVertices.GetItem(GetFacePointKey(qj)), }); newVertices.Add(edgePointKey, edgePoint); } } } } // control-point for (int vi = 0; vi < vertices.Length; ++vi) { int[] vjs = meshStruct.GetAllNeighbors(vi); // check hole edges int[] hvjs = vjs.Where(vj => meshStruct.FindQuad(vi, vj) == -1 || meshStruct.FindQuad(vj, vi) == -1 ).ToArray(); if (hvjs.Any()) // hole edge vertices // edge-midpoints { Vertex edgeMidAverage = Average(hvjs.Select(vj => edgeMidPoints[GetEdgePointKey(vi, vj, meshStruct)] ).ToArray()); // new control-point Vertex controlPoint = Average(new[] { edgeMidAverage, vertices[vi] }); newVertices.items[vi] = controlPoint; // set control-point to reserved index } else { // edge-midpoints Vertex edgeMidAverage = Average(vjs.Select(vj => edgeMidPoints[GetEdgePointKey(vi, vj, meshStruct)] ).ToArray()); // face-points Vertex faceAverage = Average(meshStruct.vertexQuads[vi].Select(qi => newVertices.GetItem(GetFacePointKey(qi)) ).ToArray()); // new control-point Vertex controlPoint = Average(new[] { faceAverage, edgeMidAverage, vertices[vi] }, new[] { 1f, 2f, vjs.Length - 3f }); newVertices.items[vi] = controlPoint; // set control-point to reserved index } } // new quads // eis[i] // q[i].----.----.q[i+1] // | | // eis[i+3]. fi. . // | | // .____.____. int ql = meshStruct.quads.Length; var newQuads = new int[ql * 4][]; for (int qi = 0; qi < ql; ++qi) { int[] q = meshStruct.quads[qi]; // int fi = newVertices.indices[GetFacePointKey(qi)]; // face-point index int[] eis = new int[4]; // edge-point indices for (int i = 0; i < 4; ++i) { eis[i] = newVertices.indices[GetEdgePointKey(q[i], q[(i + 1) % 4], meshStruct)]; } for (int i = 0; i < 4; ++i) { int[] qq = new int[4]; // shift indices (+ i) to keep quad orientation qq[(0 + i) % 4] = q[i]; qq[(1 + i) % 4] = eis[i]; qq[(2 + i) % 4] = fi; qq[(3 + i) % 4] = eis[(i + 3) % 4]; //newQuads[qi * 4 + i] = qq; // less fragmentized quads newQuads[ql * i + qi] = qq; // more fragmentized, but we keep "first quad" (i == 0) indices in quads array } } MeshData md = new MeshData(newVertices.items.ToArray(), newQuads, vertexContent); return(md); }