public static IGeometryAttributes FlipWindingOrder(this IGeometryAttributes g) => g.VertexAttributes() .Concat(g.NoneAttributes()) .Concat(g.FaceAttributes()) .Concat(g.EdgeAttributes().Select(attr => attr.Remap(g.IndexFlippedRemapping()))) .Concat(g.CornerAttributes().Select(attr => attr.Remap(g.IndexFlippedRemapping()))) .Concat(g.WholeGeometryAttributes()) .FlipNormalAttributes() .ToGeometryAttributes();
/// <summary> /// Low-level remap function. Maps faces and corners at the same time. /// In some cases, this is important (e.g. triangulating quads). /// Note: sub-geometries are lost. /// </summary> public static IGeometryAttributes RemapFacesAndCorners(this IGeometryAttributes g, IArray <int> faceRemap, IArray <int> cornerRemap, int numCornersPerFace = -1) => g.VertexAttributes() .Concat(g.NoneAttributes()) .Concat(g.FaceAttributes().Select(attr => attr.Remap(faceRemap))) .Concat(g.EdgeAttributes().Select(attr => attr.Remap(cornerRemap))) .Concat(g.CornerAttributes().Select(attr => attr.Remap(cornerRemap))) .Concat(g.WholeGeometryAttributes()) .SetFaceSizeAttribute(numCornersPerFace) .ToGeometryAttributes();
/// <summary> /// Updates the vertex buffer (e.g. after identifying unwanted faces) and the index /// buffer. Vertices are either re-ordered, removed, or deleted. Does not affect any other /// </summary> public static IGeometryAttributes RemapVertices(this IGeometryAttributes g, IArray <int> newVertices, IArray <int> newIndices) => (new[] { newIndices.ToIndexAttribute() } .Concat( g.VertexAttributes() .Select(attr => attr.Remap(newVertices))) .Concat(g.NoneAttributes()) .Concat(g.FaceAttributes()) .Concat(g.EdgeAttributes()) .Concat(g.CornerAttributes()) .Concat(g.WholeGeometryAttributes()) ) .ToGeometryAttributes();
/// <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())); }