Exemplo n.º 1
0
        public H3D ToH3D(string TextureSearchPath = null)
        {
            H3D Output = new H3D();

            H3DModel Model = new H3DModel();

            Model.Name = "Model";

            ushort MaterialIndex = 0;

            if (Skeleton.Count > 0)
            {
                Model.Flags = H3DModelFlags.HasSkeleton;
            }

            Model.BoneScaling = H3DBoneScaling.Maya;
            Model.MeshNodesVisibility.Add(true);

            foreach (SMDMesh Mesh in Meshes)
            {
                Vector3 MinVector = new Vector3();
                Vector3 MaxVector = new Vector3();

                Dictionary <PICAVertex, int> Vertices = new Dictionary <PICAVertex, int>();

                List <H3DSubMesh> SubMeshes = new List <H3DSubMesh>();

                Queue <PICAVertex> VerticesQueue = new Queue <PICAVertex>();

                foreach (PICAVertex Vertex in Mesh.Vertices)
                {
                    VerticesQueue.Enqueue(Vertex);
                }

                while (VerticesQueue.Count > 2)
                {
                    List <ushort> Indices     = new List <ushort>();
                    List <ushort> BoneIndices = new List <ushort>();

                    int TriCount = VerticesQueue.Count / 3;

                    while (TriCount-- > 0)
                    {
                        PICAVertex[] Triangle = new PICAVertex[3];

                        Triangle[0] = VerticesQueue.Dequeue();
                        Triangle[1] = VerticesQueue.Dequeue();
                        Triangle[2] = VerticesQueue.Dequeue();

                        List <ushort> TempIndices = new List <ushort>();

                        for (int Tri = 0; Tri < 3; Tri++)
                        {
                            PICAVertex Vertex = Triangle[Tri];

                            for (int i = 0; i < 4; i++)
                            {
                                ushort Index = (ushort)Vertex.Indices[i];

                                if (!(BoneIndices.Contains(Index) || TempIndices.Contains(Index)))
                                {
                                    TempIndices.Add(Index);
                                }
                            }
                        }

                        if (BoneIndices.Count + TempIndices.Count > 20)
                        {
                            VerticesQueue.Enqueue(Triangle[0]);
                            VerticesQueue.Enqueue(Triangle[1]);
                            VerticesQueue.Enqueue(Triangle[2]);

                            continue;
                        }

                        for (int Tri = 0; Tri < 3; Tri++)
                        {
                            PICAVertex Vertex = Triangle[Tri];

                            for (int Index = 0; Index < 4; Index++)
                            {
                                int BoneIndex = BoneIndices.IndexOf((ushort)Vertex.Indices[Index]);

                                if (BoneIndex == -1)
                                {
                                    BoneIndex = BoneIndices.Count;
                                    BoneIndices.Add((ushort)Vertex.Indices[Index]);
                                }

                                Vertex.Indices[Index] = BoneIndex;
                            }

                            if (Vertices.ContainsKey(Vertex))
                            {
                                Indices.Add((ushort)Vertices[Vertex]);
                            }
                            else
                            {
                                Indices.Add((ushort)Vertices.Count);

                                if (Vertex.Position.X < MinVector.X)
                                {
                                    MinVector.X = Vertex.Position.X;
                                }
                                if (Vertex.Position.Y < MinVector.Y)
                                {
                                    MinVector.Y = Vertex.Position.Y;
                                }
                                if (Vertex.Position.Z < MinVector.Z)
                                {
                                    MinVector.Z = Vertex.Position.Z;
                                }

                                if (Vertex.Position.X > MaxVector.X)
                                {
                                    MaxVector.X = Vertex.Position.X;
                                }
                                if (Vertex.Position.Y > MaxVector.Y)
                                {
                                    MaxVector.Y = Vertex.Position.Y;
                                }
                                if (Vertex.Position.Z > MaxVector.Z)
                                {
                                    MaxVector.Z = Vertex.Position.Z;
                                }

                                Vertices.Add(Vertex, Vertices.Count);
                            }
                        }
                    }

                    SubMeshes.Add(new H3DSubMesh()
                    {
                        Skinning         = H3DSubMeshSkinning.Smooth,
                        BoneIndicesCount = (ushort)BoneIndices.Count,
                        BoneIndices      = BoneIndices.ToArray(),
                        Indices          = Indices.ToArray()
                    });
                }

                List <PICAAttribute> Attributes = PICAAttribute.GetAttributes(
                    PICAAttributeName.Position,
                    PICAAttributeName.Normal,
                    PICAAttributeName.Color,
                    PICAAttributeName.TexCoord0,
                    PICAAttributeName.BoneIndex,
                    PICAAttributeName.BoneWeight);

                //Mesh
                H3DMesh M = new H3DMesh(Vertices.Keys, Attributes, SubMeshes)
                {
                    Skinning      = H3DMeshSkinning.Smooth,
                    MeshCenter    = (MinVector + MaxVector) * 0.5f,
                    MaterialIndex = MaterialIndex
                };

                //Material
                string TexName = Path.GetFileNameWithoutExtension(Mesh.MaterialName);
                string MatName = $"Mat{MaterialIndex++.ToString("D5")}_{TexName}";

                H3DMaterial Material = H3DMaterial.GetSimpleMaterial(Model.Name, MatName, TexName);

                Model.Materials.Add(Material);

                if (TextureSearchPath != null && !Output.Textures.Contains(TexName))
                {
                    string TextureFile = Path.Combine(TextureSearchPath, Mesh.MaterialName);

                    if (File.Exists(TextureFile))
                    {
                        Output.Textures.Add(new H3DTexture(TextureFile));
                    }
                }

                M.UpdateBoolUniforms(Material);

                Model.AddMesh(M);
            }

            //Build Skeleton
            foreach (SMDBone Bone in Skeleton)
            {
                SMDNode Node = Nodes[Bone.NodeIndex];

                Model.Skeleton.Add(new H3DBone()
                {
                    Name        = Node.Name,
                    ParentIndex = (short)Node.ParentIndex,
                    Translation = Bone.Translation,
                    Rotation    = Bone.Rotation,
                    Scale       = Vector3.One
                });
            }

            //Calculate Absolute Inverse Transforms for all bones
            foreach (H3DBone Bone in Model.Skeleton)
            {
                Bone.CalculateTransform(Model.Skeleton);
            }

            Output.Models.Add(Model);

            Output.CopyMaterials();

            return(Output);
        }
