using UnityEngine; using UnityEditor; using System.IO; using System.Text; public class MeshAssetObjExporter { [MenuItem("Assets/Export Mesh (.asset) to OBJ", true)] static bool ValidateExport() { return Selection.activeObject is Mesh; } [MenuItem("Assets/Export Mesh (.asset) to OBJ")] static void ExportMeshAssetToObj() { Mesh mesh = Selection.activeObject as Mesh; if (mesh == null) { Debug.LogWarning("Please select a Mesh .asset file in the Project window."); return; } string path = EditorUtility.SaveFilePanel("Export OBJ", "", mesh.name + ".obj", "obj"); if (string.IsNullOrEmpty(path)) return; ExportMeshToObj(mesh, path); Debug.Log("Mesh exported to: " + path); } static Vector3 ConvertToBlender(Vector3 v) { // Unity坐标 (X, Y, Z) → Blender坐标 (X, Z, -Y) return new Vector3(v.x, v.z, -v.y); } static void ExportMeshToObj(Mesh mesh, string path) { StringBuilder sb = new StringBuilder(); sb.AppendLine("# Exported from Unity MeshAssetObjExporter"); sb.AppendLine("g " + mesh.name); // 顶点 foreach (Vector3 v in mesh.vertices) { Vector3 vBlender = ConvertToBlender(v); sb.AppendFormat("v {0} {1} {2}\n", vBlender.x, vBlender.y, vBlender.z); } // 法线 foreach (Vector3 n in mesh.normals) { Vector3 nBlender = ConvertToBlender(n); sb.AppendFormat("vn {0} {1} {2}\n", nBlender.x, nBlender.y, nBlender.z); } // UV foreach (Vector2 uv in mesh.uv) { sb.AppendFormat("vt {0} {1}\n", uv.x, uv.y); } // 面 for (int i = 0; i < mesh.subMeshCount; i++) { int[] tris = mesh.GetTriangles(i); for (int j = 0; j < tris.Length; j += 3) { int a = tris[j] + 1; int b = tris[j + 1] + 1; int c = tris[j + 2] + 1; sb.AppendFormat("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n", a, b, c); } } File.WriteAllText(path, sb.ToString()); } }