Exemplo n.º 1
0
 public static Color FromAssimp(Assimp.Color4D value)
 {
     return(new Color(( byte )(value.R * 255f),
                      ( byte )(value.G * 255f),
                      ( byte )(value.B * 255f),
                      ( byte )(value.A * 255f)));
 }
Exemplo n.º 2
0
        static void BindUniform(ref Action bind, Shader shader, string name, Assimp.Color4D color)
        {
            var location = GL.GetUniformLocation(shader.Program, name);

            if (location >= 0)
            {
                bind += () => GL.Uniform4(location, color.R, color.G, color.B, color.A);
            }
        }
Exemplo n.º 3
0
        public static Color ToColor(this Assimp.Color4D color)
        {
            Color c;

            c.R = (byte)(color.R * 255);
            c.G = (byte)(color.G * 255);
            c.B = (byte)(color.B * 255);
            c.A = (byte)(color.A * 255);
            return(c);
        }
Exemplo n.º 4
0
 private Color[] ConvertColors(Assimp.Color4D[] colors)
 {
     Color[] data = new Color[colors.Length];
     for (int i = 0; i < colors.Length; i++)
     {
         Assimp.Color4D color = colors[i];
         data[i] = new Color(color.R, color.G, color.B, color.A);
     }
     return(data);
 }
        internal static Color4 ToColor4(this Assimp.Color4D color)
        {
            Color4 c;

            c.Red   = color.R; //(byte)(color.R * 255);
            c.Green = color.G; //(byte)(color.G * 255);
            c.Blue  = color.B; //(byte)(color.B * 255);
            c.Alpha = color.A; //(byte)(color.A * 255);
            return(c);
        }
Exemplo n.º 6
0
 private static Color ConvertVertexColor(Assimp.Color4D clr, byte maxColorIntensity, byte maxAlpha) => new Color(
     (byte)Math.Min(255, clr.R * maxColorIntensity),
     (byte)Math.Min(255, clr.G * maxColorIntensity),
     (byte)Math.Min(255, clr.B * maxColorIntensity),
     (byte)Math.Min(255, clr.A * maxAlpha)
     );
Exemplo n.º 7
0
 private string colourToString(Assimp.Color4D colour)
 {
     return(colour.R.ToString() + "," + colour.G.ToString() + "," + colour.B.ToString() + "," + colour.A.ToString());
 }
Exemplo n.º 8
0
        public void SetUpTev(bool hasTexture, bool hasVtxColor, int texIndex, string texName, Assimp.Material meshMat)
        {
            Flag = 1;
            // Set up channel control 0 to use vertex colors, if they're present
            if (hasVtxColor)
            {
                AddChannelControl(J3DColorChannelId.Color0, false, ColorSrc.Vertex, LightId.None, DiffuseFn.None, J3DAttenuationFn.None_0, ColorSrc.Register);
                AddChannelControl(J3DColorChannelId.Alpha0, false, ColorSrc.Vertex, LightId.None, DiffuseFn.None, J3DAttenuationFn.None_0, ColorSrc.Register);
            }
            else
            {
                AddChannelControl(J3DColorChannelId.Color0, false, ColorSrc.Register, LightId.None, DiffuseFn.Clamp, J3DAttenuationFn.Spec, ColorSrc.Register);
                AddChannelControl(J3DColorChannelId.Alpha0, false, ColorSrc.Register, LightId.None, DiffuseFn.Clamp, J3DAttenuationFn.Spec, ColorSrc.Register);
            }

            // These settings are common to all the configurations we can use
            TevStageParameters stageParams = new TevStageParameters
            {
                ColorInD   = CombineColorInput.Zero,
                ColorOp    = TevOp.Add,
                ColorBias  = TevBias.Zero,
                ColorScale = TevScale.Scale_1,
                ColorClamp = true,
                ColorRegId = TevRegisterId.TevPrev,

                AlphaInD   = CombineAlphaInput.Zero,
                AlphaOp    = TevOp.Add,
                AlphaBias  = TevBias.Zero,
                AlphaScale = TevScale.Scale_1,
                AlphaClamp = true,
                AlphaRegId = TevRegisterId.TevPrev
            };

            if (hasTexture)
            {
                // Generate texture stuff
                AddTexGen(TexGenType.Matrix2x4, TexGenSrc.Tex0, Enums.TexMatrix.Identity);
                AddTexMatrix(TexGenType.Matrix3x4, 0, OpenTK.Vector3.Zero, OpenTK.Vector2.One, 0, OpenTK.Vector2.Zero, OpenTK.Matrix4.Identity);
                AddTevOrder(TexCoordId.TexCoord0, TexMapId.TexMap0, GXColorChannelId.Color0A0);
                AddTexIndex(texIndex);

                // Texture + Vertex Color
                if (hasVtxColor)
                {
                    stageParams.ColorInA = CombineColorInput.Zero;
                    stageParams.ColorInB = CombineColorInput.RasColor;
                    stageParams.ColorInC = CombineColorInput.TexColor;
                    stageParams.AlphaInA = CombineAlphaInput.Zero;
                    stageParams.AlphaInB = CombineAlphaInput.RasAlpha;
                    stageParams.AlphaInC = CombineAlphaInput.TexAlpha;
                }
                // Texture alone
                else
                {
                    stageParams.ColorInA = CombineColorInput.TexColor;
                    stageParams.ColorInB = CombineColorInput.Zero;
                    stageParams.ColorInC = CombineColorInput.Zero;
                    stageParams.AlphaInA = CombineAlphaInput.TexAlpha;
                    stageParams.AlphaInB = CombineAlphaInput.Zero;
                    stageParams.AlphaInC = CombineAlphaInput.Zero;
                }
            }
            // No texture!
            else
            {
                AddTevOrder(TexCoordId.Null, TexMapId.Null, GXColorChannelId.Color0A0);

                // No vertex colors either, so make sure there's a material color to use instead
                if (!hasVtxColor)
                {
                    if (meshMat.HasColorDiffuse)   // Use model's diffuse color
                    {
                        Assimp.Color4D color = meshMat.ColorDiffuse;
                        MaterialColors[0] = new Color(color.R, color.G, color.B, color.A);
                    }
                    else   // Otherwise default to white
                    {
                        MaterialColors[0] = new Color(1, 1, 1, 1);
                    }

                    AddChannelControl(J3DColorChannelId.Color0, false, ColorSrc.Register, LightId.None, DiffuseFn.None, J3DAttenuationFn.None_0, ColorSrc.Register);
                    AddChannelControl(J3DColorChannelId.Alpha0, false, ColorSrc.Register, LightId.None, DiffuseFn.None, J3DAttenuationFn.None_0, ColorSrc.Register);
                }

                // Set up TEV to use the material color we just set
                stageParams.ColorInA = CombineColorInput.RasColor;
                stageParams.ColorInB = CombineColorInput.Zero;
                stageParams.ColorInC = CombineColorInput.Zero;
                stageParams.AlphaInA = CombineAlphaInput.RasAlpha;
                stageParams.AlphaInB = CombineAlphaInput.Zero;
                stageParams.AlphaInC = CombineAlphaInput.Zero;
            }

            AddTevStage(stageParams);
        }
