/// <summary> /// Splits the mesh into submeshes that use <paramref name="maxVertexCount"/> or less vertices. /// </summary> /// <param name="mesh"></param> /// <param name="maxVertexCount"></param> /// <returns></returns> public static List <Assimp.Mesh> SplitMeshByVertexCount(Assimp.Mesh mesh, int maxVertexCount) { if (mesh.VertexCount <= maxVertexCount) { return new List <Assimp.Mesh> { mesh } } ; var vertexWeights = mesh.HasBones ? mesh.GetVertexWeights() : null; var remainingFaces = mesh.Faces.ToList(); var subMeshes = new List <Assimp.Mesh>(); while (remainingFaces.Count > 0) { // Build submesh var subMesh = new Assimp.Mesh() { MaterialIndex = mesh.MaterialIndex, //MorphMethod = mesh.MorphMethod, Name = mesh.Name + $"_submesh{subMeshes.Count}", PrimitiveType = mesh.PrimitiveType, }; var processedFaces = new List <Face>(); var vertexCache = new List <Vertex>(); // Get faces that fit inside the new mesh foreach (var face in remainingFaces) { var newVertices = new List <Vertex>(); var newFace = new Face(); foreach (var i in face.Indices) { var cacheIndex = FindVertexCacheIndex(mesh, i, vertexWeights, vertexCache, out var vertex); if (cacheIndex == -1) { // This vertex is new cacheIndex = vertexCache.Count + newVertices.Count; newVertices.Add(vertex); } newFace.Indices.Add(cacheIndex); } if (vertexCache.Count + newVertices.Count > maxVertexCount) { // Doesn't fit continue; } // It does fit \(^o^)/ subMesh.Faces.Add(newFace); processedFaces.Add(face); foreach (var vertex in newVertices) { vertexCache.Add(vertex); } Debug.Assert(vertexCache.Count <= maxVertexCount); if (vertexCache.Count == maxVertexCount) { // We're done for sure break; } } // Remove the faces we processed from the remaining list foreach (var face in processedFaces) { remainingFaces.Remove(face); } PopulateSubMeshVertexData(mesh, subMesh, vertexCache); #if DEBUG CheckIfAllVerticesHaveWeights(subMesh); #endif subMeshes.Add(subMesh); } return(subMeshes); }
/// <summary> /// Splits the mesh into submeshes that use <paramref name="maxBoneCount"/> or less bones. /// </summary> /// <param name="scene"></param> /// <param name="mesh"></param> /// <param name="maxBoneCount"></param> /// <returns></returns> public static List <Assimp.Mesh> SplitMeshByBoneCount(Assimp.Mesh mesh, int maxBoneCount) { if (mesh.BoneCount <= maxBoneCount) { return new List <Assimp.Mesh> { mesh } } ; var vertexWeights = mesh.GetVertexWeights(); var subMeshes = new List <Assimp.Mesh>(); var remainingFaces = mesh.Faces.ToList(); while (remainingFaces.Count > 0) { var usedBones = new HashSet <Assimp.Bone>(); var faces = new List <Face>(); // Get faces that fit inside the new mesh foreach (var face in remainingFaces) { var faceUsedBones = face.Indices.SelectMany(y => vertexWeights[y].Select(z => z.Item1)).ToList(); var faceUniqueUsedBoneCount = faceUsedBones.Count(x => !usedBones.Contains(x)); if ((usedBones.Count + faceUniqueUsedBoneCount) > maxBoneCount) { // Skip continue; } // It does fit \(^o^)/ faces.Add(face); foreach (var node in faceUsedBones) { usedBones.Add(node); } Debug.Assert(usedBones.Count <= maxBoneCount); } if (faces.Count == 0) { if (remainingFaces.All(x => x.Indices.SelectMany(y => vertexWeights[y].Select(z => z.Item1)).Count() > maxBoneCount)) { // Need to reduce weights per face Debug.Assert(false); // would need averaging.. } } // Remove the faces we claimed from the pool foreach (var face in faces) { remainingFaces.Remove(face); } // Build submesh var subMesh = new Assimp.Mesh() { MaterialIndex = mesh.MaterialIndex, //MorphMethod = mesh.MorphMethod, Name = mesh.Name + $"_submesh{subMeshes.Count}", PrimitiveType = mesh.PrimitiveType, }; var vertexCache = new List <Vertex>(); foreach (var face in faces) { var newFace = new Face(); foreach (var index in face.Indices) { var cacheIndex = FindVertexCacheIndex(mesh, index, vertexWeights, vertexCache, out var vertex); if (cacheIndex == -1) { // This vertex is new #if DEBUG for (int i = 0; i < vertex.Weights.Count; i++) { Debug.Assert(usedBones.Contains(vertex.Weights[i].Item1)); } #endif cacheIndex = vertexCache.Count; vertexCache.Add(vertex); } newFace.Indices.Add(cacheIndex); } subMesh.Faces.Add(newFace); } PopulateSubMeshVertexData(mesh, subMesh, vertexCache); #if DEBUG CheckIfAllVerticesHaveWeights(subMesh); #endif subMeshes.Add(subMesh); } return(subMeshes); }