Exemplo n.º 2
0
        public H3DModel ToH3DModel()
        {
            H3DModel Output = new H3DModel()
            {
                Name = Name
            };

            //Skeleton
            foreach (GFBone Bone in Skeleton)
            {
                Output.Skeleton.Add(new H3DBone()
                {
                    ParentIndex = (short)Skeleton.FindIndex(x => x.Name == Bone.Parent),

                    Name        = Bone.Name,
                    Scale       = Bone.Scale,
                    Rotation    = Bone.Rotation,
                    Translation = Bone.Translation
                });
            }

            foreach (H3DBone Bone in Output.Skeleton)
            {
                Bone.CalculateTransform(Output.Skeleton);

                Bone.Flags |= H3DBoneFlags.IsSegmentScaleCompensate;
            }

            if (Output.Skeleton.Count > 0)
            {
                Output.Flags = H3DModelFlags.HasSkeleton;
            }

            //Materials
            foreach (GFMaterial Material in Materials)
            {
                H3DMaterial Mat = new H3DMaterial();

                H3DMaterialParams Params = Mat.MaterialParams;

                Mat.Name = Material.MaterialName;

                Params.FragmentFlags = H3DFragmentFlags.IsLUTReflectionEnabled;

                Array.Copy(Material.TextureSources, Params.TextureSources, 4);

                for (int Unit = 0; Unit < Material.TextureCoords.Length; Unit++)
                {
                    string TextureName = Material.TextureCoords[Unit].Name;

                    Mat.EnabledTextures[Unit] = TextureName != null;

                    switch (Unit)
                    {
                    case 0: Mat.Texture0Name = TextureName; break;

                    case 1: Mat.Texture1Name = TextureName; break;

                    case 2: Mat.Texture2Name = TextureName; break;
                    }

                    //Texture Coords
                    GFTextureMappingType MappingType = Material.TextureCoords[Unit].MappingType;

                    Params.TextureCoords[Unit].MappingType = (H3DTextureMappingType)MappingType;

                    Params.TextureCoords[Unit].Scale       = Material.TextureCoords[Unit].Scale;
                    Params.TextureCoords[Unit].Rotation    = Material.TextureCoords[Unit].Rotation;
                    Params.TextureCoords[Unit].Translation = Material.TextureCoords[Unit].Translation;

                    //Texture Mapper
                    Mat.TextureMappers[Unit].WrapU = (PICATextureWrap)Material.TextureCoords[Unit].WrapU;
                    Mat.TextureMappers[Unit].WrapV = (PICATextureWrap)Material.TextureCoords[Unit].WrapV;

                    Mat.TextureMappers[Unit].MagFilter = (H3DTextureMagFilter)Material.TextureCoords[Unit].MagFilter;
                    Mat.TextureMappers[Unit].MinFilter = (H3DTextureMinFilter)Material.TextureCoords[Unit].MinFilter;

                    Mat.TextureMappers[Unit].MinLOD = (byte)Material.TextureCoords[Unit].MinLOD;

                    Mat.TextureMappers[Unit].BorderColor = Material.BorderColor[Unit];
                }

                Params.EmissionColor  = Material.EmissionColor;
                Params.AmbientColor   = Material.AmbientColor;
                Params.DiffuseColor   = Material.DiffuseColor;
                Params.Specular0Color = Material.Specular0Color;
                Params.Specular1Color = Material.Specular1Color;
                Params.Constant0Color = Material.Constant0Color;
                Params.Constant1Color = Material.Constant1Color;
                Params.Constant2Color = Material.Constant2Color;
                Params.Constant3Color = Material.Constant3Color;
                Params.Constant4Color = Material.Constant4Color;
                Params.Constant5Color = Material.Constant5Color;
                Params.BlendColor     = Material.BlendColor;

                //HACK: It's usually 0 on Sun/Moon, this causes issues on some
                //models being rendered transparent (Shader differences).
                Params.DiffuseColor.A = 0xff;

                Params.ColorScale = 1f;

                Params.LUTInputAbsolute  = Material.LUTInputAbsolute;
                Params.LUTInputSelection = Material.LUTInputSelection;
                Params.LUTInputScale     = Material.LUTInputScale;

                Params.ColorOperation   = Material.ColorOperation;
                Params.BlendFunction    = Material.BlendFunction;
                Params.LogicalOperation = Material.LogicalOperation;
                Params.AlphaTest        = Material.AlphaTest;
                Params.StencilTest      = Material.StencilTest;
                Params.StencilOperation = Material.StencilOperation;
                Params.DepthColorMask   = Material.DepthColorMask;
                Params.FaceCulling      = Material.FaceCulling;

                Params.ColorBufferRead  = Material.ColorBufferRead;
                Params.ColorBufferWrite = Material.ColorBufferWrite;

                Params.StencilBufferRead  = Material.StencilBufferRead;
                Params.StencilBufferWrite = Material.StencilBufferWrite;

                Params.DepthBufferRead  = Material.DepthBufferRead;
                Params.DepthBufferWrite = Material.DepthBufferWrite;

                if (Material.LUT0HashId != 0)
                {
                    Params.LUTReflecRTableName   = DefaultLUTName;
                    Params.LUTReflecRSamplerName = GetLUTName(Material.LUT0HashId);
                }

                if (Material.LUT1HashId != 0)
                {
                    Params.LUTReflecGTableName   = DefaultLUTName;
                    Params.LUTReflecGSamplerName = GetLUTName(Material.LUT1HashId);
                }

                if (Material.LUT2HashId != 0)
                {
                    Params.LUTReflecBTableName   = DefaultLUTName;
                    Params.LUTReflecBSamplerName = GetLUTName(Material.LUT2HashId);
                }

                if (Material.BumpTexture != -1)
                {
                    Params.BumpTexture = (byte)Material.BumpTexture;
                    Params.BumpMode    = H3DBumpMode.AsBump;
                }

                Params.Constant0Assignment = Material.Constant0Assignment;
                Params.Constant1Assignment = Material.Constant1Assignment;
                Params.Constant2Assignment = Material.Constant2Assignment;
                Params.Constant3Assignment = Material.Constant3Assignment;
                Params.Constant4Assignment = Material.Constant4Assignment;
                Params.Constant5Assignment = Material.Constant5Assignment;

                string VtxShaderName = Material.VtxShaderName;

                //Make shader names match X/Y/OR/AS shader names.
                if (VtxShaderName == "Poke" ||
                    VtxShaderName == "PokeNormal")
                {
                    VtxShaderName = "PokePack";
                }

                Params.ShaderReference = $"0@{VtxShaderName}";
                Params.ModelReference  = $"{Mat.Name}@{Name}";

                /*
                 * Add those for compatibility with the older BCH models.
                 * It's worth noting that ShaderParam0 is usually used as "UVScale" on model that uses
                 * geometry shader to make billboarded point sprites. On the new shader it have a
                 * multiplication of the Color by 3, while the older one doesn't have such multiplication,
                 * so for compatibility with the older shader, the easiest thing to do is just multiply the
                 * scale by 3 to give the same results on the old shader.
                 */
                Params.MetaData = new H3DMetaData();

                Params.MetaData.Add(new H3DMetaDataValue("EdgeType", Material.EdgeType));
                Params.MetaData.Add(new H3DMetaDataValue("IDEdgeEnable", Material.IDEdgeEnable));
                Params.MetaData.Add(new H3DMetaDataValue("EdgeID", Material.EdgeID));

                Params.MetaData.Add(new H3DMetaDataValue("ProjectionType", Material.ProjectionType));

                Params.MetaData.Add(new H3DMetaDataValue("RimPow", Material.RimPower));
                Params.MetaData.Add(new H3DMetaDataValue("RimScale", Material.RimScale));
                Params.MetaData.Add(new H3DMetaDataValue("PhongPow", Material.PhongPower));
                Params.MetaData.Add(new H3DMetaDataValue("PhongScale", Material.PhongScale));

                Params.MetaData.Add(new H3DMetaDataValue("IDEdgeOffsetEnable", Material.IDEdgeOffsetEnable));

                Params.MetaData.Add(new H3DMetaDataValue("EdgeMapAlphaMask", Material.EdgeMapAlphaMask));

                Params.MetaData.Add(new H3DMetaDataValue("BakeTexture0", Material.BakeTexture0));
                Params.MetaData.Add(new H3DMetaDataValue("BakeTexture1", Material.BakeTexture1));
                Params.MetaData.Add(new H3DMetaDataValue("BakeTexture2", Material.BakeTexture2));
                Params.MetaData.Add(new H3DMetaDataValue("BakeConstant0", Material.BakeConstant0));
                Params.MetaData.Add(new H3DMetaDataValue("BakeConstant1", Material.BakeConstant1));
                Params.MetaData.Add(new H3DMetaDataValue("BakeConstant2", Material.BakeConstant2));
                Params.MetaData.Add(new H3DMetaDataValue("BakeConstant3", Material.BakeConstant3));
                Params.MetaData.Add(new H3DMetaDataValue("BakeConstant4", Material.BakeConstant4));
                Params.MetaData.Add(new H3DMetaDataValue("BakeConstant5", Material.BakeConstant5));

                Params.MetaData.Add(new H3DMetaDataValue("VertexShaderType", Material.VertexShaderType));

                Params.MetaData.Add(new H3DMetaDataValue("ShaderParam0", Material.ShaderParam0 * 3));
                Params.MetaData.Add(new H3DMetaDataValue("ShaderParam1", Material.ShaderParam1));
                Params.MetaData.Add(new H3DMetaDataValue("ShaderParam2", Material.ShaderParam2));
                Params.MetaData.Add(new H3DMetaDataValue("ShaderParam3", Material.ShaderParam3));

                Output.Materials.Add(Mat);
            }

            //Meshes
            Output.MeshNodesTree = new H3DPatriciaTree();

            foreach (GFMesh Mesh in Meshes)
            {
                //Note: GFModel have one Vertex Buffer for each SubMesh,
                //while on H3D all SubMeshes shares the same Vertex Buffer.
                //For this reason we need to store SubMeshes as Meshes on H3D.
                foreach (GFSubMesh SubMesh in Mesh.SubMeshes)
                {
                    int NodeIndex = Output.MeshNodesTree.Find(Mesh.Name);

                    if (NodeIndex == -1)
                    {
                        Output.MeshNodesTree.Add(Mesh.Name);
                        Output.MeshNodesVisibility.Add(true);

                        NodeIndex = Output.MeshNodesCount++;
                    }

                    List <H3DSubMesh> SubMeshes = new List <H3DSubMesh>();

                    ushort[] BoneIndices = new ushort[SubMesh.BoneIndicesCount];

                    for (int Index = 0; Index < BoneIndices.Length; Index++)
                    {
                        BoneIndices[Index] = SubMesh.BoneIndices[Index];
                    }

                    H3DSubMeshSkinning SMSk = Output.Skeleton.Count > 0
                        ? H3DSubMeshSkinning.Smooth
                        : H3DSubMeshSkinning.None;

                    SubMeshes.Add(new H3DSubMesh()
                    {
                        Skinning         = SMSk,
                        BoneIndicesCount = SubMesh.BoneIndicesCount,
                        BoneIndices      = BoneIndices,
                        Indices          = SubMesh.Indices
                    });

                    H3DMesh M = new H3DMesh(
                        SubMesh.RawBuffer,
                        SubMesh.VertexStride,
                        SubMesh.Attributes,
                        SubMesh.FixedAttributes,
                        SubMeshes);

                    M.Skinning = H3DMeshSkinning.Smooth;

                    int MatIndex = Materials.FindIndex(x => x.MaterialName == SubMesh.Name);

                    GFMaterial Mat = Materials[MatIndex];

                    M.MaterialIndex = (ushort)MatIndex;
                    M.NodeIndex     = (ushort)NodeIndex;
                    M.Layer         = Mat.RenderLayer;
                    M.Priority      = Mat.RenderPriority;

                    M.UpdateBoolUniforms(Output.Materials[MatIndex]);

                    Output.AddMesh(M);
                }
            }

            return(Output);
        }
