コード例 #1
0
        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);
        }
コード例 #2
0
        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);
            }
        }
コード例 #3
0
        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);
        }
コード例 #4
0
        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);
            }
        }
コード例 #5
0
        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);
        }
コード例 #6
0
        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;
                }
            }
        }
コード例 #7
0
        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));
        }
コード例 #8
0
        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);
        }
コード例 #9
0
        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);
        }
コード例 #10
0
        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);
        }
コード例 #11
0
        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);
        }