Exemplo n.º 9
0
        public static Assimp.Scene ToAssimpScene(RwClumpNode clumpNode)
        {
            // Scene
            var aiScene = new Assimp.Scene();

            // RootNode
            var rootFrame  = clumpNode.FrameList[0];
            var aiRootNode = new Assimp.Node("RootNode", null);

            aiRootNode.Transform = new Assimp.Matrix4x4(rootFrame.Transform.M11, rootFrame.Transform.M21, rootFrame.Transform.M31, rootFrame.Transform.M41,
                                                        rootFrame.Transform.M12, rootFrame.Transform.M22, rootFrame.Transform.M32, rootFrame.Transform.M42,
                                                        rootFrame.Transform.M13, rootFrame.Transform.M23, rootFrame.Transform.M33, rootFrame.Transform.M43,
                                                        rootFrame.Transform.M14, rootFrame.Transform.M24, rootFrame.Transform.M34, rootFrame.Transform.M44);

            aiScene.RootNode = aiRootNode;

            for (int i = 1; i < clumpNode.FrameList.Count; i++)
            {
                var frame     = clumpNode.FrameList[i];
                var frameName = "_" + frame.HAnimFrameExtensionNode.NameId;

                Assimp.Node aiParentNode = null;
                if (frame.Parent != null)
                {
                    string parentName = "RootNode";
                    if (frame.Parent.HasHAnimExtension)
                    {
                        parentName = "_" + frame.Parent.HAnimFrameExtensionNode.NameId;
                    }

                    aiParentNode = aiRootNode.FindNode(parentName);
                }

                var aiNode = new Assimp.Node(frameName, aiParentNode);
                aiNode.Transform = new Assimp.Matrix4x4(frame.Transform.M11, frame.Transform.M21, frame.Transform.M31, frame.Transform.M41,
                                                        frame.Transform.M12, frame.Transform.M22, frame.Transform.M32, frame.Transform.M42,
                                                        frame.Transform.M13, frame.Transform.M23, frame.Transform.M33, frame.Transform.M43,
                                                        frame.Transform.M14, frame.Transform.M24, frame.Transform.M34, frame.Transform.M44);
                aiParentNode.Children.Add(aiNode);
            }

            // Meshes, Materials
            for (int atomicIndex = 0; atomicIndex < clumpNode.Atomics.Count; atomicIndex++)
            {
                var atomic   = clumpNode.Atomics[atomicIndex];
                var geometry = clumpNode.GeometryList[atomic.GeometryIndex];
                var frame    = clumpNode.FrameList[atomic.FrameIndex];

                var aiNodeName          = $"Atomic{atomicIndex}";
                var aiNode              = new Assimp.Node(aiNodeName, aiScene.RootNode);
                var frameWorldTransform = frame.WorldTransform;
                aiNode.Transform = new Assimp.Matrix4x4(frameWorldTransform.M11, frameWorldTransform.M21, frameWorldTransform.M31, frameWorldTransform.M41,
                                                        frameWorldTransform.M12, frameWorldTransform.M22, frameWorldTransform.M32, frameWorldTransform.M42,
                                                        frameWorldTransform.M13, frameWorldTransform.M23, frameWorldTransform.M33, frameWorldTransform.M43,
                                                        frameWorldTransform.M14, frameWorldTransform.M24, frameWorldTransform.M34, frameWorldTransform.M44);
                aiScene.RootNode.Children.Add(aiNode);

                bool hasVertexWeights = geometry.SkinNode != null;

                for (int meshIndex = 0; meshIndex < geometry.MeshListNode.MaterialMeshes.Length; meshIndex++)
                {
                    var mesh   = geometry.MeshListNode.MaterialMeshes[meshIndex];
                    var aiMesh = new Assimp.Mesh($"Atomic{atomicIndex}_Geometry{atomic.GeometryIndex}_Mesh{meshIndex}", Assimp.PrimitiveType.Triangle);

                    // get triangle list indices
                    int[] indices;

                    if (geometry.MeshListNode.PrimitiveType == RwPrimitiveType.TriangleList)
                    {
                        indices = mesh.Indices;
                    }
                    else
                    {
                        indices = MeshUtilities.ToTriangleList(mesh.Indices, false);
                    }

                    // Faces
                    for (int i = 0; i < indices.Length; i += 3)
                    {
                        var faceIndices = new[] { i, i + 1, i + 2 };
                        var aiFace      = new Assimp.Face(faceIndices);
                        aiMesh.Faces.Add(aiFace);
                    }

                    // TextureCoordinateChannels, VertexColorChannels, Vertices, MaterialIndex, Normals
                    for (int triIdx = 0; triIdx < indices.Length; triIdx += 3)
                    {
                        for (int triVertIdx = 0; triVertIdx < 3; triVertIdx++)
                        {
                            int vertexIndex = indices[triIdx + triVertIdx];

                            // TextureCoordinateChannels
                            if (geometry.HasTextureCoordinates)
                            {
                                for (int channelIdx = 0; channelIdx < geometry.TextureCoordinateChannelCount; channelIdx++)
                                {
                                    var textureCoordinate   = geometry.TextureCoordinateChannels[channelIdx][vertexIndex];
                                    var aiTextureCoordinate = new Assimp.Vector3D(textureCoordinate.X, textureCoordinate.Y, 0f);
                                    aiMesh.TextureCoordinateChannels[channelIdx].Add(aiTextureCoordinate);
                                }
                            }

                            // VertexColorChannels
                            if (geometry.HasColors)
                            {
                                var color   = geometry.Colors[vertexIndex];
                                var aiColor = new Assimp.Color4D(color.R / 255f, color.G / 255f, color.B / 255f, color.A / 255f);
                                aiMesh.VertexColorChannels[0].Add(aiColor);
                            }

                            // Vertices
                            if (geometry.HasVertices)
                            {
                                var vertex   = geometry.Vertices[vertexIndex];
                                var aiVertex = new Assimp.Vector3D(vertex.X, vertex.Y, vertex.Z);
                                aiMesh.Vertices.Add(aiVertex);
                            }

                            // Normals
                            if (geometry.HasNormals)
                            {
                                var normal   = geometry.Normals[vertexIndex];
                                var aiNormal = new Assimp.Vector3D(normal.X, normal.Y, normal.Z);
                                aiMesh.Normals.Add(aiNormal);
                            }
                        }
                    }

                    // Bones
                    if (hasVertexWeights)
                    {
                        var skinNode  = geometry.SkinNode;
                        var aiBoneMap = new Dictionary <int, Assimp.Bone>();

                        for (int i = 0; i < indices.Length; i++)
                        {
                            var vertexIndex     = indices[i];
                            int realVertexIndex = i;

                            for (int j = 0; j < 4; j++)
                            {
                                var boneIndex  = skinNode.VertexBoneIndices[vertexIndex][j];
                                var boneWeight = skinNode.VertexBoneWeights[vertexIndex][j];

                                if (boneWeight == 0.0f)
                                {
                                    continue;
                                }

                                if (!aiBoneMap.Keys.Contains(boneIndex))
                                {
                                    var aiBone    = new Assimp.Bone();
                                    var boneFrame = clumpNode.FrameList.GetFrameByHierarchyIndex(boneIndex);

                                    aiBone.Name = boneFrame.HasHAnimExtension ? "_" + boneFrame.HAnimFrameExtensionNode.NameId : "RootNode";
                                    aiBone.VertexWeights.Add(new Assimp.VertexWeight(realVertexIndex, boneWeight));

                                    Matrix4x4.Invert(frame.WorldTransform, out Matrix4x4 invertedFrameWorldTransform);
                                    Matrix4x4.Invert(boneFrame.WorldTransform * invertedFrameWorldTransform, out Matrix4x4 offsetMatrix);
                                    aiBone.OffsetMatrix = new Assimp.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);
                                    aiBoneMap[boneIndex] = aiBone;
                                }

                                if (!aiBoneMap[boneIndex].VertexWeights.Any(x => x.VertexID == realVertexIndex))
                                {
                                    aiBoneMap[boneIndex].VertexWeights.Add(new Assimp.VertexWeight(realVertexIndex, boneWeight));
                                }
                            }
                        }

                        aiMesh.Bones.AddRange(aiBoneMap.Values);
                    }
                    else
                    {
                        var aiBone = new Assimp.Bone();

                        // Name
                        aiBone.Name = frame.HasHAnimExtension ? "_" + frame.HAnimFrameExtensionNode.NameId : "RootNode";

                        // VertexWeights
                        for (int i = 0; i < aiMesh.Vertices.Count; i++)
                        {
                            var aiVertexWeight = new Assimp.VertexWeight(i, 1f);
                            aiBone.VertexWeights.Add(aiVertexWeight);
                        }

                        // OffsetMatrix

                        /*
                         * Matrix4x4.Invert( frame.WorldTransform, out Matrix4x4 offsetMatrix );
                         * aiBone.OffsetMatrix = new Assimp.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 );
                         */
                        aiBone.OffsetMatrix = Assimp.Matrix4x4.Identity;

                        aiMesh.Bones.Add(aiBone);
                    }

                    var material   = geometry.Materials[mesh.MaterialIndex];
                    var aiMaterial = new Assimp.Material();

                    if (material.IsTextured)
                    {
                        // TextureDiffuse
                        var texture = material.TextureReferenceNode;
                        aiMaterial.TextureDiffuse = new Assimp.TextureSlot(
                            texture.Name + ".png", Assimp.TextureType.Diffuse, 0, Assimp.TextureMapping.FromUV, 0, 0, Assimp.TextureOperation.Add, Assimp.TextureWrapMode.Wrap, Assimp.TextureWrapMode.Wrap, 0);
                    }

                    // Name
                    aiMaterial.Name = material.Name ?? $"Geometry{atomic.GeometryIndex}_Material{mesh.MaterialIndex}";
                    if (material.IsTextured && material.Name == null)
                    {
                        aiMaterial.Name = material.TextureReferenceNode.Name;
                    }

                    aiMaterial.ShadingMode = Assimp.ShadingMode.Phong;

                    // Add mesh to meshes
                    aiScene.Meshes.Add(aiMesh);

                    // Add material to materials
                    aiScene.Materials.Add(aiMaterial);

                    // MaterialIndex
                    aiMesh.MaterialIndex = aiScene.Materials.Count - 1;

                    // Add mesh index to node
                    aiNode.MeshIndices.Add(aiScene.Meshes.Count - 1);
                }
            }

            return(aiScene);
        }