Exemplo n.º 3
0
        public H3D ToH3D()
        {
            H3D Output = new H3D();

            H3DModel Model = new H3DModel();

            Model.MeshNodesTree = new H3DPatriciaTree();

            Model.Flags = BoneIndicesGroups.Length > 0 ? H3DModelFlags.HasSkeleton : 0;
            Model.Name  = "Model";

            foreach (MTMaterial Mat in Materials)
            {
                H3DMaterial Mtl = H3DMaterial.GetSimpleMaterial(
                    Model.Name,
                    Mat.Name,
                    Path.GetFileNameWithoutExtension(Mat.Texture0Name));

                Mtl.MaterialParams.ColorOperation.BlendMode  = Mat.AlphaBlend.BlendMode;
                Mtl.MaterialParams.BlendFunction             = Mat.AlphaBlend.BlendFunction;
                Mtl.MaterialParams.DepthColorMask.RedWrite   = Mat.AlphaBlend.RedWrite;
                Mtl.MaterialParams.DepthColorMask.GreenWrite = Mat.AlphaBlend.GreenWrite;
                Mtl.MaterialParams.DepthColorMask.BlueWrite  = Mat.AlphaBlend.BlueWrite;
                Mtl.MaterialParams.DepthColorMask.AlphaWrite = Mat.AlphaBlend.AlphaWrite;
                Mtl.MaterialParams.DepthColorMask.Enabled    = Mat.DepthStencil.DepthTest;
                Mtl.MaterialParams.DepthColorMask.DepthWrite = Mat.DepthStencil.DepthWrite;
                Mtl.MaterialParams.DepthColorMask.DepthFunc  = Mat.DepthStencil.DepthFunc;

                Model.Materials.Add(Mtl);
            }

            ushort Index = 0;

            foreach (MTMesh Mesh in Meshes)
            {
                if (Mesh.RenderType != -1)
                {
                    continue;
                }

                H3DMesh M = new H3DMesh(
                    Mesh.RawBuffer,
                    Mesh.VertexStride,
                    Mesh.Attributes,
                    null,
                    null)
                {
                    MaterialIndex = (ushort)Mesh.MaterialIndex,
                    NodeIndex     = Index,
                    Priority      = Mesh.RenderPriority
                };

                byte[] BoneIndices = BoneIndicesGroups[Mesh.BoneIndicesIndex];

                if ((Model.Flags & H3DModelFlags.HasSkeleton) != 0 && BoneIndices.Length > 0)
                {
                    M.Skinning = H3DMeshSkinning.Smooth;

                    PICAVertex[] Vertices = M.GetVertices();

                    for (int v = 0; v < Vertices.Length; v++)
                    {
                        Vector4 Position = Vector4.Zero;

                        float WeightSum = 0;

                        for (int i = 0; i < 4; i++)
                        {
                            if (Vertices[v].Weights[i] == 0)
                            {
                                break;
                            }

                            WeightSum += Vertices[v].Weights[i];

                            int bi = BoneIndicesGroups[Mesh.BoneIndicesIndex][Vertices[v].Indices[i]];

                            Vector4 Trans = Vector4.Zero;

                            for (int b = bi; b != -1; b = Skeleton[b].ParentIndex)
                            {
                                Trans += new Vector4(
                                    Skeleton[b].LocalTransform.M41,
                                    Skeleton[b].LocalTransform.M42,
                                    Skeleton[b].LocalTransform.M43, 0);
                            }

                            Matrix4x4 WT = Skeleton[bi].WorldTransform;

                            Vector3 P = new Vector3(
                                Vertices[v].Position.X,
                                Vertices[v].Position.Y,
                                Vertices[v].Position.Z);

                            Vector4 TP = Vector4.Transform(P, WT);

                            Position += (TP + Trans) * Vertices[v].Weights[i];
                        }

                        if (WeightSum < 1)
                        {
                            Position += Vertices[v].Position * (1 - WeightSum);
                        }

                        Vertices[v].Position = Position;
                    }

                    /*
                     * Removes unused bone from bone indices list, also splits sub meshes on exceeding bones if
                     * current Mesh uses more than 20 (BCH only supports up to 20).
                     */
                    Queue <ushort> IndicesQueue = new Queue <ushort>(Mesh.Indices);

                    while (IndicesQueue.Count > 0)
                    {
                        int Count = IndicesQueue.Count / 3;

                        List <ushort> Indices = new List <ushort>();
                        List <int>    Bones   = new List <int>();

                        while (Count-- > 0)
                        {
                            ushort i0 = IndicesQueue.Dequeue();
                            ushort i1 = IndicesQueue.Dequeue();
                            ushort i2 = IndicesQueue.Dequeue();

                            List <int> TempBones = new List <int>(12);

                            for (int j = 0; j < 4; j++)
                            {
                                int b0 = Vertices[i0].Indices[j];
                                int b1 = Vertices[i1].Indices[j];
                                int b2 = Vertices[i2].Indices[j];

                                if (!(Bones.Contains(b0) || TempBones.Contains(b0)))
                                {
                                    TempBones.Add(b0);
                                }
                                if (!(Bones.Contains(b1) || TempBones.Contains(b1)))
                                {
                                    TempBones.Add(b1);
                                }
                                if (!(Bones.Contains(b2) || TempBones.Contains(b2)))
                                {
                                    TempBones.Add(b2);
                                }
                            }

                            if (Bones.Count + TempBones.Count > 20)
                            {
                                IndicesQueue.Enqueue(i0);
                                IndicesQueue.Enqueue(i1);
                                IndicesQueue.Enqueue(i2);
                            }
                            else
                            {
                                Indices.Add(i0);
                                Indices.Add(i1);
                                Indices.Add(i2);

                                Bones.AddRange(TempBones);
                            }
                        }

                        H3DSubMesh SM = new H3DSubMesh();

                        SM.Skinning         = H3DSubMeshSkinning.Smooth;
                        SM.Indices          = Indices.ToArray();
                        SM.BoneIndicesCount = (ushort)Bones.Count;

                        for (int i = 0; i < Bones.Count; i++)
                        {
                            SM.BoneIndices[i] = BoneIndices[Bones[i]];
                        }

                        bool[] Visited = new bool[Vertices.Length];

                        foreach (ushort i in Indices)
                        {
                            if (!Visited[i])
                            {
                                Visited[i] = true;

                                Vertices[i].Indices[0] = Bones.IndexOf(Vertices[i].Indices[0]);
                                Vertices[i].Indices[1] = Bones.IndexOf(Vertices[i].Indices[1]);
                                Vertices[i].Indices[2] = Bones.IndexOf(Vertices[i].Indices[2]);
                                Vertices[i].Indices[3] = Bones.IndexOf(Vertices[i].Indices[3]);
                            }
                        }

                        M.SubMeshes.Add(SM);
                    }

                    M.RawBuffer = VerticesConverter.GetBuffer(Vertices, M.Attributes);
                }
                else
                {
                    M.SubMeshes.Add(new H3DSubMesh()
                    {
                        Indices = Mesh.Indices
                    });
                }

                Model.AddMesh(M);

                Model.MeshNodesTree.Add($"Mesh_{Index++}");

                Model.MeshNodesVisibility.Add(true);
            }

            int BoneIndex = 0;

            foreach (MTBone Bone in Skeleton)
            {
                Model.Skeleton.Add(new H3DBone()
                {
                    Name        = $"Bone_{BoneIndex++}",
                    ParentIndex = Bone.ParentIndex,
                    Translation = Bone.Position,
                    Scale       = Vector3.One
                });
            }

            foreach (H3DBone Bone in Model.Skeleton)
            {
                Bone.CalculateTransform(Model.Skeleton);
            }

            if (Model.Materials.Count == 0)
            {
                Model.Materials.Add(H3DMaterial.GetSimpleMaterial(Model.Name, "DummyMaterial", null));
            }

            Output.Models.Add(Model);

            Output.CopyMaterials();

            return(Output);
        }