private Ai.Node ConvertNode(Scene scene, Node node, Ai.Node aiParent) { var aiNode = new Ai.Node(AssimpConverterCommon.EscapeName(node.Name), aiParent) { Transform = new Ai.Matrix4x4(node.LocalTransform.M11, node.LocalTransform.M21, node.LocalTransform.M31, node.LocalTransform.M41, node.LocalTransform.M12, node.LocalTransform.M22, node.LocalTransform.M32, node.LocalTransform.M42, node.LocalTransform.M13, node.LocalTransform.M23, node.LocalTransform.M33, node.LocalTransform.M43, node.LocalTransform.M14, node.LocalTransform.M24, node.LocalTransform.M34, node.LocalTransform.M44) }; if (node.HasProperties) { ConvertNodeProperties(node.Properties, aiNode); } if (node.HasAttachments) { ConvertNodeAttachments(scene, node, aiNode); } if (node.HasChildren) { foreach (var childNode in node.Children) { aiNode.Children.Add(ConvertNode(scene, childNode, aiNode)); } } return(aiNode); }
private static void ProcessAssimpNodeMeshesRecursively(Ai.Node aiNode, Ai.Scene aiScene, Dictionary <string, NodeInfo> nodeLookup, ref int nextBoneIndex, Dictionary <int, List <int> > nodeToBoneIndices, List <Matrix4x4> boneInverseBindMatrices, List <Vector3> transformedVertices, SceneConverterOptions options) { if (aiNode.HasMeshes) { var nodeInfo = nodeLookup[AssimpConverterCommon.UnescapeName(aiNode.Name)]; var node = nodeInfo.Node; var nodeWorldTransform = node.WorldTransform; Matrix4x4.Invert(nodeWorldTransform, out var nodeInverseWorldTransform); foreach (var aiMeshIndex in aiNode.MeshIndices) { var aiMesh = aiScene.Meshes[aiMeshIndex]; var aiMaterial = aiScene.Materials[aiMesh.MaterialIndex]; var geometry = ConvertAssimpMeshToGeometry(aiMesh, aiMaterial, nodeLookup, ref nextBoneIndex, nodeToBoneIndices, boneInverseBindMatrices, ref nodeWorldTransform, ref nodeInverseWorldTransform, transformedVertices, options); if (!nodeInfo.IsMeshAttachment) { node.Attachments.Add(new NodeGeometryAttachment(geometry)); } else { node.Parent.Attachments.Add(new NodeGeometryAttachment(geometry)); node.Parent.RemoveChildNode(node); } } } foreach (var aiNodeChild in aiNode.Children) { ProcessAssimpNodeMeshesRecursively(aiNodeChild, aiScene, nodeLookup, ref nextBoneIndex, nodeToBoneIndices, boneInverseBindMatrices, transformedVertices, options); } }
private static Node ConvertAssimpNodeRecursively(Ai.Node aiNode, Dictionary <string, NodeInfo> nodeLookup, ref int nextIndex) { aiNode.Transform.Decompose(out var scale, out var rotation, out var translation); // Create node var node = new Node(AssimpConverterCommon.UnescapeName(aiNode.Name), new Vector3(translation.X, translation.Y, translation.Z), new Quaternion(rotation.X, rotation.Y, rotation.Z, rotation.W), new Vector3(scale.X, scale.Y, scale.Z)); if (!IsMeshAttachmentNode(aiNode)) { // Convert properties ConvertAssimpMetadataToProperties(aiNode.Metadata, node); if (!nodeLookup.ContainsKey(node.Name)) { // Add to lookup nodeLookup.Add(node.Name, new NodeInfo(aiNode, node, nextIndex++, false)); } else { throw new Exception($"Duplicate node name '{node.Name}'"); } // Process children foreach (var aiNodeChild in aiNode.Children) { if (aiNodeChild.Name == "RootNode") { // For compatibility with old exports // Merge children of 'RootNode' node with actual root node foreach (var aiFakeRootNodeChild in aiNodeChild.Children) { var childNode = ConvertAssimpNodeRecursively(aiFakeRootNodeChild, nodeLookup, ref nextIndex); node.AddChildNode(childNode); } } else { var childNode = ConvertAssimpNodeRecursively(aiNodeChild, nodeLookup, ref nextIndex); node.AddChildNode(childNode); } } } else { nodeLookup.Add(node.Name, new NodeInfo(aiNode, node, -1, true)); } return(node); }
private void ConvertTextures(TextureDictionary textureDictionary) { Directory.CreateDirectory(mTextureBaseDirectoryPath); foreach (var texture in textureDictionary.Textures) { var texturePath = Path.Combine(mTextureBaseDirectoryPath, AssimpConverterCommon.EscapeName(texture.Name)); File.WriteAllBytes(texturePath, texture.Data); } }
private Ai.Material ConvertMaterial(Material material) { var aiMaterial = new Ai.Material { Name = AssimpConverterCommon.EscapeName(material.Name), ColorAmbient = new Ai.Color4D(material.Ambient.X, material.Ambient.Y, material.Ambient.Z, material.Ambient.W), ColorDiffuse = new Ai.Color4D(material.Diffuse.X, material.Diffuse.Y, material.Diffuse.Z, material.Diffuse.W), ColorSpecular = new Ai.Color4D(material.Specular.X, material.Specular.Y, material.Specular.Z, material.Specular.W), ColorEmissive = new Ai.Color4D(material.Emissive.X, material.Emissive.Y, material.Emissive.Z, material.Emissive.W) }; if (material.Flags.HasFlag(MaterialFlags.HasDiffuseMap)) { aiMaterial.TextureDiffuse = new Ai.TextureSlot( Path.Combine(mTextureBaseRelativeDirectoryPath, AssimpConverterCommon.EscapeName(material.DiffuseMap.Name)), Ai.TextureType.Diffuse, 0, Ai.TextureMapping.FromUV, 0, 0, Ai.TextureOperation.Add, Ai.TextureWrapMode.Wrap, Ai.TextureWrapMode.Wrap, 0); } if (material.Flags.HasFlag(MaterialFlags.HasNormalMap)) { aiMaterial.TextureNormal = new Ai.TextureSlot( Path.Combine(mTextureBaseRelativeDirectoryPath, AssimpConverterCommon.EscapeName(material.NormalMap.Name)), Ai.TextureType.Normals, 1, Ai.TextureMapping.FromUV, 0, 0, Ai.TextureOperation.Add, Ai.TextureWrapMode.Wrap, Ai.TextureWrapMode.Wrap, 0); } if (material.Flags.HasFlag(MaterialFlags.HasSpecularMap)) { aiMaterial.TextureSpecular = new Ai.TextureSlot( Path.Combine(mTextureBaseRelativeDirectoryPath, AssimpConverterCommon.EscapeName(material.SpecularMap.Name)), Ai.TextureType.Specular, 2, Ai.TextureMapping.FromUV, 0, 0, Ai.TextureOperation.Add, Ai.TextureWrapMode.Wrap, Ai.TextureWrapMode.Wrap, 0); } if (material.Flags.HasFlag(MaterialFlags.HasReflectionMap)) { aiMaterial.TextureReflection = new Ai.TextureSlot( Path.Combine(mTextureBaseRelativeDirectoryPath, AssimpConverterCommon.EscapeName(material.ReflectionMap.Name)), Ai.TextureType.Reflection, 3, Ai.TextureMapping.FromUV, 0, 0, Ai.TextureOperation.Add, Ai.TextureWrapMode.Wrap, Ai.TextureWrapMode.Wrap, 0); } // todo: add more textures return(aiMaterial); }
private void ConvertNodeAttachments(Scene scene, Node node, Ai.Node aiNode) { for (int i = 0; i < node.Attachments.Count; i++) { var attachment = node.Attachments[i]; switch (attachment.Type) { case NodeAttachmentType.Geometry: { var mesh = ConvertGeometry(scene, node, attachment.GetValue <Geometry>()); mesh.Name = $"{AssimpConverterCommon.EscapeName(node.Name)}_Attachment{i}_Geometry"; aiNode.MeshIndices.Add(mAiScene.Meshes.Count); mAiScene.Meshes.Add(mesh); } break; default: //throw new NotImplementedException(); break; } } }
private static TextureInfo ConvertTexture(Ai.TextureSlot aiTextureSlot, string baseDirectoryPath) { var relativeFilePath = aiTextureSlot.FilePath; var fullFilePath = Path.GetFullPath(Path.Combine(baseDirectoryPath, relativeFilePath)); var textureName = AssimpConverterCommon.UnescapeName(Path.GetFileNameWithoutExtension(relativeFilePath) + ".dds"); Texture texture; if (!File.Exists(fullFilePath)) { texture = Texture.CreateDefaultTexture(textureName); } else if (relativeFilePath.EndsWith(".dds", StringComparison.InvariantCultureIgnoreCase)) { texture = new Texture(textureName, TextureFormat.DDS, File.ReadAllBytes(fullFilePath)); } else { var bitmap = new Bitmap(fullFilePath); texture = TextureEncoder.Encode(textureName, TextureFormat.DDS, bitmap); } return(TextureInfo.GetTextureInfo(texture)); }
public static Animation ConvertFromAssimpScene(Ai.Scene aiScene, Ai.Animation aiAnimation, AnimationConverterOptions options) { var animation = new Animation(options.Version); animation.Duration = ConvertTime(aiAnimation.DurationInTicks, aiAnimation.TicksPerSecond); foreach (var aiChannel in aiAnimation.NodeAnimationChannels) { if (AssimpConverterCommon.MeshAttachmentNameRegex.IsMatch(aiChannel.NodeName)) { continue; } var nodeName = AssimpConverterCommon.UnescapeName(aiChannel.NodeName); Ai.Node node = aiScene.RootNode.FindNode(nodeName); if (node == null) { continue; } var controller = new AnimationController(options.Version) { TargetKind = TargetKind.Node, TargetName = nodeName, TargetId = GetTargetIdForNode(aiScene.RootNode, nodeName) }; var layer = new AnimationLayer(options.Version); // NodePRS only for now layer.KeyType = KeyType.NodePRS; // Fetch the unique key frame timings from all position, rotation and scale keys. var aiKeyTimings = aiChannel.PositionKeys .Select(x => x.Time) .Concat(aiChannel.RotationKeys.Select(x => x.Time)) .Concat(aiChannel.ScalingKeys.Select(x => x.Time)) .Distinct() .OrderBy(x => x) .ToList(); // Decompose the local transform of the affected node so we can use them as the base values for our keyframes node.Transform.Decompose(out var nodeBaseScale, out var nodeBaseRotation, out var nodeBaseTranslation); // Keep track of the last position, rotation and scale used to ensure that interpolation works properly var lastPosition = nodeBaseTranslation; var lastRotation = nodeBaseRotation; var lastScale = nodeBaseScale; for (var i = 0; i < aiKeyTimings.Count; i++) { var aiTime = aiKeyTimings[i]; // Start building the keyframe var key = new PRSKey(layer.KeyType) { Position = new Vector3(lastPosition.X, lastPosition.Y, lastPosition.Z), Rotation = new Quaternion(lastRotation.X, lastRotation.Y, lastRotation.Z, lastRotation.W), Scale = new Vector3(lastScale.X, lastScale.Y, lastScale.Z) }; // Fetch the Assimp keys for this time var aiPositionKey = aiChannel.PositionKeys.SingleOrDefault(x => x.Time == aiTime); var aiRotationKey = aiChannel.RotationKeys.SingleOrDefault(x => x.Time == aiTime); var aiScaleKey = aiChannel.ScalingKeys.SingleOrDefault(x => x.Time == aiTime); if (aiPositionKey != default) { key.Position = new Vector3(aiPositionKey.Value.X, aiPositionKey.Value.Y, aiPositionKey.Value.Z); lastPosition = aiPositionKey.Value; } if (aiRotationKey != default) { key.Rotation = new Quaternion(aiRotationKey.Value.X, aiRotationKey.Value.Y, aiRotationKey.Value.Z, aiRotationKey.Value.W); lastRotation = aiRotationKey.Value; } if (aiScaleKey != default) { key.Scale = new Vector3(aiScaleKey.Value.X, aiScaleKey.Value.Y, aiScaleKey.Value.Z); lastScale = aiScaleKey.Value; } key.Time = ConvertTime(aiTime, aiAnimation.TicksPerSecond); layer.Keys.Add(key); } controller.Layers.Add(layer); animation.Controllers.Add(controller); } return(animation); }
private Ai.Mesh ConvertGeometry(Scene scene, Node geometryNode, Geometry geometry) { var aiMesh = new Ai.Mesh(Ai.PrimitiveType.Triangle); if (geometry.Flags.HasFlag(GeometryFlags.HasMaterial)) { aiMesh.MaterialIndex = mAiScene.Materials.FindIndex(x => x.Name == AssimpConverterCommon.EscapeName(geometry.MaterialName)); } if (geometry.Flags.HasFlag(GeometryFlags.HasTriangles)) { foreach (var triangle in geometry.Triangles) { var aiFace = new Ai.Face(); aiFace.Indices.Add(( int )triangle.A); aiFace.Indices.Add(( int )triangle.B); aiFace.Indices.Add(( int )triangle.C); aiMesh.Faces.Add(aiFace); } } if (geometry.VertexAttributeFlags.HasFlag(VertexAttributeFlags.Position)) { foreach (var vertex in geometry.Vertices) { aiMesh.Vertices.Add(new Ai.Vector3D(vertex.X, vertex.Y, vertex.Z)); } } if (geometry.VertexAttributeFlags.HasFlag(VertexAttributeFlags.Normal)) { foreach (var normal in geometry.Normals) { aiMesh.Normals.Add(new Ai.Vector3D(normal.X, normal.Y, normal.Z)); } } if (geometry.VertexAttributeFlags.HasFlag(VertexAttributeFlags.Tangent)) { foreach (var tangent in geometry.Tangents) { aiMesh.Tangents.Add(new Ai.Vector3D(tangent.X, tangent.Y, tangent.Z)); } } if (geometry.VertexAttributeFlags.HasFlag(VertexAttributeFlags.Binormal)) { foreach (var binormal in geometry.Binormals) { aiMesh.BiTangents.Add(new Ai.Vector3D(binormal.X, binormal.Y, binormal.Z)); } } if (geometry.VertexAttributeFlags.HasFlag(VertexAttributeFlags.TexCoord0)) { foreach (var vertex in geometry.TexCoordsChannel0) { aiMesh.TextureCoordinateChannels[0].Add(new Ai.Vector3D(vertex.X, vertex.Y, 0)); } } if (geometry.VertexAttributeFlags.HasFlag(VertexAttributeFlags.TexCoord1)) { foreach (var vertex in geometry.TexCoordsChannel1) { aiMesh.TextureCoordinateChannels[1].Add(new Ai.Vector3D(vertex.X, vertex.Y, 0)); } } if (geometry.VertexAttributeFlags.HasFlag(VertexAttributeFlags.TexCoord2)) { foreach (var vertex in geometry.TexCoordsChannel2) { aiMesh.TextureCoordinateChannels[2].Add(new Ai.Vector3D(vertex.X, vertex.Y, 0)); } } /* todo: colors * if ( geometry.VertexAttributeFlags.HasFlag( VertexAttributeFlags.Color0 ) ) * { * foreach ( var color in geometry.ColorChannel0 ) * { * aiMesh.VertexColorChannels[0].Add( new Ai.Color4D( color. )) * } * } */ if (geometry.Flags.HasFlag(GeometryFlags.HasVertexWeights)) { var boneMap = new Dictionary <int, Ai.Bone>(); for (int i = 0; i < geometry.VertexWeights.Length; i++) { var vertexWeight = geometry.VertexWeights[i]; for (int j = 0; j < 4; j++) { var boneWeight = vertexWeight.Weights[j]; if (boneWeight == 0f) { continue; } var boneIndex = vertexWeight.Indices[j]; var nodeIndex = scene.BonePalette.BoneToNodeIndices[boneIndex]; if (!boneMap.ContainsKey(nodeIndex)) { var aiBone = new Ai.Bone(); var boneNode = scene.GetNode(nodeIndex); aiBone.Name = AssimpConverterCommon.EscapeName(boneNode.Name); aiBone.VertexWeights.Add(new Ai.VertexWeight(i, boneWeight)); Matrix4x4.Invert(geometryNode.WorldTransform, out Matrix4x4 invGeometryNodeWorldTransform); Matrix4x4.Invert(boneNode.WorldTransform * invGeometryNodeWorldTransform, out Matrix4x4 offsetMatrix); aiBone.OffsetMatrix = new Ai.Matrix4x4(offsetMatrix.M11, offsetMatrix.M21, offsetMatrix.M31, offsetMatrix.M41, offsetMatrix.M12, offsetMatrix.M22, offsetMatrix.M32, offsetMatrix.M42, offsetMatrix.M13, offsetMatrix.M23, offsetMatrix.M33, offsetMatrix.M43, offsetMatrix.M14, offsetMatrix.M24, offsetMatrix.M34, offsetMatrix.M44); boneMap[nodeIndex] = aiBone; } else { boneMap[nodeIndex].VertexWeights.Add(new Ai.VertexWeight(i, boneWeight)); } } } aiMesh.Bones.AddRange(boneMap.Values); } return(aiMesh); }
private static Material ConvertMaterialAndTextures(Ai.Material aiMaterial, ModelConverterOptions options, string baseDirectoryPath, TextureDictionary textureDictionary) { // Convert all textures TextureInfo diffuseTexture = null; if (aiMaterial.HasTextureDiffuse) { diffuseTexture = ConvertTexture(aiMaterial.TextureDiffuse, baseDirectoryPath); } TextureInfo lightmapTexture = null; if (aiMaterial.HasTextureLightMap) { lightmapTexture = ConvertTexture(aiMaterial.TextureLightMap, baseDirectoryPath); } TextureInfo displacementTexture = null; if (aiMaterial.HasTextureDisplacement) { displacementTexture = ConvertTexture(aiMaterial.TextureDisplacement, baseDirectoryPath); } TextureInfo opacityTexture = null; if (aiMaterial.HasTextureOpacity) { opacityTexture = ConvertTexture(aiMaterial.TextureOpacity, baseDirectoryPath); } TextureInfo normalTexture = null; if (aiMaterial.HasTextureNormal) { normalTexture = ConvertTexture(aiMaterial.TextureNormal, baseDirectoryPath); } TextureInfo heightTexture = null; if (aiMaterial.HasTextureHeight) { heightTexture = ConvertTexture(aiMaterial.TextureHeight, baseDirectoryPath); } TextureInfo emissiveTexture = null; if (aiMaterial.HasTextureEmissive) { emissiveTexture = ConvertTexture(aiMaterial.TextureEmissive, baseDirectoryPath); } TextureInfo ambientTexture = null; if (aiMaterial.HasTextureAmbient) { ambientTexture = ConvertTexture(aiMaterial.TextureAmbient, baseDirectoryPath); } TextureInfo specularTexture = null; if (aiMaterial.HasTextureSpecular) { specularTexture = ConvertTexture(aiMaterial.TextureSpecular, baseDirectoryPath); } TextureInfo reflectionTexture = null; if (aiMaterial.HasTextureReflection) { reflectionTexture = ConvertTexture(aiMaterial.TextureReflection, baseDirectoryPath); } // Convert material Material material = null; string materialName = AssimpConverterCommon.UnescapeName(aiMaterial.Name); switch (options.MaterialPreset) { case MaterialPreset.FieldTerrain: { if (diffuseTexture != null) { textureDictionary.Add(diffuseTexture.Texture); material = MaterialFactory.CreateFieldTerrainMaterial(materialName, diffuseTexture.Name, HasAlpha(diffuseTexture.PixelFormat)); } } break; case MaterialPreset.FieldTerrainCastShadow: { if (diffuseTexture != null) { textureDictionary.Add(diffuseTexture.Texture); material = MaterialFactory.CreateFieldTerrainCastShadowMaterial(materialName, diffuseTexture.Name, HasAlpha(diffuseTexture.PixelFormat)); } } break; case MaterialPreset.CharacterSkinP5: { if (diffuseTexture != null) { textureDictionary.Add(diffuseTexture.Texture); string shadowTextureName = diffuseTexture.Name; if (ambientTexture != null) { textureDictionary.Add(ambientTexture.Texture); shadowTextureName = ambientTexture.Name; } // TODO: transparency material = MaterialFactory.CreateCharacterSkinP5Material(materialName, diffuseTexture.Name, shadowTextureName, HasAlpha(diffuseTexture.PixelFormat)); } } break; case MaterialPreset.CharacterClothP4D: { if (diffuseTexture != null) { textureDictionary.Add(diffuseTexture.Texture); material = MaterialFactory.CreateCharacterClothP4DMaterial(materialName, diffuseTexture.Name, HasAlpha(diffuseTexture.PixelFormat)); } } break; } // Create dummy material if none was created if (material == null) { material = new Material(materialName); } return(material); }
private static Geometry ConvertAssimpMeshToGeometry(Ai.Mesh aiMesh, Ai.Material material, Dictionary <string, NodeInfo> nodeLookup, ref int nextBoneIndex, Dictionary <int, List <int> > nodeToBoneIndices, List <Matrix4x4> boneInverseBindMatrices, ref Matrix4x4 nodeWorldTransform, ref Matrix4x4 nodeInverseWorldTransform, List <Vector3> transformedVertices, SceneConverterOptions options) { if (!aiMesh.HasVertices) { throw new Exception("Assimp mesh has no vertices"); } var geometry = new Geometry(); var geometryTransformedVertices = new Vector3[aiMesh.VertexCount]; geometry.Vertices = aiMesh.Vertices .Select(x => new Vector3(x.X, x.Y, x.Z)) .ToArray(); for (int i = 0; i < geometry.Vertices.Length; i++) { geometryTransformedVertices[i] = Vector3.Transform(geometry.Vertices[i], nodeWorldTransform); } transformedVertices.AddRange(geometryTransformedVertices); if (aiMesh.HasNormals) { geometry.Normals = aiMesh.Normals .Select(x => new Vector3(x.X, x.Y, x.Z)) .ToArray(); } if (aiMesh.HasTextureCoords(0)) { geometry.TexCoordsChannel0 = aiMesh.TextureCoordinateChannels[0] .Select(x => new Vector2(x.X, x.Y)) .ToArray(); } if (aiMesh.HasTextureCoords(1)) { geometry.TexCoordsChannel1 = aiMesh.TextureCoordinateChannels[1] .Select(x => new Vector2(x.X, x.Y)) .ToArray(); } if (aiMesh.HasTextureCoords(2)) { geometry.TexCoordsChannel2 = aiMesh.TextureCoordinateChannels[2] .Select(x => new Vector2(x.X, x.Y)) .ToArray(); } if (aiMesh.HasVertexColors(0)) { geometry.ColorChannel0 = aiMesh.VertexColorChannels[0] .Select(x => ( uint )(( byte )(x.B * 255f) | ( byte )(x.G * 255f) << 8 | ( byte )(x.R * 255f) << 16 | ( byte )(x.A * 255f) << 24)) .ToArray(); } else if (options.GenerateVertexColors) { geometry.ColorChannel0 = new uint[geometry.VertexCount]; for (int i = 0; i < geometry.ColorChannel0.Length; i++) { geometry.ColorChannel0[i] = 0xFFFFFFFF; } } if (aiMesh.HasVertexColors(1)) { geometry.ColorChannel1 = aiMesh.VertexColorChannels[1] .Select(x => ( uint )(( byte )(x.B * 255f) | ( byte )(x.G * 255f) << 8 | ( byte )(x.R * 255f) << 16 | ( byte )(x.A * 255f) << 24)) .ToArray(); } if (aiMesh.HasFaces) { geometry.TriangleIndexType = aiMesh.VertexCount <= ushort.MaxValue ? TriangleIndexType.UInt16 : TriangleIndexType.UInt32; geometry.Triangles = aiMesh.Faces .Select(x => new Triangle(( uint )x.Indices[0], ( uint )x.Indices[1], ( uint )x.Indices[2])) .ToArray(); } if (aiMesh.HasBones) { geometry.VertexWeights = new VertexWeight[geometry.VertexCount]; for (int i = 0; i < geometry.VertexWeights.Length; i++) { geometry.VertexWeights[i].Indices = new byte[4]; geometry.VertexWeights[i].Weights = new float[4]; } var vertexWeightCounts = new int[geometry.VertexCount]; for (var i = 0; i < aiMesh.Bones.Count; i++) { var aiMeshBone = aiMesh.Bones[i]; // Find node index for the bone var boneLookupData = nodeLookup[AssimpConverterCommon.UnescapeName(aiMeshBone.Name)]; int nodeIndex = boneLookupData.Index; // Calculate inverse bind matrix var boneNode = boneLookupData.Node; var bindMatrix = boneNode.WorldTransform * nodeInverseWorldTransform; if (options.ConvertSkinToZUp) { bindMatrix *= YToZUpMatrix; } Matrix4x4.Invert(bindMatrix, out var inverseBindMatrix); // Get bone index int boneIndex; if (!nodeToBoneIndices.TryGetValue(nodeIndex, out var boneIndices)) { // No entry for the node was found, so we add a new one boneIndex = nextBoneIndex++; nodeToBoneIndices.Add(nodeIndex, new List <int>() { boneIndex }); boneInverseBindMatrices.Add(inverseBindMatrix); } else { // Entry for the node was found // Try to find the bone index based on whether the inverse bind matrix matches boneIndex = -1; foreach (int index in boneIndices) { if (boneInverseBindMatrices[index].Equals(inverseBindMatrix)) { boneIndex = index; } } if (boneIndex == -1) { // None matching inverse bind matrix was found, so we add a new entry boneIndex = nextBoneIndex++; nodeToBoneIndices[nodeIndex].Add(boneIndex); boneInverseBindMatrices.Add(inverseBindMatrix); } } foreach (var aiVertexWeight in aiMeshBone.VertexWeights) { int vertexWeightCount = vertexWeightCounts[aiVertexWeight.VertexID]++; geometry.VertexWeights[aiVertexWeight.VertexID].Indices[vertexWeightCount] = ( byte )boneIndex; geometry.VertexWeights[aiVertexWeight.VertexID].Weights[vertexWeightCount] = aiVertexWeight.Weight; } } } geometry.MaterialName = AssimpConverterCommon.UnescapeName(material.Name); geometry.BoundingBox = BoundingBox.Calculate(geometry.Vertices); geometry.BoundingSphere = BoundingSphere.Calculate(geometry.BoundingBox.Value, geometry.Vertices); geometry.Flags |= GeometryFlags.Flag80000000; return(geometry); }