// Creating a dictionary mapping from new points to their corresponding edge midpoints private static Dictionary <int, List <Vector3> > CreatePointsToEdgeMidpointsMap(CCMeshData mesh) { Dictionary <int, List <Vector3> > pointsToAvgEdgesMap = new Dictionary <int, List <Vector3> >(); foreach (var edge in mesh.edges) { var p1 = (int)edge[0]; var p2 = (int)edge[1]; var avg = (mesh.points[p1] + mesh.points[p2]) / 2; if (!pointsToAvgEdgesMap.ContainsKey(p1)) { pointsToAvgEdgesMap[p1] = new List <Vector3>(); } if (!pointsToAvgEdgesMap.ContainsKey(p2)) { pointsToAvgEdgesMap[p2] = new List <Vector3>(); } pointsToAvgEdgesMap[p1].Add(avg); pointsToAvgEdgesMap[p2].Add(avg); } return(pointsToAvgEdgesMap); }
// Returns a list of new locations of the original points for the given CCMeshData, as described in the CC algorithm public static List <Vector3> GetNewPoints(CCMeshData mesh) { List <Vector3> new_points = new List <Vector3>(mesh.points); Vector3[] edges_avg = new Vector3[mesh.points.Count]; int[] edges_counter = new int[mesh.points.Count]; Vector3[] faces_avg = new Vector3[mesh.points.Count]; int[] faces_counter = new int[mesh.points.Count]; foreach (Vector4 edge in mesh.edges) { Vector3 avg = (mesh.points[(int)edge[0]] + mesh.points[(int)edge[1]]) / 2; edges_avg[(int)edge[0]] += avg; edges_counter[(int)edge[0]]++; edges_avg[(int)edge[1]] += avg; edges_counter[(int)edge[1]]++; } for (int i = 0; i < mesh.faces.Count; ++i) { for (int j = 0; j < 4; ++j) { faces_avg[(int)mesh.faces[i][j]] += mesh.facePoints[i]; faces_counter[(int)mesh.faces[i][j]]++; } } for (int i = 0; i < new_points.Count; ++i) { edges_avg[i] /= edges_counter[i]; faces_avg[i] /= faces_counter[i]; new_points[i] = (faces_avg[i] + 2 * (edges_avg[i]) + ((faces_counter[i] - 3) * mesh.points[i])) / faces_counter[i]; } return(new_points); }
//return the edges that touches a original point at specific face key = (face, point), value = list of edges (2) public static Dictionary <Vector2, List <int> > edges_dict(CCMeshData meshData) { var result = new Dictionary <Vector2, List <int> >(); for (int i = 0; i < meshData.edges.Count; i++) { for (int f_idx = 2; f_idx < 4; f_idx++) { var f = meshData.edges[i][f_idx]; if (f == -1) { continue; } for (int p_idx = 0; p_idx < 2; p_idx++) { var p = meshData.edges[i][p_idx]; var key = new Vector2(f, p); if (!result.ContainsKey(key)) { result[key] = new List <int>(); } result[key].Add(i); } } } return(result); }
// Returns a list of all edges in the mesh defined by given points and faces. // Each edge is represented by Vector4(p1, p2, f1, f2) // p1, p2 are the edge vertices // f1, f2 are faces incident to the edge. If the edge belongs to one face only, f2 is -1 public static List <Vector4> GetEdges(CCMeshData mesh) { var edgeDict = new Dictionary <int[], Vector4>(new EdgeComparer()); // Iterate over faces, add as values to dictionary[current edge] for (var faceIdx = 0; faceIdx < mesh.faces.Count; ++faceIdx) { for (var idx = 0; idx < 4; ++idx) { var currentKey = new int[2] { (int)mesh.faces[faceIdx][idx % 4], (int)mesh.faces[faceIdx][(idx + 1) % 4] }; if (edgeDict.ContainsKey(currentKey)) { edgeDict[currentKey] = new Vector4(edgeDict[currentKey].x, edgeDict[currentKey].y, edgeDict[currentKey].z, faceIdx); } else { edgeDict[currentKey] = new Vector4(currentKey[0], currentKey[1], faceIdx, -1); } } } return(edgeDict.Values.ToList()); }
//return dictionary of the index for the new points public static Dictionary <Vector3, int> getPointDict(CCMeshData meshData, List <Vector3> vertices) { var dict = new Dictionary <Vector3, int>(); var cur_idx = 0; for (int i = 0; i < meshData.facePoints.Count; i++) { vertices.Add(meshData.facePoints[i]); dict[meshData.facePoints[i]] = cur_idx; cur_idx++; } for (int i = 0; i < meshData.edgePoints.Count; i++) { vertices.Add(meshData.edgePoints[i]); dict[meshData.edgePoints[i]] = cur_idx; cur_idx++; } for (int i = 0; i < meshData.newPoints.Count; i++) { vertices.Add(meshData.newPoints[i]); dict[meshData.newPoints[i]] = cur_idx; cur_idx++; } return(dict); }
//Creating the new mesh after the subdivision private static QuadMeshData CreateNewQuadMeshData(CCMeshData mesh) { var vertices = mesh.newPoints.Concat(mesh.edgePoints.Concat(mesh.facePoints)).ToList(); var quads = QuadsFromDict(mesh.pairToEdgeMapping); return(new QuadMeshData(vertices, quads)); }
// Returns a list of "edge points" for the given CCMeshData, as described in the Catmull-Clark algorithm public static List <Vector3> GetEdgePoints(CCMeshData mesh) { mesh.pairToEdgeMapping = new Dictionary <int[], int[]>(new EdgeComparer()); return(mesh.edges.Select((edge, idx) => { var edgesOffset = mesh.points.Count; // offset of edge points in final QuadMeshData vertices var facesOffset = edgesOffset + mesh.edges.Count; // offset of face points in final QuadMeshData vertices var p1 = (int)edge[0]; var p2 = (int)edge[1]; var f1 = (int)edge[2]; var f2 = (int)edge[3]; // Add pairs (f1, p1), (p2, f1) (and if f2 exists (p1, f2), (f2, p2)) and their connecting edge points // (f1, p1) and (f2, p2) added in reverse order from (p2, f1) and (p1, f2) to get a clockwise sequence var pair1 = new int[2] { f1 + facesOffset, p1 }; var pair2 = new int[2] { p2, f1 + facesOffset }; var pairs = new List <int[]>() { pair1, pair2 }; if (f2 != -1) { var pair3 = new int[2] { p1, f2 + facesOffset }; var pair4 = new int[2] { f2 + facesOffset, p2 }; pairs.AddRange(new[] { pair3, pair4 }); } // Map to index of edge point being calculated foreach (var pair in pairs) { mesh.pairToEdgeMapping[pair] = mesh.pairToEdgeMapping.ContainsKey(pair) ? new int[2] { mesh.pairToEdgeMapping[pair][0], idx + edgesOffset } : new int[] { idx + edgesOffset }; } // Calculate edge point var numToDivide = 3; var lastFacePoint = Vector3.zero; if (f2 != -1) { numToDivide = 4; lastFacePoint = mesh.facePoints[f2]; } return (mesh.points[p1] + mesh.points[p2] + mesh.facePoints[f1] + lastFacePoint) / numToDivide; }).ToList()); }
// Returns a list of "edge points" for the given CCMeshData, as described in the Catmull-Clark algorithm public static List <Vector3> GetEdgePoints(CCMeshData mesh) { List <Vector3> edge_points = new List <Vector3>(); foreach (Vector4 edge in mesh.edges) { Vector3 new_point = (mesh.points[(int)edge[0]] + mesh.points[(int)edge[1]] + mesh.facePoints[(int)edge[2]] + mesh.facePoints[(int)edge[3]]) / 4; edge_points.Add(new_point); } return(edge_points); }
// Returns a list of "face points" for the given CCMeshData, as described in the Catmull-Clark algorithm public static List <Vector3> GetFacePoints(CCMeshData mesh) { List <Vector3> face_points = new List <Vector3>(); foreach (Vector4 face in mesh.faces) { Vector3 new_point = (mesh.points[(int)face[0]] + mesh.points[(int)face[1]] + mesh.points[(int)face[2]] + mesh.points[(int)face[3]]) / 4; face_points.Add(new_point); } return(face_points); }
// Returns a QuadMeshData representing the input mesh after one iteration of Catmull-Clark subdivision. public static QuadMeshData Subdivide(QuadMeshData quadMeshData) { // Create and initialize a CCMeshData corresponding to the given QuadMeshData CCMeshData meshData = new CCMeshData(); meshData.points = quadMeshData.vertices; meshData.faces = quadMeshData.quads; meshData.edges = GetEdges(meshData); meshData.facePoints = GetFacePoints(meshData); meshData.edgePoints = GetEdgePoints(meshData); meshData.newPoints = GetNewPoints(meshData); // Combine facePoints, edgePoints and newPoints into a subdivided QuadMeshData return(CreateNewQuadMeshData(meshData)); }
// Returns a list of "face points" for the given CCMeshData, as described in the Catmull-Clark algorithm public static List <Vector3> GetFacePoints(CCMeshData mesh) { List <Vector3> facePoints = new List <Vector3>(); for (int i = 0; i < mesh.faces.Count; i++) { Vector3 facePoint = Vector3.zero; for (int j = 0; j < 4; j++) { facePoint += mesh.points[(int)mesh.faces[i][j]]; } facePoint /= 4; facePoints.Add(facePoint); } return(facePoints); }
// Returns a list of all edges in the mesh defined by given points and faces. // Each edge is represented by Vector4(p1, p2, f1, f2) // p1, p2 are the edge vertices // f1, f2 are faces incident to the edge. If the edge belongs to one face only, f2 is -1 public static List <Vector4> GetEdges(CCMeshData mesh) { Vec2Comparer c = new Vec2Comparer(); Dictionary <Vector2, Vector4> edges = new Dictionary <Vector2, Vector4>(c); for (int i = 0; i < mesh.faces.Count; i++) { for (int j = 0; j < 3; j++) { Vector2 edgeVertices = new Vector2(mesh.faces[i][j], mesh.faces[i][j + 1]); addEdge(edgeVertices, edges, i); } //adding the last edge Vector2 edgeVertices1 = new Vector2(mesh.faces[i][3], mesh.faces[i][0]); addEdge(edgeVertices1, edges, i); } return(edges.Values.ToList()); }
// Returns a list of new locations of the original points for the given CCMeshData, as described in the CC algorithm public static List <Vector3> GetNewPoints(CCMeshData mesh) { var dataPoints = createList(mesh.points.Count); // foreach(var edge in mesh.edges) for (int j = 0; j < mesh.edges.Count; j++) { int p1 = (int)mesh.edges[j][0]; int p2 = (int)mesh.edges[j][1]; var midEdge = (mesh.points[p1] + mesh.points[p2]) / 2; dataPoints[p1].r += midEdge; dataPoints[p2].r += midEdge; for (int i = 2; i < 4; i++) { int face = (int)mesh.edges[j][i]; if (!dataPoints[p1].visited.Contains(face)) { dataPoints[p1].visited.Add(face); dataPoints[p1].f += mesh.facePoints[face]; dataPoints[p1].n++; } if (!dataPoints[p2].visited.Contains(face)) { dataPoints[p2].visited.Add(face); dataPoints[p2].f += mesh.facePoints[face]; dataPoints[p2].n++; } } } var newPoints = new List <Vector3>(); for (int i = 0; i < mesh.points.Count; i++) { var p = dataPoints[i]; var position = (p.f) / p.n + 2 * p.r / p.n + (p.n - 3) * mesh.points[i]; position /= p.n; newPoints.Add(position); } return(newPoints); }
// Returns a list of new locations of the original points for the given CCMeshData, as described in the CC algorithm public static List <Vector3> GetNewPoints(CCMeshData mesh) { var pointsToEdgePointsMap = CreatePointsToEdgeMidpointsMap(mesh); var pointToFacePointMap = CreatePointToFacePointMap(mesh); return(mesh.points.Select((p, i) => { var midPoint = pointsToEdgePointsMap[i]; var facePoint = pointToFacePointMap[i]; var r = Vector3.zero; var f = Vector3.zero; r = midPoint.Aggregate(r, (current, point) => current + point); f = facePoint.Aggregate(f, (current, point) => current + point); var n = midPoint.Count; r /= n; f /= n; return (f + 2 * r + (n - 3) * p) / n; }).ToList()); }
// Creating a dictionary mapping from new points to their corresponding face points private static Dictionary <int, List <Vector3> > CreatePointToFacePointMap(CCMeshData mesh) { Dictionary <int, List <Vector3> > pointToFacePointMap = new Dictionary <int, List <Vector3> >(); for (var i = 0; i < mesh.faces.Count; i++) { var face = mesh.faces[i]; for (var j = 0; j < 4; j++) { var p = (int)face[j]; if (!pointToFacePointMap.ContainsKey(p)) { pointToFacePointMap[p] = new List <Vector3>(); } pointToFacePointMap[p].Add(mesh.facePoints[i]); } } return(pointToFacePointMap); }
// Returns a list of "edge points" for the given CCMeshData, as described in the Catmull-Clark algorithm public static List <Vector3> GetEdgePoints(CCMeshData mesh) { List <Vector3> edgePoints = new List <Vector3>(); for (int i = 0; i < mesh.edges.Count; i++) { Vector3 edgePoint = mesh.points[(int)mesh.edges[i][0]] + mesh.points[(int)mesh.edges[i][1]] + mesh.facePoints[(int)mesh.edges[i][2]]; if (mesh.edges[i][3] != -1) { edgePoint += mesh.facePoints[(int)mesh.edges[i][3]]; edgePoint /= 4; } else { edgePoint /= 3; } edgePoints.Add(edgePoint); } return(edgePoints); }
// Returns a list of all edges in the mesh defined by given points and faces. // Each edge is represented by Vector4(p1, p2, f1, f2) // p1, p2 are the edge vertices // f1, f2 are faces incident to the edge. If the edge belongs to one face only, f2 is -1 public static List <Vector4> GetEdges(CCMeshData mesh) { List <Vector4> edges = new List <Vector4>(); Dictionary <Vector3, Vector4> dict = new Dictionary <Vector3, Vector4>(); for (int i = 0; i < mesh.faces.Count; ++i) { Vector3 place; for (int j = 0; j < 3; ++j) { place = (mesh.points[(int)mesh.faces[i][j]]) + (mesh.points[(int)mesh.faces[i][j + 1]]); if (dict.ContainsKey(place)) { dict[place] = new Vector4(dict[place][0], dict[place][1], dict[place][2], i); } else { dict.Add(place, new Vector4(mesh.faces[i][j], mesh.faces[i][j + 1], i, -1)); } } place = mesh.points[(int)mesh.faces[i][3]] + mesh.points[(int)mesh.faces[i][0]]; if (dict.ContainsKey(place)) { dict[place] = new Vector4(dict[place][0], dict[place][1], dict[place][2], i); } else { dict.Add(place, new Vector4(mesh.faces[i][3], mesh.faces[i][0], i, -1)); } } foreach (Vector4 value in dict.Values) { edges.Add(value); } return(edges); }
// Returns a QuadMeshData representing the input mesh after one iteration of Catmull-Clark subdivision. public static QuadMeshData Subdivide(QuadMeshData quadMeshData) { // Create and initialize a CCMeshData corresponding to the given QuadMeshData CCMeshData meshData = new CCMeshData(); meshData.points = quadMeshData.vertices; meshData.faces = quadMeshData.quads; meshData.edges = GetEdges(meshData); meshData.facePoints = GetFacePoints(meshData); meshData.edgePoints = GetEdgePoints(meshData); meshData.newPoints = GetNewPoints(meshData); // Combine facePoints, edgePoints and newPoints into a subdivided QuadMeshData // Your implementation here... var quads = new List <Vector4>(); var vertices = new List <Vector3>(); var vertToIdx = getPointDict(meshData, vertices); var t = edges_dict(meshData); for (int i = 0; i < meshData.facePoints.Count; i++) { var fp = vertToIdx[meshData.facePoints[i]]; var points = meshData.faces[i]; for (int p = 0; p < 4; p++) { var np = vertToIdx[meshData.newPoints[(int)points[p]]]; var next_point = (int)points[(p + 1) % 4]; var original_point_cur = (int)points[p]; var key1 = new Vector2(i, original_point_cur); var key2 = new Vector2(i, next_point); var cur_original_edges = t[key1]; var next_edges = t[key2]; int right = 0; for (int idx1 = 0; idx1 < cur_original_edges.Count; idx1++) { for (int idx2 = 0; idx2 < next_edges.Count; idx2++) { if (cur_original_edges[idx1] == next_edges[idx2]) { right = idx1; break; } } } var quad = new Vector4(fp, vertToIdx[meshData.edgePoints[(int)cur_original_edges[(right + 1) % 2]]], np, vertToIdx[meshData.edgePoints[(int)cur_original_edges[right % 2]]]); quads.Add(quad); } } return(new QuadMeshData(vertices, quads)); }
// Returns a list of "face points" for the given CCMeshData, as described in the Catmull-Clark algorithm public static List <Vector3> GetFacePoints(CCMeshData mesh) { return(mesh.faces.Select(face => (mesh.points[(int)face[0]] + mesh.points[(int)face[1]] + mesh.points[(int)face[2]] + mesh.points[(int)face[3]]) / 4).ToList()); }
// Returns a QuadMeshData representing the input mesh after one iteration of Catmull-Clark subdivision. public static QuadMeshData Subdivide(QuadMeshData quadMeshData) { // Create and initialize a CCMeshData corresponding to the given QuadMeshData CCMeshData meshData = new CCMeshData(); meshData.points = quadMeshData.vertices; meshData.faces = quadMeshData.quads; meshData.edges = GetEdges(meshData); meshData.facePoints = GetFacePoints(meshData); meshData.edgePoints = GetEdgePoints(meshData); meshData.newPoints = GetNewPoints(meshData); List <List <int> > faceEdges = new List <List <int> >(); for (int i = 0; i < meshData.faces.Count; ++i) { faceEdges.Add(new List <int>()); } for (int i = 0; i < meshData.edges.Count; ++i) { faceEdges[(int)meshData.edges[i][2]].Add(i); faceEdges[(int)meshData.edges[i][3]].Add(i); } List <List <Vector2> > faceEdgePoints = new List <List <Vector2> >(); for (int i = 0; i < faceEdges.Count; ++i) { faceEdgePoints.Add(new List <Vector2>()); for (int j = 0; j < 4; ++j) { if (i == meshData.edges[faceEdges[i][j]][2]) { faceEdgePoints[i].Add(new Vector2(meshData.edges[faceEdges[i][j]][0], meshData.edges[faceEdges[i][j]][1])); } else { faceEdgePoints[i].Add(new Vector2(meshData.edges[faceEdges[i][j]][1], meshData.edges[faceEdges[i][j]][0])); } } } List <Vector3> newPoints = meshData.facePoints.Concat(meshData.edgePoints).Concat(meshData.newPoints).ToList(); int edgePointStart = meshData.facePoints.Count; int newPointStart = edgePointStart + meshData.edgePoints.Count; List <Vector4> newFaces = new List <Vector4>(); for (int i = 0; i < faceEdges.Count; ++i) { for (int j = 0; j < 3; ++j) { for (int k = j + 1; k < 4; ++k) { if (faceEdgePoints[i][j][0] == faceEdgePoints[i][k][1]) { newFaces.Add(new Vector4(i, faceEdges[i][k] + edgePointStart, faceEdgePoints[i][j][0] + newPointStart, faceEdges[i][j] + edgePointStart)); } else if (faceEdgePoints[i][j][1] == faceEdgePoints[i][k][0]) { newFaces.Add(new Vector4(i, faceEdges[i][j] + edgePointStart, faceEdgePoints[i][j][1] + newPointStart, faceEdges[i][k] + edgePointStart)); } } } } return(new QuadMeshData(newPoints, newFaces)); }