| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556 |
- /*
- Based on ObjExporter.cs, this "wrapper" lets you export to .OBJ directly from the editor menu.
-
- This should be put in your "Editor"-folder. Use by selecting the objects you want to export, and select
- the appropriate menu item from "Custom->Export". Exported models are put in a folder called
- "ExportedObj" in the root of your Unity-project. Textures should also be copied and placed in the
- same folder.
- N.B. there may be a bug so if the custom option doesn't come up refer to this thread http://answers.unity3d.com/questions/317951/how-to-use-editorobjexporter-obj-saving-script-fro.html
-
- Updated for Unity 5.3
- */
- using UnityEngine;
- using UnityEditor;
- using UnityEditor.SceneManagement;
- using System.Collections;
- using System.Collections.Generic;
- using System.IO;
- using System.Text;
- using System;
- struct ObjMaterial
- {
- public string name;
- public string textureName;
- }
- public class EditorObjExporter : ScriptableObject
- {
- private static int vertexOffset = 0;
- private static int normalOffset = 0;
- private static int uvOffset = 0;
- //User should probably be able to change this. It is currently left as an excercise for
- //the reader.
- private static string targetFolder = "ExportedObj";
- private static string MeshToString(MeshFilter mf, Dictionary<string, ObjMaterial> materialList)
- {
- Mesh m = mf.sharedMesh;
- Material[] mats = mf.GetComponent<Renderer>().sharedMaterials;
- StringBuilder sb = new StringBuilder();
- sb.Append("g ").Append(mf.name).Append("\n");
- foreach (Vector3 lv in m.vertices)
- {
- Vector3 wv = mf.transform.TransformPoint(lv);
- //This is sort of ugly - inverting x-component since we're in
- //a different coordinate system than "everyone" is "used to".
- sb.Append(string.Format("v {0} {1} {2}\n", -wv.x, wv.y, wv.z));
- }
- sb.Append("\n");
- foreach (Vector3 lv in m.normals)
- {
- Vector3 wv = mf.transform.TransformDirection(lv);
- sb.Append(string.Format("vn {0} {1} {2}\n", -wv.x, wv.y, wv.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");
- //See if this material is already in the materiallist.
- try
- {
- ObjMaterial objMaterial = new ObjMaterial();
- objMaterial.name = mats[material].name;
- if (mats[material].mainTexture)
- objMaterial.textureName = AssetDatabase.GetAssetPath(mats[material].mainTexture);
- else
- objMaterial.textureName = null;
- materialList.Add(objMaterial.name, objMaterial);
- }
- catch (ArgumentException)
- {
- //Already in the dictionary
- }
- int[] triangles = m.GetTriangles(material);
- for (int i = 0; i < triangles.Length; i += 3)
- {
- //Because we inverted the x-component, we also needed to alter the triangle winding.
- sb.Append(string.Format("f {1}/{1}/{1} {0}/{0}/{0} {2}/{2}/{2}\n",
- triangles[i] + 1 + vertexOffset, triangles[i + 1] + 1 + normalOffset, triangles[i + 2] + 1 + uvOffset));
- }
- }
- vertexOffset += m.vertices.Length;
- normalOffset += m.normals.Length;
- uvOffset += m.uv.Length;
- return sb.ToString();
- }
- private static void Clear()
- {
- vertexOffset = 0;
- normalOffset = 0;
- uvOffset = 0;
- }
- private static Dictionary<string, ObjMaterial> PrepareFileWrite()
- {
- Clear();
- return new Dictionary<string, ObjMaterial>();
- }
- private static void MaterialsToFile(Dictionary<string, ObjMaterial> materialList, string folder, string filename)
- {
- using (StreamWriter sw = new StreamWriter(folder + "_" + filename + ".mtl"))
- {
- foreach (KeyValuePair<string, ObjMaterial> kvp in materialList)
- {
- sw.Write("\n");
- sw.Write("newmtl {0}\n", kvp.Key);
- sw.Write("Ka 0.6 0.6 0.6\n");
- sw.Write("Kd 0.6 0.6 0.6\n");
- sw.Write("Ks 0.9 0.9 0.9\n");
- sw.Write("d 1.0\n");
- sw.Write("Ns 0.0\n");
- sw.Write("illum 2\n");
- if (kvp.Value.textureName != null)
- {
- string destinationFile = kvp.Value.textureName;
- int stripIndex = destinationFile.LastIndexOf(Path.PathSeparator);
- if (stripIndex >= 0)
- destinationFile = destinationFile.Substring(stripIndex + 1).Trim();
- string relativeFile = destinationFile;
- destinationFile = folder + "_" + destinationFile;
- Debug.Log("Copying texture from " + kvp.Value.textureName + " to " + destinationFile);
- try
- {
- //Copy the source file
- File.Copy(kvp.Value.textureName, destinationFile);
- }
- catch
- {
- }
- sw.Write("map_Kd {0}", relativeFile);
- }
- sw.Write("\n\n\n");
- }
- }
- }
- public static void MeshToFile(MeshFilter mf, string folder, string filename)
- {
- Dictionary<string, ObjMaterial> materialList = PrepareFileWrite();
- using (StreamWriter sw = new StreamWriter("Assets/ExportedObj/"+filename + ".obj"))
- {
- //sw.Write("mtllib ./" + filename + ".mtl\n");
- sw.Write(MeshToString(mf, materialList));
- sw.Close();
- }
- //MaterialsToFile(materialList, folder, filename);
- }
- private static void MeshesToFile(MeshFilter[] mf, string folder, string filename)
- {
- Dictionary<string, ObjMaterial> materialList = PrepareFileWrite();
- using (StreamWriter sw = new StreamWriter(folder + Path.PathSeparator + filename + ".obj"))
- {
- sw.Write("mtllib ./" + filename + ".mtl\n");
- for (int i = 0; i < mf.Length; i++)
- {
- sw.Write(MeshToString(mf[i], materialList));
- }
- }
- MaterialsToFile(materialList, folder, filename);
- }
- private static bool CreateTargetFolder()
- {
- try
- {
- string exportPath = Application.dataPath +"/"+ targetFolder;
- if (!System.IO.Directory.Exists(exportPath))
- System.IO.Directory.CreateDirectory(exportPath);
- }
- catch
- {
- EditorUtility.DisplayDialog("Error!", "Failed to create target folder!", "");
- return false;
- }
- return true;
- }
- //[MenuItem("Custom/Export/Export all MeshFilters in selection to separate OBJs")]
- //static void ExportSelectionToSeparate()
- //{
- // if (!CreateTargetFolder())
- // return;
- // Transform[] selection = Selection.GetTransforms(SelectionMode.Editable | SelectionMode.ExcludePrefab);
- // if (selection.Length == 0)
- // {
- // EditorUtility.DisplayDialog("No source object selected!", "Please select one or more target objects", "");
- // return;
- // }
- // int exportedObjects = 0;
- // for (int i = 0; i < selection.Length; i++)
- // {
- // Component[] meshfilter = selection[i].GetComponentsInChildren(typeof(MeshFilter));
- // for (int m = 0; m < meshfilter.Length; m++)
- // {
- // exportedObjects++;
- // MeshToFile((MeshFilter)meshfilter[m], targetFolder, selection[i].name + "_" + i + "_" + m);
- // }
- // }
- // if (exportedObjects > 0)
- // EditorUtility.DisplayDialog("Objects exported", "Exported " + exportedObjects + " objects", "");
- // else
- // EditorUtility.DisplayDialog("Objects not exported", "Make sure at least some of your selected objects have mesh filters!", "");
- //}
- //[MenuItem("Custom/Export/Export each selected to single OBJ")]
- //static void ExportEachSelectionToSingle()
- //{
- // if (!CreateTargetFolder())
- // return;
- // Transform[] selection = Selection.GetTransforms(SelectionMode.Editable | SelectionMode.ExcludePrefab);
- // if (selection.Length == 0)
- // {
- // EditorUtility.DisplayDialog("No source object selected!", "Please select one or more target objects", "");
- // return;
- // }
- // int exportedObjects = 0;
- // for (int i = 0; i < selection.Length; i++)
- // {
- // Component[] meshfilter = selection[i].GetComponentsInChildren(typeof(MeshFilter));
- // MeshFilter[] mf = new MeshFilter[meshfilter.Length];
- // for (int m = 0; m < meshfilter.Length; m++)
- // {
- // exportedObjects++;
- // mf[m] = (MeshFilter)meshfilter[m];
- // }
- // MeshesToFile(mf, targetFolder, selection[i].name + "_" + i);
- // }
- // if (exportedObjects > 0)
- // {
- // EditorUtility.DisplayDialog("Objects exported", "Exported " + exportedObjects + " objects", "");
- // }
- // else
- // EditorUtility.DisplayDialog("Objects not exported", "Make sure at least some of your selected objects have mesh filters!", "");
- //}
- [MenuItem("RO_Tool/Mesh/合并成预设")]
- static void CombineToPrefab()
- {
- if (!CreateTargetFolder())
- return;
- GameObject selectGo = Selection.activeGameObject;
- if (selectGo == null)
- {
- EditorUtility.DisplayDialog("No source object selected!", "Please select one or more target objects", "");
- return;
- }
- GameObject go = null;
- PrefabInstanceStatus status = PrefabInstanceStatus.Disconnected;
- if (PrefabUtility.GetPrefabAssetType(selectGo) == PrefabAssetType.Regular)
- {
- status = PrefabUtility.GetPrefabInstanceStatus(selectGo);
- if (status == PrefabInstanceStatus.NotAPrefab)
- {
- go = GameObject.Instantiate(selectGo);
- }
- else
- {
- go = selectGo;
- }
- }
- else
- {
- go = selectGo as GameObject;
- }
- if (go == null) return;
- MeshRenderer mSMR = go.GetComponent<MeshRenderer>();
- if (mSMR != null)
- UnityEngine.Object.DestroyImmediate(mSMR);
- MeshFilter meshFilter = go.GetComponent<MeshFilter>();
- if (meshFilter != null)
- UnityEngine.Object.DestroyImmediate(meshFilter);
- List<CombineInstance> combineInstances = new List<CombineInstance>();
- MeshRenderer[] smrList = go.GetComponentsInChildren<MeshRenderer>();
- Material mat = null;
- for (int idx = 0; idx < smrList.Length; idx++)
- {
- MeshRenderer smr = smrList[idx];
- if (mat == null)
- mat = smr.sharedMaterial;
- MeshFilter mf = smr.GetComponent<MeshFilter>();
- CombineInstance ci = new CombineInstance();
- ci.mesh = mf.sharedMesh;
- ci.transform = mf.transform.localToWorldMatrix;
- combineInstances.Add(ci);
- }
- mSMR = go.AddComponent<MeshRenderer>();
- meshFilter = go.AddComponent<MeshFilter>();
- mSMR.sharedMaterial = mat;
- meshFilter.sharedMesh = new Mesh();
- meshFilter.sharedMesh.CombineMeshes(combineInstances.ToArray(), true);
- MeshToFile(meshFilter, targetFolder, selectGo.name);
- UnityEngine.Object.DestroyImmediate(mSMR);
- UnityEngine.Object.DestroyImmediate(meshFilter);
- if (status == PrefabInstanceStatus.NotAPrefab)
- {
- UnityEngine.Object.DestroyImmediate(go);
- }
- AssetDatabase.Refresh();
- string objFilePath = "Assets/ExportedObj/" + selectGo.name + ".obj";
- string objPrefabFilePath = "Assets/ExportedObj/" + selectGo.name + ".prefab";
- UnityEngine.GameObject obj = AssetDatabase.LoadAssetAtPath<GameObject>(objFilePath);
- GameObject objInst = GameObject.Instantiate(obj);
- MeshRenderer r = objInst.GetComponentInChildren<MeshRenderer>();
- r.sharedMaterial = mat;
- PrefabUtility.SaveAsPrefabAsset(objInst, objPrefabFilePath);
- UnityEngine.Object.DestroyImmediate(objInst);
- }
- [MenuItem("RO_Tool/Mesh/合并mesh")]
- static void CombineToMesh()
- {
- if (!CreateTargetFolder())
- return;
- GameObject selectGo = Selection.activeGameObject;
- if (selectGo == null)
- {
- EditorUtility.DisplayDialog("No source object selected!", "Please select one or more target objects", "");
- return;
- }
- GameObject go = null;
- PrefabInstanceStatus status = PrefabInstanceStatus.Disconnected;
- if (PrefabUtility.GetPrefabAssetType(selectGo) == PrefabAssetType.Regular)
- {
- status = PrefabUtility.GetPrefabInstanceStatus(selectGo);
- if (status == PrefabInstanceStatus.NotAPrefab)
- {
- go = GameObject.Instantiate(selectGo);
- }
- else
- {
- go = selectGo;
- }
- }
- else
- {
- go = selectGo as GameObject;
- }
- if (go == null) return;
-
- MeshRenderer mSMR = go.GetComponent<MeshRenderer>();
- if (mSMR != null)
- UnityEngine.Object.DestroyImmediate(mSMR);
- MeshFilter meshFilter = go.GetComponent<MeshFilter>();
- if (meshFilter != null)
- UnityEngine.Object.DestroyImmediate(meshFilter);
- List<CombineInstance> combineInstances = new List<CombineInstance>();
- MeshRenderer[] smrList = go.GetComponentsInChildren<MeshRenderer>();
- Material mat = null;
- for (int idx = 0; idx < smrList.Length; idx++)
- {
- MeshRenderer smr = smrList[idx];
- if (mat == null)
- mat = smr.sharedMaterial;
- MeshFilter mf = smr.GetComponent<MeshFilter>();
- CombineInstance ci = new CombineInstance();
- ci.mesh = mf.sharedMesh;
- ci.transform = mf.transform.localToWorldMatrix;
- combineInstances.Add(ci);
- }
- mSMR = go.AddComponent<MeshRenderer>();
- meshFilter = go.AddComponent<MeshFilter>();
- mSMR.sharedMaterial = mat;
- meshFilter.sharedMesh = new Mesh();
- meshFilter.sharedMesh.CombineMeshes(combineInstances.ToArray(), true,true,true);
- MeshToFile(meshFilter, targetFolder, selectGo.name);
- UnityEngine.Object.DestroyImmediate(mSMR);
- UnityEngine.Object.DestroyImmediate(meshFilter);
- if (status == PrefabInstanceStatus.NotAPrefab)
- {
- UnityEngine.Object.DestroyImmediate(go);
- }
- AssetDatabase.Refresh();
- }
- [MenuItem("Assets/ExportMesh")]
- static void TestMesh()
- {
- if (!CreateTargetFolder())
- return;
- UnityEngine.Object obj = Selection.activeObject;
- if (obj == null || PrefabUtility.GetPrefabAssetType(obj) != PrefabAssetType.Model) return;
- GameObject objGo = obj as GameObject;
- MeshFilter mf = objGo.GetComponent<MeshFilter>();
- Vector3 offset = mf.sharedMesh.bounds.center;
- offset.y = 0;
- if (mf != null)
- {
- List<Vector3> newVertices = new List<Vector3>();
- Mesh mesh = mf.sharedMesh;
- for (int idx = 0; idx < mesh.vertices.Length; idx++)
- {
- Vector3 newPos = mesh.vertices[idx] - offset;
- newVertices.Add(newPos);
- }
- mesh.SetVertices(newVertices);
- mf.sharedMesh = mesh;
- MeshToFile(mf, targetFolder, obj.name);
- Debug.Log("Completed");
- }
- }
- [MenuItem("Custom/Export/Export whole selection to single OBJ")]
- static void ExportWholeSelectionToSingle()
- {
- if (!CreateTargetFolder())
- return;
- Transform[] selection = Selection.GetTransforms(SelectionMode.Editable | SelectionMode.ExcludePrefab);
- if (selection.Length == 0)
- {
- EditorUtility.DisplayDialog("No source object selected!", "Please select one or more target objects", "");
- return;
- }
- int exportedObjects = 0;
- ArrayList mfList = new ArrayList();
- for (int i = 0; i < selection.Length; i++)
- {
- Component[] meshfilter = selection[i].GetComponentsInChildren(typeof(MeshFilter));
- for (int m = 0; m < meshfilter.Length; m++)
- {
- exportedObjects++;
- mfList.Add(meshfilter[m]);
- }
- }
- if (exportedObjects > 0)
- {
- MeshFilter[] mf = new MeshFilter[mfList.Count];
- for (int i = 0; i < mfList.Count; i++)
- {
- mf[i] = (MeshFilter)mfList[i];
- }
- string filename = EditorSceneManager.GetActiveScene().name + "_" + exportedObjects;
- int stripIndex = filename.LastIndexOf(Path.PathSeparator);
- if (stripIndex >= 0)
- filename = filename.Substring(stripIndex + 1).Trim();
- MeshesToFile(mf, targetFolder, filename);
- EditorUtility.DisplayDialog("Objects exported", "Exported " + exportedObjects + " objects to " + filename, "");
- }
- else
- EditorUtility.DisplayDialog("Objects not exported", "Make sure at least some of your selected objects have mesh filters!", "");
- }
- }
|