示例#1
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="mesh"></param>
        /// <returns></returns>
        private Node ProcessMesh(IOMesh mesh, IOModel model)
        {
            Node n = new Node()
            {
                Name = mesh.Name,
                sID  = mesh.Name,
                ID   = mesh.Name,
                Type = Node_Type.NODE
            };

            var materials = mesh.Polygons.Select(e => e.MaterialName).Distinct();

            if (mesh.HasEnvelopes())
            {
                var geom = new Instance_Controller();

                geom.URL = "#" + GenerateGeometryController(mesh, model.Skeleton);

                n.Instance_Controller = new Instance_Controller[] { geom };

                n.Instance_Controller[0].Bind_Material = new IONET.Collada.FX.Materials.Bind_Material[]
                {
                    new Bind_Material()
                    {
                        Technique_Common = new FX.Technique_Common.Technique_Common_Bind_Material()
                        {
                            Instance_Material = materials.Select(e => new Instance_Material_Geometry()
                            {
                                Symbol = e, Target = "#" + e
                            }).ToArray()
                        }
                    }
                };
            }
            else
            {
                var geom = new Instance_Geometry();

                geom.URL = "#" + GenerateGeometry(mesh);

                n.Instance_Geometry = new Instance_Geometry[] { geom };

                n.Instance_Geometry[0].Bind_Material = new IONET.Collada.FX.Materials.Bind_Material[]
                {
                    new Bind_Material()
                    {
                        Technique_Common = new FX.Technique_Common.Technique_Common_Bind_Material()
                        {
                            Instance_Material = materials.Select(e => new Instance_Material_Geometry()
                            {
                                Symbol = e, Target = "#" + e
                            }).ToArray()
                        }
                    }
                };
            }

            return(n);
        }
示例#2
0
        /// <summary>
        /// Gets the model information in this scene as an IO Model
        /// </summary>
        /// <returns></returns>
        public override IOModel GetIOModel()
        {
            IOModel iomodel = new IOModel();

            iomodel.Skeleton = (SBSkeleton)Skeleton;

            foreach (var mesh in Model.Meshes)
            {
                var iomesh = new IOMesh();
                iomesh.Name = mesh.Name;
                iomodel.Meshes.Add(iomesh);

                iomesh.HasPositions = mesh.ExportAttributes.Contains(SSBHLib.Tools.MESHAttribute.Position0);
                iomesh.HasNormals   = mesh.ExportAttributes.Contains(SSBHLib.Tools.MESHAttribute.Normal0);
                iomesh.HasUV0       = mesh.ExportAttributes.Contains(SSBHLib.Tools.MESHAttribute.map1);
                iomesh.HasUV1       = mesh.ExportAttributes.Contains(SSBHLib.Tools.MESHAttribute.uvSet);
                iomesh.HasUV2       = mesh.ExportAttributes.Contains(SSBHLib.Tools.MESHAttribute.uvSet1);
                iomesh.HasUV3       = mesh.ExportAttributes.Contains(SSBHLib.Tools.MESHAttribute.uvSet2);
                iomesh.HasColor     = mesh.ExportAttributes.Contains(SSBHLib.Tools.MESHAttribute.colorSet1);

                iomesh.HasBoneWeights = true;

                iomesh.Indices.AddRange(mesh.Indices);

                foreach (var vertex in mesh.Vertices)
                {
                    var iovertex = new IOVertex();

                    iovertex.Position    = vertex.Position0;
                    iovertex.Normal      = vertex.Normal0;
                    iovertex.Tangent     = vertex.Tangent0;
                    iovertex.UV0         = vertex.Map1;
                    iovertex.UV1         = vertex.UvSet;
                    iovertex.UV2         = vertex.UvSet1;
                    iovertex.Color       = vertex.ColorSet1;
                    iovertex.BoneIndices = new Vector4(vertex.BoneIndices.X, vertex.BoneIndices.Y, vertex.BoneIndices.Z, vertex.BoneIndices.W);
                    iovertex.BoneWeights = vertex.BoneWeights;

                    // single bind fix
                    if (mesh.ParentBone != "" && Skeleton != null)
                    {
                        var parentBone = Skeleton[mesh.ParentBone];
                        if (parentBone != null)
                        {
                            iovertex.Position    = Vector3.TransformPosition(vertex.Position0, parentBone.WorldTransform);
                            iovertex.Normal      = Vector3.TransformNormal(vertex.Normal0, parentBone.WorldTransform);
                            iovertex.BoneIndices = new Vector4(Skeleton.IndexOfBone(parentBone), 0, 0, 0);
                            iovertex.BoneWeights = new Vector4(1, 0, 0, 0);
                        }
                    }

                    iomesh.Vertices.Add(iovertex);
                }
            }

            return(iomodel);
        }
