ExportOBJ

From Unify Community Wiki
(Difference between revisions)
Jump to: navigation, search
(Updated the page with the referenced .obj exporter script. Added a more confident description of the script's behavior. See the discussion page for more details.)
(Replaced the sample script with a more complete version. This expands on the original script by writing the GameObject to disk 'as the user sees it.')
Line 1: Line 1:
Author: DaveA
+
Author: [[User:DaveA|DaveA]], [[User:KeliHlodversson|KeliHlodversson]], [[User:tgraupmann|tgraupmann]], [[User:drobe|drobe]]
  
 
==Description==
 
==Description==
Allows you to export the currently selected mesh(es) to OBJ format. Simply wraps the Utility script(s) [[ObjExporterColorUvs|ObjExporter.cs]].  
+
Exports a GameObject and its child hierarchy's meshes to a Wavefront .OBJ file.
 +
This will export the meshes as you see it.  
 +
 
  
'''NOTE''': This will write mesh data for a single GameObject. It will not write mesh data for its children. If you happen to have several GameObjects selected at once, whatever GameObject is considered to have been selected last will be exported. See the [[Talk:ExportOBJ|discussion]] page for more information.
 
  
 
==Usage==
 
==Usage==
 
You must place the script in a folder named '''Editor''' in your project's Assets folder for it to work properly.
 
You must place the script in a folder named '''Editor''' in your project's Assets folder for it to work properly.
  
Select an object in the scene heirarchy, then choose '''File -> Export -> Wavefront OBJ'''. The default name will be of the object selected (or first selected), but you can change it.
+
Select an object in the scene hierarchy or a prefab in your project and then choose '''File -> Export -> Wavefront OBJ'''. Follow the save-file dialog to name and locate the intended exported .OBJ file.
  
== C# - ExportOBJ.cs ==
+
A second option, '''File -> Export -> Wavefront OBJ (No Submeshes)''', will export the entire GameObject hierarchy as a single mesh. Use this option if you want to combine a prefab or object into a single mesh.
  
 +
'''NOTE''': This will only process a single GameObject selection at a time. If multiple GameObjects are selected, only the "first" one will be processed.
 +
 +
'''NOTE''': The (No Submeshes) option does not trim faces, generate multi-materials, or even adjust UV coordinates. If you want to optimize or correct the mesh, you are encouraged to refer to a more specialized piece of software to handle that.
 +
 +
'''NOTE''': the Wavefront .OBJ format does not have a concept of submesh offset, scale, or rotation, so every submesh will be rotated, scaled, offset, ''and then'' written. All of your meshes will be loaded into Unity with a (0,0,0) rotation, position, and scale.
 +
 +
== C# - ObjExporter.cs ==
 
<syntaxhighlight lang="csharp">
 
<syntaxhighlight lang="csharp">
 
using UnityEngine;
 
using UnityEngine;
 
using UnityEditor;
 
using UnityEditor;
 
using System.Collections;
 
using System.Collections;
 +
using System.IO;
 +
using System.Text;
 +
 +
public class ObjExporterScript
 +
{
 +
private static int StartIndex = 0;
 +
 +
public static void Start()
 +
{
 +
StartIndex = 0;
 +
}
 +
public static void End()
 +
{
 +
StartIndex = 0;
 +
}
 +
 +
 +
public static string MeshToString(MeshFilter mf, Transform t)
 +
{
 +
Vector3 s = t.localScale;
 +
Vector3 p = t.localPosition;
 +
Quaternion r = t.localRotation;
 +
 +
 +
int numVertices = 0;
 +
Mesh m = mf.sharedMesh;
 +
if (!m)
 +
{
 +
return "####Error####";
 +
}
 +
Material[] mats = mf.renderer.sharedMaterials;
 +
 +
StringBuilder sb = new StringBuilder();
 +
 +
foreach(Vector3 vv in m.vertices)
 +
{
 +
Vector3 v = t.TransformPoint(vv);
 +
numVertices++;
 +
sb.Append(string.Format("v {0} {1} {2}\n",v.x,v.y,v.z));
 +
}
 +
sb.Append("\n");
 +
foreach(Vector3 nn in m.normals)
 +
{
 +
Vector3 v = r * nn;
 +
sb.Append(string.Format("vn {0} {1} {2}\n",v.x,v.y,v.z));
 +
}
 +
sb.Append("\n");
 +
foreach(Vector3 v in m.uv)
 +
{
 +
sb.Append(string.Format("vt {0} {1}\n",v.x,v.y));
 +
}
 +
for (int material=0; material < m.subMeshCount; material ++)
 +
{
 +
sb.Append("\n");
 +
sb.Append("usemtl ").Append(mats[material].name).Append("\n");
 +
sb.Append("usemap ").Append(mats[material].name).Append("\n");
 +
 +
int[] triangles = m.GetTriangles(material);
 +
for (int i=0;i<triangles.Length;i+=3) {
 +
sb.Append(string.Format("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n",
 +
triangles[i]+1+StartIndex, triangles[i+1]+1+StartIndex, triangles[i+2]+1+StartIndex));
 +
}
 +
}
 +
 +
StartIndex += numVertices;
 +
return sb.ToString();
 +
}
 +
}
  
 
public class ObjExporter : ScriptableObject
 
public class ObjExporter : ScriptableObject
 
{
 
{
    [MenuItem ("File/Export/Wavefront OBJ")]
+
[MenuItem ("File/Export/Wavefront OBJ")]
    static void DoExport()
+
static void DoExportWSubmeshes()
    {
+
{
 +
DoExport(true);
 +
}
 +
 +
[MenuItem ("File/Export/Wavefront OBJ (No Submeshes)")]
 +
static void DoExportWOSubmeshes()
 +
{
 +
DoExport(false);
 +
}
 +
 +
 +
static void DoExport(bool makeSubmeshes)
 +
{
 +
if (Selection.gameObjects.Length == 0)
 +
{
 +
Debug.Log("Didn't Export Any Meshes; Nothing was selected!");
 +
return;
 +
}
 +
 
string meshName = Selection.gameObjects[0].name;
 
string meshName = Selection.gameObjects[0].name;
        string fileName = EditorUtility.SaveFilePanel("Export .obj file", "", meshName, "obj");
+
string fileName = EditorUtility.SaveFilePanel("Export .obj file", "", meshName, "obj");
foreach  (GameObject o in Selection.gameObjects)
+
 +
ObjExporterScript.Start();
 +
 +
StringBuilder meshString = new StringBuilder();
 +
 +
meshString.Append("#" + meshName + ".obj"
 +
+ "\n#" + System.DateTime.Now.ToLongDateString()
 +
+ "\n#" + System.DateTime.Now.ToLongTimeString()
 +
+ "\n#-------"
 +
+ "\n\n");
 +
 +
Transform t = Selection.gameObjects[0].transform;
 +
 +
Vector3 originalPosition = t.position;
 +
t.position = Vector3.zero;
 +
 +
if (!makeSubmeshes)
 +
{
 +
meshString.Append("g ").Append(t.name).Append("\n");
 +
}
 +
meshString.Append(processTransform(t, makeSubmeshes));
 +
 +
WriteToFile(meshString.ToString(),fileName);
 +
 +
t.position = originalPosition;
 +
 +
ObjExporterScript.End();
 +
Debug.Log("Exported Mesh: " + fileName);
 +
}
 +
 +
static string processTransform(Transform t, bool makeSubmeshes)
 +
{
 +
StringBuilder meshString = new StringBuilder();
 +
 +
meshString.Append("#" + t.name
 +
+ "\n#-------"
 +
+ "\n");
 +
 +
if (makeSubmeshes)
 +
{
 +
meshString.Append("g ").Append(t.name).Append("\n");
 +
}
 +
 +
MeshFilter mf = t.GetComponent<MeshFilter>();
 +
if (mf)
 +
{
 +
meshString.Append(ObjExporterScript.MeshToString(mf, t));
 +
}
 +
 +
for(int i = 0; i < t.childCount; i++)
 +
{
 +
meshString.Append(processTransform(t.GetChild(i), makeSubmeshes));
 +
}
 +
 
 +
return meshString.ToString();
 +
}
 +
 +
static void WriteToFile(string s, string filename)
 +
{
 +
using (StreamWriter sw = new StreamWriter(filename))  
 
{
 
{
MeshFilter mf = o.GetComponent(typeof(MeshFilter)) as MeshFilter;
+
sw.Write(s);
ObjExporterScript.MeshToFile(mf, fileName, false);  
+
 
}
 
}
    }
+
}
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>

Revision as of 06:22, 13 January 2013

Author: DaveA, KeliHlodversson, tgraupmann, drobe

Description

Exports a GameObject and its child hierarchy's meshes to a Wavefront .OBJ file. This will export the meshes as you see it.


Usage

You must place the script in a folder named Editor in your project's Assets folder for it to work properly.

Select an object in the scene hierarchy or a prefab in your project and then choose File -> Export -> Wavefront OBJ. Follow the save-file dialog to name and locate the intended exported .OBJ file.

A second option, File -> Export -> Wavefront OBJ (No Submeshes), will export the entire GameObject hierarchy as a single mesh. Use this option if you want to combine a prefab or object into a single mesh.

NOTE: This will only process a single GameObject selection at a time. If multiple GameObjects are selected, only the "first" one will be processed.

NOTE: The (No Submeshes) option does not trim faces, generate multi-materials, or even adjust UV coordinates. If you want to optimize or correct the mesh, you are encouraged to refer to a more specialized piece of software to handle that.

NOTE: the Wavefront .OBJ format does not have a concept of submesh offset, scale, or rotation, so every submesh will be rotated, scaled, offset, and then written. All of your meshes will be loaded into Unity with a (0,0,0) rotation, position, and scale.

C# - ObjExporter.cs

using UnityEngine;
using UnityEditor;
using System.Collections;
using System.IO;
using System.Text;
 
public class ObjExporterScript
{
	private static int StartIndex = 0;
 
	public static void Start()
	{
		StartIndex = 0;
	}
	public static void End()
	{
		StartIndex = 0;
	}
 
 
	public static string MeshToString(MeshFilter mf, Transform t) 
	{	
		Vector3 s 		= t.localScale;
		Vector3 p 		= t.localPosition;
		Quaternion r 	= t.localRotation;
 
 
		int numVertices = 0;
		Mesh m = mf.sharedMesh;
		if (!m)
		{
			return "####Error####";
		}
		Material[] mats = mf.renderer.sharedMaterials;
 
		StringBuilder sb = new StringBuilder();
 
		foreach(Vector3 vv in m.vertices)
		{
			Vector3 v = t.TransformPoint(vv);
			numVertices++;
			sb.Append(string.Format("v {0} {1} {2}\n",v.x,v.y,v.z));
		}
		sb.Append("\n");
		foreach(Vector3 nn in m.normals) 
		{
			Vector3 v = r * nn;
			sb.Append(string.Format("vn {0} {1} {2}\n",v.x,v.y,v.z));
		}
		sb.Append("\n");
		foreach(Vector3 v in m.uv) 
		{
			sb.Append(string.Format("vt {0} {1}\n",v.x,v.y));
		}
		for (int material=0; material < m.subMeshCount; material ++) 
		{
			sb.Append("\n");
			sb.Append("usemtl ").Append(mats[material].name).Append("\n");
			sb.Append("usemap ").Append(mats[material].name).Append("\n");
 
			int[] triangles = m.GetTriangles(material);
			for (int i=0;i<triangles.Length;i+=3) {
				sb.Append(string.Format("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n", 
					triangles[i]+1+StartIndex, triangles[i+1]+1+StartIndex, triangles[i+2]+1+StartIndex));
			}
		}
 
		StartIndex += numVertices;
		return sb.ToString();
	}
}
 
public class ObjExporter : ScriptableObject
{
	[MenuItem ("File/Export/Wavefront OBJ")]
	static void DoExportWSubmeshes()
	{
		DoExport(true);
	}
 
	[MenuItem ("File/Export/Wavefront OBJ (No Submeshes)")]
	static void DoExportWOSubmeshes()
	{
		DoExport(false);
	}
 
 
	static void DoExport(bool makeSubmeshes)
	{
		if (Selection.gameObjects.Length == 0)
		{
			Debug.Log("Didn't Export Any Meshes; Nothing was selected!");
			return;
		}
 
		string meshName = Selection.gameObjects[0].name;
		string fileName = EditorUtility.SaveFilePanel("Export .obj file", "", meshName, "obj");
 
		ObjExporterScript.Start();
 
		StringBuilder meshString = new StringBuilder();
 
		meshString.Append("#" + meshName + ".obj"
							+ "\n#" + System.DateTime.Now.ToLongDateString() 
							+ "\n#" + System.DateTime.Now.ToLongTimeString()
							+ "\n#-------" 
							+ "\n\n");
 
		Transform t = Selection.gameObjects[0].transform;
 
		Vector3 originalPosition = t.position;
		t.position = Vector3.zero;
 
		if (!makeSubmeshes)
		{
			meshString.Append("g ").Append(t.name).Append("\n");
		}
		meshString.Append(processTransform(t, makeSubmeshes));
 
		WriteToFile(meshString.ToString(),fileName);
 
		t.position = originalPosition;
 
		ObjExporterScript.End();
		Debug.Log("Exported Mesh: " + fileName);
	}
 
	static string processTransform(Transform t, bool makeSubmeshes)
	{
		StringBuilder meshString = new StringBuilder();
 
		meshString.Append("#" + t.name
						+ "\n#-------" 
						+ "\n");
 
		if (makeSubmeshes)
		{
			meshString.Append("g ").Append(t.name).Append("\n");
		}
 
		MeshFilter mf = t.GetComponent<MeshFilter>();
		if (mf)
		{
			meshString.Append(ObjExporterScript.MeshToString(mf, t));
		}
 
		for(int i = 0; i < t.childCount; i++)
		{
			meshString.Append(processTransform(t.GetChild(i), makeSubmeshes));
		}
 
		return meshString.ToString();
	}
 
	static void WriteToFile(string s, string filename)
	{
		using (StreamWriter sw = new StreamWriter(filename)) 
		{
			sw.Write(s);
		}
	}
}
Personal tools
Namespaces

Variants
Actions
Navigation
Extras
Toolbox