NavMeshExporter.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750
  1. using System;
  2. using System.IO;
  3. using System.Collections.Generic;
  4. using System.Text;
  5. using ET;
  6. using UnityEditor;
  7. using UnityEditor.UI;
  8. using UnityEngine;
  9. using UnityEngine.AI;
  10. using UnityEngine.Networking;
  11. using UnityEngine.SceneManagement;
  12. namespace ETEditor
  13. {
  14. /// <summary>
  15. /// 从Unity的NavMesh组件里导出地图数据,供服务器来使用
  16. /// https://blog.csdn.net/huutu/article/details/52672505
  17. /// </summary>
  18. public class NavMeshExporter: Editor
  19. {
  20. public const byte VERSION = 1;
  21. private class Vert
  22. {
  23. public int id;
  24. public float x;
  25. public float y;
  26. public float z;
  27. public UnityEngine.Vector3 ToVector3()
  28. {
  29. return new UnityEngine.Vector3(x, y, z);
  30. }
  31. }
  32. private class Face
  33. {
  34. public int id;
  35. public int area;
  36. public float centerX;
  37. public float centerZ;
  38. public float normalX;
  39. public float normalZ;
  40. public double normalA;
  41. public double normalB;
  42. public double normalC;
  43. public double normalD;
  44. public uint sortValue;
  45. public List<Vert> verts = new List<Vert>();
  46. }
  47. private class Pair
  48. {
  49. public float centerX;
  50. public float centerZ;
  51. public float distance;
  52. public Face firstEdgeFace;
  53. public int firstEdgeIndex;
  54. public Face secondEdgeFace;
  55. public int secondEdgeIndex;
  56. }
  57. private static List<Vert> vertList = new List<Vert>();
  58. private static List<Face> faceList = new List<Face>();
  59. private static List<Pair> pairList = new List<Pair>();
  60. private static Dictionary<Vert, Face> vertFaceDict = new Dictionary<Vert, Face>();
  61. private static Dictionary<Vert, Dictionary<Vert, Pair>> vertPairDict = new Dictionary<Vert, Dictionary<Vert, Pair>>();
  62. private static Dictionary<float, Dictionary<float, Vert>> pointVertDict = new Dictionary<float, Dictionary<float, Vert>>();
  63. private static Dictionary<int, Vert> indexVertDict = new Dictionary<int, Vert>();
  64. private static string outputClientFolder = "../RecastNavMesh/Meshes/";
  65. private static string outputServerFolder = "../Config/RecastNavData/ExportedObj/";
  66. #region 菜单主函数
  67. [MenuItem("ET/NavMesh/ExportSceneObj")]
  68. public static void ExportScene()
  69. {
  70. var triangulation = UnityEngine.AI.NavMesh.CalculateTriangulation();
  71. if (triangulation.indices.Length < 3)
  72. {
  73. Debug.LogError($"NavMeshExporter ExportScene Error - 场景里没有需要被导出的物体,请先用NavMesh进行Bake。");
  74. return;
  75. }
  76. vertList.Clear();
  77. faceList.Clear();
  78. pairList.Clear();
  79. vertFaceDict.Clear();
  80. vertPairDict.Clear();
  81. pointVertDict.Clear();
  82. indexVertDict.Clear();
  83. InputVertices(triangulation.vertices);
  84. InputTriangles(triangulation.indices, triangulation.areas);
  85. IndexVertsAndFaces();
  86. //WriteFile();
  87. // 导出*_internal.Obj,仅供Unity编辑器自己查看
  88. //WriteUnityObjFile();
  89. // 导出Recast可用的*.Obj文件
  90. WriteRecastObjFile();
  91. // 拷贝Obj和Bytes文件到服务器目录下 TODO 暂不需要
  92. //CopyObjFiles();
  93. Debug.Log($"NavMesh Output Info - Vertices:[{vertList.Count}] - Faces:[{faceList.Count}]");
  94. }
  95. #endregion
  96. #region 导出Bytes
  97. private static void InputVertices(Vector3[] vertices)
  98. {
  99. for (int i = 0, n = vertices.Length - 1; i <= n; i++)
  100. {
  101. var point = vertices[i];
  102. var x = (float) Math.Round(point.x, 2);
  103. var y = (float) Math.Round(point.y, 2);
  104. var z = (float) Math.Round(point.z, 2);
  105. if (!pointVertDict.ContainsKey(x))
  106. {
  107. pointVertDict.Add(x, new Dictionary<float, Vert>());
  108. }
  109. Vert vert;
  110. if (pointVertDict[x].ContainsKey(z))
  111. {
  112. vert = pointVertDict[x][z];
  113. }
  114. else
  115. {
  116. vert = new Vert();
  117. vert.x = x;
  118. vert.y = y;
  119. vert.z = z;
  120. pointVertDict[x][z] = vert;
  121. }
  122. indexVertDict.Add(i, vert);
  123. }
  124. }
  125. private static void InputTriangles(int[] indices, int[] areas)
  126. {
  127. Face face = null;
  128. var faceIndices = new HashSet<int>();
  129. for (int i = 0, n = areas.Length; i < n; i++)
  130. {
  131. var triangleIndexList = new int[3];
  132. var triangleVertList = new Vert[3];
  133. for (var j = 0; j < 3; j++)
  134. {
  135. triangleIndexList[j] = indices[i * 3 + j];
  136. triangleVertList[j] = indexVertDict[triangleIndexList[j]];
  137. }
  138. var vert0 = triangleVertList[0];
  139. var vert1 = triangleVertList[1];
  140. var vert2 = triangleVertList[2];
  141. if (vert0 == vert1 || vert1 == vert2 || vert2 == vert0)
  142. {
  143. continue;
  144. }
  145. var newFace = true;
  146. var area = areas[i] >= 3? areas[i] - 2 : 0;
  147. if (face != null && face.area == area)
  148. {
  149. for (var j = 0; j < 3; j++)
  150. {
  151. if (faceIndices.Contains(triangleIndexList[j]))
  152. {
  153. newFace = false;
  154. break;
  155. }
  156. }
  157. }
  158. if (newFace)
  159. {
  160. if (face != null)
  161. {
  162. InitFace(face);
  163. faceIndices.Clear();
  164. }
  165. face = new Face();
  166. face.area = area;
  167. }
  168. double x1 = vert1.x - vert0.x;
  169. double y1 = vert1.y - vert0.y;
  170. double z1 = vert1.z - vert0.z;
  171. double x2 = vert2.x - vert0.x;
  172. double y2 = vert2.y - vert0.y;
  173. double z2 = vert2.z - vert0.z;
  174. double normalA = y1 * z2 - z1 * y2;
  175. double normalB = z1 * x2 - x1 * z2;
  176. double normalC = x1 * y2 - y1 * x2;
  177. if (normalB < -0.000001 || 0.000001 < normalB)
  178. {
  179. var normalD = normalA + normalB + normalC;
  180. if (normalD > face.normalD)
  181. {
  182. face.normalA = normalA;
  183. face.normalB = normalB;
  184. face.normalC = normalC;
  185. face.normalD = normalD;
  186. }
  187. }
  188. for (var j = 0; j < 3; j++)
  189. {
  190. if (!faceIndices.Contains(triangleIndexList[j]))
  191. {
  192. faceIndices.Add(triangleIndexList[j]);
  193. face.verts.Add(triangleVertList[j]);
  194. }
  195. }
  196. }
  197. if (face != null)
  198. {
  199. InitFace(face);
  200. }
  201. foreach (var pair in pairList)
  202. {
  203. var firstFace = pair.firstEdgeFace;
  204. var secondFace = pair.secondEdgeFace;
  205. var firstDistance = GetDistance(firstFace.centerX - pair.centerX, firstFace.centerZ - pair.centerZ);
  206. var secondDistance = GetDistance(secondFace.centerX - pair.centerX, secondFace.centerZ - pair.centerZ);
  207. pair.distance = firstDistance + secondDistance;
  208. }
  209. }
  210. private static float GetDistance(float deltaX, float deltaZ)
  211. {
  212. return (float) Math.Round(Math.Sqrt((double) deltaX * (double) deltaX + (double) deltaZ * (double) deltaZ), 2);
  213. }
  214. private static void InitFace(Face face)
  215. {
  216. face.centerX = 0;
  217. face.centerZ = 0;
  218. var vertCount = face.verts.Count;
  219. foreach (var vert in face.verts)
  220. {
  221. face.centerX += vert.x;
  222. face.centerZ += vert.z;
  223. if (!vertFaceDict.ContainsKey(vert))
  224. {
  225. vertFaceDict.Add(vert, face);
  226. vertList.Add(vert);
  227. }
  228. }
  229. face.centerX /= vertCount;
  230. face.centerZ /= vertCount;
  231. if (face.normalB != 0)
  232. {
  233. face.normalX = (float) Math.Round(face.normalA / face.normalB, 6);
  234. face.normalZ = (float) Math.Round(face.normalC / face.normalB, 6);
  235. }
  236. for (int i = 0, n = vertCount - 1; i <= n; i++)
  237. {
  238. var firstVert = face.verts[i];
  239. var secondVert = face.verts[i == n? 0 : i + 1];
  240. if (!vertPairDict.ContainsKey(firstVert))
  241. {
  242. vertPairDict.Add(firstVert, new Dictionary<Vert, Pair>());
  243. }
  244. if (!vertPairDict.ContainsKey(secondVert))
  245. {
  246. vertPairDict.Add(secondVert, new Dictionary<Vert, Pair>());
  247. }
  248. if (!vertPairDict[secondVert].ContainsKey(firstVert))
  249. {
  250. var pair = new Pair();
  251. pair.firstEdgeFace = face;
  252. pair.firstEdgeIndex = i;
  253. vertPairDict[firstVert][secondVert] = pair;
  254. }
  255. else
  256. {
  257. var pair = vertPairDict[secondVert][firstVert];
  258. pair.centerX = (firstVert.x + secondVert.x) / 2;
  259. pair.centerZ = (firstVert.z + secondVert.z) / 2;
  260. pair.secondEdgeFace = face;
  261. pair.secondEdgeIndex = i;
  262. pairList.Add(pair);
  263. }
  264. }
  265. faceList.Add(face);
  266. }
  267. private static void IndexVertsAndFaces()
  268. {
  269. var minX = float.MaxValue;
  270. var maxX = float.MinValue;
  271. var minZ = float.MaxValue;
  272. var maxZ = float.MinValue;
  273. foreach (var vert in vertList)
  274. {
  275. if (minX > vert.x)
  276. {
  277. minX = vert.x;
  278. }
  279. if (maxX < vert.x)
  280. {
  281. maxX = vert.x;
  282. }
  283. if (minZ > vert.z)
  284. {
  285. minZ = vert.z;
  286. }
  287. if (maxZ < vert.x)
  288. {
  289. maxZ = vert.x;
  290. }
  291. }
  292. var hilbertX = 65535f / (maxX - minX);
  293. var hilbertZ = 65535f / (maxZ - minZ);
  294. foreach (var face in faceList)
  295. {
  296. var X = (uint) Math.Round((face.centerX - minX) * hilbertX);
  297. var Z = (uint) Math.Round((face.centerZ - minZ) * hilbertZ);
  298. var a = X ^ Z;
  299. var b = 0xFFFF ^ a;
  300. var c = 0xFFFF ^ (X | Z);
  301. var d = X & (Z ^ 0xFFFF);
  302. var A = a | (b >> 1);
  303. var B = (a >> 1) ^ a;
  304. var C = ((c >> 1) ^ (b & (d >> 1))) ^ c;
  305. var D = ((a & (c >> 1)) ^ (d >> 1)) ^ d;
  306. a = A;
  307. b = B;
  308. c = C;
  309. d = D;
  310. A = (a & (a >> 2)) ^ (b & (b >> 2));
  311. B = (a & (b >> 2)) ^ (b & ((a ^ b) >> 2));
  312. C ^= (a & (c >> 2)) ^ (b & (d >> 2));
  313. D ^= (b & (c >> 2)) ^ ((a ^ b) & (d >> 2));
  314. a = A;
  315. b = B;
  316. c = C;
  317. d = D;
  318. A = (a & (a >> 4)) ^ (b & (b >> 4));
  319. B = (a & (b >> 4)) ^ (b & ((a ^ b) >> 4));
  320. C ^= (a & (c >> 4)) ^ (b & (d >> 4));
  321. D ^= (b & (c >> 4)) ^ ((a ^ b) & (d >> 4));
  322. a = A;
  323. b = B;
  324. c = C;
  325. d = D;
  326. C ^= (a & (c >> 8)) ^ (b & (d >> 8));
  327. D ^= (b & (c >> 8)) ^ ((a ^ b) & (d >> 8));
  328. C ^= C >> 1;
  329. D ^= D >> 1;
  330. c = X ^ Z;
  331. d = D | (0xFFFF ^ (c | C));
  332. c = (c | (c << 8)) & 0x00FF00FF;
  333. c = (c | (c << 4)) & 0x0F0F0F0F;
  334. c = (c | (c << 2)) & 0x33333333;
  335. c = (c | (c << 1)) & 0x55555555;
  336. d = (d | (d << 8)) & 0x00FF00FF;
  337. d = (d | (d << 4)) & 0x0F0F0F0F;
  338. d = (d | (d << 2)) & 0x33333333;
  339. d = (d | (d << 1)) & 0x55555555;
  340. face.sortValue = (d << 1) | c;
  341. }
  342. faceList.Sort(SortComparison);
  343. for (int i = 0, n = vertList.Count; i < n; i++)
  344. {
  345. vertList[i].id = i;
  346. }
  347. for (int i = 0, n = faceList.Count; i < n; i++)
  348. {
  349. faceList[i].id = i;
  350. }
  351. }
  352. private static int SortComparison(Face a, Face b)
  353. {
  354. return a.sortValue.CompareTo(b.sortValue);
  355. }
  356. private static void WriteFile()
  357. {
  358. if (!System.IO.Directory.Exists(outputClientFolder))
  359. {
  360. System.IO.Directory.CreateDirectory(outputClientFolder);
  361. }
  362. var path = outputClientFolder + SceneManager.GetActiveScene().name + ".bytes";
  363. var writer = new BinaryWriter(new FileStream(path, FileMode.Create));
  364. writer.Write('N');
  365. writer.Write('a');
  366. writer.Write('v');
  367. writer.Write('M');
  368. writer.Write('e');
  369. writer.Write('s');
  370. writer.Write('h');
  371. writer.Write(VERSION);
  372. writer.Write(vertList.Count);
  373. foreach (var vert in vertList)
  374. {
  375. writer.Write(vert.x);
  376. writer.Write(vert.y);
  377. writer.Write(vert.z);
  378. }
  379. writer.Write(faceList.Count);
  380. foreach (var face in faceList)
  381. {
  382. writer.Write(face.area);
  383. writer.Write(face.normalX);
  384. writer.Write(face.normalZ);
  385. writer.Write(face.verts.Count);
  386. foreach (var vert in face.verts)
  387. {
  388. writer.Write(vert.id);
  389. }
  390. }
  391. writer.Write(pairList.Count);
  392. foreach (var pair in pairList)
  393. {
  394. writer.Write(pair.distance);
  395. writer.Write(pair.firstEdgeFace.id);
  396. writer.Write(pair.firstEdgeIndex);
  397. writer.Write(pair.secondEdgeFace.id);
  398. writer.Write(pair.secondEdgeIndex);
  399. }
  400. writer.Flush();
  401. writer.Close();
  402. AssetDatabase.Refresh();
  403. }
  404. #endregion
  405. #region 导出*_internal.Obj
  406. // ————————————————
  407. // 版权声明:本文为CSDN博主「_Captain」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
  408. // 原文链接:https://blog.csdn.net/huutu/article/details/52672505
  409. private static void WriteUnityObjFile()
  410. {
  411. var path = outputClientFolder + SceneManager.GetActiveScene().name + "_internal.obj";
  412. StreamWriter tmpStreamWriter = new StreamWriter(path);
  413. NavMeshTriangulation tmpNavMeshTriangulation = UnityEngine.AI.NavMesh.CalculateTriangulation();
  414. //顶点
  415. for (int i = 0; i < tmpNavMeshTriangulation.vertices.Length; i++)
  416. {
  417. tmpStreamWriter.WriteLine("v " + tmpNavMeshTriangulation.vertices[i].x + " " + tmpNavMeshTriangulation.vertices[i].y + " " +
  418. tmpNavMeshTriangulation.vertices[i].z);
  419. }
  420. tmpStreamWriter.WriteLine("g pPlane1");
  421. //索引
  422. for (int i = 0; i < tmpNavMeshTriangulation.indices.Length;)
  423. {
  424. tmpStreamWriter.WriteLine("f " + (tmpNavMeshTriangulation.indices[i] + 1) + " " + (tmpNavMeshTriangulation.indices[i + 1] + 1) + " " +
  425. (tmpNavMeshTriangulation.indices[i + 2] + 1));
  426. i = i + 3;
  427. }
  428. tmpStreamWriter.Flush();
  429. tmpStreamWriter.Close();
  430. AssetDatabase.Refresh();
  431. }
  432. #endregion
  433. #region 导出Obj(Recast使用)
  434. // ————————————————
  435. // 版权声明:本文为CSDN博主「Rhett_Yuan」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
  436. // 原文链接:https://blog.csdn.net/rhett_yuan/article/details/79483387
  437. // https://www.cnblogs.com/koshio0219/p/12195974.html
  438. // http://wiki.unity3d.com/index.php?title=ObjExporter#EditorObjExporter.cs
  439. /// <summary>
  440. /// 将NavMesh里的所有物体导出成为RecastNavigation可以识别的Obj文件。July.11.2020. Liu Gang.
  441. /// </summary>
  442. private static void WriteRecastObjFile()
  443. {
  444. if (!System.IO.Directory.Exists(outputClientFolder))
  445. {
  446. System.IO.Directory.CreateDirectory(outputClientFolder);
  447. }
  448. var filename = SceneManager.GetActiveScene().name;
  449. var path = outputClientFolder + filename + ".obj";
  450. StreamWriter sw = new StreamWriter(path);
  451. Dictionary<string, ObjMaterial> materialList = PrepareFileWrite();
  452. List<MeshFilter> meshes = Collect();
  453. int count = 0;
  454. foreach (MeshFilter mf in meshes)
  455. {
  456. sw.Write("mtllib ./" + filename + ".mtl\n");
  457. string strMes = MeshToString(mf, materialList);
  458. sw.Write(strMes);
  459. EditorUtility.DisplayProgressBar("Exporting objects...", mf.name, count++ / (float) meshes.Count);
  460. }
  461. sw.Flush();
  462. sw.Close();
  463. EditorUtility.ClearProgressBar();
  464. AssetDatabase.Refresh();
  465. }
  466. // Global containers for all active mesh/terrain tags
  467. public static List<MeshFilter> m_Meshes = new List<MeshFilter>();
  468. private static string NAVMESH_TAG = "NavMesh";
  469. private static int vertexOffset = 0;
  470. private static int normalOffset = 0;
  471. private static int uvOffset = 0;
  472. public struct ObjMaterial
  473. {
  474. public string name;
  475. public string textureName;
  476. }
  477. private static void Clear()
  478. {
  479. vertexOffset = 0;
  480. normalOffset = 0;
  481. uvOffset = 0;
  482. }
  483. private static Dictionary<string, ObjMaterial> PrepareFileWrite()
  484. {
  485. Clear();
  486. return new Dictionary<string, ObjMaterial>();
  487. }
  488. public static List<MeshFilter> Collect()
  489. {
  490. List<MeshFilter> meshes = new List<MeshFilter>();
  491. // 确定场景内必须有NAVMESH_TAG这个tag
  492. // ————————————————
  493. // 版权声明:本文为CSDN博主「懵懵爸爸」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
  494. // 原文链接:https://blog.csdn.net/ljason1993/article/details/80924723
  495. bool bFindTag = false;
  496. string[] strTags = UnityEditorInternal.InternalEditorUtility.tags;
  497. foreach (string tag in strTags)
  498. {
  499. if (tag == NAVMESH_TAG)
  500. {
  501. bFindTag = true;
  502. break;
  503. }
  504. }
  505. if (!bFindTag)
  506. {
  507. Debug.LogError($"NavMeshExporter Collect Error - 所有需要被NavMesh导出的物体的Tag必须是:[{NAVMESH_TAG}],目前的项目里没有这个Tag。");
  508. return meshes;
  509. }
  510. MeshFilter[] meshFilters = FindObjectsOfType<MeshFilter>();
  511. foreach (MeshFilter mf in meshFilters)
  512. {
  513. if (mf.gameObject.tag == NAVMESH_TAG)
  514. {
  515. meshes.Add(mf);
  516. }
  517. }
  518. if (meshes.Count == 0)
  519. {
  520. Debug.LogError($"NavMeshExporter Collect Error - 场景里没有需要被导出的物体,需要被导出的物体,它们的Tag必须是:[{NAVMESH_TAG}]。");
  521. }
  522. return meshes;
  523. }
  524. public static string MeshToString(MeshFilter mf, Dictionary<string, ObjMaterial> materialList)
  525. {
  526. Mesh m = mf.sharedMesh;
  527. Material[] mats = mf.GetComponent<Renderer>().sharedMaterials;
  528. StringBuilder sb = new StringBuilder();
  529. sb.Append("g ").Append(mf.name).Append("\n");
  530. // foreach(Vector3 v in m.vertices) {
  531. // sb.Append(string.Format("v {0} {1} {2}\n",v.x,v.y,v.z));
  532. // }
  533. foreach (Vector3 lv in m.vertices)
  534. {
  535. Vector3 wv = mf.transform.TransformPoint(lv);
  536. //This is sort of ugly - inverting x-component since we're in
  537. //a different coordinate system than "everyone" is "used to".
  538. sb.Append(string.Format("v {0} {1} {2}\n", -wv.x, wv.y, wv.z));
  539. }
  540. sb.Append("\n");
  541. // foreach(Vector3 v in m.normals) {
  542. // sb.Append(string.Format("vn {0} {1} {2}\n",v.x,v.y,v.z));
  543. // }
  544. foreach (Vector3 lv in m.normals)
  545. {
  546. Vector3 wv = mf.transform.TransformDirection(lv);
  547. sb.Append(string.Format("vn {0} {1} {2}\n", -wv.x, wv.y, wv.z));
  548. }
  549. sb.Append("\n");
  550. foreach (Vector3 v in m.uv)
  551. {
  552. sb.Append(string.Format("vt {0} {1}\n", v.x, v.y));
  553. }
  554. int countMat = m.subMeshCount;
  555. if (mats == null)
  556. {
  557. Debug.LogWarning($"NavMeshExporter MeshToString Error - 没有找到材质");
  558. return sb.ToString();
  559. }
  560. else if (mats.Length < countMat)
  561. {
  562. Debug.LogWarning($"NavMeshExporter MeshToString Error - 共享材质数量小于该物体的子物体数量 - {mats.Length} / {countMat}");
  563. countMat = mats.Length;
  564. }
  565. for (int material = 0; material < countMat; material++)
  566. {
  567. string nameMat = "null";
  568. Texture mainTexture = null;
  569. if (mats[material] != null)
  570. {
  571. nameMat = mats[material].name;
  572. mainTexture = mats[material].mainTexture;
  573. }
  574. sb.Append("\n");
  575. sb.Append("usemtl ").Append(nameMat).Append("\n");
  576. sb.Append("usemap ").Append(nameMat).Append("\n");
  577. //See if this material is already in the materiallist.
  578. try
  579. {
  580. ObjMaterial objMaterial = new ObjMaterial();
  581. objMaterial.name = nameMat;
  582. if (mainTexture)
  583. objMaterial.textureName = AssetDatabase.GetAssetPath(mainTexture);
  584. else
  585. objMaterial.textureName = null;
  586. materialList.Add(objMaterial.name, objMaterial);
  587. }
  588. catch (ArgumentException)
  589. {
  590. //Already in the dictionary
  591. }
  592. // int[] triangles = m.GetTriangles(material);
  593. // for (int i=0;i<triangles.Length;i+=3) {
  594. // sb.Append(string.Format("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n",
  595. // triangles[i]+1, triangles[i+1]+1, triangles[i+2]+1));
  596. // }
  597. int[] triangles = m.GetTriangles(material);
  598. for (int i = 0; i < triangles.Length; i += 3)
  599. {
  600. //Because we inverted the x-component, we also needed to alter the triangle winding.
  601. sb.Append(string.Format("f {1}/{1}/{1} {0}/{0}/{0} {2}/{2}/{2}\n",
  602. triangles[i] + 1 + vertexOffset, triangles[i + 1] + 1 + normalOffset, triangles[i + 2] + 1 + uvOffset));
  603. }
  604. }
  605. vertexOffset += m.vertices.Length;
  606. normalOffset += m.normals.Length;
  607. uvOffset += m.uv.Length;
  608. return sb.ToString();
  609. }
  610. #endregion
  611. #region 拷贝文件
  612. /// <summary>
  613. /// 把生成的Obj文件拷贝到服务器
  614. /// https://www.cnblogs.com/wangjianhui008/p/3234519.html
  615. /// </summary>
  616. private static void CopyObjFiles()
  617. {
  618. string sourceFolder = outputClientFolder;
  619. // *.bytes, *.obj, *_internal.obj文件不再拷贝到服务器的Config/Navmesh目录下,不再需要了,减少服务器数据文件的大小。Aug.27.2020. Liu Gang.
  620. //得到原文件根目录下的所有文件
  621. // {
  622. // string[] files = System.IO.Directory.GetFiles(sourceFolder);
  623. // foreach (string file in files)
  624. // {
  625. // string name = System.IO.Path.GetFileName(file);
  626. // // 仅拷贝bytes文件和obj文件,但是不包括文件名里包含“internal”字样的obj文件。
  627. // var ext = Path.GetExtension(file);
  628. // if (ext == ".bytes" || (ext == ".obj" && !file.Contains("_internal.")))
  629. // {
  630. // string dest = System.IO.Path.Combine(destFolder, name);
  631. // System.IO.File.Copy(file, dest, true); //复制文件
  632. // }
  633. // }
  634. // }
  635. // 拷贝到RecastDemo配置路径
  636. {
  637. string[] files = System.IO.Directory.GetFiles(sourceFolder);
  638. //如果目标路径不存在,则创建目标路径
  639. if (!System.IO.Directory.Exists(outputServerFolder))
  640. {
  641. System.IO.Directory.CreateDirectory(outputServerFolder);
  642. }
  643. foreach (string file in files)
  644. {
  645. string name = System.IO.Path.GetFileName(file);
  646. // 仅拷贝bytes文件和obj文件,但是不包括文件名里包含“internal”字样的obj文件。
  647. var ext = Path.GetExtension(file);
  648. if (ext == ".obj" && !file.Contains("_internal."))
  649. {
  650. string dest = System.IO.Path.Combine(outputServerFolder, name);
  651. System.IO.File.Copy(file, dest, true); //复制文件
  652. UnityEngine.Debug.Log($"Recast:从{file}复制obj文件到{dest}成功");
  653. }
  654. }
  655. }
  656. }
  657. #endregion
  658. }
  659. }