Exemplo n.º 10
0
 public static XNAV4 ToXnaVector(this Assimp.Color4D v)
 {
     return(new XNAV4(v.R, v.G, v.B, v.A));
 }
Exemplo n.º 11
0
 private Vector3 ConvertColorToVector3(Assimp.Color4D color)
 {
     return(new Vector3(color.R, color.G, color.B));
 }
Exemplo n.º 12
0
 private Color ConvertColor(Assimp.Color4D color)
 {
     return(new Color(color.R, color.G, color.B, color.A));
 }
Exemplo n.º 13
0
        public static Assimp.Scene AssimpPRMExport(string filePath, PRMModel prm)
        {
            Assimp.Scene aiScene = new Assimp.Scene();

            //Create an array to hold references to these since Assimp lacks a way to grab these by order or id
            //We don't need the nodo count in this since they can't be parents
            Assimp.Node[] boneArray = new Assimp.Node[2];

            //Set up root node
            var aiRootNode = new Assimp.Node("RootNode", null);

            aiRootNode.Transform = Assimp.Matrix4x4.Identity;

            boneArray[0]     = aiRootNode;
            aiScene.RootNode = aiRootNode;

            //Set up single child node
            var aiNode = new Assimp.Node(Path.GetFileNameWithoutExtension(filePath) + "_node", aiRootNode);

            //Use inverse bind matrix as base

            //Get local transform
            aiNode.Transform = aiRootNode.Transform;

            aiRootNode.Children.Add(aiNode);
            boneArray[1] = aiNode;

            //Mesh
            string aiMeshName = Path.GetFileNameWithoutExtension(filePath);

            var aiMesh = new Assimp.Mesh(aiMeshName, Assimp.PrimitiveType.Triangle);

            //Vertex face data - PSO2 Actually doesn't do this, it just has per vertex data so we can just map a vertice's data to each face using it
            //It may actually be possible to add this to the previous loop, but my reference didn't so I'm doing it in a separate loop for safety
            //Reference: https://github.com/TGEnigma/Amicitia/blob/master/Source/AmicitiaLibrary/Graphics/RenderWare/RWClumpNode.cs
            for (int vertId = 0; vertId < prm.vertices.Count; vertId++)
            {
                var prmVert = prm.vertices[vertId];

                var pos = prmVert.pos * 100;
                aiMesh.Vertices.Add(new Assimp.Vector3D(pos.X, pos.Y, pos.Z));

                var nrm = prmVert.normal;
                aiMesh.Normals.Add(new Assimp.Vector3D(nrm.X, nrm.Y, nrm.Z));

                //Vert colors are bgra
                var rawClr = prmVert.color;
                var clr    = new Assimp.Color4D(clrToFloat(rawClr[2]), clrToFloat(rawClr[1]), clrToFloat(rawClr[0]), clrToFloat(rawClr[3]));
                aiMesh.VertexColorChannels[0].Add(clr);

                var uv1   = prmVert.uv1;
                var aiUV1 = new Assimp.Vector3D(uv1.X, uv1.Y, 0f);
                aiMesh.TextureCoordinateChannels[0].Add(aiUV1);


                var uv2   = prmVert.uv2;
                var aiUV2 = new Assimp.Vector3D(uv2.X, uv2.Y, 0f);
                aiMesh.TextureCoordinateChannels[1].Add(aiUV2);
            }

            //Handle rigid meshes
            {
                var aiBone  = new Assimp.Bone();
                var aqnBone = boneArray[0];

                // Name
                aiBone.Name = aiNode.Name;

                // VertexWeights
                for (int i = 0; i < aiMesh.Vertices.Count; i++)
                {
                    var aiVertexWeight = new Assimp.VertexWeight(i, 1f);
                    aiBone.VertexWeights.Add(aiVertexWeight);
                }

                aiBone.OffsetMatrix = Assimp.Matrix4x4.Identity;

                aiMesh.Bones.Add(aiBone);
            }

            //Faces
            foreach (var face in prm.faces)
            {
                aiMesh.Faces.Add(new Assimp.Face(new int[] { (int)face.X, (int)face.Y, (int)face.Z }));
            }

            //Material
            Assimp.Material mate = new Assimp.Material();

            mate.ColorDiffuse = new Assimp.Color4D(1, 1, 1, 1);
            mate.Name         = aiMeshName + "_material";

            mate.ShadingMode = Assimp.ShadingMode.Phong;

            var meshNodeName = Path.GetFileNameWithoutExtension(filePath);

            // Add mesh to meshes
            aiScene.Meshes.Add(aiMesh);

            // Add material to materials
            aiScene.Materials.Add(mate);

            // MaterialIndex
            aiMesh.MaterialIndex = aiScene.Materials.Count - 1;

            // Set up mesh node and add this mesh's index to it (This tells assimp to export it as a mesh for various formats)
            var meshNode = new Assimp.Node(meshNodeName, aiScene.RootNode);

            meshNode.Transform = Assimp.Matrix4x4.Identity;

            aiScene.RootNode.Children.Add(meshNode);

            meshNode.MeshIndices.Add(aiScene.Meshes.Count - 1);


            return(aiScene);
        }
