csharp/aeroson/lego-game-and-Unity-like-engine/MyEngine/myengine/ObjLoader.cs

ObjLoader.cs
using System;
using System.IO;
using System.Collections.Generic;
using OpenTK;


namespace MyEngine
{
    public partial clast ObjLoader
    {

        public static Mesh Load(Resource resource, GameObject appendToGameObject=null)
        {
            var mesh = new ObjLoader().Parse(resource, appendToGameObject);
            mesh.resource = resource;
            return mesh;
        }

        static char[] splitCharacters = new char[] { ' ' };
        static char[] trimCharacters = new char[] { ' ', '\t' };
        

        List verticesObj = new List();
        List normalsObj = new List();
        List uvsObj = new List();

        List verticesMesh = new List();
        List normalsMesh = new List();
        List uvsMesh = new List();
        List triangleIndiciesMesh = new List();

        bool gotUvs = false;
        bool gotNormal = false;

        Dictionary objFaceToMeshIndicie = new Dictionary();

        MaterialLibrary materialLibrary;
        MaterialPBR lastMaterial;

        int failedParse = 0;
        void Parse(ref string str, ref float t)
        {
            if (!float.TryParse(str, System.Globalization.NumberStyles.Any, System.Globalization.NumberFormatInfo.InvariantInfo, out t))
                failedParse++;
        }
        Mesh Parse(Resource resource, GameObject appendToGameObject)
        {
            using (StreamReader textReader = new StreamReader(resource))
            {

                int i1, i2, i3, i4;

                string line;
                while ((line = textReader.ReadLine()) != null)
                {
                    line = line.Trim(trimCharacters);
                    line = line.Replace("  ", " ");

                    string[] parameters = line.Split(splitCharacters);

                    switch (parameters[0])
                    {
                        case "p": // Point
                            break;

                        case "v": // Vertex
                            var v = Vector3.Zero;
                            Parse(ref parameters[1], ref v.X);
                            Parse(ref parameters[2], ref v.Y);
                            Parse(ref parameters[3], ref v.Z);
                            verticesObj.Add(v);
                            break;

                        case "vt": // TexCoord
                            gotUvs = true;
                            var vt = Vector2.Zero;
                            Parse(ref parameters[1], ref vt.X);
                            Parse(ref parameters[2], ref vt.Y);
                            uvsObj.Add(vt);
                            break;

                        case "vn": // Normal
                            gotNormal = true;
                            var vn = Vector3.Zero;
                            Parse(ref parameters[1], ref vn.X);
                            Parse(ref parameters[2], ref vn.Y);
                            Parse(ref parameters[3], ref vn.Z);
                            normalsObj.Add(vn);
                            break;

                        case "f":
                            switch (parameters.Length)
                            {
                                case 4:
                                    i1 = ParseFaceParameter(parameters[1]);
                                    i2 = ParseFaceParameter(parameters[2]);
                                    i3 = ParseFaceParameter(parameters[3]);
                                    triangleIndiciesMesh.Add(i1);
                                    triangleIndiciesMesh.Add(i2);
                                    triangleIndiciesMesh.Add(i3);
                                    break;

                                case 5:
                                    i1 = ParseFaceParameter(parameters[1]);
                                    i2 = ParseFaceParameter(parameters[2]);
                                    i3 = ParseFaceParameter(parameters[3]);
                                    i4 = ParseFaceParameter(parameters[4]);
                                    triangleIndiciesMesh.Add(i1);
                                    triangleIndiciesMesh.Add(i2);
                                    triangleIndiciesMesh.Add(i3);
                                    triangleIndiciesMesh.Add(i1);
                                    triangleIndiciesMesh.Add(i3);
                                    triangleIndiciesMesh.Add(i4);
                                    break;
                            }
                            break;
                        case "mtllib":
                            if (Resource.ResourceInFolderExists(resource, parameters[1]))
                            {
                                materialLibrary = new MaterialLibrary(Resource.GetResourceInFolder(resource, parameters[1]));
                            }
                            break;
                        case "usemtl":
                            if (materialLibrary!=null) lastMaterial = materialLibrary.GetMat(parameters[1]);
                            break;
                    }
                }

                textReader.Close();
            }


            if(appendToGameObject!=null) return EndObjPart(appendToGameObject);

            Debug.Info("Loaded " + resource.originalPath + " vertices:" + verticesMesh.Count + " faces:" + triangleIndiciesMesh.Count / 3);

            return EndMesh();
    
        }


        Mesh EndMesh()
        {
            var mesh = new Mesh();

            mesh.vertices = verticesMesh.ToArray();
            mesh.uvs = uvsMesh.ToArray();
            mesh.triangleIndicies = triangleIndiciesMesh.ToArray();

            if (failedParse > 0) Debug.Warning("Failed to parse data " + failedParse + " times");
            failedParse = 0;

            if (gotNormal) mesh.normals = normalsMesh.ToArray();
            else mesh.RecalculateNormals();

            return mesh;
        }

        Mesh EndObjPart(GameObject appendToGameObject)
        {  
            var renderer = appendToGameObject.AddComponent();
            renderer.material = lastMaterial;
            renderer.mesh = EndMesh();
            return renderer.mesh;
        }




        static char[] faceParamaterSplitter = new char[] { '/' };
        int ParseFaceParameter(string faceParameter)
        {
            Vector3 vertex = new Vector3();
            Vector2 texCoord = new Vector2();
            Vector3 normal = new Vector3();

            string[] parameters = faceParameter.Split(faceParamaterSplitter);

            int vertexIndex = int.Parse(parameters[0]);
            if (vertexIndex < 0) vertexIndex = verticesObj.Count + vertexIndex;
            else vertexIndex = vertexIndex - 1;
            vertex = verticesObj[vertexIndex];

            if (parameters.Length > 1)
            {
                int texCoordIndex = 0;
                if (int.TryParse(parameters[1], out texCoordIndex))
                {
                    if (texCoordIndex < 0) texCoordIndex = uvsObj.Count + texCoordIndex;
                    else texCoordIndex = texCoordIndex - 1;
                    texCoord = uvsObj[texCoordIndex];
                }
            }

            if (parameters.Length > 2)
            {
                int normalIndex = 0;
                if (int.TryParse(parameters[2], out normalIndex))
                {
                    if (normalIndex < 0) normalIndex = normalsObj.Count + normalIndex;
                    else normalIndex = normalIndex - 1;
                    normal = normalsObj[normalIndex];
                }
            }

            return FindOrAddObjVertex(ref faceParameter, ref vertex, ref texCoord, ref normal);
        }

        int FindOrAddObjVertex(ref string faceParamater, ref Vector3 vertex, ref Vector2 texCoord, ref Vector3 normal)
        {
            int index;
            if (objFaceToMeshIndicie.TryGetValue(faceParamater, out index)) {
                return index;
            } else {

                verticesMesh.Add(vertex);
                uvsMesh.Add(texCoord);
                normalsMesh.Add(normal);

                index = verticesMesh.Count - 1;
                objFaceToMeshIndicie[faceParamater]=index;

                return index;
            }
        }


    }
}