244 lines
8.0 KiB
C#
244 lines
8.0 KiB
C#
![]() |
/// <summary>
|
||
|
/// This is a simple utility class for importing obj files into a Unity mesh at runtime.
|
||
|
/// This version of ObjImporter first reads through the entire file, getting a count of how large
|
||
|
/// the final arrays will be, and then uses standard arrays for everything (as opposed to ArrayLists
|
||
|
/// or any other fancy things).
|
||
|
/// \author el anónimo at the UnifyCommunity wiki (at least he seems to have created the page)
|
||
|
/// </summary>
|
||
|
|
||
|
using UnityEngine;
|
||
|
using System.Collections.Generic;
|
||
|
using System.IO;
|
||
|
#if NETFX_CORE && !UNITY_EDITOR
|
||
|
//using MarkerMetro.Unity.WinLegacy.IO;
|
||
|
#endif
|
||
|
|
||
|
namespace Pathfinding {
|
||
|
public class ObjImporter {
|
||
|
private struct meshStruct {
|
||
|
public Vector3[] vertices;
|
||
|
public Vector3[] normals;
|
||
|
public Vector2[] uv;
|
||
|
public int[] triangles;
|
||
|
public Vector3[] faceData;
|
||
|
public string fileName;
|
||
|
}
|
||
|
|
||
|
// Use this for initialization
|
||
|
public static Mesh ImportFile (string filePath) {
|
||
|
#if NETFX_CORE
|
||
|
throw new System.NotSupportedException("Method not available on this platform");
|
||
|
#else
|
||
|
if (!File.Exists(filePath)) {
|
||
|
Debug.LogError("No file was found at '"+filePath+"'");
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
meshStruct newMesh = createMeshStruct(filePath);
|
||
|
populateMeshStruct(ref newMesh);
|
||
|
|
||
|
Vector3[] newVerts = new Vector3[newMesh.faceData.Length];
|
||
|
Vector2[] newUVs = new Vector2[newMesh.faceData.Length];
|
||
|
Vector3[] newNormals = new Vector3[newMesh.faceData.Length];
|
||
|
int i = 0;
|
||
|
/* The following foreach loops through the facedata and assigns the appropriate vertex, uv, or normal
|
||
|
* for the appropriate Unity mesh array.
|
||
|
*/
|
||
|
foreach (Vector3 v in newMesh.faceData) {
|
||
|
newVerts[i] = newMesh.vertices[(int)v.x - 1];
|
||
|
if (v.y >= 1)
|
||
|
newUVs[i] = newMesh.uv[(int)v.y - 1];
|
||
|
|
||
|
if (v.z >= 1)
|
||
|
newNormals[i] = newMesh.normals[(int)v.z - 1];
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
Mesh mesh = new Mesh();
|
||
|
|
||
|
mesh.vertices = newVerts;
|
||
|
mesh.uv = newUVs;
|
||
|
mesh.normals = newNormals;
|
||
|
mesh.triangles = newMesh.triangles;
|
||
|
|
||
|
mesh.RecalculateBounds();
|
||
|
//mesh.Optimize();
|
||
|
|
||
|
return mesh;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
private static meshStruct createMeshStruct (string filename) {
|
||
|
#if NETFX_CORE
|
||
|
throw new System.NotSupportedException("Method not available on this platform");
|
||
|
#else
|
||
|
int triangles = 0;
|
||
|
int vertices = 0;
|
||
|
int vt = 0;
|
||
|
int vn = 0;
|
||
|
int face = 0;
|
||
|
meshStruct mesh = new meshStruct();
|
||
|
mesh.fileName = filename;
|
||
|
StreamReader stream = File.OpenText(filename);
|
||
|
string entireText = stream.ReadToEnd();
|
||
|
stream.Dispose();
|
||
|
using (StringReader reader = new StringReader(entireText))
|
||
|
{
|
||
|
string currentText = reader.ReadLine();
|
||
|
char[] splitIdentifier = { ' ' };
|
||
|
string[] brokenString;
|
||
|
while (currentText != null) {
|
||
|
if (!currentText.StartsWith("f ") && !currentText.StartsWith("v ") && !currentText.StartsWith("vt ")
|
||
|
&& !currentText.StartsWith("vn ")) {
|
||
|
currentText = reader.ReadLine();
|
||
|
if (currentText != null) {
|
||
|
currentText = currentText.Replace(" ", " ");
|
||
|
}
|
||
|
} else {
|
||
|
currentText = currentText.Trim(); //Trim the current line
|
||
|
brokenString = currentText.Split(splitIdentifier, 50); //Split the line into an array, separating the original line by blank spaces
|
||
|
switch (brokenString[0]) {
|
||
|
case "v":
|
||
|
vertices++;
|
||
|
break;
|
||
|
case "vt":
|
||
|
vt++;
|
||
|
break;
|
||
|
case "vn":
|
||
|
vn++;
|
||
|
break;
|
||
|
case "f":
|
||
|
face = face + brokenString.Length - 1;
|
||
|
triangles = triangles + 3 * (brokenString.Length - 2); /*brokenString.Length is 3 or greater since a face must have at least
|
||
|
* 3 vertices. For each additional vertice, there is an additional
|
||
|
* triangle in the mesh (hence this formula).*/
|
||
|
break;
|
||
|
}
|
||
|
currentText = reader.ReadLine();
|
||
|
if (currentText != null) {
|
||
|
currentText = currentText.Replace(" ", " ");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
mesh.triangles = new int[triangles];
|
||
|
mesh.vertices = new Vector3[vertices];
|
||
|
mesh.uv = new Vector2[vt];
|
||
|
mesh.normals = new Vector3[vn];
|
||
|
mesh.faceData = new Vector3[face];
|
||
|
return mesh;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
private static void populateMeshStruct (ref meshStruct mesh) {
|
||
|
#if NETFX_CORE
|
||
|
throw new System.NotSupportedException("Method not available on this platform");
|
||
|
#else
|
||
|
StreamReader stream = File.OpenText(mesh.fileName);
|
||
|
string entireText = stream.ReadToEnd();
|
||
|
stream.Close();
|
||
|
using (StringReader reader = new StringReader(entireText))
|
||
|
{
|
||
|
string currentText = reader.ReadLine();
|
||
|
|
||
|
char[] splitIdentifier = { ' ' };
|
||
|
char[] splitIdentifier2 = { '/' };
|
||
|
string[] brokenString;
|
||
|
string[] brokenBrokenString;
|
||
|
int f = 0;
|
||
|
int f2 = 0;
|
||
|
int v = 0;
|
||
|
int vn = 0;
|
||
|
int vt = 0;
|
||
|
int vt1 = 0;
|
||
|
int vt2 = 0;
|
||
|
while (currentText != null) {
|
||
|
if (!currentText.StartsWith("f ") && !currentText.StartsWith("v ") && !currentText.StartsWith("vt ") &&
|
||
|
!currentText.StartsWith("vn ") && !currentText.StartsWith("g ") && !currentText.StartsWith("usemtl ") &&
|
||
|
!currentText.StartsWith("mtllib ") && !currentText.StartsWith("vt1 ") && !currentText.StartsWith("vt2 ") &&
|
||
|
!currentText.StartsWith("vc ") && !currentText.StartsWith("usemap ")) {
|
||
|
currentText = reader.ReadLine();
|
||
|
if (currentText != null) {
|
||
|
currentText = currentText.Replace(" ", " ");
|
||
|
}
|
||
|
} else {
|
||
|
currentText = currentText.Trim();
|
||
|
brokenString = currentText.Split(splitIdentifier, 50);
|
||
|
switch (brokenString[0]) {
|
||
|
case "g":
|
||
|
break;
|
||
|
case "usemtl":
|
||
|
break;
|
||
|
case "usemap":
|
||
|
break;
|
||
|
case "mtllib":
|
||
|
break;
|
||
|
case "v":
|
||
|
mesh.vertices[v] = new Vector3(System.Convert.ToSingle(brokenString[1]), System.Convert.ToSingle(brokenString[2]),
|
||
|
System.Convert.ToSingle(brokenString[3]));
|
||
|
v++;
|
||
|
break;
|
||
|
case "vt":
|
||
|
mesh.uv[vt] = new Vector2(System.Convert.ToSingle(brokenString[1]), System.Convert.ToSingle(brokenString[2]));
|
||
|
vt++;
|
||
|
break;
|
||
|
case "vt1":
|
||
|
mesh.uv[vt1] = new Vector2(System.Convert.ToSingle(brokenString[1]), System.Convert.ToSingle(brokenString[2]));
|
||
|
vt1++;
|
||
|
break;
|
||
|
case "vt2":
|
||
|
mesh.uv[vt2] = new Vector2(System.Convert.ToSingle(brokenString[1]), System.Convert.ToSingle(brokenString[2]));
|
||
|
vt2++;
|
||
|
break;
|
||
|
case "vn":
|
||
|
mesh.normals[vn] = new Vector3(System.Convert.ToSingle(brokenString[1]), System.Convert.ToSingle(brokenString[2]),
|
||
|
System.Convert.ToSingle(brokenString[3]));
|
||
|
vn++;
|
||
|
break;
|
||
|
case "vc":
|
||
|
break;
|
||
|
case "f":
|
||
|
|
||
|
int j = 1;
|
||
|
List<int> intArray = new List<int>();
|
||
|
while (j < brokenString.Length && ("" + brokenString[j]).Length > 0) {
|
||
|
Vector3 temp = new Vector3();
|
||
|
brokenBrokenString = brokenString[j].Split(splitIdentifier2, 3); //Separate the face into individual components (vert, uv, normal)
|
||
|
temp.x = System.Convert.ToInt32(brokenBrokenString[0]);
|
||
|
if (brokenBrokenString.Length > 1) { //Some .obj files skip UV and normal
|
||
|
if (brokenBrokenString[1] != "") { //Some .obj files skip the uv and not the normal
|
||
|
temp.y = System.Convert.ToInt32(brokenBrokenString[1]);
|
||
|
}
|
||
|
temp.z = System.Convert.ToInt32(brokenBrokenString[2]);
|
||
|
}
|
||
|
j++;
|
||
|
|
||
|
mesh.faceData[f2] = temp;
|
||
|
intArray.Add(f2);
|
||
|
f2++;
|
||
|
}
|
||
|
j = 1;
|
||
|
while (j + 2 < brokenString.Length) { //Create triangles out of the face data. There will generally be more than 1 triangle per face.
|
||
|
mesh.triangles[f] = intArray[0];
|
||
|
f++;
|
||
|
mesh.triangles[f] = intArray[j];
|
||
|
f++;
|
||
|
mesh.triangles[f] = intArray[j+1];
|
||
|
f++;
|
||
|
|
||
|
j++;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
currentText = reader.ReadLine();
|
||
|
if (currentText != null) {
|
||
|
currentText = currentText.Replace(" ", " "); //Some .obj files insert double spaces, this removes them.
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
}
|