Exemplo n.º 14
0
        public static Assimp.Scene AssimpExport(string filePath, AquaObject aqp, AquaNode aqn)
        {
            if (aqp is NGSAquaObject)
            {
                //NGS aqps will give lots of isolated vertices if we don't handle them
                //Since we're not actually altering the data so much as rearranging references, we can just do this
                aqp = aqp.Clone();
                aqp.splitVSETPerMesh();
            }
            Assimp.Scene aiScene = new Assimp.Scene();

            //Create an array to hold references to these since Assimp lacks a way to grab these by order or id
            //We don't need the nodo count in this since they can't be parents
            Assimp.Node[] boneArray = new Assimp.Node[aqn.nodeList.Count];

            //Set up root node
            var root       = aqn.nodeList[0];
            var aiRootNode = new Assimp.Node("RootNode", null);

            aiRootNode.Transform = Assimp.Matrix4x4.Identity;

            aiScene.RootNode = aiRootNode;

            //Assign bones
            for (int i = 0; i < aqn.nodeList.Count; i++)
            {
                var         bn = aqn.nodeList[i];
                Assimp.Node parentNode;
                var         parentTfm = Matrix4x4.Identity;
                if (bn.parentId == -1)
                {
                    parentNode = aiRootNode;
                }
                else
                {
                    parentNode = boneArray[bn.parentId];
                    var pn = aqn.nodeList[bn.parentId];
                    parentTfm = new Matrix4x4(pn.m1.X, pn.m1.Y, pn.m1.Z, pn.m1.W,
                                              pn.m2.X, pn.m2.Y, pn.m2.Z, pn.m2.W,
                                              pn.m3.X, pn.m3.Y, pn.m3.Z, pn.m3.W,
                                              pn.m4.X * 100, pn.m4.Y * 100, pn.m4.Z * 100, pn.m4.W);
                }
                var aiNode = new Assimp.Node($"({i})" + bn.boneName.GetString(), parentNode);

                //Use inverse bind matrix as base
                var bnMat = new Matrix4x4(bn.m1.X, bn.m1.Y, bn.m1.Z, bn.m1.W,
                                          bn.m2.X, bn.m2.Y, bn.m2.Z, bn.m2.W,
                                          bn.m3.X, bn.m3.Y, bn.m3.Z, bn.m3.W,
                                          bn.m4.X * 100, bn.m4.Y * 100, bn.m4.Z * 100, bn.m4.W);
                Matrix4x4.Invert(bnMat, out bnMat);

                //Get local transform
                aiNode.Transform = GetAssimpMat4(bnMat * parentTfm);

                parentNode.Children.Add(aiNode);
                boneArray[i] = aiNode;
            }

            foreach (AquaNode.NODO bn in aqn.nodoList)
            {
                var parentNodo = boneArray[bn.parentId];
                var aiNode     = new Assimp.Node(bn.boneName.GetString(), parentNodo);

                //NODOs are a bit more primitive. We need to generate the matrix for these ones.
                var matrix   = Assimp.Matrix4x4.Identity;
                var rotation = Assimp.Matrix4x4.FromRotationX(bn.eulRot.X) *
                               Assimp.Matrix4x4.FromRotationY(bn.eulRot.Y) *
                               Assimp.Matrix4x4.FromRotationZ(bn.eulRot.Z);

                matrix          *= rotation;
                matrix          *= Assimp.Matrix4x4.FromTranslation(new Assimp.Vector3D(bn.pos.X * 100, bn.pos.Y * 100, bn.pos.Z * 100));
                aiNode.Transform = matrix;

                parentNodo.Children.Add(aiNode);
            }

            //Assign meshes and materials
            foreach (AquaObject.MESH msh in aqp.meshList)
            {
                var vtxl = aqp.vtxlList[msh.vsetIndex];

                //Mesh
                var  aiMeshName       = string.Format("mesh[{4}]_{0}_{1}_{2}_{3}_mesh", msh.mateIndex, msh.rendIndex, msh.shadIndex, msh.tsetIndex, aiScene.Meshes.Count);
                bool hasVertexWeights = aqp.vtxlList[msh.vsetIndex].vertWeightIndices.Count > 0;

                var aiMesh = new Assimp.Mesh(aiMeshName, Assimp.PrimitiveType.Triangle);

                //Vertex face data - PSO2 Actually doesn't do this, it just has per vertex data so we can just map a vertice's data to each face using it
                //It may actually be possible to add this to the previous loop, but my reference didn't so I'm doing it in a separate loop for safety
                //Reference: https://github.com/TGEnigma/Amicitia/blob/master/Source/AmicitiaLibrary/Graphics/RenderWare/RWClumpNode.cs
                //UVs will have dummied data to ensure that if the game arbitrarily writes them, they will still be exported back in the same order
                for (int vertId = 0; vertId < vtxl.vertPositions.Count; vertId++)
                {
                    if (vtxl.vertPositions.Count > 0)
                    {
                        var pos = vtxl.vertPositions[vertId] * 100;
                        aiMesh.Vertices.Add(new Assimp.Vector3D(pos.X, pos.Y, pos.Z));
                    }

                    if (vtxl.vertNormals.Count > 0)
                    {
                        var nrm = vtxl.vertNormals[vertId];
                        aiMesh.Normals.Add(new Assimp.Vector3D(nrm.X, nrm.Y, nrm.Z));
                    }

                    if (vtxl.vertColors.Count > 0)
                    {
                        //Vert colors are bgra
                        var rawClr = vtxl.vertColors[vertId];
                        var clr    = new Assimp.Color4D(clrToFloat(rawClr[2]), clrToFloat(rawClr[1]), clrToFloat(rawClr[0]), clrToFloat(rawClr[3]));
                        aiMesh.VertexColorChannels[0].Add(clr);
                    }

                    if (vtxl.vertColor2s.Count > 0)
                    {
                        //Vert colors are bgra
                        var rawClr = vtxl.vertColor2s[vertId];
                        var clr    = new Assimp.Color4D(clrToFloat(rawClr[2]), clrToFloat(rawClr[1]), clrToFloat(rawClr[0]), clrToFloat(rawClr[3]));
                        aiMesh.VertexColorChannels[1].Add(clr);
                    }

                    if (vtxl.uv1List.Count > 0)
                    {
                        var textureCoordinate   = vtxl.uv1List[vertId];
                        var aiTextureCoordinate = new Assimp.Vector3D(textureCoordinate.X, textureCoordinate.Y, 0f);
                        aiMesh.TextureCoordinateChannels[0].Add(aiTextureCoordinate);
                    }
                    else
                    {
                        var aiTextureCoordinate = new Assimp.Vector3D(0, 0, 0f);
                        aiMesh.TextureCoordinateChannels[0].Add(aiTextureCoordinate);
                    }

                    if (vtxl.uv2List.Count > 0)
                    {
                        var textureCoordinate   = vtxl.uv2List[vertId];
                        var aiTextureCoordinate = new Assimp.Vector3D(textureCoordinate.X, textureCoordinate.Y, 0f);
                        aiMesh.TextureCoordinateChannels[1].Add(aiTextureCoordinate);
                    }
                    else
                    {
                        var aiTextureCoordinate = new Assimp.Vector3D(0, 0, 0f);
                        aiMesh.TextureCoordinateChannels[1].Add(aiTextureCoordinate);
                    }

                    if (vtxl.uv3List.Count > 0)
                    {
                        var textureCoordinate   = vtxl.uv3List[vertId];
                        var aiTextureCoordinate = new Assimp.Vector3D(textureCoordinate.X, textureCoordinate.Y, 0f);
                        aiMesh.TextureCoordinateChannels[2].Add(aiTextureCoordinate);
                    }
                    else
                    {
                        var aiTextureCoordinate = new Assimp.Vector3D(0, 0, 0f);
                        aiMesh.TextureCoordinateChannels[2].Add(aiTextureCoordinate);
                    }

                    if (vtxl.uv4List.Count > 0)
                    {
                        var textureCoordinate   = vtxl.uv4List[vertId];
                        var aiTextureCoordinate = new Assimp.Vector3D(textureCoordinate.X, textureCoordinate.Y, 0f);
                        aiMesh.TextureCoordinateChannels[3].Add(aiTextureCoordinate);
                    }
                    else
                    {
                        var aiTextureCoordinate = new Assimp.Vector3D(0, 0, 0f);
                        aiMesh.TextureCoordinateChannels[3].Add(aiTextureCoordinate);
                    }

                    if (vtxl.vert0x22.Count > 0)
                    {
                        var textureCoordinate   = vtxl.vert0x22[vertId];
                        var aiTextureCoordinate = new Assimp.Vector3D(uvShortToFloat(textureCoordinate[0]), uvShortToFloat(textureCoordinate[1]), 0f);
                        aiMesh.TextureCoordinateChannels[4].Add(aiTextureCoordinate);
                    }
                    else
                    {
                        var aiTextureCoordinate = new Assimp.Vector3D(0, 0, 0f);
                        aiMesh.TextureCoordinateChannels[4].Add(aiTextureCoordinate);
                    }

                    if (vtxl.vert0x23.Count > 0)
                    {
                        var textureCoordinate   = vtxl.vert0x23[vertId];
                        var aiTextureCoordinate = new Assimp.Vector3D(uvShortToFloat(textureCoordinate[0]), uvShortToFloat(textureCoordinate[1]), 0f);
                        aiMesh.TextureCoordinateChannels[5].Add(aiTextureCoordinate);
                    }
                    else
                    {
                        var aiTextureCoordinate = new Assimp.Vector3D(0, 0, 0f);
                        aiMesh.TextureCoordinateChannels[5].Add(aiTextureCoordinate);
                    }

                    if (vtxl.vert0x24.Count > 0)
                    {
                        var textureCoordinate   = vtxl.vert0x24[vertId];
                        var aiTextureCoordinate = new Assimp.Vector3D(uvShortToFloat(textureCoordinate[0]), uvShortToFloat(textureCoordinate[1]), 0f);
                        aiMesh.TextureCoordinateChannels[6].Add(aiTextureCoordinate);
                    }
                    else
                    {
                        var aiTextureCoordinate = new Assimp.Vector3D(0, 0, 0f);
                        aiMesh.TextureCoordinateChannels[6].Add(aiTextureCoordinate);
                    }

                    if (vtxl.vert0x25.Count > 0)
                    {
                        var textureCoordinate   = vtxl.vert0x25[vertId];
                        var aiTextureCoordinate = new Assimp.Vector3D(uvShortToFloat(textureCoordinate[0]), uvShortToFloat(textureCoordinate[1]), 0f);
                        aiMesh.TextureCoordinateChannels[7].Add(aiTextureCoordinate);
                    }
                    else
                    {
                        var aiTextureCoordinate = new Assimp.Vector3D(0, 0, 0f);
                        aiMesh.TextureCoordinateChannels[7].Add(aiTextureCoordinate);
                    }
                }

                //Assimp Bones - Assimp likes to store vertex weights in bones and bones references in meshes
                if (hasVertexWeights)
                {
                    //Get bone palette
                    List <uint> bonePalette;
                    if (aqp.objc.bonePaletteOffset > 0)
                    {
                        bonePalette = aqp.bonePalette;
                    }
                    else
                    {
                        bonePalette = new List <uint>();
                        for (int bn = 0; bn < vtxl.bonePalette.Count; bn++)
                        {
                            bonePalette.Add(vtxl.bonePalette[bn]);
                        }
                    }
                    var aiBoneMap = new Dictionary <int, Assimp.Bone>();

                    //Iterate through vertices
                    for (int vertId = 0; vertId < vtxl.vertWeightIndices.Count; vertId++)
                    {
                        var boneIndices = vtxl.vertWeightIndices[vertId];
                        var boneWeights = Vector4ToFloatArray(vtxl.vertWeights[vertId]);

                        //Iterate through weights
                        for (int wt = 0; wt < 4; wt++)
                        {
                            var boneIndex  = boneIndices[wt];
                            var boneWeight = boneWeights[wt];

                            if (boneWeight == 0.0f)
                            {
                                continue;
                            }

                            if (!aiBoneMap.Keys.Contains(boneIndex))
                            {
                                var aiBone  = new Assimp.Bone();
                                var aqnBone = boneArray[bonePalette[boneIndex]];
                                var rawBone = aqn.nodeList[(int)bonePalette[boneIndex]];

                                aiBone.Name = $"({bonePalette[boneIndex]})" + rawBone.boneName.GetString();
                                aiBone.VertexWeights.Add(new Assimp.VertexWeight(vertId, boneWeight));

                                var invTransform = new Assimp.Matrix4x4(rawBone.m1.X, rawBone.m2.X, rawBone.m3.X, rawBone.m4.X,
                                                                        rawBone.m1.Y, rawBone.m2.Y, rawBone.m3.Y, rawBone.m4.Y,
                                                                        rawBone.m1.Z, rawBone.m2.Z, rawBone.m3.Z, rawBone.m4.Z,
                                                                        rawBone.m1.W, rawBone.m2.W, rawBone.m3.W, rawBone.m4.W);

                                aiBone.OffsetMatrix = invTransform;

                                aiBoneMap[boneIndex] = aiBone;
                            }

                            if (!aiBoneMap[boneIndex].VertexWeights.Any(x => x.VertexID == vertId))
                            {
                                aiBoneMap[boneIndex].VertexWeights.Add(new Assimp.VertexWeight(vertId, boneWeight));
                            }
                        }
                    }

                    //Add the bones to the mesh
                    aiMesh.Bones.AddRange(aiBoneMap.Values);
                }
                else   //Handle rigid meshes
                {
                    var aiBone  = new Assimp.Bone();
                    var aqnBone = boneArray[msh.baseMeshNodeId];

                    // Name
                    aiBone.Name = aqnBone.Name;

                    // VertexWeights
                    for (int i = 0; i < aiMesh.Vertices.Count; i++)
                    {
                        var aiVertexWeight = new Assimp.VertexWeight(i, 1f);
                        aiBone.VertexWeights.Add(aiVertexWeight);
                    }

                    aiBone.OffsetMatrix = Assimp.Matrix4x4.Identity;

                    aiMesh.Bones.Add(aiBone);
                }

                //Faces
                foreach (var face in aqp.strips[msh.vsetIndex].GetTriangles(true))
                {
                    aiMesh.Faces.Add(new Assimp.Face(new int[] { (int)face.X, (int)face.Y, (int)face.Z }));
                }

                //Material
                var             mat        = aqp.mateList[msh.mateIndex];
                var             shaderSet  = AquaObjectMethods.GetShaderNames(aqp, msh.shadIndex);
                var             textureSet = AquaObjectMethods.GetTexListNames(aqp, msh.tsetIndex);
                Assimp.Material mate       = new Assimp.Material();

                mate.ColorDiffuse = new Assimp.Color4D(mat.diffuseRGBA.X, mat.diffuseRGBA.Y, mat.diffuseRGBA.Z, mat.diffuseRGBA.W);
                if (mat.alphaType.GetString().Equals("add"))
                {
                    mate.BlendMode = Assimp.BlendMode.Additive;
                }
                mate.Name = "|[]{}~`!@#$%^&*;:'\"?><,./(" + shaderSet[0] + "," + shaderSet[1] + ")" + "{" + mat.alphaType.GetString() + "}" + mat.matName.GetString();

                //Set textures - PSO2 Texture slots are NOT consistent and depend entirely on the selected shader. As such, slots will be somewhat arbitrary after albedo/diffuse
                for (int i = 0; i < textureSet.Count; i++)
                {
                    switch (i)
                    {
                    case 0:
                        mate.TextureDiffuse = new Assimp.TextureSlot(
                            textureSet[i], Assimp.TextureType.Diffuse, i, Assimp.TextureMapping.FromUV, aqp.tstaList[aqp.tsetList[msh.tsetIndex].tstaTexIDs[i]].modelUVSet, 0,
                            Assimp.TextureOperation.Add, Assimp.TextureWrapMode.Wrap, Assimp.TextureWrapMode.Wrap, 0);
                        break;

                    case 1:
                        mate.TextureSpecular = new Assimp.TextureSlot(
                            textureSet[i], Assimp.TextureType.Specular, i, Assimp.TextureMapping.FromUV, aqp.tstaList[aqp.tsetList[msh.tsetIndex].tstaTexIDs[i]].modelUVSet, 0,
                            Assimp.TextureOperation.Add, Assimp.TextureWrapMode.Wrap, Assimp.TextureWrapMode.Wrap, 0);
                        break;

                    case 2:
                        mate.TextureNormal = new Assimp.TextureSlot(
                            textureSet[i], Assimp.TextureType.Normals, i, Assimp.TextureMapping.FromUV, aqp.tstaList[aqp.tsetList[msh.tsetIndex].tstaTexIDs[i]].modelUVSet, 0,
                            Assimp.TextureOperation.Add, Assimp.TextureWrapMode.Wrap, Assimp.TextureWrapMode.Wrap, 0);
                        break;

                    case 3:
                        mate.TextureLightMap = new Assimp.TextureSlot(
                            textureSet[i], Assimp.TextureType.Lightmap, i, Assimp.TextureMapping.FromUV, aqp.tstaList[aqp.tsetList[msh.tsetIndex].tstaTexIDs[i]].modelUVSet, 0,
                            Assimp.TextureOperation.Add, Assimp.TextureWrapMode.Wrap, Assimp.TextureWrapMode.Wrap, 0);
                        break;

                    case 4:
                        mate.TextureDisplacement = new Assimp.TextureSlot(
                            textureSet[i], Assimp.TextureType.Displacement, i, Assimp.TextureMapping.FromUV, aqp.tstaList[aqp.tsetList[msh.tsetIndex].tstaTexIDs[i]].modelUVSet, 0,
                            Assimp.TextureOperation.Add, Assimp.TextureWrapMode.Wrap, Assimp.TextureWrapMode.Wrap, 0);
                        break;

                    case 5:
                        mate.TextureOpacity = new Assimp.TextureSlot(
                            textureSet[i], Assimp.TextureType.Opacity, i, Assimp.TextureMapping.FromUV, aqp.tstaList[aqp.tsetList[msh.tsetIndex].tstaTexIDs[i]].modelUVSet, 0,
                            Assimp.TextureOperation.Add, Assimp.TextureWrapMode.Wrap, Assimp.TextureWrapMode.Wrap, 0);
                        break;

                    case 6:
                        mate.TextureHeight = new Assimp.TextureSlot(
                            textureSet[i], Assimp.TextureType.Height, i, Assimp.TextureMapping.FromUV, aqp.tstaList[aqp.tsetList[msh.tsetIndex].tstaTexIDs[i]].modelUVSet, 0,
                            Assimp.TextureOperation.Add, Assimp.TextureWrapMode.Wrap, Assimp.TextureWrapMode.Wrap, 0);
                        break;

                    case 7:
                        mate.TextureEmissive = new Assimp.TextureSlot(
                            textureSet[i], Assimp.TextureType.Emissive, i, Assimp.TextureMapping.FromUV, aqp.tstaList[aqp.tsetList[msh.tsetIndex].tstaTexIDs[i]].modelUVSet, 0,
                            Assimp.TextureOperation.Add, Assimp.TextureWrapMode.Wrap, Assimp.TextureWrapMode.Wrap, 0);
                        break;

                    case 8:
                        mate.TextureAmbient = new Assimp.TextureSlot(
                            textureSet[i], Assimp.TextureType.Ambient, i, Assimp.TextureMapping.FromUV, aqp.tstaList[aqp.tsetList[msh.tsetIndex].tstaTexIDs[i]].modelUVSet, 0,
                            Assimp.TextureOperation.Add, Assimp.TextureWrapMode.Wrap, Assimp.TextureWrapMode.Wrap, 0);
                        break;

                    case 9:
                        mate.TextureReflection = new Assimp.TextureSlot(
                            textureSet[i], Assimp.TextureType.Reflection, i, Assimp.TextureMapping.FromUV, aqp.tstaList[aqp.tsetList[msh.tsetIndex].tstaTexIDs[i]].modelUVSet, 0,
                            Assimp.TextureOperation.Add, Assimp.TextureWrapMode.Wrap, Assimp.TextureWrapMode.Wrap, 0);
                        break;

                    default:
                        break;
                    }
                }

                mate.ShadingMode = Assimp.ShadingMode.Phong;


                var meshNodeName = string.Format("mesh[{4}]_{0}_{1}_{2}_{3}#{4}#{5}", msh.mateIndex, msh.rendIndex, msh.shadIndex, msh.tsetIndex, aiScene.Meshes.Count, msh.baseMeshNodeId, msh.baseMeshDummyId);

                // Add mesh to meshes
                aiScene.Meshes.Add(aiMesh);

                // Add material to materials
                aiScene.Materials.Add(mate);

                // MaterialIndex
                aiMesh.MaterialIndex = aiScene.Materials.Count - 1;

                // Set up mesh node and add this mesh's index to it (This tells assimp to export it as a mesh for various formats)
                var meshNode = new Assimp.Node(meshNodeName, aiScene.RootNode);
                meshNode.Transform = Assimp.Matrix4x4.Identity;

                aiScene.RootNode.Children.Add(meshNode);

                meshNode.MeshIndices.Add(aiScene.Meshes.Count - 1);
            }

            return(aiScene);
        }