Search Unity

[FREE] Runtime OBJ Loader

Discussion in 'Assets and Asset Store' started by ZO5KmUG6R, Nov 4, 2015.

  1. tomekkie2

    tomekkie2

    Joined:
    Jul 6, 2012
    Posts:
    973
    Is this asset capable of calculating lightmap uv's?
     
  2. ibyte

    ibyte

    Joined:
    Aug 14, 2009
    Posts:
    1,048
    How can we avoid this ?

    upload_2020-12-5_10-49-38.png
     
  3. Zachi_mann

    Zachi_mann

    Joined:
    Apr 9, 2017
    Posts:
    1
    Hi,
    Posting here for whoever enjoyed this asset like me, but faced errors with some OBJ files while others worked just fine. I found one bug in the importer code that manifests when you have face definition with negative indices (need to count backwards relative to the current vertex/uv/normal count), for example:
    f -774/-774/-774 -773/-773/-773 -772/-772/-772


    In ObjLoader.cs, there is a section that handles vertex, uv and normal indices, if the OBJ file contains negative indices, the calculation is wrong. This is what is currently done:
    Code (CSharp):
    1. if (vertexIndex < 0)      
    2.      vertexIndex = Vertices.Count - vertexIndex;
    3. vertexIndex--;
    While it should be:
    Code (CSharp):
    1. //since index is negative, we need to add,
    2. //and not subtract the index from the count
    3. if (vertexIndex < 0)
    4.       vertexIndex = Vertices.Count + vertexIndex;
    5. else
    6.       vertexIndex--;
    Same should be done for uv and normal indices.
     
    CashewTTS, efge, JoeStrout and 2 others like this.
  4. HungryCatGames

    HungryCatGames

    Joined:
    Jun 24, 2020
    Posts:
    40
    Any idea what I'm doing wrong? I created a simple object in Blender, applied 5 materials to color it. Exported as OBJ direct to a folder in my project. I reference the folder from within my code to load the object at runtime. It appears just fine. However it's all purple. I paused and checked and the Mesh Renderer of the object has all 5 materials applied to it. If I look at each the Albedo reflects the correct colors. If I look at the Cylinder object (Mesh Filter) it also shows all 5 materials but the preview icon shows purple yet if I click the Albedo of the "Main Maps" shows the correct color. Rendering mode is Opaque and the Project Settings has Standard/Specular included as recommended in the setup. So, from all the reading I've done it should all be working fine, yet purple objects is all I get. Any ideas what I missed? If I can get past this hurdle I think this will be a fantastic tool for my needs. I'd like to have user created content allowing for the addition of 3D objects to my game which can only happen at run time.
     
  5. HungryCatGames

    HungryCatGames

    Joined:
    Jun 24, 2020
    Posts:
    40
    OK, I made some progress. If I change the "Standard shader" on each material (in inspector) under the Mesh Filter to the URP and select the Autodesk Interactive then the previews show correctly and the item is colored properly. Now I have to figure out how to do this programatically with each import. If I can get that working then I think the concept will work.
     
  6. HungryCatGames

    HungryCatGames

    Joined:
    Jun 24, 2020
    Posts:
    40

    Instead of changing my code, I realized I can change one line in the OBJ importer code and change the shader... unfortunately I have yet to figure out the correct wording. Basically what I want is:

    var newMtl = new Material(Shader.Find("Universal Render Pipeline/Autodesk Interactive/")) { name = materialName };

    But when I change the line of code to this the object no longer imports. If I let the import happen and then pause and change the mesh filters by hand to the URP/AI it works just fine.
     
  7. HungryCatGames

    HungryCatGames

    Joined:
    Jun 24, 2020
    Posts:
    40
    Figured it out... there is a double "Autodesk Interactive". The correct name should be: "Universal Render Pipeline/Autodesk Interactive/Autodesk Interactive"
     
    InsomiSnorlax likes this.
  8. mmiknis

    mmiknis

    Joined:
    Nov 7, 2020
    Posts:
    1
    I wanted to ask while using this in runtime is there any way that when a user picks a different model it would replace the old one? Currently as I pick a model to load it just loads a new one but keep the old one in the scene.
     
  9. SimRuJ

    SimRuJ

    Joined:
    Apr 7, 2016
    Posts:
    247
    Just delete the old one yourself with "Destroy(myGameObject)" once the new one is done loading (or before it even starts, your choice).
     
  10. fusobotic

    fusobotic

    Joined:
    Feb 27, 2010
    Posts:
    15
    Anyone know why this is X inverted (-1) scale is done to the GameObject in OBJLoader.cs?

    obj.transform.localScale = new Vector3(-1f, 1f, 1f);


    This has caused some headaches with other functionality I had to write. Had to produce a workaround where I invert the vertices and normal data then flip the faces themselves before assigning to the game object so I can keep a non-inverted scale.

    Am I going to screw anything up by doing this? Why does it need the flip to begin with? I took a look at my .obj file and the vert coordinates aren't misaligned with unity's axis afaik. I've attached the re-factor for anyone else having this same issue (just replace -1f in the code line above with 1f before you use this):

    Code (CSharp):
    1. /*
    2. * Copyright (c) 2019 Dummiesman
    3. *
    4. * Permission is hereby granted, free of charge, to any person obtaining a copy
    5. * of this software and associated documentation files (the "Software"), to deal
    6. * in the Software without restriction, including without limitation the rights
    7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    8. * copies of the Software, and to permit persons to whom the Software is
    9. * furnished to do so, subject to the following conditions:
    10. *
    11. * The above copyright notice and this permission notice shall be included in
    12. * all copies or substantial portions of the Software.
    13. */
    14.  
    15. using Dummiesman;
    16. using System.Collections.Generic;
    17. using UnityEngine;
    18. using System.Linq;
    19.  
    20. namespace Dummiesman {
    21. public class OBJObjectBuilder {
    22.     //
    23.     public int PushedFaceCount { get; private set; } = 0;
    24.  
    25.     //stuff passed in by ctor
    26.     private OBJLoader _loader;
    27.     private string _name;
    28.  
    29.     private Dictionary<ObjLoopHash, int> _globalIndexRemap = new Dictionary<ObjLoopHash, int>();
    30.     private Dictionary<string, List<int>> _materialIndices = new Dictionary<string, List<int>>();
    31.     private List<int> _currentIndexList;
    32.     private string _lastMaterial = null;
    33.  
    34.     //our local vert/normal/uv
    35.     private List<Vector3> _vertices = new List<Vector3>();
    36.     private List<Vector3> _normals = new List<Vector3>();
    37.     private List<Vector2> _uvs = new List<Vector2>();
    38.  
    39.     //this will be set if the model has no normals or missing normal info
    40.     private bool recalculateNormals = false;
    41.  
    42.     /// <summary>
    43.     /// Loop hasher helper class
    44.     /// </summary>
    45.     private class ObjLoopHash {
    46.         public int vertexIndex;
    47.         public int normalIndex;
    48.         public int uvIndex;
    49.  
    50.         public override bool Equals(object obj) {
    51.             if (!(obj is ObjLoopHash))
    52.                 return false;
    53.  
    54.             var hash = obj as ObjLoopHash;
    55.             return (hash.vertexIndex == vertexIndex) && (hash.uvIndex == uvIndex) && (hash.normalIndex == normalIndex);
    56.         }
    57.  
    58.         public override int GetHashCode() {
    59.             int hc = 3;
    60.             hc = unchecked(hc * 314159 + vertexIndex);
    61.             hc = unchecked(hc * 314159 + normalIndex);
    62.             hc = unchecked(hc * 314159 + uvIndex);
    63.             return hc;
    64.         }
    65.     }
    66.  
    67.     public GameObject Build() {
    68.         var go = new GameObject(_name);
    69.  
    70.         //add meshrenderer
    71.         var mr = go.AddComponent<MeshRenderer>();
    72.         int submesh = 0;
    73.  
    74.  
    75.         //locate the material for each submesh
    76.         Material[] materialArray = new Material[_materialIndices.Count];
    77.         foreach (var kvp in _materialIndices) {
    78.             Material material = null;
    79.             if (_loader.Materials == null) {
    80.                 material = OBJLoaderHelper.CreateNullMaterial();
    81.                 material.name = kvp.Key;
    82.             } else {
    83.                 if (!_loader.Materials.TryGetValue(kvp.Key, out material)) {
    84.                     material = OBJLoaderHelper.CreateNullMaterial();
    85.                     material.name = kvp.Key;
    86.                     _loader.Materials[kvp.Key] = material;
    87.                 }
    88.             }
    89.             materialArray[submesh] = material;
    90.             submesh++;
    91.         }
    92.         mr.sharedMaterials = materialArray;
    93.  
    94.         //add meshfilter
    95.         var mf = go.AddComponent<MeshFilter>();
    96.         submesh = 0;
    97.  
    98.         var msh = new Mesh() {
    99.             name = _name,
    100.             indexFormat = (_vertices.Count > 65535) ? UnityEngine.Rendering.IndexFormat.UInt32 : UnityEngine.Rendering.IndexFormat.UInt16,
    101.             subMeshCount = _materialIndices.Count
    102.         };
    103.  
    104.         //set vertex data
    105.  
    106.         //Patch 6-25-21  start
    107.         //Vert/Normal inversion on X axis happens here rather than in localScale
    108.         for (int i = 0; i<_vertices.Count; i++)
    109.         {
    110.                 _vertices[i] = new Vector3(-_vertices[i].x, _vertices[i].y, _vertices[i].z);
    111.         }
    112.         for (int i = 0; i< _normals.Count; i++)
    113.         {
    114.                 _normals[i] = new Vector3(-_normals[i].x, _normals[i].y, _normals[i].z);
    115.         }
    116.         //Patch 6-25-21 end
    117.      
    118.         msh.SetVertices(_vertices);
    119.         msh.SetNormals(_normals);
    120.         msh.SetUVs(0, _uvs);
    121.  
    122.         //set faces
    123.         foreach (var kvp in _materialIndices) {
    124.             msh.SetTriangles(kvp.Value, submesh);
    125.             submesh++;
    126.         }
    127.  
    128.         //recalculations
    129.         if (recalculateNormals)
    130.             msh.RecalculateNormals();
    131.  
    132.      
    133.         msh.triangles = msh.triangles.Reverse().ToArray(); //Patch 6-25-21 need to flip mesh
    134.  
    135.         msh.RecalculateTangents();
    136.         msh.RecalculateBounds();
    137.  
    138.         mf.sharedMesh = msh;
    139.  
    140.         return go;
    141.     }
    142.  
    143.     public void SetMaterial(string name) {
    144.         if (!_materialIndices.TryGetValue(name, out _currentIndexList))
    145.         {
    146.             _currentIndexList = new List<int>();
    147.             _materialIndices[name] = _currentIndexList;
    148.         }
    149.     }
    150.  
    151.  
    152.     public void PushFace(string material, List<int> vertexIndices, List<int> normalIndices, List<int> uvIndices) {
    153.         //invalid face size?
    154.         if (vertexIndices.Count < 3) {
    155.             return;
    156.         }
    157.  
    158.         //set material
    159.         if (material != _lastMaterial) {
    160.             SetMaterial(material);
    161.             _lastMaterial = material;
    162.         }
    163.  
    164.         //remap
    165.         int[] indexRemap = new int[vertexIndices.Count];
    166.         for (int i = 0; i < vertexIndices.Count; i++) {
    167.             int vertexIndex = vertexIndices[i];
    168.             int normalIndex = normalIndices[i];
    169.             int uvIndex = uvIndices[i];
    170.  
    171.             var hashObj = new ObjLoopHash() {
    172.                 vertexIndex = vertexIndex,
    173.                 normalIndex = normalIndex,
    174.                 uvIndex = uvIndex
    175.             };
    176.             int remap = -1;
    177.  
    178.             if (!_globalIndexRemap.TryGetValue(hashObj, out remap)) {
    179.                 //add to dict
    180.                 _globalIndexRemap.Add(hashObj, _vertices.Count);
    181.                 remap = _vertices.Count;
    182.  
    183.                 //add new verts and what not
    184.                 _vertices.Add((vertexIndex >= 0 && vertexIndex < _loader.Vertices.Count) ? _loader.Vertices[vertexIndex] : Vector3.zero);
    185.                 _normals.Add((normalIndex >= 0 && normalIndex < _loader.Normals.Count) ? _loader.Normals[normalIndex] : Vector3.zero);
    186.                 _uvs.Add((uvIndex >= 0 && uvIndex < _loader.UVs.Count) ? _loader.UVs[uvIndex] : Vector2.zero);
    187.  
    188.                 //mark recalc flag
    189.                 if (normalIndex < 0)
    190.                     recalculateNormals = true;
    191.             }
    192.  
    193.             indexRemap[i] = remap;
    194.         }
    195.  
    196.  
    197.         //add face to our mesh list
    198.         if (indexRemap.Length == 3) {
    199.             _currentIndexList.AddRange(new int[] { indexRemap[0], indexRemap[1], indexRemap[2] });
    200.         } else if (indexRemap.Length == 4) {
    201.             _currentIndexList.AddRange(new int[] { indexRemap[0], indexRemap[1], indexRemap[2] });
    202.             _currentIndexList.AddRange(new int[] { indexRemap[2], indexRemap[3], indexRemap[0] });
    203.         } else if (indexRemap.Length > 4) {
    204.             for (int i = indexRemap.Length - 1; i >= 2; i--) {
    205.                 _currentIndexList.AddRange(new int[] { indexRemap[0], indexRemap[i - 1], indexRemap[i] });
    206.             }
    207.         }
    208.  
    209.         PushedFaceCount++;
    210.     }
    211.  
    212.     public OBJObjectBuilder(string name, OBJLoader loader) {
    213.         _name = name;
    214.         _loader = loader;
    215.     }
    216. }
    217. }
     
  11. Dreugui

    Dreugui

    Joined:
    May 11, 2021
    Posts:
    4
    Hi, can anyone can help me ? I would like to load an Obj with texture that don't have normal, so my face are x/x y/y z/z for the id vertex then id UV. but the loader dosn't load the texture.

    EDIT : HI ! I just found the problem ! it wasn't the loader that didn't loaded the texture because there wasn't a normal ! it was because of the 'usemtl Material" line in the obj ! there was a SPACE at the end of the line in the .obj X_X I've just delet the space and now the loader load properly the texture ...
     
    Last edited: Jul 6, 2021
  12. Chen-Gary

    Chen-Gary

    Joined:
    Jul 30, 2020
    Posts:
    1
    Hello.

    I am not sure whether you have solved you problem. I run into the same problem and now I have solved it. This plugin can run in Android app.:)

    As @t-heo have mentioned, there might be some problems related to "android permissions". You may check whether this is also your case by using
    adb
    (if you do not know how to use
    adb
    , check [this link](https://answers.unity.com/questions/492681/how-to-use-adb-logcat.html#answer-571021) provided by @mgear)

    If you see something like "you are rejected to access certain directory because you do not have the permission" (this is definitely not the original error message. sorry, I forgot the original one) in the log, then you may have the same problem as me.

    My solution is to put the obj file in
    Application.persistentDataPath
    (check this api: https://docs.unity3d.com/ScriptReference/Application-persistentDataPath.html), and let my app to read the obj file from this directory.

    It seems that apps are allowed to read/write in this directory in Android. (I really do not know much about Android...)

    * BTW, I also try to request permission by this api: https://docs.unity3d.com/Manual/android-RequestingPermissions.html. But I failed... :(

    Hope this helps you. And thanks to the creator of this nice plugin.;)
     
    Last edited: Jul 8, 2021
    Zochi likes this.
  13. alepozzoli

    alepozzoli

    Joined:
    May 26, 2021
    Posts:
    1
    Hello everyone, first thanks to @aaro4130 for this asset.
    I'm having troubles trying to run this importer on a built application on Hololens, did anyone manage to make it work?
     
  14. Leorn

    Leorn

    Joined:
    Mar 27, 2013
    Posts:
    4
    The author of your project is incredible, thank you very much. Can someone help make friends with HDRP, I tried but it turned out, maybe someone has already done it?
     
    Last edited: Nov 9, 2021
  15. fusobotic

    fusobotic

    Joined:
    Feb 27, 2010
    Posts:
    15
    I made a URP MTLLoader.cs if you want to take a look at it. It is made for mobile though so keep that in mind. And I'm using mtl tag standards from here: http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr
     

    Attached Files:

    mgear likes this.
  16. joshuaradec

    joshuaradec

    Joined:
    Jun 14, 2019
    Posts:
    14
    Does the objfromstream support loading mtl as well? cant seem to get it working with the link I have.
     
  17. batman89

    batman89

    Joined:
    Apr 7, 2016
    Posts:
    1
    Does the asset support loading colors of an obj file? I have two obj files with colors, which load without colors in Unity. They just appear white. If the plugin does support loading obj with colors, I would very much like to know how :)

    Thank you for your work on the plugin.
     
  18. faziii

    faziii

    Joined:
    Jun 11, 2019
    Posts:
    17
    Are you able to load it without freezing? Cause i am looking for same. Loading many different object and its freezing my main thread? There is no async way by default.
     
  19. faziii

    faziii

    Joined:
    Jun 11, 2019
    Posts:
    17
    Hi aaro4130! its 2022 ! Did you added any async way of loading?
     
  20. Zochi

    Zochi

    Joined:
    May 26, 2022
    Posts:
    1
    Hi, @aaro4130
    This asset is great but I'm having a little issue with it. I tried to use this on an android phone but it doesn't work on it. I did the same thing on a computer and it works perfectly but on an android phone it stops on the line of code that's supposed to load it. Is there a solution for this?
     
  21. funkyCoty

    funkyCoty

    Joined:
    May 22, 2018
    Posts:
    727
    Hey aaro, I just wanted to let you know that we used this plugin for Wave Break. Our level editor allows importing OBJ files because of your work, thanks! I'm also going to be using it in a future project, which also has a level editor.

    In both projects, I've had to edit your plugin a bit, though. For each, I'm using custom shaders (not standard unity stuff) and so I had to dig through a bit to find all the stuff to replace. It would be very nice if you modified your plugin in a way to make it easier to do this in the future.
     
  22. ilya_m_3ds

    ilya_m_3ds

    Joined:
    Jan 15, 2020
    Posts:
    9
    To those not already aware, even if this process is run asynchronously (async) the lag actually comes from Instantiating the object.

    I setup the program to load files asynchronously and still encountered the lag spikes when actually Instantiating the objects.
     
  23. TyxLePirate

    TyxLePirate

    Joined:
    Jan 21, 2017
    Posts:
    1
    I'm upvoting this answer because it totally worked for me ! :D
     
  24. NEBIS

    NEBIS

    Joined:
    Jul 7, 2023
    Posts:
    6
    Hi,
    thanks for the nice work ideed!!! Do you know if "vertex color" support is planned? I would be happy to contribute if you give me some guidance, thanks!!!
     
  25. Unity_User1250

    Unity_User1250

    Joined:
    Jul 4, 2023
    Posts:
    2
    Hi ilya,
    Can you please share the code for async loading? It will be helpful for me. Thanks in advance.
     
  26. JarvisMini

    JarvisMini

    Joined:
    Nov 4, 2023
    Posts:
    1
    i am trying this on build and it does not load the object, any solutions to this?
     
  27. Ndogg27

    Ndogg27

    Joined:
    Feb 3, 2023
    Posts:
    19
    Has anyone figured out how to switch the y and z values? I am really struggling with it and would like the object to load in correctly instead of having a permanent -90 x rotation on my object.
     
  28. Evolut1

    Evolut1

    Joined:
    May 30, 2023
    Posts:
    3
    upload_2024-4-19_10-51-55.png
    hi, when i load a file that contains 2 objects with mtl, the second one is always bugged like this, any help? Thanks.