Last active
May 21, 2024 05:42
-
-
Save supachailllpay/893cd5b0c31dff3bb025 to your computer and use it in GitHub Desktop.
Load .obj and .mtl in Unity at runtime
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Load .obj and .mtl in Unity at runtime |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using System.Collections; | |
public class Manager : MonoBehaviour { | |
public GameObject model; | |
void Start () { | |
ObjectLoader loader = model.AddComponent <ObjectLoader> (); | |
loader.Load (@"/model/path/", "model.obj"); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* The .obj and .mtl files must follow Wavefront OBJ Specifications. | |
* | |
* OBJ Supported List | |
* | |
* Vertex Data | |
* - v Geometric vertices (not support w) | |
* - vt Texture vertices (not support w) | |
* - vn Vertex normals | |
* | |
* Elements | |
* - f Face (only support triangulate faces) | |
* | |
* Grouping | |
* - o Object name | |
* | |
* Display | |
* - mtllib Material library (not support multiple files) | |
* - usemtl Material name | |
* | |
* MTL Supported List | |
* | |
* Material Name | |
* - newmtl Material name | |
* | |
* Texture Map | |
* - map_Kd Texture file is linked to the diffuse (not support options) | |
*/ | |
using System; | |
using System.IO; | |
using System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
public class FileReader { | |
public struct ObjectFile { | |
public string o; | |
public string mtllib; | |
public List<string> usemtl; | |
public List<Vector3> v; | |
public List<Vector3> vn; | |
public List<Vector2> vt; | |
public List<List<int[]>> f; | |
} | |
public struct MaterialFile { | |
public List<string> newmtl; | |
public List<string> mapKd; | |
} | |
public static ObjectFile ReadObjectFile (string path) { | |
ObjectFile obj = new ObjectFile (); | |
string[] lines = File.ReadAllLines (path); | |
obj.usemtl = new List<string> (); | |
obj.v = new List<Vector3> (); | |
obj.vn = new List<Vector3> (); | |
obj.vt = new List<Vector2> (); | |
obj.f = new List<List<int[]>> (); | |
foreach (string line in lines) { | |
if (line == "" || line.StartsWith ("#")) | |
continue; | |
string[] token = line.Split (' '); | |
switch (token [0]) { | |
case ("o"): | |
obj.o = token [1]; | |
break; | |
case ("mtllib"): | |
obj.mtllib = token [1]; | |
break; | |
case ("usemtl"): | |
obj.usemtl.Add (token [1]); | |
obj.f.Add (new List<int[]> ()); | |
break; | |
case ("v"): | |
obj.v.Add (new Vector3 ( | |
float.Parse (token [1]), | |
float.Parse (token [2]), | |
float.Parse (token [3]))); | |
break; | |
case ("vn"): | |
obj.vn.Add (new Vector3 ( | |
float.Parse (token [1]), | |
float.Parse (token [2]), | |
float.Parse (token [3]))); | |
break; | |
case ("vt"): | |
obj.vt.Add (new Vector3 ( | |
float.Parse (token [1]), | |
float.Parse (token [2]))); | |
break; | |
case ("f"): | |
for (int i = 1; i < 4; i += 1) { | |
int[] triplet = Array.ConvertAll (token [i].Split ('/'), x => { | |
if (String.IsNullOrEmpty (x)) | |
return 0; | |
return int.Parse (x); | |
}); | |
obj.f [obj.f.Count - 1].Add (triplet); | |
} | |
break; | |
} | |
} | |
return obj; | |
} | |
public static MaterialFile ReadMaterialFile (string path) { | |
MaterialFile mtl = new MaterialFile (); | |
string[] lines = File.ReadAllLines (path); | |
mtl.newmtl = new List<string> (); | |
mtl.mapKd = new List<string> (); | |
foreach (string line in lines) { | |
if (line == "" || line.StartsWith ("#")) | |
continue; | |
string[] token = line.Split (' '); | |
switch (token [0]) { | |
case ("newmtl"): | |
mtl.newmtl.Add (token [1]); | |
break; | |
case ("map_Kd"): | |
mtl.mapKd.Add (token [1]); | |
break; | |
} | |
} | |
return mtl; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.IO; | |
using System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
public class ObjectLoader : MonoBehaviour { | |
public string directoryPath; | |
public bool isLoaded; | |
void Awake () { | |
isLoaded = true; | |
} | |
public void Load (string path, string filename) { | |
if (!isLoaded) | |
return; | |
directoryPath = path; | |
StartCoroutine (ConstructModel (filename)); | |
} | |
IEnumerator ConstructModel (string filename) { | |
isLoaded = false; | |
FileReader.ObjectFile obj = FileReader.ReadObjectFile (directoryPath + filename); | |
FileReader.MaterialFile mtl = FileReader.ReadMaterialFile (directoryPath + obj.mtllib); | |
MeshFilter filter = gameObject.AddComponent<MeshFilter> (); | |
MeshRenderer renderer = gameObject.AddComponent<MeshRenderer> (); | |
filter.mesh = PopulateMesh (obj); | |
renderer.materials = DefineMaterial (obj, mtl); | |
isLoaded = true; | |
yield return null; | |
} | |
Mesh PopulateMesh (FileReader.ObjectFile obj) { | |
Mesh mesh = new Mesh (); | |
List<int[]> triplets = new List<int[]> (); | |
List<int> submeshes = new List<int> (); | |
for (int i = 0; i < obj.f.Count; i += 1) { | |
for (int j = 0; j < obj.f [i].Count; j += 1) { | |
triplets.Add (obj.f [i] [j]); | |
} | |
submeshes.Add (obj.f [i].Count); | |
} | |
Vector3[] vertices = new Vector3[triplets.Count]; | |
Vector3[] normals = new Vector3[triplets.Count]; | |
Vector2[] uvs = new Vector2[triplets.Count]; | |
for (int i = 0; i < triplets.Count; i += 1) { | |
vertices [i] = obj.v [triplets [i] [0] - 1]; | |
normals [i] = obj.vn [triplets [i] [2] - 1]; | |
if (triplets [i] [1] > 0) | |
uvs [i] = obj.vt [triplets [i] [1] - 1]; | |
} | |
mesh.name = obj.o; | |
mesh.vertices = vertices; | |
mesh.normals = normals; | |
mesh.uv = uvs; | |
mesh.subMeshCount = submeshes.Count; | |
int vertex = 0; | |
for (int i = 0; i < submeshes.Count; i += 1) { | |
int[] triangles = new int[submeshes [i]]; | |
for (int j = 0; j < submeshes [i]; j += 1) { | |
triangles [j] = vertex; | |
vertex += 1; | |
} | |
mesh.SetTriangles (triangles, i); | |
} | |
mesh.RecalculateBounds (); | |
mesh.Optimize (); | |
return mesh; | |
} | |
Material[] DefineMaterial (FileReader.ObjectFile obj, FileReader.MaterialFile mtl) { | |
Material[] materials = new Material[obj.usemtl.Count]; | |
for (int i = 0; i < obj.usemtl.Count; i += 1) { | |
int index = mtl.newmtl.IndexOf (obj.usemtl [i]); | |
Texture2D texture = new Texture2D (1, 1); | |
texture.LoadImage (File.ReadAllBytes (directoryPath + mtl.mapKd [index])); | |
materials [i] = new Material (Shader.Find ("Diffuse")); | |
materials [i].name = mtl.newmtl [index]; | |
materials [i].mainTexture = texture; | |
} | |
return materials; | |
} | |
} |
Thank you a billion times over. I had to make some changes to this to make it work in my unity project, such as adding multi-object support, but without this code, I wouldn't have been able to add a vital feature to my game. Thank you. Thank you. Thank you.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi, excellent work but how it works? Sorry i cant load my 3d object