Example #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);
        }
Example #2
0
        private static IGeometry ConvertCollisionGeometry(Assimp.Node curAiNode, Assimp.Matrix4x4 nodeWorldTransform,
                                                          List <Assimp.Mesh> aiMeshes,
                                                          List <MaterialBuildInfo> materialBuildInfos)
        {
            var vertices  = new List <Assimp.Vector3D>();
            var triangles = new List <Basic.Triangle>();

            foreach (var aiMeshIndex in curAiNode.MeshIndices)
            {
                var aiMesh = aiMeshes[aiMeshIndex];

                for (var i = 0; i < aiMesh.Faces.Count; i++)
                {
                    var aiFace   = aiMesh.Faces[i];
                    var triangle = new Basic.Triangle();
                    var flip     = false;

                    if (!flip)
                    {
                        triangle.A = ( ushort )vertices.AddUnique(aiMesh.Vertices[aiFace.Indices[0]]);
                        triangle.B = aiFace.IndexCount > 1 ? ( ushort )vertices.AddUnique(aiMesh.Vertices[aiFace.Indices[1]]) : triangle.A;
                        triangle.C = aiFace.IndexCount > 2 ? ( ushort )vertices.AddUnique(aiMesh.Vertices[aiFace.Indices[2]]) : triangle.B;
                    }
                    else
                    {
                        triangle.C = ( ushort )vertices.AddUnique(aiMesh.Vertices[aiFace.Indices[0]]);
                        triangle.B = aiFace.IndexCount > 1 ? ( ushort )vertices.AddUnique(aiMesh.Vertices[aiFace.Indices[1]]) : triangle.A;
                        triangle.A = aiFace.IndexCount > 2 ? ( ushort )vertices.AddUnique(aiMesh.Vertices[aiFace.Indices[2]]) : triangle.B;
                    }

                    triangles.Add(triangle);
                }
            }

            var geometry = new Basic.Geometry
            {
                Meshes = new[]
                {
                    new Basic.Mesh
                    {
                        PrimitiveType = PrimitiveType.Triangles,
                        Primitives    = triangles.Cast <Basic.IPrimitive>().ToArray(),
                    }
                },
                VertexPositions = vertices.Select(x =>
                {
                    Assimp.Unmanaged.AssimpLibrary.Instance.TransformVecByMatrix4(ref x, ref nodeWorldTransform);
                    return(AssimpHelper.FromAssimp(x));
                }
                                                  ).ToArray()
            };

            geometry.Bounds = BoundingSphere.Calculate(geometry.VertexPositions);

            return(geometry);
        }
Example #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);
        }
