/// <summary> /// The vertRemap is a list of vertices in the new vertex buffer, and where they came from. /// This could be a reordering of the original vertex buffer, it could even be a repetition. /// It could also be some vertices were deleted, BUT if those vertices are still referenced /// then this will throw an exception. /// The values in the index buffer will change, but it will stay the same length. /// </summary> public static IGeometryAttributes RemapVertices(this IGeometryAttributes g, IArray <int> vertRemap) { var vertLookup = (-1).Repeat(g.NumVertices).ToArray(); for (var i = 0; i < vertRemap.Count; ++i) { var oldVert = vertRemap[i]; vertLookup[oldVert] = i; } var oldIndices = g.GetAttributeIndex()?.Data ?? g.NumVertices.Range(); var newIndices = oldIndices.Select(i => vertLookup[i]).Evaluate(); if (newIndices.Any(x => x == -1)) { throw new Exception("At least one of the indices references a vertex that no longer exists"); } return(g.RemapVertices(vertRemap, newIndices)); }
public static IArray <int> GetAttributeDataIndex(this IGeometryAttributes self) => self.GetAttributeIndex()?.Data;
/// <summary> /// For mesh g, create a new mesh from the passed selected faces, /// discarding un-referenced data and generating new index, vertex & face buffers. /// </summary> public static IGeometryAttributes SelectFaces(this IGeometryAttributes g, IArray <int> faces) { // Early exit, if all selected no need to do anything if (g.NumFaces == faces.Count) { return(g); } // Early exit, if none selected no need to do anything if (faces.Count == 0) { return(null); } // First, get all the indices for this array of faces var oldIndices = g.GetAttributeIndex(); var oldSelIndices = new int[faces.Count * g.NumCornersPerFace]; // var oldIndices = faces.SelectMany(f => f.Indices()); for (int i = 0; i < faces.Count; i++) { for (int t = 0; t < 3; t++) { oldSelIndices[i * 3 + t] = oldIndices.Data[faces[i] * g.NumCornersPerFace + t]; } } // We need to create list of newIndices, and remapping // of oldVertices to newVertices var newIndices = (-1).Repeat(oldSelIndices.Length).ToArray(); // Each index could potentially be in the new mesh var indexLookup = (-1).Repeat(oldIndices.ElementCount).ToArray(); // Build mapping. For each index, if the vertex it has // already been referred to has been mapped, use the // mapped index. Otherwise, remember that we need this vertex var numUsedVertices = 0; // remapping from old vert array => new vert array // Cache built of the old indices of vertices that are used // should be equivalent to indexLookup.Where(i => i != -1) var oldVertices = g.GetAttributePosition(); var usedVertices = (-1).Repeat(oldVertices.ElementCount).ToArray(); for (var i = 0; i < oldSelIndices.Length; ++i) { var oldIndex = oldSelIndices[i]; var newIndex = indexLookup[oldIndex]; if (newIndex < 0) { // remapping from old vert array => new vert array usedVertices[numUsedVertices] = oldIndex; newIndex = indexLookup[oldIndex] = numUsedVertices++; } newIndices[i] = newIndex; } //var faceRemapping = faces.Select(f => f.Index); var vertRemapping = usedVertices.Take(numUsedVertices).ToIArray(); var cornerRemapping = g.FaceIndicesToCornerIndices(faces); return(g.VertexAttributes() .Select(attr => attr.Remap(vertRemapping)) .Concat(g.NoneAttributes()) .Concat(g.FaceAttributes().Select(attr => attr.Remap(faces))) .Concat(g.EdgeAttributes().Select(attr => attr.Remap(cornerRemapping))) .Concat(g.CornerAttributes().Select(attr => attr.Remap(cornerRemapping))) .Concat(g.WholeGeometryAttributes()) .ToGeometryAttributes() .SetAttribute(newIndices.ToIndexAttribute())); }