/// <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())); }
/// <summary> /// Leaves the vertex buffer intact and creates a new geometry that remaps all of the group, corner, and face data. /// The newFaces array is a list of indices into the old face array. /// Note: sub-geometries are lost. /// </summary> public static IGeometryAttributes RemapFaces(this IGeometryAttributes g, IArray <int> faceRemap) => g.RemapFacesAndCorners(faceRemap, g.FaceIndicesToCornerIndices(faceRemap));