示例#3
0
        public static void CalculateTangentBitangent(this IOMesh mesh)
        {
            Vector3[] pos = mesh.Vertices.Select(e => new Vector3(e.Position.X, e.Position.Y, e.Position.Z)).ToArray();
            Vector3[] nrm = mesh.Vertices.Select(e => new Vector3(e.Normal.X, e.Normal.Y, e.Normal.Z)).ToArray();
            Vector2[] uvs = mesh.Vertices.Select(e => new Vector2(e.UVs[0].X, e.UVs[0].Y)).ToArray();

            List <int> indices = new List <int>();

            foreach (var poly in mesh.Polygons)
            {
                poly.ToTriangles(mesh);
                if (poly.PrimitiveType == IOPrimitive.TRIANGLE)
                {
                    indices.AddRange(poly.Indicies);
                }
            }

            CalculateTangentsBitangents(pos, nrm, uvs, indices, out Vector3[] tan, out Vector3[] bitan);
示例#4
0
        /// <summary>
        ///
        /// </summary>
        /// <returns></returns>
        private string GenerateGeometry(IOMesh mesh)
        {
            // convert mesh to triangles to simplify
            mesh.MakeTriangles();

            // create a unique geometry id
            var geomID = GetUniqueID(mesh.Name + "-geometry");

            // create geometry element
            Geometry geom = new Geometry()
            {
                ID   = geomID,
                Name = mesh.Name
            };

            geom.Mesh = new Mesh();

            // generate sources
            SourceGenerator srcgen = new SourceGenerator();

            srcgen.AddSourceData(
                geomID, Input_Semantic.POSITION,
                mesh.Vertices.SelectMany(e => new float[] { e.Position.X, e.Position.Y, e.Position.Z }).ToArray());

            srcgen.AddSourceData(
                geomID, Input_Semantic.NORMAL,
                mesh.Vertices.SelectMany(e => new float[] { e.Normal.X, e.Normal.Y, e.Normal.Z }).ToArray());

            for (int i = 0; i < 7; i++)
            {
                if (mesh.HasUVSet(i))
                {
                    srcgen.AddSourceData(
                        geomID, Input_Semantic.TEXCOORD,
                        mesh.Vertices.SelectMany(e => new float[] { e.UVs[i].X, e.UVs[i].Y }).ToArray(),
                        i);
                }
            }

            for (int i = 0; i < 7; i++)
            {
                if (mesh.HasColorSet(i))
                {
                    srcgen.AddSourceData(
                        geomID, Input_Semantic.COLOR,
                        mesh.Vertices.SelectMany(e => new float[] { e.Colors[i].X, e.Colors[i].Y, e.Colors[i].Z, e.Colors[i].W }).ToArray(),
                        i);
                }
            }

            // fill in vertex info
            geom.Mesh.Vertices = new Vertices()
            {
                ID    = GetUniqueID(mesh.Name + "-vertices"),
                Input = new Input_Unshared[] {
                    new Input_Unshared()
                    {
                        Semantic = IONET.Collada.Enums.Input_Semantic.POSITION,
                        source   = "#" + srcgen.GetID(Input_Semantic.POSITION)
                    }
                }
            };

            // fill in triangles

            var polyIndex = 0;

            geom.Mesh.Triangles = new Triangles[mesh.Polygons.Count];
            foreach (var poly in mesh.Polygons)
            {
                if (poly.PrimitiveType != IOPrimitive.TRIANGLE)
                {
                    System.Diagnostics.Debug.WriteLine("Warning: " + poly.PrimitiveType + " not currently supported");
                    continue;
                }

                Triangles tri = new Triangles()
                {
                    Count    = poly.Indicies.Count / 3,
                    Material = poly.MaterialName
                };

                List <Input_Shared> inputs = new List <Input_Shared>();
                inputs.Add(new Input_Shared()
                {
                    Semantic = Input_Semantic.VERTEX,
                    Offset   = inputs.Count,
                    source   = "#" + geom.Mesh.Vertices.ID
                });

                inputs.Add(new Input_Shared()
                {
                    Semantic = Input_Semantic.NORMAL,
                    Offset   = inputs.Count,
                    source   = "#" + srcgen.GetID(Input_Semantic.NORMAL)
                });

                for (int i = 0; i < 7; i++)
                {
                    if (mesh.HasUVSet(i))
                    {
                        inputs.Add(new Input_Shared()
                        {
                            Semantic = Input_Semantic.TEXCOORD,
                            source   = "#" + srcgen.GetID(Input_Semantic.TEXCOORD, i),
                            Offset   = inputs.Count,
                            Set      = i
                        });
                    }
                }

                for (int i = 0; i < 7; i++)
                {
                    if (mesh.HasColorSet(i))
                    {
                        inputs.Add(new Input_Shared()
                        {
                            Semantic = Input_Semantic.COLOR,
                            source   = "#" + srcgen.GetID(Input_Semantic.COLOR, i),
                            Offset   = inputs.Count,
                            Set      = i
                        });
                    }
                }

                tri.Input = inputs.ToArray();

                tri.P = new IONET.Collada.Types.Int_Array_String()
                {
                    Value_As_String = string.Join(" ", srcgen.Remap(poly.Indicies))
                };

                geom.Mesh.Triangles[polyIndex++] = tri;
            }


            // generate sources
            geom.Mesh.Source = srcgen.GetSources();


            // add geometry element to document
            if (_collada.Library_Geometries == null)
            {
                _collada.Library_Geometries = new Library_Geometries();
            }

            if (_collada.Library_Geometries.Geometry == null)
            {
                _collada.Library_Geometries.Geometry = new Geometry[0];
            }

            Array.Resize(ref _collada.Library_Geometries.Geometry, _collada.Library_Geometries.Geometry.Length + 1);

            _collada.Library_Geometries.Geometry[_collada.Library_Geometries.Geometry.Length - 1] = geom;

            // return geometry id
            return(geomID);
        }
示例#5
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="mesh"></param>
        /// <returns></returns>
        private string GenerateGeometryController(IOMesh mesh, IOSkeleton skeleton)
        {
            Controller con = new Controller()
            {
                ID   = GetUniqueID(mesh.Name + "-controller"),
                Name = mesh.Name
            };

            con.Skin = new Skin()
            {
                sourceid = "#" + GenerateGeometry(mesh)
            };

            con.Skin.Bind_Shape_Matrix = new IONET.Collada.Types.Float_Array_String()
            {
                Value_As_String = "1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1"
            };

            List <int> weightCounts = new List <int>();
            List <int> binds        = new List <int>();

            List <string> boneNames = new List <string>();
            List <float>  boneBinds = new List <float>();
            List <float>  weights   = new List <float>();

            foreach (var v in mesh.Vertices)
            {
                weightCounts.Add(v.Envelope.Weights.Count);
                foreach (var bw in v.Envelope.Weights)
                {
                    if (!boneNames.Contains(bw.BoneName))
                    {
                        boneNames.Add(bw.BoneName);
                        Matrix4x4.Invert(skeleton.GetBoneByName(bw.BoneName).WorldTransform, out Matrix4x4 mat);
                        boneBinds.AddRange(new float[] {
                            mat.M11, mat.M21, mat.M31, mat.M41,
                            mat.M12, mat.M22, mat.M32, mat.M42,
                            mat.M13, mat.M23, mat.M33, mat.M43,
                            mat.M14, mat.M24, mat.M34, mat.M44
                        });
                    }
                    if (!weights.Contains(bw.Weight))
                    {
                        weights.Add(bw.Weight);
                    }

                    binds.Add(boneNames.IndexOf(bw.BoneName));
                    binds.Add(weights.IndexOf(bw.Weight));
                }
            }

            var mid = GetUniqueID(mesh.Name + "-matrices");
            var jid = GetUniqueID(mesh.Name + "-joints");
            var wid = GetUniqueID(mesh.Name + "-weights");

            var midarr = GetUniqueID(mesh.Name + "-matrices-array");
            var jidarr = GetUniqueID(mesh.Name + "-joints-array");
            var widarr = GetUniqueID(mesh.Name + "-weights-array");

            con.Skin.Source = new Source[]
            {
                new Source()
                {
                    ID         = jid,
                    Name_Array = new Name_Array()
                    {
                        Count           = boneNames.Count,
                        ID              = jidarr,
                        Value_Pre_Parse = string.Join(" ", boneNames)
                    },
                    Technique_Common = new IONET.Collada.Core.Technique_Common.Technique_Common_Source()
                    {
                        Accessor = new Accessor()
                        {
                            Count  = (uint)boneNames.Count,
                            Source = "#" + jidarr,
                            Param  = new IONET.Collada.Core.Parameters.Param[]
                            {
                                new IONET.Collada.Core.Parameters.Param()
                                {
                                    Type = "name"
                                }
                            },
                            Stride = 1
                        }
                    }
                },
                new Source()
                {
                    ID          = mid,
                    Float_Array = new Float_Array()
                    {
                        Count           = boneBinds.Count,
                        ID              = midarr,
                        Value_As_String = string.Join(" ", boneBinds)
                    },
                    Technique_Common = new IONET.Collada.Core.Technique_Common.Technique_Common_Source()
                    {
                        Accessor = new Accessor()
                        {
                            Count  = (uint)boneBinds.Count / 16,
                            Source = "#" + midarr,
                            Param  = new IONET.Collada.Core.Parameters.Param[]
                            {
                                new IONET.Collada.Core.Parameters.Param()
                                {
                                    Type = "float4x4"
                                }
                            },
                            Stride = 16
                        }
                    }
                },
                new Source()
                {
                    ID          = wid,
                    Float_Array = new Float_Array()
                    {
                        Count           = weights.Count,
                        ID              = widarr,
                        Value_As_String = string.Join(" ", weights)
                    },
                    Technique_Common = new IONET.Collada.Core.Technique_Common.Technique_Common_Source()
                    {
                        Accessor = new Accessor()
                        {
                            Count  = (uint)weights.Count,
                            Source = "#" + widarr,
                            Param  = new IONET.Collada.Core.Parameters.Param[]
                            {
                                new IONET.Collada.Core.Parameters.Param()
                                {
                                    Type = "float"
                                }
                            },
                            Stride = 1
                        }
                    }
                },
            };

            con.Skin.Joints = new Joints()
            {
                Input = new Input_Unshared[]
                {
                    new Input_Unshared()
                    {
                        Semantic = Input_Semantic.JOINT,
                        source   = "#" + jid
                    },
                    new Input_Unshared()
                    {
                        Semantic = Input_Semantic.INV_BIND_MATRIX,
                        source   = "#" + mid
                    },
                }
            };

            con.Skin.Vertex_Weights = new Vertex_Weights()
            {
                Count = weightCounts.Count,
                V     = new IONET.Collada.Types.Int_Array_String()
                {
                    Value_As_String = string.Join(" ", binds)
                },
                VCount = new IONET.Collada.Types.Int_Array_String()
                {
                    Value_As_String = string.Join(" ", weightCounts)
                },
                Input = new Input_Shared[]
                {
                    new Input_Shared()
                    {
                        Semantic = Input_Semantic.JOINT,
                        source   = "#" + jid,
                        Offset   = 0
                    },
                    new Input_Shared()
                    {
                        Semantic = Input_Semantic.WEIGHT,
                        source   = "#" + wid,
                        Offset   = 1
                    },
                }
            };


            // add geometry element to document
            if (_collada.Library_Controllers == null)
            {
                _collada.Library_Controllers = new Library_Controllers();
            }

            if (_collada.Library_Controllers.Controller == null)
            {
                _collada.Library_Controllers.Controller = new Controller[0];
            }

            Array.Resize(ref _collada.Library_Controllers.Controller, _collada.Library_Controllers.Controller.Length + 1);

            _collada.Library_Controllers.Controller[_collada.Library_Controllers.Controller.Length - 1] = con;

            return(con.ID);
        }
示例#6
0
        public override IOModel GetIOModel()
        {
            var iomodel = new IOModel();

            iomodel.Skeleton = (SBSkeleton)Skeleton;

            List <SBHsdBone> bones = new List <SBHsdBone>();

            foreach (SBHsdBone bone in Skeleton.Bones)
            {
                bones.Add(bone);
            }

            Dictionary <HSDStruct, string> tobjToName = new Dictionary <HSDStruct, string>();

            foreach (var tex in tobjToSurface)
            {
                tex.Value.Name = $"TOBJ_{iomodel.Textures.Count}";
                tobjToName.Add(tex.Key, tex.Value.Name);
                iomodel.Textures.Add(tex.Value);
            }

            foreach (SBHsdMesh me in GetMeshObjects())
            {
                var dobj = me.DOBJ;

                var parent = Skeleton.Bones[0];
                foreach (var b in Skeleton.Bones)
                {
                    if (b is SBHsdBone bone)
                    {
                        if (bone.GetJOBJ().Dobj != null)
                        {
                            if (bone.GetJOBJ().Dobj.List.Contains(dobj))
                            {
                                parent = b;
                                break;
                            }
                        }
                    }
                }

                var iomesh = new IOMesh();
                iomesh.Name = me.Name;
                iomodel.Meshes.Add(iomesh);

                iomesh.HasPositions   = true;
                iomesh.HasColor       = true;
                iomesh.HasNormals     = true;
                iomesh.HasBoneWeights = true;
                iomesh.HasUV0         = true;

                if (dobj.Pobj != null)
                {
                    foreach (var pobj in dobj.Pobj.List)
                    {
                        var dl       = pobj.ToDisplayList();
                        var vertices = GX_VertexAccessor.GetDecodedVertices(dl, pobj);

                        HSD_Envelope[] bindGroups = null;;
                        if (pobj.EnvelopeWeights != null)
                        {
                            bindGroups = pobj.EnvelopeWeights;
                        }

                        var offset = 0;
                        foreach (var v in dl.Primitives)
                        {
                            List <GX_Vertex> strip = new List <GX_Vertex>();
                            for (int i = 0; i < v.Count; i++)
                            {
                                strip.Add(vertices[offset + i]);
                            }
                            offset += v.Count;
                            iomesh.Vertices.AddRange(ConvertGXDLtoTriangleList(v.PrimitiveType, SBHsdMesh.GXVertexToHsdVertex(strip, bones, bindGroups), (SBHsdBone)parent));
                        }
                    }
                }


                iomesh.Optimize();

                // flip faces
                var temp = iomesh.Indices.ToArray();
                iomesh.Indices.Clear();
                for (int i = 0; i < temp.Length; i += 3)
                {
                    if (i + 2 < temp.Length)
                    {
                        iomesh.Indices.Add(temp[i + 2]);
                        iomesh.Indices.Add(temp[i + 1]);
                        iomesh.Indices.Add(temp[i]);
                    }
                    else
                    {
                        break;
                    }
                }

                iomesh.MaterialIndex = iomodel.Materials.Count;

                IOMaterialPhong mat = new IOMaterialPhong();
                mat.Name = iomesh.Name + "_material";
                if (dobj.Mobj.Material != null)
                {
                    mat.DiffuseColor  = dobj.Mobj.Material.DiffuseColor;
                    mat.SpecularColor = dobj.Mobj.Material.SpecularColor;
                    mat.AmbientColor  = dobj.Mobj.Material.AmbientColor;
                }
                if (dobj.Mobj.Textures != null)
                {
                    mat.DiffuseTexture = tobjToName[dobj.Mobj.Textures._s];
                }
                iomodel.Materials.Add(mat);
            }

            return(iomodel);
        }
示例#7
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public IOScene GetScene(string filePath)
        {
            IOScene scene = new IOScene();

            IOModel model = new IOModel();

            scene.Models.Add(model);

            using (FileStream stream = new FileStream(filePath, FileMode.Open))
                using (StreamReader r = new StreamReader(stream))
                {
                    Dictionary <string, IOMesh> nameToMesh  = new Dictionary <string, IOMesh>();
                    HashSet <IOBone>            meshBones   = new HashSet <IOBone>();
                    Dictionary <int, IOBone>    idxToBone   = new Dictionary <int, IOBone>();
                    Dictionary <int, int>       idxToParent = new Dictionary <int, int>();

                    string mode = "";
                    int    time = 0;

                    while (!r.EndOfStream)
                    {
                        // read and clean line args
                        var line = r.ReadLine().Trim();
                        var args = Regex.Replace(line, @"\s+", " ").Split(' ');

                        // check for grouping
                        switch (args[0])
                        {
                        case "nodes":
                        case "skeleton":
                        case "triangles":
                        case "end":
                            mode = args[0];
                            break;
                        }

                        switch (mode)
                        {
                        case "nodes":
                            if (args.Length >= 3)
                            {
                                args = line.Split('"');

                                var index       = int.Parse(args[0].Trim());
                                var name        = args[1];
                                var parentIndex = int.Parse(args[2].Trim());

                                IOBone bone = new IOBone()
                                {
                                    Name = name
                                };

                                idxToBone.Add(index, bone);
                                idxToParent.Add(index, parentIndex);
                            }
                            break;

                        case "skeleton":
                            if (args.Length == 2 && args[0] == "time")
                            {
                                int.TryParse(args[1], out time);
                            }
                            if (args.Length == 7)
                            {
                                var index = int.Parse(args[0]);

                                if (time == 0)
                                {
                                    idxToBone[index].Translation   = new System.Numerics.Vector3(float.Parse(args[1]), float.Parse(args[2]), float.Parse(args[3]));
                                    idxToBone[index].RotationEuler = new System.Numerics.Vector3(float.Parse(args[4]), float.Parse(args[5]), float.Parse(args[6]));
                                }
                            }
                            break;

                        case "triangles":
                        {
                            if (args.Length > 0 && args.Length < 9 && args[0] != "triangles")
                            {
                                var material = string.Join(" ", args);

                                var v1 = ParseVertex(r.ReadLine(), idxToBone, out IOBone parent);
                                var v2 = ParseVertex(r.ReadLine(), idxToBone, out parent);
                                var v3 = ParseVertex(r.ReadLine(), idxToBone, out parent);

                                var meshName = parent.Name + material;

                                if (!meshBones.Contains(parent))
                                {
                                    meshBones.Add(parent);
                                }

                                meshName = Regex.Replace(meshName.Trim(), @"\s+", "_").Replace("#", "");

                                if (!nameToMesh.ContainsKey(meshName))
                                {
                                    // create and load material
                                    IOMaterial mat = new IOMaterial()
                                    {
                                        Name = material
                                    };
                                    scene.Materials.Add(mat);

                                    // create io mesh
                                    var iomesh = new IOMesh()
                                    {
                                        Name = meshName
                                    };

                                    // create triangle polygon
                                    iomesh.Polygons.Add(new IOPolygon()
                                        {
                                            MaterialName  = material,
                                            PrimitiveType = IOPrimitive.TRIANGLE
                                        });

                                    nameToMesh.Add(meshName, iomesh);
                                }

                                var mesh = nameToMesh[meshName];
                                mesh.Polygons[0].Indicies.Add(mesh.Vertices.Count);
                                mesh.Polygons[0].Indicies.Add(mesh.Vertices.Count + 1);
                                mesh.Polygons[0].Indicies.Add(mesh.Vertices.Count + 2);
                                mesh.Vertices.Add(v1);
                                mesh.Vertices.Add(v2);
                                mesh.Vertices.Add(v3);
                            }
                        }
                        break;
                        }
                    }

                    // create skeleton hierarchy
                    foreach (var bone in idxToBone)
                    {
                        var parent = idxToParent[bone.Key];

                        if (parent == -1)
                        {
                            if (meshBones.Count > 1 && meshBones.Contains(bone.Value))
                            {
                                continue;
                            }
                            else
                            {
                                model.Skeleton.RootBones.Add(bone.Value);
                            }
                        }
                        else
                        {
                            idxToBone[parent].AddChild(bone.Value);
                        }
                    }

                    // dump mesh
                    model.Meshes.AddRange(nameToMesh.Values);
                }

            return(scene);
        }
示例#8
0
        public override IOModel ImportFromFile(string filename)
        {
            string path = Path.GetDirectoryName(filename);

            // Use Assimp.NET for importing model
            AssimpContext context = new AssimpContext();

            context.SetConfig(new NormalSmoothingAngleConfig(90.0f));
            Scene scene = context.ImportFile(filename, PostProcessPreset.TargetRealTimeMaximumQuality);

            var newModel = new IOModel();
            var textures = new Dictionary <int, Texture>();

            if (_settings.ProcessGeometry)
            {
                var tmpList = new List <IOMesh>();

                // Create the list of materials to load
                for (int i = 0; i < scene.Materials.Count; i++)
                {
                    var mat      = scene.Materials[i];
                    var material = new IOMaterial(mat.HasName ? mat.Name : "Material_" + i);

                    var diffusePath = mat.HasTextureDiffuse ? mat.TextureDiffuse.FilePath : null;
                    if (string.IsNullOrWhiteSpace(diffusePath))
                    {
                        continue;
                    }

                    // Don't add materials with missing textures
                    var texture = GetTexture(path, diffusePath);
                    if (texture == null)
                    {
                        logger.Warn("Texture for material " + mat.Name + " is missing. Meshes referencing this material won't be imported.");
                        continue;
                    }
                    else
                    {
                        textures.Add(i, texture);
                    }

                    // Create the new material
                    material.Texture          = textures[i];
                    material.AdditiveBlending = (mat.HasBlendMode && mat.BlendMode == global::Assimp.BlendMode.Additive) || mat.Opacity < 1.0f;
                    material.DoubleSided      = mat.HasTwoSided && mat.IsTwoSided;
                    material.Shininess        = mat.HasShininess ? (int)mat.Shininess : 0;
                    newModel.Materials.Add(material);
                }

                var lastBaseVertex = 0;

                // Loop for each mesh loaded in scene
                foreach (var mesh in scene.Meshes)
                {
                    // Discard nullmeshes
                    if (!mesh.HasFaces || !mesh.HasVertices || mesh.VertexCount < 3 ||
                        mesh.TextureCoordinateChannelCount == 0 || !mesh.HasTextureCoords(0))
                    {
                        logger.Warn("Mesh \"" + (mesh.Name ?? "") + "\" has no faces, no texture coordinates or wrong vertex count.");
                        continue;
                    }

                    // Import only textured meshes with valid materials
                    Texture faceTexture;
                    if (!textures.TryGetValue(mesh.MaterialIndex, out faceTexture))
                    {
                        logger.Warn("Mesh \"" + (mesh.Name ?? "") + "\" does have material index " + mesh.MaterialIndex + " which is unsupported or can't be found.");
                        continue;
                    }

                    // Make sure we have appropriate material in list. If not, skip mesh and warn user.
                    var material = newModel.Materials.FirstOrDefault(mat => mat.Name.Equals(scene.Materials[mesh.MaterialIndex].Name));
                    if (material == null)
                    {
                        logger.Warn("Can't find material with specified index (" + mesh.MaterialIndex + "). Probably you're missing textures or using non-diffuse materials only for this mesh.");
                        continue;
                    }

                    // Assimp's mesh is our IOSubmesh so we import meshes with just one submesh
                    var newMesh    = new IOMesh(mesh.Name);
                    var newSubmesh = new IOSubmesh(material);
                    newMesh.Submeshes.Add(material, newSubmesh);

                    bool hasColors  = _settings.UseVertexColor && mesh.VertexColorChannelCount > 0 && mesh.HasVertexColors(0);
                    bool hasNormals = mesh.HasNormals;

                    // Additional integrity checks
                    if ((mesh.VertexCount != mesh.TextureCoordinateChannels[0].Count) ||
                        (hasColors && mesh.VertexCount != mesh.VertexColorChannels[0].Count) ||
                        (hasNormals && mesh.VertexCount != mesh.Normals.Count))
                    {
                        logger.Warn("Mesh \"" + (mesh.Name ?? "") + "\" data structure is inconsistent.");
                        continue;
                    }

                    // Source data
                    var positions = mesh.Vertices;
                    var normals   = mesh.Normals;
                    var texCoords = mesh.TextureCoordinateChannels[0];
                    var colors    = mesh.VertexColorChannels[0];

                    for (int i = 0; i < mesh.VertexCount; i++)
                    {
                        // Create position
                        var position = new Vector3(positions[i].X, positions[i].Y, positions[i].Z);
                        position = ApplyAxesTransforms(position);
                        newMesh.Positions.Add(position);

                        // Create normal
                        if (hasNormals)
                        {
                            var normal = new Vector3(normals[i].X, normals[i].Y, normals[i].Z);
                            normal = ApplyAxesTransforms(normal);
                            newMesh.Normals.Add(normal);
                        }
                        else
                        {
                            newMesh.CalculateNormals();
                        }

                        // Create UV
                        var currentUV = new Vector2(texCoords[i].X, texCoords[i].Y);
                        if (faceTexture != null)
                        {
                            currentUV = ApplyUVTransform(currentUV, faceTexture.Image.Width, faceTexture.Image.Height);
                        }
                        newMesh.UV.Add(currentUV);

                        // Create colors
                        if (hasColors)
                        {
                            var color = ApplyColorTransform(new Vector4(colors[i].R, colors[i].G, colors[i].B, colors[i].A));
                            newMesh.Colors.Add(color);
                        }
                    }

                    // Add polygons
                    foreach (var face in mesh.Faces)
                    {
                        if (face.IndexCount == 3)
                        {
                            var poly = new IOPolygon(IOPolygonShape.Triangle);

                            poly.Indices.Add(lastBaseVertex + face.Indices[0]);
                            poly.Indices.Add(lastBaseVertex + face.Indices[1]);
                            poly.Indices.Add(lastBaseVertex + face.Indices[2]);

                            if (_settings.InvertFaces)
                            {
                                poly.Indices.Reverse();
                            }

                            newSubmesh.Polygons.Add(poly);
                        }
                        else if (face.IndexCount == 4)
                        {
                            var poly = new IOPolygon(IOPolygonShape.Quad);

                            poly.Indices.Add(lastBaseVertex + face.Indices[0]);
                            poly.Indices.Add(lastBaseVertex + face.Indices[1]);
                            poly.Indices.Add(lastBaseVertex + face.Indices[2]);
                            poly.Indices.Add(lastBaseVertex + face.Indices[3]);

                            if (_settings.InvertFaces)
                            {
                                poly.Indices.Reverse();
                            }

                            newSubmesh.Polygons.Add(poly);
                        }
                    }

                    tmpList.Add(newMesh);
                }

                // Sort meshes by name, if specified
                if (_settings.SortByName)
                {
                    tmpList = tmpList.OrderBy(m => m.Name, new CustomComparer <string>(NaturalComparer.Do)).ToList();
                }

                foreach (var mesh in tmpList)
                {
                    newModel.Meshes.Add(mesh);
                }
            }

            if (_settings.ProcessAnimations &&
                scene.HasAnimations && scene.AnimationCount > 0)
            {
                // Find all mesh nodes to count against animation nodes
                var meshNameList = CollectMeshNodeNames(scene.RootNode);

                // Sort animations by name, if specified
                if (_settings.SortByName)
                {
                    meshNameList = meshNameList.OrderBy(s => s, new CustomComparer <string>(NaturalComparer.Do)).ToList();
                }

                // Loop through all animations and add appropriate ones.
                // Integrity check: there should be meshes and mesh count should be equal to unique mesh name count.
                if (scene.MeshCount <= 0 || scene.MeshCount != meshNameList.Count)
                {
                    logger.Warn("Actual number of meshes doesn't correspond to mesh list. Animations won't be imported.");
                }
                else
                {
                    for (int i = 0; i < scene.AnimationCount; i++)
                    {
                        var anim = scene.Animations[i];

                        // Integrity check: support only time-based node animations
                        if (!anim.HasNodeAnimations || anim.DurationInTicks <= 0)
                        {
                            logger.Warn("Anim " + i + " isn't a valid type of animation for TR formats.");
                            continue;
                        }

                        // Guess possible maximum frame and time
                        var    frameCount  = 0;
                        double maximumTime = 0;
                        foreach (var node in anim.NodeAnimationChannels)
                        {
                            if (node.HasPositionKeys)
                            {
                                var maxNodeTime = node.PositionKeys.Max(key => key.Time);
                                maximumTime = maximumTime >= maxNodeTime ? maximumTime : maxNodeTime;
                                frameCount  = frameCount >= node.PositionKeyCount ? frameCount : node.PositionKeyCount;
                            }
                            if (node.HasRotationKeys)
                            {
                                var maxNodeTime = node.RotationKeys.Max(key => key.Time);
                                maximumTime = maximumTime >= maxNodeTime ? maximumTime : maxNodeTime;
                                frameCount  = frameCount >= node.RotationKeyCount ? frameCount : node.RotationKeyCount;
                            }
                        }

                        // Calculate time multiplier
                        var timeMult = (double)(frameCount - 1) / anim.DurationInTicks;

                        // Integrity check: maximum frame time shouldn't excess duration
                        if (timeMult * maximumTime >= frameCount)
                        {
                            logger.Warn("Anim " + i + " has frames outside of time limits and won't be imported.");
                            continue;
                        }

                        IOAnimation ioAnim = new IOAnimation(string.IsNullOrEmpty(anim.Name) ? "Imported animation " + i : anim.Name,
                                                             scene.MeshCount);

                        // Precreate frames and set them to identity
                        for (int j = 0; j < frameCount; j++)
                        {
                            ioAnim.Frames.Add(new IOFrame());
                        }

                        // Precreate rotations and set them to identity
                        // I am using generic foreach here instead of linq foreach because for some reason it
                        // returns wrong amount of angles during enumeration with Enumerable.Repeat.
                        foreach (var frame in ioAnim.Frames)
                        {
                            var angleList = Enumerable.Repeat(Vector3.Zero, scene.MeshCount);
                            frame.Angles.AddRange(angleList);
                        }

                        // Search through all nodes and put data into corresponding frames.
                        // It's not clear what should we do in case if multiple nodes refer to same mesh, but sometimes
                        // it happens, e. g. in case of fbx format. In this case, we'll just add to existing values for now.

                        foreach (var chan in anim.NodeAnimationChannels)
                        {
                            // Look if this channel belongs to any mesh in list.
                            // If so, attribute it to appropriate frame.
                            var chanIndex = meshNameList.IndexOf(item => chan.NodeName.Contains(item));

                            // Integrity check: no appropriate mesh found
                            if (chanIndex < 0)
                            {
                                logger.Warn("Anim " + i + " channel " + chan.NodeName + " has no corresponding mesh in meshtree and will be ignored");
                                continue;
                            }

                            // Apply translation only if found channel belongs to root mesh.
                            if (chanIndex == 0 && chan.HasPositionKeys && chan.PositionKeyCount > 0)
                            {
                                foreach (var key in chan.PositionKeys)
                                {
                                    // Integrity check: frame shouldn't fall out of keyframe array bounds.
                                    var frameIndex = (int)Math.Round(key.Time * timeMult, MidpointRounding.AwayFromZero);
                                    if (frameIndex >= frameCount)
                                    {
                                        logger.Warn("Anim " + i + " channel " + chan.NodeName + " has a key outside of time limits and will be ignored.");
                                        continue;
                                    }

                                    float rX = key.Value.X;
                                    float rY = key.Value.Y;
                                    float rZ = key.Value.Z;

                                    if (_settings.SwapXY)
                                    {
                                        var temp = rX; rX = rY; rY = temp;
                                    }
                                    if (_settings.SwapXZ)
                                    {
                                        var temp = rX; rX = rZ; rZ = temp;
                                    }
                                    if (_settings.SwapYZ)
                                    {
                                        var temp = rY; rY = rZ; rZ = temp;
                                    }

                                    if (_settings.FlipX)
                                    {
                                        rX = -rX;
                                    }
                                    if (_settings.FlipY)
                                    {
                                        rY = -rY;
                                    }
                                    if (_settings.FlipZ)
                                    {
                                        rZ = -rZ;
                                    }

                                    ioAnim.Frames[frameIndex].Offset += new Vector3(rX, rY, rZ);
                                }
                            }

                            if (chan.HasRotationKeys && chan.RotationKeyCount > 0)
                            {
                                foreach (var key in chan.RotationKeys)
                                {
                                    // Integrity check: frame shouldn't fall out of keyframe array bounds.
                                    var frameIndex = (int)Math.Round(key.Time * timeMult, MidpointRounding.AwayFromZero);
                                    if (frameIndex >= frameCount)
                                    {
                                        logger.Warn("Anim " + i + " channel " + chan.NodeName + " has a key outside of time limits and will be ignored.");
                                        continue;
                                    }

                                    // Convert quaternions back to rotations.
                                    // This is similar to TRViewer's conversion routine.

                                    var quatI = System.Numerics.Quaternion.Identity;
                                    var quat  = new System.Numerics.Quaternion(key.Value.X,
                                                                               key.Value.Z,
                                                                               key.Value.Y,
                                                                               -key.Value.W);
                                    quatI *= quat;

                                    var eulers   = MathC.QuaternionToEuler(quatI);
                                    var rotation = new Vector3(eulers.X * 180.0f / (float)Math.PI,
                                                               eulers.Y * 180.0f / (float)Math.PI,
                                                               eulers.Z * 180.0f / (float)Math.PI);

                                    ioAnim.Frames[frameIndex].Angles[chanIndex] += MathC.NormalizeAngle(rotation);
                                }
                            }
                        }

                        newModel.Animations.Add(ioAnim);
                    }
                }
            }

            return(newModel);
        }
示例#9
0
        public override IOModel ImportFromFile(string filename)
        {
            var model     = new IOModel();
            var materials = new List <string>();
            var positions = new List <Vector3>();
            var textures  = new Dictionary <int, Texture>();

            using (var reader = new StreamReader(File.OpenRead(filename)))
            {
                var line = reader.ReadLine();
                if (line.Trim() != "Metasequoia Document")
                {
                    logger.Error("Not a valid Metasequoia file");
                    return(null);
                }

                while (!reader.EndOfStream)
                {
                    line = reader.ReadLine().Trim();
                    if (line == "" || line == "}")
                    {
                        continue;
                    }

                    // Parse chunks
                    var chunk = line.Split(' ')[0];
                    if (chunk == "Format")
                    {
                    }
                    else if (chunk == "Thumbnail")
                    {
                        IgnoreChunk(reader);
                    }
                    else if (chunk == "Scene")
                    {
                        IgnoreChunk(reader);
                    }
                    else if (chunk == "Material")
                    {
                        var numMaterials = int.Parse(line.Split(' ')[1]);
                        if (numMaterials == 0)
                        {
                            return(null);
                        }
                        for (var i = 0; i < numMaterials; i++)
                        {
                            var materialString = reader.ReadLine().Trim();
                            var tokensMaterial = materialString.Split(' ');
                            var material       = new IOMaterial(tokensMaterial[0]);

                            for (var j = 0; j < tokensMaterial.Length; j++)
                            {
                                var texturePath = "";
                                if (tokensMaterial[j].StartsWith("tex"))
                                {
                                    texturePath = tokensMaterial[j].Substring(5, tokensMaterial[j].Length - 7);
                                    if (texturePath != "")
                                    {
                                        string basePath = Path.GetDirectoryName(filename);
                                        if (!File.Exists(Path.Combine(basePath, texturePath)))
                                        {
                                            basePath = Path.Combine(Path.GetDirectoryName(filename), "Texture");
                                        }
                                        if (!File.Exists(Path.Combine(basePath, texturePath)))
                                        {
                                            throw new FileNotFoundException("Texture " + texturePath + " could not be found");
                                        }

                                        textures.Add(i, GetTexture(basePath, texturePath));
                                    }

                                    material.Texture = textures[i];
                                    break;
                                }
                            }

                            model.Materials.Add(material);
                        }
                    }
                    else if (chunk == "Object")
                    {
                        var name       = line.Split(' ')[1];
                        var mesh       = new IOMesh(name.Replace("\"", ""));
                        var tokensName = mesh.Name.Split('_');
                        positions = new List <Vector3>();

                        if (name.Contains("TeRoom_"))
                        {
                            model.HasMultipleRooms = true;
                        }

                        var lastVertex  = 0;
                        var translation = Vector3.Zero;

                        while (!reader.EndOfStream)
                        {
                            line = reader.ReadLine().Trim();
                            var tokens = line.Split(' ');

                            if (tokens[0] == "translation" && model.HasMultipleRooms)
                            {
                                translation = ApplyAxesTransforms(new Vector3(ParseFloatCultureInvariant(tokens[1]),
                                                                              ParseFloatCultureInvariant(tokens[2]),
                                                                              ParseFloatCultureInvariant(tokens[3])));
                            }
                            else if (tokens[0] == "vertex")
                            {
                                var numVertices = int.Parse(tokens[1]);
                                for (var i = 0; i < numVertices; i++)
                                {
                                    var tokensPosition = reader.ReadLine().Trim().Split(' ');
                                    var newPos         = ApplyAxesTransforms(new Vector3(ParseFloatCultureInvariant(tokensPosition[0]),
                                                                                         ParseFloatCultureInvariant(tokensPosition[1]),
                                                                                         ParseFloatCultureInvariant(tokensPosition[2]))
                                                                             );
                                    positions.Add(newPos);
                                }
                                line = reader.ReadLine().Trim();
                            }
                            else if (tokens[0] == "face")
                            {
                                var numFaces = int.Parse(tokens[1]);
                                for (var i = 0; i < numFaces; i++)
                                {
                                    line = reader.ReadLine().Trim();

                                    var numVerticesInFace = int.Parse(line.Substring(0, line.IndexOf(' ')));
                                    var poly = new IOPolygon(numVerticesInFace == 3 ? IOPolygonShape.Triangle : IOPolygonShape.Quad);

                                    // We MUST have vertices
                                    var stringVertices = GetSubBlock(line, "V");
                                    if (stringVertices == "")
                                    {
                                        return(null);
                                    }
                                    var tokensVertices = stringVertices.Split(' ');
                                    for (var k = 0; k < numVerticesInFace; k++)
                                    {
                                        var index = int.Parse(tokensVertices[k]);
                                        mesh.Positions.Add(positions[index]);
                                        poly.Indices.Add(lastVertex);
                                        lastVertex++;
                                    }

                                    // Change vertex winding
                                    if (_settings.InvertFaces)
                                    {
                                        poly.Indices.Reverse();
                                    }

                                    // UV
                                    var stringUV = GetSubBlock(line, "UV");
                                    if (stringUV != "")
                                    {
                                        var tokensUV = stringUV.Split(' ');
                                        for (var k = 0; k < numVerticesInFace; k++)
                                        {
                                            var uv = ApplyUVTransform(new Vector2(ParseFloatCultureInvariant(tokensUV[2 * k]),
                                                                                  ParseFloatCultureInvariant(tokensUV[2 * k + 1])),
                                                                      textures[0].Image.Width,
                                                                      textures[0].Image.Height);
                                            mesh.UV.Add(uv);
                                        }
                                    }

                                    // Colors
                                    var stringColor = GetSubBlock(line, "COL");
                                    if (stringColor != "")
                                    {
                                        var tokensColor = stringColor.Split(' ');
                                        for (var k = 0; k < numVerticesInFace; k++)
                                        {
                                            var color = ApplyColorTransform(GetColor(long.Parse(tokensColor[k])));
                                            mesh.Colors.Add(color);
                                        }
                                    }
                                    else
                                    {
                                        for (var k = 0; k < numVerticesInFace; k++)
                                        {
                                            var color = ApplyColorTransform(Vector4.One);
                                            mesh.Colors.Add(color);
                                        }
                                    }

                                    // Material index
                                    var stringMaterialIndex = GetSubBlock(line, "M");
                                    var materialIndex       = 0;
                                    if (stringMaterialIndex != "")
                                    {
                                        materialIndex = int.Parse(stringMaterialIndex);
                                    }

                                    // Add polygon to the submesh (and add submesh if not existing yet)
                                    var material = model.Materials[materialIndex];
                                    if (!mesh.Submeshes.ContainsKey(material))
                                    {
                                        mesh.Submeshes.Add(material, new IOSubmesh(material));
                                    }

                                    mesh.Submeshes[material].Polygons.Add(poly);
                                }
                                line = reader.ReadLine().Trim();
                            }
                            else if (tokens[0] == "vertexattr")
                            {
                                // section to ignore
                                IgnoreChunk(reader);
                            }
                            else if (tokens[0] == "}")
                            {
                                break;
                            }
                        }

                        model.Meshes.Add(mesh);
                    }
                }
            }

            CalculateNormals(model);

            return(model);
        }
示例#10
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="settings"></param>
        /// <param name="material"></param>
        /// <returns></returns>
        private HSD_MOBJ GenerateMaterial(IOMesh mesh, IOMaterial material)
        {
            // create blank mobj
            var Mobj = new HSD_MOBJ();

            Mobj.Material = new HSD_Material()
            {
                AMB_A         = 0xFF,
                AMB_R         = 0x7F,
                AMB_G         = 0x7F,
                AMB_B         = 0x7F,
                DiffuseColor  = System.Drawing.Color.White,
                SpecularColor = System.Drawing.Color.White,
                Shininess     = 50,
                Alpha         = 1
            };

            // detect and set flags
            if (Settings.ImportVertexColor && (mesh.HasColorSet(0) || mesh.HasColorSet(1)))
            {
                Mobj.RenderFlags |= RENDER_MODE.VERTEX;
            }

            if (Settings.EnableDiffuse)
            {
                Mobj.RenderFlags |= RENDER_MODE.DIFFUSE;
            }

            if (Settings.EnableConstant)
            {
                Mobj.RenderFlags |= RENDER_MODE.CONSTANT;
            }

            // Properties
            if (material != null && Settings.ImportMaterialInfo)
            {
                Mobj.Material.Shininess = material.Shininess;
                Mobj.Material.Alpha     = material.Alpha;

                Mobj.Material.AMB_R = (byte)(material.AmbientColor.X * 255);
                Mobj.Material.AMB_G = (byte)(material.AmbientColor.Y * 255);
                Mobj.Material.AMB_B = (byte)(material.AmbientColor.Z * 255);
                Mobj.Material.AMB_A = (byte)(material.AmbientColor.W * 255);

                Mobj.Material.DIF_R = (byte)(material.DiffuseColor.X * 255);
                Mobj.Material.DIF_G = (byte)(material.DiffuseColor.Y * 255);
                Mobj.Material.DIF_B = (byte)(material.DiffuseColor.Z * 255);
                Mobj.Material.DIF_A = (byte)(material.DiffuseColor.W * 255);

                Mobj.Material.SPC_R = (byte)(material.SpecularColor.X * 255);
                Mobj.Material.SPC_G = (byte)(material.SpecularColor.Y * 255);
                Mobj.Material.SPC_B = (byte)(material.SpecularColor.Z * 255);
                Mobj.Material.SPC_A = (byte)(material.SpecularColor.W * 255);
            }

            // Textures
            if (material != null && Settings.ImportTexture)
            {
                if (material.DiffuseMap != null && !string.IsNullOrEmpty(material.DiffuseMap.FilePath))
                {
                    var texturePath = material.DiffuseMap.FilePath;

                    if (texturePath.Contains("file://"))
                    {
                        texturePath = texturePath.Replace("file://", "");
                    }

                    if (File.Exists(Path.Combine(_cache.FolderPath, texturePath)))
                    {
                        texturePath = Path.Combine(_cache.FolderPath, texturePath);
                    }

                    if (File.Exists(material.DiffuseMap.FilePath))
                    {
                        texturePath = material.DiffuseMap.FilePath;
                    }

                    if (File.Exists(texturePath + ".png"))
                    {
                        texturePath = texturePath + ".png";
                    }


                    var mobjPath = Path.Combine(Path.GetDirectoryName(texturePath), Path.GetFileNameWithoutExtension(texturePath)) + ".mobj";

                    if (Settings.ImportMOBJ && File.Exists(mobjPath))
                    {
                        var dat = new HSDRaw.HSDRawFile(mobjPath);
                        Mobj._s = dat.Roots[0].Data._s;
                        return(Mobj);
                    }
                    else
                    /// special mobj loading
                    if (Path.GetExtension(texturePath).ToLower() == ".mobj")
                    {
                        var dat = new HSDRaw.HSDRawFile(texturePath);
                        Mobj._s = dat.Roots[0].Data._s;
                        return(Mobj);
                    }
                    else
                    if (File.Exists(texturePath) && (texturePath.ToLower().EndsWith(".png") || texturePath.ToLower().EndsWith(".bmp")))
                    {
                        Mobj.RenderFlags |= RENDER_MODE.TEX0;

                        var tobj = TOBJConverter.ImportTOBJFromFile(texturePath, Settings.TextureFormat, Settings.PaletteFormat);
                        tobj.Flags = TOBJ_FLAGS.LIGHTMAP_DIFFUSE | TOBJ_FLAGS.COORD_UV | TOBJ_FLAGS.COLORMAP_MODULATE;

                        tobj.GXTexGenSrc = GXTexGenSrc.GX_TG_TEX0;
                        tobj.TexMapID    = GXTexMapID.GX_TEXMAP0;

                        tobj.WrapS = ToGXWrapMode(material.DiffuseMap.WrapS);
                        tobj.WrapT = ToGXWrapMode(material.DiffuseMap.WrapT);

                        if (TOBJConverter.IsTransparent(tobj))
                        {
                            _cache.HasXLU     = true;
                            Mobj.RenderFlags |= RENDER_MODE.XLU;
                            tobj.Flags       |= TOBJ_FLAGS.ALPHAMAP_MODULATE;
                        }

                        Mobj.Textures = tobj;
                    }
                }
            }

            return(Mobj);
        }
示例#11
0
        public override IOScene GetIOModel()
        {
            IOScene scene = new IOScene();

            IOModel iomodel = new IOModel();

            scene.Models.Add(iomodel);

            iomodel.Skeleton = ((SBSkeleton)Skeleton).ToIOSkeleton();

            // bone indices
            List <SBHsdBone> bones = new List <SBHsdBone>();

            foreach (SBHsdBone bone in Skeleton.Bones)
            {
                bones.Add(bone);
            }


            // gather textures
            Dictionary <HSDStruct, string> tobjToName = new Dictionary <HSDStruct, string>();

            List <SBSurface> textures = new List <SBSurface>();

            foreach (var tex in tobjToSurface)
            {
                tex.Value.Name = $"TOBJ_{textures.Count}";
                tobjToName.Add(tex.Key, tex.Value.Name);
                textures.Add(tex.Value);
            }


            // process mesh
            foreach (SBHsdMesh me in GetMeshObjects())
            {
                var dobj = me.DOBJ;

                var parent = Skeleton.Bones[0];
                foreach (var b in Skeleton.Bones)
                {
                    if (b is SBHsdBone bone)
                    {
                        if (bone.GetJOBJ().Dobj != null)
                        {
                            if (bone.GetJOBJ().Dobj.List.Contains(dobj))
                            {
                                parent = b;
                                break;
                            }
                        }
                    }
                }

                var iomesh = new IOMesh();
                iomesh.Name = me.Name;
                iomodel.Meshes.Add(iomesh);
                IOPolygon poly = new IOPolygon();
                iomesh.Polygons.Add(poly);

                if (dobj.Pobj != null)
                {
                    foreach (var pobj in dobj.Pobj.List)
                    {
                        var dl       = pobj.ToDisplayList();
                        var vertices = GX_VertexAccessor.GetDecodedVertices(dl, pobj);

                        HSD_Envelope[] bindGroups = null;;
                        if (pobj.EnvelopeWeights != null)
                        {
                            bindGroups = pobj.EnvelopeWeights;
                        }

                        var offset = 0;
                        foreach (var v in dl.Primitives)
                        {
                            List <GX_Vertex> strip = new List <GX_Vertex>();
                            for (int i = 0; i < v.Count; i++)
                            {
                                strip.Add(vertices[offset + i]);
                            }
                            offset += v.Count;
                            iomesh.Vertices.AddRange(ConvertGXDLtoTriangleList(v.PrimitiveType, SBHsdMesh.GXVertexToHsdVertex(strip, bones, bindGroups), (SBHsdBone)parent));
                        }
                    }
                }


                // flip faces
                for (int i = 0; i < iomesh.Vertices.Count; i += 3)
                {
                    if (i + 2 < iomesh.Vertices.Count)
                    {
                        poly.Indicies.Add(i + 2);
                        poly.Indicies.Add(i + 1);
                        poly.Indicies.Add(i);
                    }
                    else
                    {
                        break;
                    }
                }

                poly.MaterialName = iomesh.Name + "_material";


                // create material
                IOMaterial mat = new IOMaterial();

                mat.Name = iomesh.Name + "_material";
                if (dobj.Mobj.Material != null)
                {
                    mat.DiffuseColor = new System.Numerics.Vector4(
                        dobj.Mobj.Material.DiffuseColor.R / 255f,
                        dobj.Mobj.Material.DiffuseColor.G / 255f,
                        dobj.Mobj.Material.DiffuseColor.B / 255f,
                        dobj.Mobj.Material.DiffuseColor.A / 255f);
                    mat.SpecularColor = new System.Numerics.Vector4(
                        dobj.Mobj.Material.SpecularColor.R / 255f,
                        dobj.Mobj.Material.SpecularColor.G / 255f,
                        dobj.Mobj.Material.SpecularColor.B / 255f,
                        dobj.Mobj.Material.SpecularColor.A / 255f);
                    mat.AmbientColor = new System.Numerics.Vector4(
                        dobj.Mobj.Material.AmbientColor.R / 255f,
                        dobj.Mobj.Material.AmbientColor.G / 255f,
                        dobj.Mobj.Material.AmbientColor.B / 255f,
                        dobj.Mobj.Material.AmbientColor.A / 255f);
                    mat.Alpha     = dobj.Mobj.Material.Alpha;
                    mat.Shininess = dobj.Mobj.Material.Shininess;
                }

                if (dobj.Mobj.Textures != null)
                {
                    mat.DiffuseMap = new IOTexture()
                    {
                        Name     = tobjToName[dobj.Mobj.Textures._s],
                        FilePath = tobjToName[dobj.Mobj.Textures._s]
                    }
                }
                ;
                scene.Materials.Add(mat);
            }

            return(scene);
        }
示例#12
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public IOScene GetScene(string filePath)
        {
            // create scene and model
            IOScene scene = new IOScene();

            // load materials
            var mtlFile = Path.Combine(Path.GetDirectoryName(filePath), Path.GetFileNameWithoutExtension(filePath) + ".mtl");

            if (File.Exists(mtlFile))
            {
                LoadMaterialLibrary(scene, mtlFile);
            }

            // parse obj file
            using (FileStream stream = new FileStream(filePath, FileMode.Open))
                using (StreamReader r = new StreamReader(stream))
                {
                    List <Vector3> v  = new List <Vector3>();
                    List <Vector2> vt = new List <Vector2>();
                    List <Vector3> vn = new List <Vector3>();

                    List <Tuple <string, string, Dictionary <IOPrimitive, List <int[]> > > > objects = new List <Tuple <string, string, Dictionary <IOPrimitive, List <int[]> > > >();

                    var objName = "Mesh";
                    var matNam  = "";
                    Dictionary <IOPrimitive, List <int[]> > polygons = new Dictionary <IOPrimitive, List <int[]> >();

                    while (!r.EndOfStream)
                    {
                        var args = Regex.Replace(r.ReadLine().Trim(), @"\s+", " ").Split(' ');

                        if (args.Length > 0)
                        {
                            switch (args[0])
                            {
                            case "v":
                                v.Add(new Vector3(
                                          args.Length > 1 ? float.Parse(args[1]) : 0,
                                          args.Length > 2 ? float.Parse(args[2]) : 0,
                                          args.Length > 3 ? float.Parse(args[3]) : 0));
                                break;

                            case "vt":
                                vt.Add(new Vector2(
                                           args.Length > 1 ? float.Parse(args[1]) : 0,
                                           args.Length > 2 ? float.Parse(args[2]) : 0));
                                break;

                            case "vn":
                                vn.Add(new Vector3(
                                           args.Length > 1 ? float.Parse(args[1]) : 0,
                                           args.Length > 2 ? float.Parse(args[2]) : 0,
                                           args.Length > 3 ? float.Parse(args[3]) : 0));
                                break;

                            case "f":
                                var faces = ParseFaces(args);

                                if (args.Length == 2)
                                {
                                    // point
                                    if (!polygons.ContainsKey(IOPrimitive.POINT))
                                    {
                                        polygons.Add(IOPrimitive.POINT, new List <int[]>());
                                    }
                                    polygons[IOPrimitive.POINT].AddRange(faces);
                                }
                                if (args.Length == 3)
                                {
                                    // line
                                    if (!polygons.ContainsKey(IOPrimitive.LINE))
                                    {
                                        polygons.Add(IOPrimitive.LINE, new List <int[]>());
                                    }
                                    polygons[IOPrimitive.LINE].AddRange(faces);
                                }
                                if (args.Length == 4)
                                {
                                    // triangle
                                    if (!polygons.ContainsKey(IOPrimitive.TRIANGLE))
                                    {
                                        polygons.Add(IOPrimitive.TRIANGLE, new List <int[]>());
                                    }
                                    polygons[IOPrimitive.TRIANGLE].AddRange(faces);
                                }
                                if (args.Length == 5)
                                {
                                    // quad
                                    if (!polygons.ContainsKey(IOPrimitive.QUAD))
                                    {
                                        polygons.Add(IOPrimitive.QUAD, new List <int[]>());
                                    }
                                    polygons[IOPrimitive.QUAD].AddRange(faces);
                                }
                                if (args.Length == 6)
                                {
                                    // strip
                                    if (!polygons.ContainsKey(IOPrimitive.TRISTRIP))
                                    {
                                        polygons.Add(IOPrimitive.TRISTRIP, new List <int[]>());
                                    }
                                    polygons[IOPrimitive.TRISTRIP].AddRange(faces);
                                }
                                break;

                            case "o":
                                if (polygons.Count > 0)
                                {
                                    objects.Add(new Tuple <string, string, Dictionary <IOPrimitive, List <int[]> > >(objName, matNam, polygons));
                                }
                                objName  = args[1];
                                matNam   = "";
                                polygons = new Dictionary <IOPrimitive, List <int[]> >();
                                break;

                            case "usemtl":
                                matNam = args[1];
                                break;
                            }
                        }
                    }

                    objects.Add(new Tuple <string, string, Dictionary <IOPrimitive, List <int[]> > >(objName, matNam, polygons));

                    // generate model
                    IOModel model = new IOModel();
                    scene.Models.Add(model);

                    // dummy bone
                    model.Skeleton.RootBones.Add(new Core.Skeleton.IOBone()
                    {
                        Name = "Root"
                    });

                    // convert and add polygons
                    foreach (var obj in objects)
                    {
                        IOMesh mesh = new IOMesh()
                        {
                            Name = obj.Item1
                        };

                        foreach (var p in obj.Item3)
                        {
                            IOPolygon poly = new IOPolygon()
                            {
                                PrimitiveType = p.Key,
                                MaterialName  = obj.Item2,
                            };

                            for (int i = 0; i < p.Value.Count; i++)
                            {
                                var face = p.Value[i];

                                IOVertex vert = new IOVertex()
                                {
                                    Position = face[0] != -1 ? v[face[0]] : Vector3.Zero,
                                    Normal   = face[2] != -1 ? vn[face[2]] : Vector3.Zero,
                                };
                                if (face[1] != -1)
                                {
                                    vert.UVs.Add(vt[face[1]]);
                                }

                                poly.Indicies.Add(mesh.Vertices.Count);
                                mesh.Vertices.Add(vert);
                            }

                            mesh.Polygons.Add(poly);
                        }

                        // add mesh to model
                        model.Meshes.Add(mesh);
                    }
                    ;
                }

            return(scene);
        }
示例#13
0
        public IOModel ImportIOModel(string FileName)
        {
            IOModel    model    = new IOModel();
            SBSkeleton skeleton = new SBSkeleton();

            model.Skeleton = skeleton;

            var test = Fbx.FbxIO.ReadBinary(FileName);

            if (test.Version != Fbx.FbxVersion.v7_4)
            {
                throw new NotSupportedException($"Only FBX version 7.4 is currently supported: Imported version = {test.Version}");
            }

            // global settings
            float Scale = 1;

            // read global settings
            var settings = test.GetNodesByName("GlobalSettings");

            if (settings.Length != 0)
            {
                var prop70 = settings[0].GetNodesByName("Properties70");
                if (prop70.Length != 0)
                {
                    foreach (var property in prop70[0].Nodes)
                    {
                        if (property == null)
                        {
                            continue;
                        }
                        if (property.Properties.Count > 0 && property.Name == "P")
                        {
                            //TODO: this is inaccurate...
                            //if (property.Properties[0].Equals("UnitScaleFactor"))
                            //    Scale = (float)(double)property.Properties[4];
                        }
                    }
                }
            }

            FbxAccessor accessor = new FbxAccessor(FileName);

            //Bones
            var limbs = accessor.GetLimbNodes();

            SBConsole.WriteLine($"Limb Node Count: {limbs.Length}");

            foreach (var limb in limbs)
            {
                skeleton.AddRoot(ConvertLimbToSBBone(limb));
            }
            foreach (var root in skeleton.Roots)
            {
                root.Scale *= Scale;
            }

            // Fast access to bone indices
            Dictionary <string, int> BoneNameToIndex = new Dictionary <string, int>();

            foreach (var b in skeleton.Bones)
            {
                BoneNameToIndex.Add(b.Name, skeleton.IndexOfBone(b));
            }

            // Mesh

            var models = accessor.GetModels();

            SBConsole.WriteLine($"Model Node Count: {models.Count}");

            int YupAxis = accessor.GetOriginalXAxis();

            SBConsole.WriteLine("Yup: " + YupAxis);
            foreach (var mod in models)
            {
                // rotation 90
                Matrix4 transform = (ImportSettings.Rotate90 ? Matrix4.CreateRotationX(-90 * DegToRag) : Matrix4.Identity) * GetModelTransform(mod);

                foreach (var geom in mod.Geometries)
                {
                    IOMesh mesh = new IOMesh();
                    mesh.Name = mod.Name;
                    model.Meshes.Add(mesh);

                    // Create Rigging information
                    Vector4[] BoneIndices = new Vector4[geom.Vertices.Length];
                    Vector4[] BoneWeights = new Vector4[geom.Vertices.Length];
                    foreach (var deformer in geom.Deformers)
                    {
                        //TODO: this shouldn't happen...
                        if (!BoneNameToIndex.ContainsKey(deformer.Name))
                        {
                            continue;
                        }
                        int index = BoneNameToIndex[deformer.Name];

                        for (int i = 0; i < deformer.Indices.Length; i++)
                        {
                            int vertexIndex = deformer.Indices[i];
                            for (int j = 0; j < 4; j++)
                            {
                                if (BoneWeights[vertexIndex][j] == 0)
                                {
                                    BoneIndices[vertexIndex][j] = index;
                                    BoneWeights[vertexIndex][j] = (float)deformer.Weights[i];
                                    break;
                                }
                            }
                        }
                        //SBConsole.WriteLine(deformer.Name + " " + deformer.Weights.Length + " " + deformer.Indices.Length + " " + index);
                    }

                    // Explanation:
                    // negative values are used to indicate a stopping point for the face
                    // so every 3rd index needed to be adjusted
                    for (int i = 0; i < geom.Indices.Length; i += 3)
                    {
                        mesh.Indices.Add((uint)i);
                        mesh.Indices.Add((uint)i + 1);
                        mesh.Indices.Add((uint)i + 2);
                        mesh.Vertices.Add(CreateVertex(transform, geom, i, BoneIndices, BoneWeights, Scale));
                        mesh.Vertices.Add(CreateVertex(transform, geom, i + 1, BoneIndices, BoneWeights, Scale));
                        mesh.Vertices.Add(CreateVertex(transform, geom, i + 2, BoneIndices, BoneWeights, Scale));
                    }

                    mesh.HasPositions = true;

                    //SBConsole.WriteLine(geom.Vertices.Length);
                    foreach (var layer in geom.Layers)
                    {
                        switch (layer.Name)
                        {
                        case "LayerElementNormal":
                        case "LayerElementUV":
                        case "LayerElementColor":
                            break;

                        default:
                            SBConsole.WriteLine(layer.Name + " " + layer.ReferenceInformationType + " " + layer.Data.Length + " " + (layer.ReferenceInformationType.Equals("IndexToDirect") ? layer.Indices.Length.ToString() : ""));
                            break;
                        }
                    }
                    mesh.Optimize();
                }
            }

            return(model);
        }
示例#14
0
        /// <summary>
        ///
        /// </summary>
        /// <returns></returns>
        private void ProcessMesh(IOScene scene, IOMesh mesh, HSD_JOBJ rootnode)
        {
            HSD_JOBJ parent = rootnode;

            HashSet <HSD_JOBJ> nodes = new HashSet <HSD_JOBJ>();

            foreach (var j in rootnode.BreathFirstList)
            {
                nodes.Add(j);
            }


            if (mesh.ParentBone != null && _cache.NameToJOBJ.ContainsKey(mesh.ParentBone.Name))
            {
                parent = _cache.NameToJOBJ[mesh.ParentBone.Name];
            }


            HSD_DOBJ root = null;
            HSD_DOBJ prev = null;

            //var skeleton = rootnode.BreathFirstList;
            Console.WriteLine("Processing " + mesh.Name);

            bool singleBinded = mesh.Name.Contains("SINGLE");

            foreach (var poly in mesh.Polygons)
            {
                // Skip Empty Polygon
                if (poly.Indicies.Count == 0)
                {
                    continue;
                }


                // convert to triangles
                poly.ToTriangles(mesh);

                if (poly.PrimitiveType != IOPrimitive.TRIANGLE)
                {
                    continue;
                }


                // Generate DOBJ
                HSD_DOBJ dobj = new HSD_DOBJ();

                if (Settings.ImportMeshNames)
                {
                    dobj.ClassName = mesh.Name;
                }

                if (root == null)
                {
                    root = dobj;
                }
                else
                {
                    prev.Next = dobj;
                }
                prev = dobj;


                // generate material
                var material = scene.Materials.Find(e => e.Name == poly.MaterialName);

                dobj.Mobj = GenerateMaterial(material);


                Console.WriteLine(mesh.Name + " " + material?.Name);


                // reflective mobjs do not use uvs
                var hasReflection = false;

                // bump maps need tangents and bitangents
                var hasBump = false;

                // Assess needed attributes based on the material MOBJ
                if (mesh.Name.Contains("REFLECTIVE"))
                {
                    hasReflection = true;
                }
#if DEBUG
                if (Settings.MetalModel)
                {
                    hasReflection = true;
                }
#endif

                if (mesh.Name.Contains("BUMP"))
                {
                    hasBump = true;
                }

                if (dobj.Mobj.Textures != null)
                {
                    foreach (var t in dobj.Mobj.Textures.List)
                    {
                        if (t.Flags.HasFlag(TOBJ_FLAGS.COORD_REFLECTION))
                        {
                            hasReflection = true;
                        }
                        if (t.Flags.HasFlag(TOBJ_FLAGS.BUMP))
                        {
                            hasBump = true;
                        }
                    }
                }

                // assess attributes
                List <GXAttribName> Attributes = new List <GXAttribName>();

                if (mesh.HasEnvelopes() && Settings.ImportRigging && !singleBinded)
                {
                    Attributes.Add(GXAttribName.GX_VA_PNMTXIDX);

                    if (hasReflection)
                    {
                        Attributes.Add(GXAttribName.GX_VA_TEX0MTXIDX);

                        if (dobj.Mobj.Textures != null && dobj.Mobj.Textures.List.Count > 1)
                        {
                            Attributes.Add(GXAttribName.GX_VA_TEX1MTXIDX);
                        }

#if DEBUG
                        if (Settings.MetalModel && !Attributes.Contains(GXAttribName.GX_VA_TEX1MTXIDX))
                        {
                            Attributes.Add(GXAttribName.GX_VA_TEX1MTXIDX);
                        }
#endif
                    }
                }


                Attributes.Add(GXAttribName.GX_VA_POS);


                if (hasBump)
                {
                    Attributes.Add(GXAttribName.GX_VA_NBT);
                }
                else
                if (mesh.HasNormals && Settings.ImportNormals)
                {
                    Attributes.Add(GXAttribName.GX_VA_NRM);
                }


                if (mesh.HasColorSet(0) && Settings.ImportVertexColor)
                {
                    Attributes.Add(GXAttribName.GX_VA_CLR0);
                }

                if (mesh.HasColorSet(1) && Settings.ImportVertexColor)
                {
                    Attributes.Add(GXAttribName.GX_VA_CLR1);
                }


                if (mesh.HasUVSet(0) && !hasReflection)
                {
                    Attributes.Add(GXAttribName.GX_VA_TEX0);
                }

                if ((mesh.HasUVSet(1) || (dobj.Mobj.Textures != null && dobj.Mobj.Textures.List.Count > 1)) && !hasReflection)
                {
                    Attributes.Add(GXAttribName.GX_VA_TEX1);
                }

                if ((mesh.HasUVSet(2) || (dobj.Mobj.Textures != null && dobj.Mobj.Textures.List.Count > 2)) && !hasReflection)
                {
                    Attributes.Add(GXAttribName.GX_VA_TEX2);
                }

                if ((mesh.HasUVSet(3) || (dobj.Mobj.Textures != null && dobj.Mobj.Textures.List.Count > 3)) && !hasReflection)
                {
                    Attributes.Add(GXAttribName.GX_VA_TEX3);
                }

                if ((mesh.HasUVSet(4) || (dobj.Mobj.Textures != null && dobj.Mobj.Textures.List.Count > 4)) && !hasReflection)
                {
                    Attributes.Add(GXAttribName.GX_VA_TEX4);
                }

                if ((mesh.HasUVSet(5) || (dobj.Mobj.Textures != null && dobj.Mobj.Textures.List.Count > 5)) && !hasReflection)
                {
                    Attributes.Add(GXAttribName.GX_VA_TEX5);
                }

                if ((mesh.HasUVSet(6) || (dobj.Mobj.Textures != null && dobj.Mobj.Textures.List.Count > 6)) && !hasReflection)
                {
                    Attributes.Add(GXAttribName.GX_VA_TEX6);
                }

                if ((mesh.HasUVSet(7) || (dobj.Mobj.Textures != null && dobj.Mobj.Textures.List.Count > 7)) && !hasReflection)
                {
                    Attributes.Add(GXAttribName.GX_VA_TEX7);
                }


                var vertices   = new List <GX_Vertex>();
                var jobjList   = new List <HSD_JOBJ[]>();
                var weightList = new List <float[]>();

                foreach (var face in poly.Indicies)
                {
                    var v = mesh.Vertices[face];

                    GX_Vertex vertex = new GX_Vertex();

                    var tkvert  = new Vector3(v.Position.X, v.Position.Y, v.Position.Z);
                    var tknrm   = new Vector3(v.Normal.X, v.Normal.Y, v.Normal.Z);
                    var tktan   = new Vector3(v.Tangent.X, v.Tangent.Y, v.Tangent.Z);
                    var tkbitan = new Vector3(v.Binormal.X, v.Binormal.Y, v.Binormal.Z);

                    var parentTransform = _cache.jobjToWorldTransform[parent].Inverted();

                    if (_cache.jobjToWorldTransform[parent] != Matrix4.Identity)
                    {
                        tkvert  = Vector3.TransformPosition(tkvert, parentTransform);
                        tknrm   = Vector3.TransformNormal(tknrm, parentTransform).Normalized();
                        tktan   = Vector3.TransformNormal(tktan, parentTransform).Normalized();
                        tkbitan = Vector3.TransformNormal(tkbitan, parentTransform).Normalized();
                    }


                    if (mesh.HasEnvelopes() && Settings.ImportRigging)
                    {
                        // create weighting lists
                        List <float>    weight = new List <float>();
                        List <HSD_JOBJ> bones  = new List <HSD_JOBJ>();

                        if (v.Envelope.Weights.Count == 0)
                        {
                            weight.Add(1);
                            bones.Add(rootnode);
                        }

                        if (v.Envelope.Weights.Count > 4)
                        {
                            throw new Exception($"Too many weights! {v.Envelope.Weights.Count} in {mesh.Name}");
                        }

                        foreach (var bw in v.Envelope.Weights)
                        {
                            // check if skeleton actually contains bone
                            if (_cache.NameToJOBJ.ContainsKey(bw.BoneName) && nodes.Contains(_cache.NameToJOBJ[bw.BoneName]))
                            {
                                // add envelope
                                bones.Add(_cache.NameToJOBJ[bw.BoneName]);
                                weight.Add(bw.Weight);

                                // indicate enveloped jobjs
                                if (!_cache.EnvelopedJOBJs.Contains(_cache.NameToJOBJ[bw.BoneName]))
                                {
                                    _cache.EnvelopedJOBJs.Add(_cache.NameToJOBJ[bw.BoneName]);
                                }
                            }
                            else
                            {
                                throw new Exception($"Bone not found \"{bw.BoneName}\" Weight: {bw.Weight} in {mesh.Name}");
                            }
                        }

                        jobjList.Add(bones.ToArray());
                        weightList.Add(weight.ToArray());

                        // invert single binds
                        if (v.Envelope.Weights.Count == 1)
                        {
                            var inv = _cache.jobjToWorldTransform[_cache.NameToJOBJ[v.Envelope.Weights[0].BoneName]].Inverted();
                            tkvert  = Vector3.TransformPosition(tkvert, inv);
                            tknrm   = Vector3.TransformNormal(tknrm, inv).Normalized();
                            tktan   = Vector3.TransformNormal(tknrm, inv).Normalized();
                            tkbitan = Vector3.TransformNormal(tknrm, inv).Normalized();
                        }
                    }

                    vertex.POS   = GXTranslator.fromVector3(tkvert);
                    vertex.NRM   = GXTranslator.fromVector3(tknrm.Normalized());
                    vertex.TAN   = GXTranslator.fromVector3(tktan);
                    vertex.BITAN = GXTranslator.fromVector3(tkbitan);

                    if (Settings.InvertNormals)
                    {
                        vertex.NRM.X   *= -1;
                        vertex.NRM.Y   *= -1;
                        vertex.NRM.Z   *= -1;
                        vertex.TAN.X   *= -1;
                        vertex.TAN.Y   *= -1;
                        vertex.TAN.Z   *= -1;
                        vertex.BITAN.X *= -1;
                        vertex.BITAN.Y *= -1;
                        vertex.BITAN.Z *= -1;
                    }

                    if (mesh.HasUVSet(0))
                    {
                        vertex.TEX0 = new GXVector2(v.UVs[0].X, v.UVs[0].Y);
                    }

                    if (mesh.HasUVSet(1))
                    {
                        vertex.TEX1 = new GXVector2(v.UVs[1].X, v.UVs[1].Y);
                    }

                    if (mesh.HasUVSet(2))
                    {
                        vertex.TEX2 = new GXVector2(v.UVs[2].X, v.UVs[2].Y);
                    }

                    if (mesh.HasUVSet(3))
                    {
                        vertex.TEX3 = new GXVector2(v.UVs[3].X, v.UVs[3].Y);
                    }

                    if (mesh.HasUVSet(4))
                    {
                        vertex.TEX4 = new GXVector2(v.UVs[4].X, v.UVs[4].Y);
                    }

                    if (mesh.HasUVSet(5))
                    {
                        vertex.TEX5 = new GXVector2(v.UVs[5].X, v.UVs[5].Y);
                    }

                    if (mesh.HasUVSet(6))
                    {
                        vertex.TEX6 = new GXVector2(v.UVs[6].X, v.UVs[6].Y);
                    }

                    if (mesh.HasUVSet(7))
                    {
                        vertex.TEX7 = new GXVector2(v.UVs[7].X, v.UVs[7].Y);
                    }

                    if (mesh.HasColorSet(0))
                    {
                        vertex.CLR0 = new GXColor4(
                            v.Colors[0].X * (Settings.MultiplyVertexColorBy2 ? 2 : 1),
                            v.Colors[0].Y * (Settings.MultiplyVertexColorBy2 ? 2 : 1),
                            v.Colors[0].Z * (Settings.MultiplyVertexColorBy2 ? 2 : 1),
                            Settings.ImportVertexAlpha ? v.Colors[0].W : 1);
                    }

                    if (mesh.HasColorSet(1))
                    {
                        vertex.CLR1 = new GXColor4(
                            v.Colors[1].X * (Settings.MultiplyVertexColorBy2 ? 2 : 1),
                            v.Colors[1].Y * (Settings.MultiplyVertexColorBy2 ? 2 : 1),
                            v.Colors[1].Z * (Settings.MultiplyVertexColorBy2 ? 2 : 1),
                            Settings.ImportVertexAlpha ? v.Colors[1].W : 1);
                    }

                    vertices.Add(vertex);
                }


                // generate pobjs
                HSD_POBJ pobj = null;

                if (mesh.HasEnvelopes() && Settings.ImportRigging && !singleBinded)
                {
                    pobj = _cache.POBJGen.CreatePOBJsFromTriangleList(vertices, Attributes.ToArray(), jobjList, weightList);
                }
                else
                {
                    pobj = _cache.POBJGen.CreatePOBJsFromTriangleList(vertices, Attributes.ToArray(), null);
                }

                if (singleBinded && jobjList.Count > 0 && jobjList[0].Length > 0)
                {
                    parent = jobjList[0][0];
                }

                if (pobj != null)
                {
                    if (dobj.Pobj == null)
                    {
                        dobj.Pobj = pobj;
                    }
                    else
                    {
                        dobj.Pobj.Add(pobj);
                    }
                }
            }

            if (parent.Dobj == null)
            {
                parent.Dobj = root;
            }
            else
            {
                parent.Dobj.Add(root);
            }
        }
示例#15
0
        public IOModel GetIOModel()
        {
            IOModel outModel = new IOModel();

            Mesh meshFile     = null;
            Matl materialFile = null;

            foreach (FileNode n in Parent.Nodes)
            {
                if (n.Text.Equals(model.MeshString))
                {
                    meshFile = ((NumsbhNode)n).mesh;
                }
                if (n.Text.Equals(model.SkeletonFileName))
                {
                    outModel.Skeleton = (RSkeleton)((SkelNode)n).GetRenderableNode();
                }
                if (n.Text.Equals(model.MaterialFileNames[0].MaterialFileName))
                {
                    materialFile = ((MatlNode)n).Material;
                }
            }

            Dictionary <string, int> indexByBoneName = new Dictionary <string, int>();

            if (outModel.Skeleton != null)
            {
                for (int i = 0; i < outModel.Skeleton.Bones.Count; i++)
                {
                    indexByBoneName.Add(outModel.Skeleton.Bones[i].Name, i);
                }
            }

            Dictionary <string, int> materialNameToIndex = new Dictionary <string, int>();

            if (materialFile != null)
            {
                int materialIndex = 0;
                foreach (var entry in materialFile.Entries)
                {
                    materialNameToIndex.Add(entry.ShaderLabel, materialIndex++);
                    IOMaterial material = new IOMaterial
                    {
                        Name = entry.ShaderLabel
                    };
                    outModel.Materials.Add(material);

                    foreach (var attr in entry.Attributes)
                    {
                        if (attr.ParamId == MatlEnums.ParamId.Texture0)
                        {
                            IOTexture dif = new IOTexture
                            {
                                Name = attr.DataObject.ToString()
                            };
                            material.DiffuseTexture = dif;
                        }
                    }
                }
            }

            if (meshFile != null)
            {
                SsbhVertexAccessor vertexAccessor = new SsbhVertexAccessor(meshFile);
                {
                    SsbhRiggingAccessor riggingAccessor = new SsbhRiggingAccessor(meshFile);
                    foreach (MeshObject obj in meshFile.Objects)
                    {
                        IOMesh outMesh = new IOMesh()
                        {
                            Name = obj.Name,
                        };
                        outModel.Meshes.Add(outMesh);

                        // get material
                        if (materialFile != null)
                        {
                            foreach (var entry in model.ModelEntries)
                            {
                                if (entry.MeshName.Equals(obj.Name) && entry.SubIndex == obj.SubMeshIndex)
                                {
                                    outMesh.MaterialIndex = materialNameToIndex[entry.MaterialLabel];
                                    break;
                                }
                            }
                        }

                        IOVertex[] vertices = new IOVertex[obj.VertexCount];
                        for (int i = 0; i < vertices.Length; i++)
                        {
                            vertices[i] = new IOVertex();
                        }

                        foreach (MeshAttribute attr in obj.Attributes)
                        {
                            SsbhVertexAttribute[] values = vertexAccessor.ReadAttribute(attr.AttributeStrings[0].Name, 0, obj.VertexCount, obj);

                            if (attr.AttributeStrings[0].Name.Equals("Position0"))
                            {
                                outMesh.HasPositions = true;
                                for (int i = 0; i < values.Length; i++)
                                {
                                    vertices[i].Position = new OpenTK.Vector3(values[i].X, values[i].Y, values[i].Z);
                                }
                            }
                            if (attr.AttributeStrings[0].Name.Equals("Normal0"))
                            {
                                outMesh.HasNormals = true;
                                for (int i = 0; i < values.Length; i++)
                                {
                                    vertices[i].Normal = new OpenTK.Vector3(values[i].X, values[i].Y, values[i].Z);
                                }
                            }

                            // Flip UVs vertically for export.
                            if (attr.AttributeStrings[0].Name.Equals("map1"))
                            {
                                outMesh.HasUV0 = true;
                                for (int i = 0; i < values.Length; i++)
                                {
                                    vertices[i].UV0 = new OpenTK.Vector2(values[i].X, 1 - values[i].Y);
                                }
                            }
                            if (attr.AttributeStrings[0].Name.Equals("uvSet"))
                            {
                                outMesh.HasUV1 = true;
                                for (int i = 0; i < values.Length; i++)
                                {
                                    vertices[i].UV1 = new OpenTK.Vector2(values[i].X, 1 - values[i].Y);
                                }
                            }
                            if (attr.AttributeStrings[0].Name.Equals("uvSet1"))
                            {
                                outMesh.HasUV2 = true;
                                for (int i = 0; i < values.Length; i++)
                                {
                                    vertices[i].UV2 = new OpenTK.Vector2(values[i].X, 1 - values[i].Y);
                                }
                            }
                            if (attr.AttributeStrings[0].Name.Equals("uvSet2"))
                            {
                                outMesh.HasUV3 = true;
                                for (int i = 0; i < values.Length; i++)
                                {
                                    vertices[i].UV3 = new OpenTK.Vector2(values[i].X, 1 - values[i].Y);
                                }
                            }
                            if (attr.AttributeStrings[0].Name.Equals("colorSet1"))
                            {
                                outMesh.HasColor = true;
                                for (int i = 0; i < values.Length; i++)
                                {
                                    vertices[i].Color = new OpenTK.Vector4(values[i].X, values[i].Y, values[i].Z, values[i].W) / 127f;
                                }
                            }
                        }

                        // Fix SingleBinds
                        if (outModel.Skeleton != null && !obj.ParentBoneName.Equals(""))
                        {
                            int parentIndex = outModel.Skeleton.GetBoneIndex(obj.ParentBoneName);
                            if (parentIndex != -1)
                            {
                                for (int i = 0; i < vertices.Length; i++)
                                {
                                    vertices[i].Position      = OpenTK.Vector3.TransformPosition(vertices[i].Position, outModel.Skeleton.Bones[parentIndex].WorldTransform);
                                    vertices[i].Normal        = OpenTK.Vector3.TransformNormal(vertices[i].Normal, outModel.Skeleton.Bones[parentIndex].WorldTransform);
                                    vertices[i].BoneIndices.X = indexByBoneName[obj.ParentBoneName];
                                    vertices[i].BoneWeights.X = 1;
                                    outMesh.HasBoneWeights    = true;
                                }
                            }
                        }

                        // Apply Rigging
                        SsbhVertexInfluence[] influences = riggingAccessor.ReadRiggingBuffer(obj.Name, (int)obj.SubMeshIndex);

                        foreach (SsbhVertexInfluence influence in influences)
                        {
                            outMesh.HasBoneWeights = true;

                            // Some influences refer to bones that don't exist in the skeleton.
                            // _eff bones?
                            if (!indexByBoneName.ContainsKey(influence.BoneName))
                            {
                                continue;
                            }

                            if (vertices[influence.VertexIndex].BoneWeights.X == 0)
                            {
                                vertices[influence.VertexIndex].BoneIndices.X = indexByBoneName[influence.BoneName];
                                vertices[influence.VertexIndex].BoneWeights.X = influence.Weight;
                            }
                            else if (vertices[influence.VertexIndex].BoneWeights.Y == 0)
                            {
                                vertices[influence.VertexIndex].BoneIndices.Y = indexByBoneName[influence.BoneName];
                                vertices[influence.VertexIndex].BoneWeights.Y = influence.Weight;
                            }
                            else if (vertices[influence.VertexIndex].BoneWeights.Z == 0)
                            {
                                vertices[influence.VertexIndex].BoneIndices.Z = indexByBoneName[influence.BoneName];
                                vertices[influence.VertexIndex].BoneWeights.Z = influence.Weight;
                            }
                            else if (vertices[influence.VertexIndex].BoneWeights.W == 0)
                            {
                                vertices[influence.VertexIndex].BoneIndices.W = indexByBoneName[influence.BoneName];
                                vertices[influence.VertexIndex].BoneWeights.W = influence.Weight;
                            }
                        }

                        outMesh.Vertices.AddRange(vertices);
                        outMesh.Indices.AddRange(vertexAccessor.ReadIndices(0, obj.IndexCount, obj));
                    }
                }
            }


            return(outModel);
        }
示例#16
0
        /// <summary>
        ///
        /// </summary>
        /// <returns></returns>
        public List <IOMesh> ExtractMesh()
        {
            // gather mesh objects
            var mesh = _document.GetNodesByName("Model").Where(e => e.Properties.Count >= NodeDescSize && e.Properties[NodeDescSize - 1].ToString() == "Mesh");

            List <IOMesh> meshes = new List <IOMesh>();

            var subdeformers = _document.GetNodesByName("SubDeformer");

            // extract geometry information into iomesh
            foreach (var m in mesh)
            {
                // generate mesh
                IOMesh iomesh = new IOMesh()
                {
                    Name = GetNameWithoutNamespace(m.Properties[NodeDescSize - 2].ToString())
                };
                meshes.Add(iomesh);

                // reverse search material
                var material  = "";
                var materials = GetChildConnections(m).Find(e => e.Name == "Material");
                if (materials != null)
                {
                    material = GetNameWithoutNamespace(materials.Properties[NodeDescSize - 2].ToString());
                }


                // collect geometry information
                // in older fbx this was stored in the mesh node
                // in newer versions it's in its own node

                var geoms = _document.GetNodesByName("Geometry").Where(e => IsParentedTo(e.Properties[0].ToString(), m.Properties[0].ToString())).ToArray();

                if (geoms.Length > 0)
                {
                    foreach (var g in geoms)
                    {
                        ProcessGeometry(g, out IOPolygon poly, out List <IOVertex> verts);
                        for (int i = 0; i < poly.Indicies.Count; i++)
                        {
                            poly.Indicies[i] += iomesh.Vertices.Count;
                        }
                        iomesh.Vertices.AddRange(verts);
                        poly.MaterialName = material;
                        iomesh.Polygons.Add(poly);
                    }
                }
                else
                {
                    ProcessGeometry(m, out IOPolygon poly, out List <IOVertex> verts);
                    poly.MaterialName = material;
                    iomesh.Vertices   = verts;
                    iomesh.Polygons.Add(poly);
                }

                // get transform for this node
                var tra = CreateBoneFromNode(m);
                iomesh.TransformVertices(tra.WorldTransform);
            }


            return(meshes);
        }
示例#17
0
        public IOModel GetIOModel()
        {
            IOModel outModel = new IOModel();

            MESH meshFile = null;

            foreach (FileNode n in Parent.Nodes)
            {
                if (n.Text.Equals(_model.MeshString))
                {
                    meshFile = ((NUMSHB_Node)n).mesh;
                }
                if (n.Text.Equals(_model.SkeletonFileName))
                {
                    outModel.Skeleton = (RSkeleton)((SKEL_Node)n).GetRenderableNode();
                }
            }

            Dictionary <string, int> indexByBoneName = new Dictionary <string, int>();

            if (outModel.Skeleton != null)
            {
                for (int i = 0; i < outModel.Skeleton.Bones.Count; i++)
                {
                    indexByBoneName.Add(outModel.Skeleton.Bones[i].Name, i);
                }
            }

            if (meshFile != null)
            {
                using (SSBHVertexAccessor vertexAccessor = new SSBHVertexAccessor(meshFile))
                {
                    SSBHRiggingAccessor riggingAccessor = new SSBHRiggingAccessor(meshFile);
                    foreach (MeshObject obj in meshFile.Objects)
                    {
                        IOMesh outMesh = new IOMesh()
                        {
                            Name = obj.Name,
                        };
                        outModel.Meshes.Add(outMesh);

                        IOVertex[] vertices = new IOVertex[obj.VertexCount];
                        for (int i = 0; i < vertices.Length; i++)
                        {
                            vertices[i] = new IOVertex();
                        }

                        foreach (MeshAttribute attr in obj.Attributes)
                        {
                            SSBHVertexAttribute[] values = vertexAccessor.ReadAttribute(attr.AttributeStrings[0].Name, 0, obj.VertexCount, obj);

                            if (attr.AttributeStrings[0].Name.Equals("Position0"))
                            {
                                outMesh.HasPositions = true;
                                for (int i = 0; i < values.Length; i++)
                                {
                                    vertices[i].Position = new OpenTK.Vector3(values[i].X, values[i].Y, values[i].Z);
                                }
                            }
                            if (attr.AttributeStrings[0].Name.Equals("Normal0"))
                            {
                                outMesh.HasNormals = true;
                                for (int i = 0; i < values.Length; i++)
                                {
                                    vertices[i].Normal = new OpenTK.Vector3(values[i].X, values[i].Y, values[i].Z);
                                }
                            }
                            if (attr.AttributeStrings[0].Name.Equals("map1"))
                            {
                                outMesh.HasUV0 = true;
                                for (int i = 0; i < values.Length; i++)
                                {
                                    vertices[i].UV0 = new OpenTK.Vector2(values[i].X, values[i].Y);
                                }
                            }
                            if (attr.AttributeStrings[0].Name.Equals("uvSet"))
                            {
                                outMesh.HasUV1 = true;
                                for (int i = 0; i < values.Length; i++)
                                {
                                    vertices[i].UV1 = new OpenTK.Vector2(values[i].X, values[i].Y);
                                }
                            }
                            if (attr.AttributeStrings[0].Name.Equals("uvSet1"))
                            {
                                outMesh.HasUV2 = true;
                                for (int i = 0; i < values.Length; i++)
                                {
                                    vertices[i].UV2 = new OpenTK.Vector2(values[i].X, values[i].Y);
                                }
                            }
                            if (attr.AttributeStrings[0].Name.Equals("uvSet2"))
                            {
                                outMesh.HasUV3 = true;
                                for (int i = 0; i < values.Length; i++)
                                {
                                    vertices[i].UV3 = new OpenTK.Vector2(values[i].X, values[i].Y);
                                }
                            }
                            if (attr.AttributeStrings[0].Name.Equals("colorSet1"))
                            {
                                outMesh.HasColor = true;
                                for (int i = 0; i < values.Length; i++)
                                {
                                    vertices[i].Color = new OpenTK.Vector4(values[i].X, values[i].Y, values[i].Z, values[i].W);
                                }
                            }
                        }

                        // Fix SingleBinds
                        if (outModel.Skeleton != null && !obj.ParentBoneName.Equals(""))
                        {
                            int parentIndex = outModel.Skeleton.GetBoneIndex(obj.ParentBoneName);
                            if (parentIndex != -1)
                            {
                                for (int i = 0; i < vertices.Length; i++)
                                {
                                    vertices[i].Position      = OpenTK.Vector3.TransformPosition(vertices[i].Position, outModel.Skeleton.Bones[parentIndex].WorldTransform);
                                    vertices[i].Normal        = OpenTK.Vector3.TransformNormal(vertices[i].Normal, outModel.Skeleton.Bones[parentIndex].WorldTransform);
                                    vertices[i].BoneIndices.X = indexByBoneName[obj.ParentBoneName];
                                    vertices[i].BoneWeights.X = 1;
                                }
                            }
                        }

                        // Apply Rigging
                        SSBHVertexInfluence[] influences = riggingAccessor.ReadRiggingBuffer(obj.Name, (int)obj.SubMeshIndex);

                        foreach (SSBHVertexInfluence influence in influences)
                        {
                            // Some influences refer to bones that don't exist in the skeleton.
                            // _eff bones?
                            if (!indexByBoneName.ContainsKey(influence.BoneName))
                            {
                                continue;
                            }

                            if (vertices[influence.VertexIndex].BoneWeights.X == 0)
                            {
                                vertices[influence.VertexIndex].BoneIndices.X = indexByBoneName[influence.BoneName];
                                vertices[influence.VertexIndex].BoneWeights.X = influence.Weight;
                            }
                            else if (vertices[influence.VertexIndex].BoneWeights.Y == 0)
                            {
                                vertices[influence.VertexIndex].BoneIndices.Y = indexByBoneName[influence.BoneName];
                                vertices[influence.VertexIndex].BoneWeights.Y = influence.Weight;
                            }
                            else if (vertices[influence.VertexIndex].BoneWeights.Z == 0)
                            {
                                vertices[influence.VertexIndex].BoneIndices.Z = indexByBoneName[influence.BoneName];
                                vertices[influence.VertexIndex].BoneWeights.Z = influence.Weight;
                            }
                            else if (vertices[influence.VertexIndex].BoneWeights.W == 0)
                            {
                                vertices[influence.VertexIndex].BoneIndices.W = indexByBoneName[influence.BoneName];
                                vertices[influence.VertexIndex].BoneWeights.W = influence.Weight;
                            }
                        }

                        outMesh.Vertices.AddRange(vertices);
                        outMesh.Indices.AddRange(vertexAccessor.ReadIndices(0, obj.IndexCount, obj));
                    }
                }
            }


            return(outModel);
        }
示例#18
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="n"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public IOMesh LoadGeometryFromID(Node n, string id, List <IOEnvelope> vertexEnvelopes = null)
        {
            // sanitize
            id = ColladaHelper.SanitizeID(id);

            // find geometry by id
            var geom = _collada.Library_Geometries.Geometry.First(e => e.ID == id);

            // not found
            if (geom == null)
            {
                return(null);
            }

            // create new mesh
            IOMesh mesh = new IOMesh()
            {
                Name = n.Name
            };

            // create source manager helper
            SourceManager srcs = new SourceManager();

            if (geom.Mesh.Source != null)
            {
                foreach (var src in geom.Mesh.Source)
                {
                    srcs.AddSource(src);
                }
            }


            // load geomtry meshes
            if (geom.Mesh.Triangles != null)
            {
                foreach (var tri in geom.Mesh.Triangles)
                {
                    var stride = tri.Input.Max(e => e.Offset) + 1;
                    var poly   = new IOPolygon()
                    {
                        PrimitiveType = IOPrimitive.TRIANGLE,
                        MaterialName  = tri.Material
                    };

                    var p = tri.P.GetValues();

                    for (int i = 0; i < tri.Count * 3; i++)
                    {
                        IOVertex vertex = new IOVertex();

                        for (int j = 0; j < tri.Input.Length; j++)
                        {
                            var input = tri.Input[j];

                            var index = p[i * stride + input.Offset];

                            ProcessInput(input.Semantic, input.source, input.Set, vertex, geom.Mesh.Vertices, index, srcs, vertexEnvelopes);
                        }

                        poly.Indicies.Add(mesh.Vertices.Count);
                        mesh.Vertices.Add(vertex);
                    }

                    mesh.Polygons.Add(poly);
                }
            }

            //TODO: collada trifan

            //TODO: collada  tristrip

            //TODO: collada linestrip

            //TODO: collada polylist

            //TODO: collada polygon

            return(mesh);
        }
示例#19
0
        public IOModel ImportIOModel(string FileName)
        {
            IOModel    model    = new IOModel();
            SBSkeleton skeleton = new SBSkeleton();

            Dictionary <int, SBBone>    indexToBone    = new Dictionary <int, SBBone>();
            Dictionary <string, IOMesh> materialToMesh = new Dictionary <string, IOMesh>();
            List <SBBone> PostProcessBones             = new List <SBBone>();
            List <int>    boneParents = new List <int>();

            string[] lines          = File.ReadAllLines(FileName);
            string   CurrentSection = "";

            for (int i = 0; i < lines.Length; i++)
            {
                string[] args = Regex.Replace(lines[i].Trim(), @"\s+", " ").Split(' ');

                if (args[0].Equals("end"))
                {
                    CurrentSection = "";
                }
                else
                if (args[0].Equals("time"))
                {
                }
                else
                if (args[0].Equals("nodes"))
                {
                    CurrentSection = args[0];
                }
                else
                if (args[0].Equals("skeleton"))
                {
                    CurrentSection = args[0];
                }
                else
                if (args[0].Equals("triangles"))
                {
                    CurrentSection = args[0];
                }
                else
                {
                    switch (CurrentSection)
                    {
                    case "nodes":
                        var bone = new SBBone();
                        bone.Name = args[1].Replace("\"", "");
                        Console.WriteLine(bone.Name + " " + args[2]);
                        boneParents.Add(int.Parse(args[2]));
                        PostProcessBones.Add(bone);
                        indexToBone.Add(int.Parse(args[0]), bone);
                        break;

                    case "skeleton":
                        var skel = PostProcessBones[int.Parse(args[0])];
                        skel.Transform = Matrix4.Identity;
                        skel.X         = float.Parse(args[1]);
                        skel.Y         = float.Parse(args[2]);
                        skel.Z         = float.Parse(args[3]);
                        skel.RX        = float.Parse(args[4]);
                        skel.RY        = float.Parse(args[5]);
                        skel.RZ        = float.Parse(args[6]);
                        break;

                    case "triangles":
                        string material = args[0];
                        if (!materialToMesh.ContainsKey(material))
                        {
                            var iomesh = new IOMesh();
                            iomesh.HasPositions   = true;
                            iomesh.HasNormals     = true;
                            iomesh.HasUV0         = true;
                            iomesh.HasBoneWeights = true;
                            iomesh.Name           = material;
                            materialToMesh.Add(material, iomesh);
                        }
                        var mesh = materialToMesh[material];
                        mesh.Vertices.Add(ReadVertex(Regex.Replace(lines[i + 1].Trim(), @"\s+", " ").Split(' ')));
                        mesh.Vertices.Add(ReadVertex(Regex.Replace(lines[i + 2].Trim(), @"\s+", " ").Split(' ')));
                        mesh.Vertices.Add(ReadVertex(Regex.Replace(lines[i + 3].Trim(), @"\s+", " ").Split(' ')));
                        i += 3;
                        break;
                    }
                }
            }

            //PostProcessBones
            int boneIndex = 0;

            foreach (var bone in PostProcessBones)
            {
                if (boneParents[boneIndex] == -1)
                {
                    skeleton.AddRoot(bone);
                }
                else
                {
                    bone.Parent = indexToBone[boneParents[boneIndex]];
                }
                boneIndex++;
            }

            model.Skeleton = skeleton;
            // finalize meshes
            foreach (var pair in materialToMesh)
            {
                model.Meshes.Add(pair.Value);
                pair.Value.Optimize();
                SBConsole.WriteLine($"Imported {pair.Key} from SMD");

                //finalize rigging
                for (int i = 0; i < pair.Value.Vertices.Count; i++)
                {
                    var vertex = pair.Value.Vertices[i];
                    vertex.BoneIndices.X   = model.Skeleton.IndexOfBone(indexToBone[(int)vertex.BoneIndices.X]);
                    vertex.BoneIndices.Y   = model.Skeleton.IndexOfBone(indexToBone[(int)vertex.BoneIndices.Y]);
                    vertex.BoneIndices.Z   = model.Skeleton.IndexOfBone(indexToBone[(int)vertex.BoneIndices.Z]);
                    vertex.BoneIndices.W   = model.Skeleton.IndexOfBone(indexToBone[(int)vertex.BoneIndices.W]);
                    pair.Value.Vertices[i] = vertex;
                }
            }

            return(model);
        }