.obj文件後綴,是一種3D模型文件格式,一種文本文件。支持多邊形模型(三個點以上的面),紀錄法線和貼圖座標。並不記錄動畫、材質特性、貼圖路徑、動力學及粒子等信息。
主要的用途在於將場景的Mesh信息導出給服務器做碰撞檢測(服務器拿到.obj文件後具體如何實現的就暫時不是很懂啦),unity也可以識別出.obj文件,我們將導出的.obj文件放入unity中,效果如下
首先來看一下一個簡單的.obj文件的內容
#type mesh
#Cube
#-------
g Cube
v -53.00000 -2.00000 -62.00000
v 66.00000 -2.00000 -62.00000
v -53.00000 1.00000 -62.00000
v 66.00000 1.00000 -62.00000
v 66.00000 -2.00000 -62.00000
v 66.00000 -2.00000 49.50000
v 66.00000 1.00000 -62.00000
v 66.00000 1.00000 50.50000
v 66.00000 -2.00000 49.50000
v -53.00000 -2.00000 49.50000
v 66.00000 1.00000 50.50000
v -53.00000 1.00000 50.50000
v -53.00000 -2.00000 49.50000
v -53.00000 -2.00000 -62.00000
v -53.00000 1.00000 50.50000
v -53.00000 1.00000 -62.00000
v -53.00000 1.00000 -62.00000
v 66.00000 1.00000 -62.00000
v -53.00000 1.00000 50.50000
v 66.00000 1.00000 50.50000
v -53.00000 -2.00000 49.50000
v 66.00000 -2.00000 49.50000
v -53.00000 -2.00000 -62.00000
v 66.00000 -2.00000 -62.00000
vn 0 0 1
vn 0 0 1
vn 0 0 1
vn 0 0 1
vn -1 0 0
vn -1 0 0
vn -1 0 0
vn -1 0 0
vn 0 0.3162278 -0.9486833
vn 0 0.3162278 -0.9486833
vn 0 0.3162278 -0.9486833
vn 0 0.3162278 -0.9486833
vn 1 0 0
vn 1 0 0
vn 1 0 0
vn 1 0 0
vn 0 -1 0
vn 0 -1 0
vn 0 -1 0
vn 0 -1 0
vn 0 1 0
vn 0 1 0
vn 0 1 0
vn 0 1 0
vt 53 -2
vt -66 -2
vt 53 1
vt -66 1
vt 62 -2
vt -49.5 -2
vt 62 1
vt -50.5 1
vt 66 13.75591
vt -53 13.75591
vt 66 16.91819
vt -53 16.91819
vt 49.5 -2
vt -62 -2
vt 50.5 1
vt -62 1
vt -53 62
vt 66 62
vt -53 -50.5
vt 66 -50.5
vt 53 -49.5
vt -66 -49.5
vt 53 62
vt -66 62
usemtl ProBuilderDefault
usemap ProBuilderDefault
f 1/1/1 2/2/2 3/3/3
f 2/2/2 4/4/4 3/3/3
f 5/5/5 6/6/6 7/7/7
f 6/6/6 8/8/8 7/7/7
f 9/9/9 10/10/10 11/11/11
f 10/10/10 12/12/12 11/11/11
f 13/13/13 14/14/14 15/15/15
f 14/14/14 16/16/16 15/15/15
f 17/17/17 18/18/18 19/19/19
f 18/18/18 20/20/20 19/19/19
f 21/21/21 22/22/22 23/23/23
f 22/22/22 24/24/24 23/23/23
其中幾個關鍵字的含義爲
# |
開頭表示註釋 |
v | 表示頂點 |
vn | 表示法線,可以共用法線 |
vt | 表示uv座標 |
f | 表示一個面,比如參數1/4/1,表示頂點索引/UV索引/法線索引 |
usemtl | 材質名稱 (Material name) |
通過這些位置信息就可以構建出模型的框架了。
至於如何導出.obj文件,可以參考文檔:http://wiki.unity3d.com/index.php/ExportOBJ ,可以根據自己項目的一些需求進行一些小的修改,例如我們的只導出static物體,一個多gameobject的場景要合併導出與拆分導出等等。
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.IO;
using System.Text;
using Base.Framework.Tools;
public class ObjExporter : ScriptableObject
{
[MenuItem("Tools/Export/Export OBJ")]
static void DoExportWSubmeshes()
{
Export(true);
}
[MenuItem("Tools/Export/Export OBJ (No Submeshes)")]
static void DoExportWOSubmeshes()
{
Export(false);
}
static void Export(bool isSubmeshes)
{
if (Selection.gameObjects == null)
{
Debug.Log("Didn't Export Any Meshes; Nothing was selected!");
return;
}
string fileName = EditorUtility.SaveFilePanel("Export .obj file", "", Selection.gameObjects[0].name, "obj");
PrefabExportToObj.DoExport(Selection.gameObjects[0], fileName, isSubmeshes);
}
[MenuItem("Tools/Export/Split Export OBJ")]
static void SplitExport()
{
if (Selection.gameObjects == null)
{
Debug.Log("Didn't Export Any Meshes; Nothing was selected!");
return;
}
string filePath = EditorUtility.SaveFolderPanel("Export .obj file", "", Selection.gameObjects[0].name);
PrefabExportToObj.SplitExport(Selection.gameObjects[0], filePath, true);
}
[MenuItem("Tools/Export/Export All OBJ")]
static void AllExport()
{
if (Selection.gameObjects == null)
{
Debug.Log("Didn't Export Any Meshes; Nothing was selected!");
return;
}
string filePath = EditorUtility.SaveFolderPanel("Export .obj file", "", Selection.gameObjects[0].name);
PrefabExportToObj.SplitExport(Selection.gameObjects[0], filePath, true);
PrefabExportToObj.DoExport(Selection.gameObjects[0], filePath + "/" + Selection.gameObjects[0].name + ".obj", true);
}
}
using System;
using System.IO;
using System.Text;
using UnityEngine;
namespace Base.Framework.Tools
{
public class PrefabExportToObj
{
static StringBuilder filesString;
static int fileCount;
public static void StartRecordFiles(string msg)
{
filesString = new StringBuilder();
filesString.Append("#");
filesString.Append(msg);
filesString.Append("\n");
fileCount = 0;
}
public static void ExportRecordFiles(string path)
{
if(filesString!=null&& filesString.Length > 0)
{
WriteToFile(filesString.ToString(), path);
}
filesString.Length = 0;
filesString = null;
}
public static void SplitExport(GameObject target, string path, bool makeSubmeshes)
{
PrefabExportToObj.StartRecordFiles("GameObject:" + target.name);
Split(target, path);
PrefabExportToObj.ExportRecordFiles(path + "/dependencies.txt");
}
static void Split(GameObject go, string path)
{
if (go.isStatic && go.activeSelf && go.activeInHierarchy && go.GetComponent<MeshFilter>() != null)
{
DoExport(go, path + "/" + fileCount + ".obj", true);
fileCount++;
}
for (int i = 0; i < go.transform.childCount; i++)
{
//Split(go.transform.GetChild(i).gameObject, path + "/" + go.transform.GetChild(i).gameObject.name);
Split(go.transform.GetChild(i).gameObject, path);
}
}
/// <param name="target"></param>
/// <param name="path">x/y/z.obj</param>
/// <param name="makeSubmeshes"></param>
public static void DoExport(GameObject target, string path, bool makeSubmeshes)
{
if (target == null)
{
Debug.Log("Didn't Export Any Meshes; Nothing was selected!");
return;
}
string meshName = target.name;
ObjExporterScript.Start();
StringBuilder meshString = new StringBuilder();
Transform t = target.transform;
meshString.Append($"#type mesh\n");
//Vector3 originalPosition = t.position;
//t.position = Vector3.zero;
if (!makeSubmeshes)
{
meshString.Append("g ").Append(t.name).Append("\n");
}
meshString.Append(processTransform(t, makeSubmeshes));
if (filesString != null)
{
filesString.Append(Path.GetFileName(path));
filesString.Append("\n");
}
WriteToFile(meshString.ToString(), path);
//t.position = originalPosition;
ObjExporterScript.End();
Debug.Log("Exported Mesh: " + path);
}
static string processTransform(Transform t, bool makeSubmeshes)
{
StringBuilder meshString = new StringBuilder();
meshString.Append("#" + t.name
+ "\n#-------"
+ "\n");
if (t.gameObject.isStatic)
{
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++)
{
Transform tc = t.GetChild(i);
if (tc.gameObject.isStatic && tc.gameObject.activeSelf && tc.gameObject.activeInHierarchy)
{
meshString.Append(processTransform(tc, makeSubmeshes));
}
}
return meshString.ToString();
}
static void WriteToFile(string s, string filename)
{
string path = Path.GetDirectoryName(filename);
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
using (StreamWriter sw = new StreamWriter(filename))
{
sw.Write(s);
}
}
}
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 || mf.GetComponent<Renderer>() == null)
{
return "####Error####";
}
Material[] mats = mf.GetComponent<Renderer>().sharedMaterials;
StringBuilder sb = new StringBuilder();
foreach (Vector3 vv in m.vertices)
{
Vector3 v = t.TransformPoint(vv);
numVertices++;
sb.Append(string.Format("v {0:f5} {1:f5} {2:f5}\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 {2}/{2}/{2} {1}/{1}/{1} {0}/{0}/{0}\n",
triangles[i] + 1 + StartIndex, triangles[i + 1] + 1 + StartIndex, triangles[i + 2] + 1 + StartIndex));
}
}
StartIndex += numVertices;
return sb.ToString();
}
}
}
最後,選擇我們需要導出的gameobject,選擇Tool -> Export即可