< Summary

Class:OBJLoader
Assembly:PluginScripts
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Plugins/OBJImport/OBJLoader.cs
Covered lines:133
Uncovered lines:122
Coverable lines:255
Total lines:495
Line coverage:52.1% (133 of 255)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
OBJLoader()0%110100%
ObjLoadMenu()0%6200%
ParseVectorFromCMPS(...)0%2.022083.33%
ParseColorFromCMPS(...)0%2100%
OBJGetFilePath(...)0%20400%
LoadMTLFile(...)0%2401500%
LoadOBJFile(...)0%90.253076.34%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Plugins/OBJImport/OBJLoader.cs

#LineLine coverage
 1/*
 2(C) 2015 AARO4130
 3DO NOT USE PARTS OF, OR THE ENTIRE SCRIPT, AND CLAIM AS YOUR OWN WORK
 4*/
 5
 6using System;
 7using UnityEngine;
 8using System.Collections.Generic;
 9using System.IO;
 10using System.Linq;
 11#if UNITY_EDITOR
 12using UnityEditor;
 13#endif
 14
 15public class OBJLoader
 16{
 117    public static bool splitByMaterial = false;
 118    public static string[] searchPaths = new string[] { "", "%FileName%_Textures" + Path.DirectorySeparatorChar };
 19    //structures
 20    struct OBJFace
 21    {
 22        public string materialName;
 23        public string meshName;
 24        public int[] indexes;
 25    }
 26
 27
 28    //functions
 29#if UNITY_EDITOR
 30    [MenuItem("GameObject/Import From OBJ")]
 31    static void ObjLoadMenu()
 32    {
 033        string pth = UnityEditor.EditorUtility.OpenFilePanel("Import OBJ", "", "obj");
 034        if (!string.IsNullOrEmpty(pth))
 35        {
 036            System.Diagnostics.Stopwatch s = new System.Diagnostics.Stopwatch();
 037            s.Start();
 038            LoadOBJFile(pth);
 039            Debug.Log("OBJ load took " + s.ElapsedMilliseconds + "ms");
 040            s.Stop();
 41        }
 042    }
 43#endif
 44
 45    public static Vector3 ParseVectorFromCMPS(string[] cmps)
 46    {
 364447        float x = float.Parse(cmps[1]);
 364448        float y = float.Parse(cmps[2]);
 364449        if (cmps.Length == 4)
 50        {
 364451            float z = float.Parse(cmps[3]);
 364452            return new Vector3(x, y, z);
 53        }
 054        return new Vector2(x, y);
 55    }
 56
 57    public static Color ParseColorFromCMPS(string[] cmps, float scalar = 1.0f)
 58    {
 059        float Kr = float.Parse(cmps[1]) * scalar;
 060        float Kg = float.Parse(cmps[2]) * scalar;
 061        float Kb = float.Parse(cmps[3]) * scalar;
 062        return new Color(Kr, Kg, Kb);
 63    }
 64
 65    public static string OBJGetFilePath(string path, string basePath, string fileName)
 66    {
 067        foreach (string sp in searchPaths)
 68        {
 069            string s = sp.Replace("%FileName%", fileName);
 70
 071            if (File.Exists(basePath + s + path))
 72            {
 073                return basePath + s + path;
 74            }
 075            else if (File.Exists(path))
 76            {
 077                return path;
 78            }
 79        }
 80
 081        return null;
 82    }
 83
 84    public static Material[] LoadMTLFile(string fn)
 85    {
 086        Material currentMaterial = null;
 087        List<Material> matlList = new List<Material>();
 088        FileInfo mtlFileInfo = new FileInfo(fn);
 089        string baseFileName = Path.GetFileNameWithoutExtension(fn);
 090        string mtlFileDirectory = mtlFileInfo.Directory.FullName + Path.DirectorySeparatorChar;
 91
 092        foreach (string ln in File.ReadAllLines(fn))
 93        {
 094            string l = ln.Trim().Replace("  ", " ");
 095            string[] cmps = l.Split(' ');
 096            string data = l.Remove(0, l.IndexOf(' ') + 1);
 97
 098            if (cmps[0] == "newmtl")
 99            {
 0100                if (currentMaterial != null)
 101                {
 0102                    matlList.Add(currentMaterial);
 103                }
 104                //NOTE(Brian): This will get pink with LWRP?
 0105                currentMaterial = new Material(Shader.Find("Standard (Specular setup)"));
 0106                currentMaterial.name = data;
 0107            }
 0108            else if (cmps[0] == "Kd")
 109            {
 0110                currentMaterial.SetColor("_Color", ParseColorFromCMPS(cmps));
 0111            }
 0112            else if (cmps[0] == "map_Kd")
 113            {
 114                //TEXTURE
 0115                string fpth = OBJGetFilePath(data, mtlFileDirectory, baseFileName);
 0116                if (fpth != null)
 117                {
 118                    //NOTE(Brian): This will break because we don't download the textures?
 0119                    currentMaterial.SetTexture("_MainTex", TextureLoader.LoadTexture(fpth));
 120                }
 0121            }
 0122            else if (cmps[0] == "map_Bump")
 123            {
 124                //TEXTURE
 0125                string fpth = OBJGetFilePath(data, mtlFileDirectory, baseFileName);
 126
 0127                if (fpth != null)
 128                {
 129                    //NOTE(Brian): This will break because we don't download the textures?
 0130                    currentMaterial.SetTexture("_BumpMap", TextureLoader.LoadTexture(fpth, true));
 0131                    currentMaterial.EnableKeyword("_NORMALMAP");
 132                }
 0133            }
 0134            else if (cmps[0] == "Ks")
 135            {
 0136                currentMaterial.SetColor("_SpecColor", ParseColorFromCMPS(cmps));
 0137            }
 0138            else if (cmps[0] == "Ka")
 139            {
 0140                currentMaterial.SetColor("_EmissionColor", ParseColorFromCMPS(cmps, 0.05f));
 0141                currentMaterial.EnableKeyword("_EMISSION");
 0142            }
 0143            else if (cmps[0] == "d")
 144            {
 0145                float visibility = float.Parse(cmps[1]);
 0146                if (visibility < 1)
 147                {
 0148                    Color temp = currentMaterial.color;
 149
 0150                    temp.a = visibility;
 0151                    currentMaterial.SetColor("_Color", temp);
 152
 153                    //TRANSPARENCY ENABLER
 0154                    currentMaterial.SetFloat("_Mode", 3);
 0155                    currentMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
 0156                    currentMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
 0157                    currentMaterial.SetInt("_ZWrite", 0);
 0158                    currentMaterial.DisableKeyword("_ALPHATEST_ON");
 0159                    currentMaterial.EnableKeyword("_ALPHABLEND_ON");
 0160                    currentMaterial.DisableKeyword("_ALPHAPREMULTIPLY_ON");
 0161                    currentMaterial.renderQueue = 3000;
 162                }
 0163            }
 0164            else if (cmps[0] == "Ns")
 165            {
 0166                float Ns = float.Parse(cmps[1]);
 0167                Ns = (Ns / 1000);
 0168                currentMaterial.SetFloat("_Glossiness", Ns);
 169
 170            }
 171        }
 172
 0173        if (currentMaterial != null)
 174        {
 0175            matlList.Add(currentMaterial);
 176        }
 177
 0178        return matlList.ToArray();
 179    }
 180
 181    public static GameObject LoadOBJFile(string filePath, bool inputIsRawOBJ = false)
 182    {
 183
 1184        string meshName = !inputIsRawOBJ ? Path.GetFileNameWithoutExtension(filePath) : "LoadedOBJMesh";
 185
 1186        bool hasNormals = false;
 187        //OBJ LISTS
 1188        List<Vector3> vertices = new List<Vector3>();
 1189        List<Vector3> normals = new List<Vector3>();
 1190        List<Vector2> uvs = new List<Vector2>();
 191        //UMESH LISTS
 1192        List<Vector3> uvertices = new List<Vector3>();
 1193        List<Vector3> unormals = new List<Vector3>();
 1194        List<Vector2> uuvs = new List<Vector2>();
 195        //MESH CONSTRUCTION
 1196        List<string> materialNames = new List<string>();
 1197        List<string> objectNames = new List<string>();
 1198        Dictionary<string, int> hashtable = new Dictionary<string, int>();
 1199        List<OBJFace> faceList = new List<OBJFace>();
 1200        string cmaterial = "";
 1201        string cmesh = "default";
 202
 203        //CACHE
 1204        Material[] materialCache = null;
 205
 206        //save this info for later
 1207        FileInfo OBJFileInfo = null;
 208
 209        string[] OBJLines;
 210
 1211        if (!inputIsRawOBJ)
 212        {
 0213            OBJFileInfo = new FileInfo(filePath);
 214
 0215            OBJLines = File.ReadAllLines(filePath);
 0216        }
 217        else
 218        {
 1219            OBJLines = filePath.Split(new[] { "\r", "\n" }, StringSplitOptions.None);
 220        }
 221
 19938222        foreach (string ln in OBJLines)
 223        {
 9968224            if (ln.Length > 0 && ln[0] != '#')
 225            {
 9965226                string l = ln.Trim().Replace("  ", " ");
 9965227                string[] cmps = l.Split(' ');
 9965228                string data = l.Remove(0, l.IndexOf(' ') + 1);
 229
 9965230                if (cmps[0] == "mtllib" && !inputIsRawOBJ && OBJFileInfo != null)
 231                {
 232                    //load cache
 0233                    string pth = OBJGetFilePath(data, OBJFileInfo.Directory.FullName + Path.DirectorySeparatorChar, mesh
 0234                    if (pth != null)
 235                    {
 0236                        materialCache = LoadMTLFile(pth);
 237                    }
 0238                }
 9965239                else if ((cmps[0] == "g" || cmps[0] == "o") && splitByMaterial == false)
 240                {
 0241                    cmesh = data;
 0242                    if (!objectNames.Contains(cmesh))
 243                    {
 0244                        objectNames.Add(cmesh);
 245                    }
 0246                }
 9965247                else if (cmps[0] == "usemtl")
 248                {
 1249                    cmaterial = data;
 1250                    if (!materialNames.Contains(cmaterial))
 251                    {
 1252                        materialNames.Add(cmaterial);
 253                    }
 254
 1255                    if (splitByMaterial)
 256                    {
 0257                        if (!objectNames.Contains(cmaterial))
 258                        {
 0259                            objectNames.Add(cmaterial);
 260                        }
 261                    }
 0262                }
 9964263                else if (cmps[0] == "v")
 264                {
 265                    //VERTEX
 3644266                    vertices.Add(ParseVectorFromCMPS(cmps));
 3644267                }
 6320268                else if (cmps[0] == "vn")
 269                {
 270                    //VERTEX NORMAL
 0271                    normals.Add(ParseVectorFromCMPS(cmps));
 0272                }
 6320273                else if (cmps[0] == "vt")
 274                {
 275                    //VERTEX UV
 0276                    uvs.Add(ParseVectorFromCMPS(cmps));
 0277                }
 6320278                else if (cmps[0] == "f")
 279                {
 6320280                    int[] indexes = new int[cmps.Length - 1];
 50560281                    for (int i = 1; i < cmps.Length; i++)
 282                    {
 18960283                        string felement = cmps[i];
 18960284                        int vertexIndex = -1;
 18960285                        int normalIndex = -1;
 18960286                        int uvIndex = -1;
 18960287                        if (felement.Contains("//"))
 288                        {
 289                            //doubleslash, no UVS.
 0290                            string[] elementComps = felement.Split('/');
 0291                            vertexIndex = int.Parse(elementComps[0]) - 1;
 0292                            normalIndex = int.Parse(elementComps[2]) - 1;
 0293                        }
 88884294                        else if (felement.Count(x => x == '/') == 2)
 295                        {
 296                            //contains everything
 0297                            string[] elementComps = felement.Split('/');
 0298                            vertexIndex = int.Parse(elementComps[0]) - 1;
 0299                            uvIndex = int.Parse(elementComps[1]) - 1;
 0300                            normalIndex = int.Parse(elementComps[2]) - 1;
 0301                        }
 18960302                        else if (!felement.Contains("/"))
 303                        {
 304                            //just vertex inedx
 18960305                            vertexIndex = int.Parse(felement) - 1;
 18960306                        }
 307                        else
 308                        {
 309                            //vertex and uv
 0310                            string[] elementComps = felement.Split('/');
 0311                            vertexIndex = int.Parse(elementComps[0]) - 1;
 0312                            uvIndex = int.Parse(elementComps[1]) - 1;
 313                        }
 18960314                        string hashEntry = vertexIndex + "|" + normalIndex + "|" + uvIndex;
 18960315                        if (hashtable.ContainsKey(hashEntry))
 316                        {
 15316317                            indexes[i - 1] = hashtable[hashEntry];
 15316318                        }
 319                        else
 320                        {
 321                            //create a new hash entry
 3644322                            indexes[i - 1] = hashtable.Count;
 3644323                            hashtable[hashEntry] = hashtable.Count;
 3644324                            uvertices.Add(vertices[vertexIndex]);
 3644325                            if (normalIndex < 0 || (normalIndex > (normals.Count - 1)))
 326                            {
 3644327                                unormals.Add(Vector3.zero);
 3644328                            }
 329                            else
 330                            {
 0331                                hasNormals = true;
 0332                                unormals.Add(normals[normalIndex]);
 333                            }
 3644334                            if (uvIndex < 0 || (uvIndex > (uvs.Count - 1)))
 335                            {
 3644336                                uuvs.Add(Vector2.zero);
 3644337                            }
 338                            else
 339                            {
 0340                                uuvs.Add(uvs[uvIndex]);
 341                            }
 342
 343                        }
 344                    }
 6320345                    if (indexes.Length < 5 && indexes.Length >= 3)
 346                    {
 6320347                        OBJFace f1 = new OBJFace();
 6320348                        f1.materialName = cmaterial;
 6320349                        f1.indexes = new int[] { indexes[0], indexes[1], indexes[2] };
 6320350                        f1.meshName = (splitByMaterial) ? cmaterial : cmesh;
 6320351                        faceList.Add(f1);
 352
 6320353                        if (indexes.Length > 3)
 354                        {
 0355                            OBJFace f2 = new OBJFace();
 0356                            f2.materialName = cmaterial;
 0357                            f2.meshName = (splitByMaterial) ? cmaterial : cmesh;
 0358                            f2.indexes = new int[] { indexes[2], indexes[3], indexes[0] };
 0359                            faceList.Add(f2);
 360                        }
 361                    }
 362                }
 363            }
 364        }
 365
 1366        if (objectNames.Count == 0)
 367        {
 1368            objectNames.Add("default");
 369        }
 370
 371        //build objects
 1372        GameObject parentObject = new GameObject(meshName);
 373
 374
 4375        for (int objectNamesIndex = 0; objectNamesIndex < objectNames.Count; objectNamesIndex++)
 376        {
 1377            string obj = objectNames[objectNamesIndex];
 1378            GameObject subObjectParent = new GameObject(obj);
 1379            subObjectParent.transform.parent = parentObject.transform;
 380
 1381            GameObject subObject = new GameObject("ChildMesh");
 1382            subObject.transform.parent = subObjectParent.transform;
 1383            subObject.transform.localScale = new Vector3(-1, 1, 1);
 384            //Create mesh
 1385            Mesh m = new Mesh();
 1386            m.name = obj;
 387            //LISTS FOR REORDERING
 1388            List<Vector3> processedVertices = new List<Vector3>();
 1389            List<Vector3> processedNormals = new List<Vector3>();
 1390            List<Vector2> processedUVs = new List<Vector2>();
 1391            List<int[]> processedIndexes = new List<int[]>();
 1392            Dictionary<int, int> remapTable = new Dictionary<int, int>();
 393            //POPULATE MESH
 1394            List<string> meshMaterialNames = new List<string>();
 395
 6321396            OBJFace[] ofaces = faceList.Where(x => x.meshName == obj).ToArray();
 397
 4398            foreach (string mn in materialNames)
 399            {
 6321400                OBJFace[] faces = ofaces.Where(x => x.materialName == mn).ToArray();
 401
 1402                if (faces.Length > 0)
 403                {
 1404                    int[] indexes = new int[0];
 405
 12642406                    foreach (OBJFace f in faces)
 407                    {
 6320408                        int l = indexes.Length;
 6320409                        System.Array.Resize(ref indexes, l + f.indexes.Length);
 6320410                        System.Array.Copy(f.indexes, 0, indexes, l, f.indexes.Length);
 411                    }
 412
 1413                    meshMaterialNames.Add(mn);
 414
 1415                    if (m.subMeshCount != meshMaterialNames.Count)
 416                    {
 0417                        m.subMeshCount = meshMaterialNames.Count;
 418                    }
 419
 37922420                    for (int i = 0; i < indexes.Length; i++)
 421                    {
 18960422                        int idx = indexes[i];
 423                        //build remap table
 18960424                        if (remapTable.ContainsKey(idx))
 425                        {
 426                            //ezpz
 15316427                            indexes[i] = remapTable[idx];
 15316428                        }
 429                        else
 430                        {
 3644431                            processedVertices.Add(uvertices[idx]);
 3644432                            processedNormals.Add(unormals[idx]);
 3644433                            processedUVs.Add(uuvs[idx]);
 3644434                            remapTable[idx] = processedVertices.Count - 1;
 3644435                            indexes[i] = remapTable[idx];
 436                        }
 437                    }
 438
 1439                    processedIndexes.Add(indexes);
 440                }
 441            }
 442
 443            //apply stuff
 1444            m.vertices = processedVertices.ToArray();
 1445            m.normals = processedNormals.ToArray();
 1446            m.uv = processedUVs.ToArray();
 447
 4448            for (int i = 0; i < processedIndexes.Count; i++)
 449            {
 1450                m.SetTriangles(processedIndexes[i], i);
 451            }
 452
 1453            if (!hasNormals)
 454            {
 1455                m.RecalculateNormals();
 456            }
 457
 1458            m.RecalculateBounds();
 459
 1460            MeshFilter mf = subObject.AddComponent<MeshFilter>();
 1461            MeshRenderer mr = subObject.AddComponent<MeshRenderer>();
 462
 1463            Material[] processedMaterials = new Material[meshMaterialNames.Count];
 464
 4465            for (int i = 0; i < meshMaterialNames.Count; i++)
 466            {
 1467                if (materialCache == null)
 468                {
 1469                    processedMaterials[i] = new Material(Shader.Find("Universal Render Pipeline/Simple Lit"));
 1470                }
 471                else
 472                {
 0473                    Material mfn = Array.Find(materialCache, x => x.name == meshMaterialNames[i]);
 474
 0475                    if (mfn == null)
 476                    {
 0477                        processedMaterials[i] = new Material(Shader.Find("Universal Render Pipeline/Simple Lit"));
 0478                    }
 479                    else
 480                    {
 0481                        processedMaterials[i] = mfn;
 482                    }
 483                }
 484
 1485                processedMaterials[i].name = meshMaterialNames[i];
 486            }
 487
 1488            mr.materials = processedMaterials;
 1489            mf.mesh = m;
 490
 491        }
 492
 1493        return parentObject;
 494    }
 495}