Beispiel #1
0
        public static Model ConvertFromAssimpScene(Ai.Scene aiScene, ModelConverterOptions options)
        {
            var scene = new Model(options.Version);

            // Convert assimp nodes to our nodes
            var nodeLookup    = new Dictionary <string, NodeInfo>();
            int nextNodeIndex = 0;

            scene.RootNode = ConvertAssimpNodeRecursively(aiScene, aiScene.RootNode, nodeLookup, ref nextNodeIndex, options);

            // Process the meshes attached to the assimp nodes
            var nodeToBoneIndices       = new Dictionary <int, List <int> >();
            int nextBoneIndex           = 0;
            var boneInverseBindMatrices = new List <Matrix4x4>();
            var transformedVertices     = new List <Vector3>();

            ProcessAssimpNodeMeshesRecursively(aiScene.RootNode, aiScene, nodeLookup, ref nextBoneIndex, nodeToBoneIndices, boneInverseBindMatrices, transformedVertices, options);

            // Don't build a bone palette if there are no skinned meshes
            if (boneInverseBindMatrices.Count > 0)
            {
                // Build bone palette for skinning
                scene.Bones = BuildBonePalette(boneInverseBindMatrices, nodeToBoneIndices);
            }

            // Build bounding box & sphere
            scene.BoundingBox    = BoundingBox.Calculate(transformedVertices);
            scene.BoundingSphere = BoundingSphere.Calculate(scene.BoundingBox.Value, transformedVertices);

            return(scene);
        }
        public static ModelPack ConvertFromAssimpScene(string filePath, ModelPackConverterOptions options)
        {
            // For importing textures
            string baseDirectoryPath = Path.GetDirectoryName(filePath);

            // Import scene
            var aiScene = AssimpSceneImporter.ImportFile(filePath);

            // Build textures & Materials
            var model = new ModelPack(options.Version);

            model.Textures  = new TextureDictionary(options.Version);
            model.Materials = new MaterialDictionary(options.Version);

            foreach (var aiSceneMaterial in aiScene.Materials)
            {
                var material = ConvertMaterialAndTextures(aiSceneMaterial, options, baseDirectoryPath, model.Textures);
                model.Materials.Add(material);
            }

            // Create scene
            var sceneConverterOptions = new ModelConverterOptions()
            {
                Version                 = options.Version,
                ConvertSkinToZUp        = options.ConvertSkinToZUp,
                GenerateVertexColors    = options.GenerateVertexColors,
                MinimalVertexAttributes = options.MinimalVertexAttributes,
            };

            model.Model = ModelConverter.ConvertFromAssimpScene(aiScene, sceneConverterOptions);

            return(model);
        }
Beispiel #3
0
        private static Mesh 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, ModelConverterOptions options)
        {
            if (!aiMesh.HasVertices)
            {
                throw new Exception("Assimp mesh has no vertices");
            }

            var geometry = new Mesh();
            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.TriangleIndexFormat = aiMesh.VertexCount <= ushort.MaxValue ? TriangleIndexFormat.UInt16 : TriangleIndexFormat.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);
        }
Beispiel #4
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, ModelConverterOptions 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 NodeMeshAttachment(geometry));
                    }
                    else
                    {
                        node.Parent.Attachments.Add(new NodeMeshAttachment(geometry));
                        node.Parent.RemoveChildNode(node);
                    }
                }
            }

            foreach (var aiNodeChild in aiNode.Children)
            {
                ProcessAssimpNodeMeshesRecursively(aiNodeChild, aiScene, nodeLookup, ref nextBoneIndex, nodeToBoneIndices, boneInverseBindMatrices, transformedVertices, options);
            }
        }
Beispiel #5
0
        public static Model ConvertFromAssimpScene(string filePath, ModelConverterOptions options)
        {
            var aiScene = AssimpSceneImporter.ImportFile(filePath);

            return(ConvertFromAssimpScene(aiScene, options));
        }
Beispiel #6
0
        private static Node ConvertAssimpNodeRecursively(Assimp.Scene aiScene, Ai.Node aiNode, Dictionary <string, NodeInfo> nodeLookup, ref int nextIndex, ModelConverterOptions options)
        {
            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 (options.SetFullBodyNodeProperties)
                {
                    if (node.Name == "See User Defined Properties")
                    {
                        TryAddProperty(node.Properties, new UserIntProperty("NiSortAdjustNode", 0));
                    }
                    else if (node.Name.EndsWith("root") || node.Name == "Bip01")
                    {
                        TryAddProperty(node.Properties, new UserIntProperty("KFAccumRoot", 0));
                    }
                    else if (sFullBodyObjectNames.Contains(node.Name))
                    {
                        TryAddFullBodyObjectProperties(node.Properties, node.Name);
                    }
                }

                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}'");
                }

                // Is this a camera?
                var index = -1;
                if ((index = aiScene.Cameras.FindIndex(x => x.Name == node.Name)) != -1)
                {
                    var aiCamera = aiScene.Cameras[index];
                    var camera   = new Camera(-aiCamera.Direction.ToNumerics(), aiCamera.Up.ToNumerics(), aiCamera.Position.ToNumerics(),
                                              aiCamera.ClipPlaneNear, aiCamera.ClipPlaneFar, MathHelper.RadiansToDegrees(aiCamera.FieldOfview),
                                              aiCamera.AspectRatio, 0
                                              )
                    {
                        Version = options.Version
                    };

                    node.Attachments.Add(new NodeCameraAttachment(camera));
                }
                else if ((index = aiScene.Lights.FindIndex(x => x.Name == node.Name)) != -1)
                {
                    var aiLight   = aiScene.Lights[index];
                    var lightType = LightType.Point;
                    switch (aiLight.LightType)
                    {
                    case Ai.LightSourceType.Directional:
                        lightType = LightType.Type1;
                        break;

                    case Ai.LightSourceType.Point:
                    case Ai.LightSourceType.Ambient:
                        lightType = LightType.Point;
                        break;

                    case Ai.LightSourceType.Spot:
                        lightType = LightType.Spot;
                        break;
                    }

                    var light = new Light
                    {
                        Version        = options.Version,
                        AmbientColor   = aiLight.ColorAmbient.ToNumerics(),
                        DiffuseColor   = aiLight.ColorDiffuse.ToNumerics(),
                        SpecularColor  = aiLight.ColorSpecular.ToNumerics(),
                        AngleInnerCone = aiLight.AngleInnerCone,
                        AngleOuterCone = aiLight.AngleOuterCone,
                        Type           = lightType,
                        Flags          = LightFlags.Bit1 | LightFlags.Bit2
                    };
                    node.Attachments.Add(new NodeLightAttachment(light));
                }

                // 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(aiScene, aiFakeRootNodeChild, nodeLookup, ref nextIndex, options);
                            node.AddChildNode(childNode);
                        }
                    }
                    else
                    {
                        var childNode = ConvertAssimpNodeRecursively(aiScene, aiNodeChild, nodeLookup, ref nextIndex, options);
                        node.AddChildNode(childNode);
                    }
                }
            }
            else
            {
                nodeLookup.Add(node.Name, new NodeInfo(aiNode, node, -1, true));
            }

            return(node);
        }