2023-04-24 15:49:03 +02:00

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
}
}
}