Example #4
0
        private static IGeometry ConvertDisplayGeometry(Assimp.Node curAiNode, Assimp.Matrix4x4 nodeWorldTransform, List <Assimp.Mesh> aiMeshes, List <MaterialBuildInfo> materialBuildInfos)
        {
            var nodeInverseWorldTransform = nodeWorldTransform;

            nodeInverseWorldTransform.Inverse();
            var nodeInverseTransposeWorldTransform = nodeInverseWorldTransform;

            nodeInverseTransposeWorldTransform.Transpose();

            var geometry = new Geometry();

            // Convert meshes
            var vertexPositions = new List <Assimp.Vector3D>();
            var vertexNormals   = new List <Assimp.Vector3D>();
            var vertexUVs       = new List <Assimp.Vector3D>();
            var vertexColors    = new List <Assimp.Color4D>();
            var lastRenderState = new MeshRenderState();

            foreach (var aiMeshIndex in curAiNode.MeshIndices)
            {
                var aiMesh      = aiMeshes[aiMeshIndex];
                var material    = materialBuildInfos[aiMesh.MaterialIndex];
                var mesh        = new Mesh();
                var renderState = new MeshRenderState();

                renderState.IndexFlags = IndexAttributeFlags.HasPosition;
                var useColors  = false;
                var hasColors  = aiMesh.HasVertexColors(0);
                var hasUVs     = aiMesh.HasTextureCoords(0);
                var hasNormals = aiMesh.HasNormals;

                if (hasColors || !hasNormals)
                {
                    renderState.IndexFlags |= IndexAttributeFlags.HasColor;
                    useColors = true;
                }
                else
                {
                    renderState.IndexFlags |= IndexAttributeFlags.HasNormal;
                }

                if (hasUVs)
                {
                    renderState.IndexFlags |= IndexAttributeFlags.HasUV;
                }

                // Convert faces
                var triangleIndices = new Index[aiMesh.FaceCount * 3];
                for (var i = 0; i < aiMesh.Faces.Count; i++)
                {
                    var aiFace = aiMesh.Faces[i];

                    for (var j = 0; j < 3; j++)
                    {
                        var triangleIndicesIndex = (i * 3) + 2 - j;

                        if (j >= aiFace.IndexCount)
                        {
                            triangleIndices[triangleIndicesIndex] = triangleIndices[triangleIndicesIndex + 1];
                            continue;
                        }

                        int aiFaceIndex = aiFace.Indices[j];

                        var position      = aiMesh.Vertices[aiFaceIndex];
                        var positionIndex = vertexPositions.AddUnique(position);
                        if (positionIndex > byte.MaxValue)
                        {
                            renderState.IndexFlags |= IndexAttributeFlags.Position16BitIndex;
                        }

                        var normalIndex = 0;
                        var colorIndex  = 0;
                        var uvIndex     = 0;

                        if (useColors)
                        {
                            var color = hasColors ? aiMesh.VertexColorChannels[0][aiFaceIndex] : new Assimp.Color4D();
                            colorIndex = vertexColors.AddUnique(color);

                            if (colorIndex > byte.MaxValue)
                            {
                                renderState.IndexFlags |= IndexAttributeFlags.Color16BitIndex;
                            }
                        }
                        else
                        {
                            var normal = aiMesh.Normals[aiFaceIndex];
                            normalIndex = vertexNormals.AddUnique(normal);

                            if (normalIndex > byte.MaxValue)
                            {
                                renderState.IndexFlags |= IndexAttributeFlags.Normal16BitIndex;
                            }
                        }

                        if (hasUVs)
                        {
                            var uv = aiMesh.TextureCoordinateChannels[0][aiFaceIndex];
                            uvIndex = vertexUVs.AddUnique(uv);

                            if (uvIndex > byte.MaxValue)
                            {
                                renderState.IndexFlags |= IndexAttributeFlags.UV16BitIndex;
                            }
                        }

                        triangleIndices[triangleIndicesIndex] = new Index
                        {
                            PositionIndex = ( ushort )positionIndex,
                            NormalIndex   = ( ushort )normalIndex,
                            ColorIndex    = ( ushort )colorIndex,
                            UVIndex       = ( ushort )uvIndex
                        };
                    }
                }

                // Build display list
                var displayList = new GXDisplayList(GXPrimitive.Triangles, triangleIndices);
                mesh.DisplayLists.Add(displayList);

                // Set up render params
                if (renderState.IndexFlags != lastRenderState.IndexFlags)
                {
                    mesh.Parameters.Add(new IndexAttributeFlagsParam(renderState.IndexFlags));
                }

                // Set up render lighting params
                {
                    if (useColors)
                    {
                        renderState.LightingValue1 = 0x0b11;
                    }
                    else
                    {
                        renderState.LightingValue2 = 0x0011;
                    }

                    renderState.LightingValue2 = 1;

                    if (renderState.LightingValue1 != lastRenderState.LightingValue1 ||
                        renderState.LightingValue2 != lastRenderState.LightingValue2)
                    {
                        mesh.Parameters.Add(new LightingParams()
                        {
                            Value1 = renderState.LightingValue1,
                            Value2 = renderState.LightingValue2
                        });
                    }
                }

                // Set up render texture params
                {
                    renderState.TextureId = ( ushort )material.TextureId;
                    renderState.TileMode  = TileMode.WrapU | TileMode.WrapV;

                    if (renderState.TextureId != lastRenderState.TextureId ||
                        renderState.TileMode != lastRenderState.TileMode)
                    {
                        mesh.Parameters.Add(new TextureParams(renderState.TextureId, renderState.TileMode));
                    }
                }

                // Set up render mipmap params
                {
                    renderState.MipMapValue1 = 0x104a;
                    renderState.MipMapValue2 = 0;

                    if (renderState.MipMapValue1 != lastRenderState.MipMapValue1 ||
                        renderState.MipMapValue2 != lastRenderState.MipMapValue2)
                    {
                        mesh.Parameters.Add(new MipMapParams {
                            Value1 = renderState.MipMapValue1, Value2 = renderState.MipMapValue2
                        });
                    }
                }

                //if ( material.UseAlpha )
                //{
                //    mesh.Parameters.Add( new BlendAlphaParam() { Flags = BlendAlphaFlags.UseAlpha } );
                //    geometry.TranslucentMeshes.Add( mesh );
                //}
                //else
                //{
                //    geometry.OpaqueMeshes.Add( mesh );
                //}

                geometry.OpaqueMeshes.Add(mesh);
                lastRenderState = renderState;
            }

            // Build vertex buffers
            if (vertexPositions.Count > 0)
            {
                Debug.Assert(vertexPositions.Count <= ushort.MaxValue);
                var localVertexPositions = vertexPositions.Select(x =>
                {
                    Assimp.Unmanaged.AssimpLibrary.Instance.TransformVecByMatrix4(ref x, ref nodeWorldTransform);
                    return(AssimpHelper.FromAssimp(x));
                }).ToArray();
                geometry.VertexBuffers.Add(new VertexPositionBuffer(localVertexPositions));
                geometry.Bounds = BoundingSphere.Calculate(localVertexPositions);
            }

            if (vertexNormals.Count > 0)
            {
                Debug.Assert(vertexNormals.Count <= ushort.MaxValue);
                geometry.VertexBuffers.Add(new VertexNormalBuffer(vertexNormals.Select(x =>
                {
                    Assimp.Unmanaged.AssimpLibrary.Instance.TransformVecByMatrix4(ref x, ref nodeInverseTransposeWorldTransform);
                    return(AssimpHelper.FromAssimp(x));
                }).ToArray()));
            }

            if (vertexColors.Count > 0)
            {
                Debug.Assert(vertexColors.Count <= ushort.MaxValue);
                geometry.VertexBuffers.Add(new VertexColorBuffer(vertexColors.Select(AssimpHelper.FromAssimp).ToArray()));
            }

            if (vertexUVs.Count > 0)
            {
                Debug.Assert(vertexUVs.Count <= ushort.MaxValue);
                geometry.VertexBuffers.Add(new VertexUVBuffer(vertexUVs.Select(x =>
                                                                               UVCodec.Encode255(AssimpHelper
                                                                                                 .FromAssimpAsVector2(x)))
                                                              .ToArray()));
            }

            return(geometry);
        }