日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Unity导出模型为Obj文件

發布時間:2023/12/20 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Unity导出模型为Obj文件 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Unity導出模型為Obj文件

    • 資源鏈接
      • 下載導入
    • 代碼紀要
    • 使用方式
    • 參考鏈接

資源鏈接

原插件代碼中只有MeshFilter的Obj導出代碼;由于項目需求,需要將SkinnedMeshRenderer導出為Obj文件,故在原代碼的基礎上,擴展出了SkinnedMeshRenderer的Obj導出功能。對原代碼有興趣的可以自行進入參考鏈接2。
下載鏈接:修改后MeshFilter和SkinnedMeshRenderer的Obj導出功能

下載導入

將下載的插件導入項目之后,就是文件夾Editor下的3個C#腳本文件。如下圖,

代碼紀要

下面對代碼進行簡單分析紀要:

  • 總共3個腳本分別為抽象泛型父類EditorObjExporter<T, R>、MeshFilter的子類EditorObjExporter_MeshFilter和SkinnedMeshRenderer的子類EditorObjExporter_SkinnedMeshRenderer。MeshFilter和SkinnedMeshRenderer的整個過程代碼都一樣只有在獲取Mesh網格和Material[]材質時有所不同。
  • 由于是Unity的工具欄程序,所以大部分為static靜態函數,尤其是3個觸發函數:ExportSelectionToSeparate、ExportWholeSelectionToSingle、ExportEachSelectionToSingle。
  • 結合前兩條,腳本之所以使用單例設計模式。是因為這個功能唯一的非靜態函數string MeshToString(MeshFilter t, Dictionary<string, ObjMaterial> materialList),用于子類分別獲取Mesh網格和Material[]材質,所以該函數使用泛型類繼承的方式;同時父類的static void MeshToFile(T t, string folder, string filename)和static void MeshesToFile(T[] ts, string folder, string filename)兩個靜態函數都需要調用該方法,但因為MeshToString是非靜態函數,使用單例對象調用是最方便簡單的。
    該功能,如果還有什么更加好的解決方案,歡迎探討。示例代碼:
  • sw.Write(GetInstance().MeshToString(t, materialList));//靜態類中使用單例對象調用函數
  • 3個觸發函數的基本流程相同:先通過Selection.GetTransforms(SelectionMode.Editable | SelectionMode.ExcludePrefab);函數獲取當前選擇的Transform對象數組;再根據泛型的每個子類不同的需求遍歷該數組,并獲取對應的組件;最后根據獲取的組件數組保存成Obj文件。示例代碼:
  • protected 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[] components = selection[i].GetComponentsInChildren(typeof(T));for (int m = 0; m < components.Length; m++){exportedObjects++;MeshToFile((T)components[m], targetFolder, selection[i].name + "_" + i + "_" + m);}}if (exportedObjects > 0)EditorUtility.DisplayDialog("Objects exported", "Exported " + exportedObjects + " objects", "");elseEditorUtility.DisplayDialog("Objects not exported", "Make sure at least some of your selected objects have mesh filters!", ""); }
  • 先通過MeshToString函數將組件中獲取的Mesh網格轉換成String字符串:先頂點數據循環,再法線數據循環,然后是UV數據循環,最后材質數據循環的同時儲存材質對象字典。函數代碼:
  • protected static string MeshToString(Mesh mesh, Material[] materials, string name, Transform transform, Dictionary<string, ObjMaterial> materialList) {StringBuilder sb = new StringBuilder();sb.Append("g ").Append(name).Append("\n");//數據起始foreach (Vector3 lv in mesh.vertices){//頂點數據循環輸入Vector3 wv = 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 mesh.normals){//法線數據循環輸入Vector3 wv = 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 mesh.uv){//UV數據循環輸入sb.Append(string.Format("vt {0} {1}\n", v.x, v.y));}for (int material = 0; material < mesh.subMeshCount; material++){//材質數據循環輸入sb.Append("\n");//空格sb.Append("usemtl ").Append(materials[material].name).Append("\n");sb.Append("usemap ").Append(materials[material].name).Append("\n");try{//See if this material is already in the materiallist.看看這個字典是否已經在字典中ObjMaterial objMaterial = new ObjMaterial{name = materials[material].name};if (materials[material].mainTexture)objMaterial.textureName = AssetDatabase.GetAssetPath(materials[material].mainTexture);//另一種方式EditorUtility.GetAssetPath(mats[material].mainTexture)elseobjMaterial.textureName = null;materialList.Add(objMaterial.name, objMaterial);}catch (ArgumentException){//已經在字典中//Already in the dictionary}int[] triangles = mesh.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 += mesh.vertices.Length;normalOffset += mesh.normals.Length;uvOffset += mesh.uv.Length;return sb.ToString(); }
  • MeshToFile和MeshesToFile函數是將Mesh網格轉換的String字符串保存到文件中,其中的最后一步MaterialsToFile是遍歷材質對象字典保存到文件中。
  • 最后,因為工具欄的出發函數必須為靜態函數,故子類都新建了靜態函數用于“包裝”父類的相應靜態函數,并使用MenuItem修飾。示例:
  • [MenuItem("Custom/Export Obj/MeshFilter/導出所有選擇的網格過濾器以分離的Obj形式")]//Export all MeshFilters in selection to separate Objs protected static void ExportSelectionToSeparate_MF() {ExportSelectionToSeparate(); }

    使用方式

  • 在場景中選擇模型對象。
  • 在工具欄選擇Custom—>Export Obj,再根據所選擇的模型對象所使用的Mesh組件選擇對應的選項:MeshFilter或SkinnedMeshRenderer。
  • 再根據自己的需求選擇導出的模式:
    • 所有網格分別輸出到一個Obj文件;
    • 輸出選擇的網格合體到一個Obj文件;
    • 輸出每個選擇的模型到單一Obj文件。
  • 之后彈出對話框,即為成功。
  • 項目文件夾下會出現一個名為ExportedObj的文件夾,其中便是保存下來的Obj文件以及對應的png圖片??梢噪p擊查看是否正確。
  • 參考鏈接

  • Unity2019導出地形terrain為obj
  • Unity 導出FBX和OBJ的方法(原代碼出處)
  • 總結

    以上是生活随笔為你收集整理的Unity导出模型为Obj文件的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。