private static List <Mesh> CreateMeshesFromAiNode(Ai.Node aiNode, Ai.Scene aiScene, Object obj, ref AxisAlignedBoundingBox objectAABB, ObjectSet objectSet, string texturesDirectoryPath) { var aiMeshes = aiNode.MeshIndices.Select(x => aiScene.Meshes[x]).Where(x => x.PrimitiveType == Ai.PrimitiveType.Triangle) .OrderBy(x => x.VertexCount).ThenBy(x => x.FaceCount).ToList(); if (aiMeshes.Count == 0) { return(null); } var meshes = new List <Mesh>(); var transform = GetWorldTransform(aiNode); int subIndex = 0; for (int index = 0; index < aiMeshes.Count;) { int begin = index; int end = index + 1; int vertexCount = aiMeshes[index].VertexCount; // Collect meshes till we hit the vertex count limit. while (end < aiMeshes.Count) { if (vertexCount + aiMeshes[end].VertexCount > 32768) { break; } vertexCount += aiMeshes[end++].VertexCount; } var mesh = new Mesh { Name = aiNode.Name }; if (index != 0) { mesh.Name += "." + (++subIndex).ToString("D3"); } var aabbMesh = new AxisAlignedBoundingBox(); int vertexOffset = 0; for (int it = begin; it < end; it++) // we goin C++ { var aiMesh = aiMeshes[it]; var aabbSubMesh = new AxisAlignedBoundingBox(); if (aiMesh.HasVertices) { if (mesh.Positions == null) { mesh.Positions = new Vector3[vertexCount]; } for (int i = 0; i < aiMesh.VertexCount; i++) { var position = Vector3.Transform(aiMesh.Vertices[i].ToNumerics(), transform); mesh.Positions[vertexOffset + i] = position; aabbSubMesh.AddPoint(position); } } if (aiMesh.HasNormals) { if (mesh.Normals == null) { mesh.Normals = new Vector3[vertexCount]; } for (int i = 0; i < aiMesh.VertexCount; i++) { mesh.Normals[vertexOffset + i] = Vector3.Normalize(Vector3.TransformNormal(aiMesh.Normals[i].ToNumerics(), transform)); } } for (int i = 0; i < 4; i++) { if (!aiMesh.HasTextureCoords(i)) { continue; } var texCoords = mesh.GetTexCoordsChannel(i); if (texCoords == null) { texCoords = new Vector2[vertexCount]; mesh.SetTexCoordsChannel(i, texCoords); } for (int j = 0; j < aiMesh.VertexCount; j++) { var texCoord = new Vector2( aiMesh.TextureCoordinateChannels[i][j].X, aiMesh.TextureCoordinateChannels[i][j].Y); texCoords[vertexOffset + j] = texCoord; } } for (int i = 0; i < 2; i++) { if (!aiMesh.HasVertexColors(i)) { continue; } var colors = mesh.GetColorsChannel(i); if (colors == null) { colors = new Color[vertexCount]; for (int j = 0; j < vertexCount; j++) { colors[j] = Color.White; } mesh.SetColorsChannel(i, colors); } for (int j = 0; j < aiMesh.VertexCount; j++) { colors[vertexOffset + j] = aiMesh.VertexColorChannels[i][j].ToMML(); } } var subMesh = new SubMesh(); if (aiMesh.HasBones) { if (mesh.BoneWeights == null) { mesh.BoneWeights = new BoneWeight[vertexCount]; for (int i = 0; i < mesh.BoneWeights.Length; i++) { mesh.BoneWeights[i] = BoneWeight.Empty; } } subMesh.BoneIndices = new ushort[aiMesh.BoneCount]; for (int i = 0; i < aiMesh.BoneCount; i++) { var aiBone = aiMesh.Bones[i]; int boneIndex = obj.Skin.Bones.FindIndex( x => x.Name == aiBone.Name); if (boneIndex == -1) { boneIndex = obj.Skin.Bones.Count; var aiBoneNode = aiScene.RootNode.FindNode(aiBone.Name); // This is not right, but I'm not sure how to transform the bind pose matrix // while not having duplicate bones. Matrix4x4.Invert(GetWorldTransform(aiBoneNode), out var inverseBindPoseMatrix); obj.Skin.Bones.Add(new BoneInfo { Name = aiBoneNode.Name, InverseBindPoseMatrix = inverseBindPoseMatrix }); } foreach (var boneWeight in aiBone.VertexWeights) { mesh.BoneWeights[vertexOffset + boneWeight.VertexID].AddWeight(i, boneWeight.Weight); } subMesh.BoneIndices[i] = ( ushort )boneIndex; } subMesh.BonesPerVertex = 4; } subMesh.Indices = aiMesh.Faces.Where(x => x.IndexCount == 3).SelectMany(x => x.Indices) .Select(x => ( uint )(vertexOffset + x)).ToArray(); subMesh.PrimitiveType = PrimitiveType.Triangles; subMesh.IndexFormat = IndexFormat.UInt16; var aiMaterial = aiScene.Materials[aiMesh.MaterialIndex]; int materialIndex = obj.Materials.FindIndex(x => x.Name == aiMaterial.Name); if (materialIndex == -1) { materialIndex = obj.Materials.Count; obj.Materials.Add(CreateMaterialFromAiMaterial(aiMaterial, objectSet.TextureSet, texturesDirectoryPath)); } subMesh.MaterialIndex = ( uint )materialIndex; subMesh.BoundingSphere = aabbSubMesh.ToBoundingSphere(); subMesh.BoundingBox = aabbSubMesh.ToBoundingBox(); mesh.SubMeshes.Add(subMesh); vertexOffset += aiMesh.VertexCount; aabbMesh.Merge(aabbSubMesh); } if (mesh.BoneWeights != null) { for (int i = 0; i < mesh.BoneWeights.Length; i++) { mesh.BoneWeights[i].Validate(); } } mesh.GenerateTangents(); mesh.BoundingSphere = aabbMesh.ToBoundingSphere(); meshes.Add(mesh); objectAABB.Merge(aabbMesh); // Go to the next mesh index = end; } return(meshes); }