ObjMesh.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. 
  2. using UnityEngine;
  3. using System.Collections;
  4. using System.Globalization;
  5. using System.Collections.Generic;
  6. using System;
  7. namespace Nxr.Internal
  8. {
  9. public class ObjMesh
  10. {
  11. public struct IndexInfo
  12. {
  13. public Vector3Int indexVec;
  14. public int index;
  15. }
  16. public struct Vector3Int
  17. {
  18. public Vector3Int(int ix, int iy, int iz)
  19. {
  20. x = ix;
  21. y = iy;
  22. z = iz;
  23. }
  24. public int x;
  25. public int y;
  26. public int z;
  27. }
  28. /// <summary>
  29. /// UV坐标列表
  30. /// </summary>
  31. private List<Vector2> uvArrayList;
  32. /// <summary>
  33. /// 法线列表
  34. /// </summary>
  35. private List<Vector3> normalArrayList;
  36. /// <summary>
  37. /// 顶点列表
  38. /// </summary>
  39. private List<Vector3> vertexArrayList;
  40. /// <summary>
  41. /// 面相关的顶点索引、UV索引列表、法线索引
  42. /// </summary>
  43. private List<Vector3Int> faceVertexUVNormal;
  44. /// <summary>
  45. /// UV坐标数组
  46. /// </summary>
  47. public Vector2[] UVArray;
  48. /// <summary>
  49. /// 法线数组
  50. /// </summary>
  51. public Vector3[] NormalArray;
  52. /// <summary>
  53. /// 顶点数组
  54. /// </summary>
  55. public Vector3[] VertexArray;
  56. /// <summary>
  57. /// 面数组
  58. /// </summary>
  59. public int[] TriangleArray;
  60. /// <summary>
  61. /// 构造函数 /// </summary>
  62. public ObjMesh()
  63. {
  64. //初始化列表
  65. uvArrayList = new List<Vector2>();
  66. normalArrayList = new List<Vector3>();
  67. vertexArrayList = new List<Vector3>();
  68. faceVertexUVNormal = new List<Vector3Int>();
  69. }
  70. /// <summary>
  71. /// 从一个文本化后的.obj文件中加载模型
  72. /// 格式 :f v/vt/vn v/vt/vn v/vt/vn(f 顶点索引 / 纹理坐标索引 / 顶点法向量索引)
  73. /// </summary>
  74. public ObjMesh LoadFromObj(string objText)
  75. {
  76. uvArrayList.Clear();
  77. normalArrayList.Clear();
  78. vertexArrayList.Clear();
  79. faceVertexUVNormal.Clear();
  80. UVArray = null;
  81. TriangleArray = null;
  82. NormalArray = null;
  83. VertexArray = null;
  84. double startMS = new TimeSpan(DateTime.Now.Ticks).TotalMilliseconds;
  85. if (objText.Length <= 0)
  86. return null;
  87. //v这一行在3dsMax中导出的.obj文件
  88. // 前面是两个空格后面是一个空格
  89. objText = objText.Replace(" ", " ");
  90. //将文本化后的obj文件内容按行分割
  91. string[] allLines = objText.Split('\n');
  92. foreach (string line in allLines)
  93. {
  94. //将每一行按空格分割
  95. string[] chars = line.Split(' ');
  96. //根据第一个字符来判断数据的类型
  97. switch (chars[0])
  98. {
  99. case "v":
  100. //处理顶点
  101. this.vertexArrayList.Add(new Vector3(
  102. ConvertToFloat(chars[1]),
  103. ConvertToFloat(chars[2]),
  104. ConvertToFloat(chars[3]))
  105. );
  106. break;
  107. case "vn":
  108. //处理法线
  109. this.normalArrayList.Add(new Vector3(
  110. ConvertToFloat(chars[1]),
  111. ConvertToFloat(chars[2]),
  112. ConvertToFloat(chars[3]))
  113. );
  114. break;
  115. case "vt":
  116. //处理UV
  117. this.uvArrayList.Add(new Vector2(
  118. ConvertToFloat(chars[1]),
  119. ConvertToFloat(chars[2]))
  120. );
  121. break;
  122. case "f":
  123. //处理面
  124. GetTriangleList(chars);
  125. break;
  126. }
  127. }
  128. //合并三角面
  129. Combine();
  130. Debug.Log("ObjMesh Finish=" + (new TimeSpan(DateTime.Now.Ticks).TotalMilliseconds - startMS) + "MS");
  131. return this;
  132. }
  133. private string GenerateKey(Vector3Int vector3)
  134. {
  135. return "key_" + (int)vector3.x + "_" + (int)vector3.y + "_" + (int)vector3.z;
  136. }
  137. /// <summary>
  138. /// 合并三角面
  139. /// </summary>
  140. private void Combine()
  141. {
  142. Dictionary<string, ArrayList> CacheDict = new Dictionary<string, ArrayList>();
  143. for (int i = 0, size = faceVertexUVNormal.Count; i < size; i++)
  144. {
  145. Vector3Int tmpVec = faceVertexUVNormal[i];
  146. string key = GenerateKey(tmpVec);
  147. IndexInfo mIndexInfo = new IndexInfo();
  148. mIndexInfo.index = i;
  149. mIndexInfo.indexVec = tmpVec;
  150. if (CacheDict.ContainsKey(key))
  151. {
  152. CacheDict[key].Add(mIndexInfo);
  153. }
  154. else
  155. {
  156. CacheDict[key] = new ArrayList();
  157. CacheDict[key].Add(mIndexInfo);
  158. }
  159. }
  160. //使用一个字典来存储要合并的索引信息
  161. Dictionary<int, ArrayList> toCambineList = new Dictionary<int, ArrayList>();
  162. for (int i = 0, size = faceVertexUVNormal.Count; i < size; i++)
  163. {
  164. if (faceVertexUVNormal[i].x != 0 && faceVertexUVNormal[i].y != 0 && faceVertexUVNormal[i].z != 0)
  165. {
  166. Vector3Int iTemp = faceVertexUVNormal[i];
  167. //相同索引的列表
  168. ArrayList SameIndexList = new ArrayList();
  169. SameIndexList.Add(i);
  170. string key = GenerateKey(iTemp);
  171. if (CacheDict.ContainsKey(key))
  172. {
  173. ArrayList mIdxInfoList = CacheDict[key];
  174. foreach (IndexInfo IndexTtem in mIdxInfoList)
  175. {
  176. int j = IndexTtem.index;
  177. if (j != i)
  178. {
  179. SameIndexList.Add(j);
  180. faceVertexUVNormal[j] = new Vector3Int(0, 0, 0);
  181. }
  182. }
  183. }
  184. //用一个索引来作为字典的键名,这样它可以代替对应列表内所有索引
  185. toCambineList.Add(i, SameIndexList);
  186. }
  187. }
  188. //初始化各个数组
  189. this.VertexArray = new Vector3[toCambineList.Count];
  190. this.UVArray = new Vector2[toCambineList.Count];
  191. this.NormalArray = new Vector3[toCambineList.Count];
  192. this.TriangleArray = new int[faceVertexUVNormal.Count];
  193. //定义遍历字典的计数器
  194. int count = 0;
  195. //遍历词典
  196. foreach (KeyValuePair<int, ArrayList> IndexTtem in toCambineList)
  197. {
  198. //根据索引给面数组赋值
  199. foreach (int item in IndexTtem.Value)
  200. {
  201. TriangleArray[item] = count;
  202. }
  203. //当前的顶点、UV、法线索引信息
  204. Vector3Int VectorTemp = faceVertexUVNormal[IndexTtem.Key];
  205. //给顶点数组赋值
  206. VertexArray[count] = vertexArrayList[VectorTemp.x - 1];
  207. //给UV数组赋值
  208. if (uvArrayList.Count > 0)
  209. {
  210. Vector2 tVec = uvArrayList[VectorTemp.y - 1];
  211. UVArray[count] = new Vector2(tVec.x, tVec.y);
  212. }
  213. //给法线数组赋值
  214. if (normalArrayList.Count > 0)
  215. {
  216. NormalArray[count] = normalArrayList[VectorTemp.z - 1];
  217. }
  218. count++;
  219. }
  220. }
  221. /// <summary>
  222. /// 获取面列表.格式 :f v/vt/vn v/vt/vn v/vt/vn(f 顶点索引 / 纹理坐标索引 / 顶点法向量索引)
  223. /// </summary>
  224. /// <param name="chars">Chars.</param>
  225. private void GetTriangleList(string[] chars)
  226. {
  227. // f 960/1058/1195 961/1059/1196 962/1060/1197
  228. // 顶点索引: 960/961/962
  229. // 纹理索引:1058/1059/1060
  230. // 法线索引:1195/1196/1197
  231. List<Vector3Int> indexVectorList = new List<Vector3Int>();
  232. for (int i = 1, size = chars.Length; i < size; ++i)
  233. {
  234. //将每一行按照空格分割后从第一个元素开始
  235. //按照/继续分割可依次获得顶点索引、UV索引和法线索引
  236. string[] indexs = chars[i].Split('/');
  237. if (indexs.Length < 3) continue;
  238. Vector3Int indexVector = new Vector3Int(0, 0, 0);
  239. //顶点索引
  240. indexVector.x = ConvertToInt(indexs[0]);
  241. //UV索引
  242. if (indexs.Length > 1)
  243. {
  244. if (indexs[1] != "")
  245. indexVector.y = ConvertToInt(indexs[1]);
  246. }
  247. //法线索引
  248. if (indexs.Length > 2)
  249. {
  250. if (indexs[2] != "")
  251. indexVector.z = ConvertToInt(indexs[2]);
  252. }
  253. //将索引向量加入列表中
  254. indexVectorList.Add(indexVector);
  255. }
  256. //这里需要研究研究
  257. for (int j = 1; j < indexVectorList.Count - 1; ++j)
  258. {
  259. //按照0,1,2这样的方式来组成面
  260. faceVertexUVNormal.Add(indexVectorList[0]);
  261. faceVertexUVNormal.Add(indexVectorList[j]);
  262. faceVertexUVNormal.Add(indexVectorList[j + 1]);
  263. }
  264. }
  265. /// <summary>
  266. /// 将一个字符串转换为浮点类型
  267. /// </summary>
  268. /// <param name="s">待转换的字符串</param>
  269. /// <returns></returns>
  270. private float ConvertToFloat(string s)
  271. {
  272. return FastFloatParse(s); //(float)System.Convert.ToDouble(s, CultureInfo.InvariantCulture);
  273. }
  274. /// <summary>
  275. /// 将一个字符串转化为整型 /// </summary>
  276. /// <returns>待转换的字符串</returns>
  277. /// <param name="s"></param>
  278. private int ConvertToInt(string s)
  279. {
  280. return FastIntParse(s); //System.Convert.ToInt32(s, CultureInfo.InvariantCulture);
  281. }
  282. /// <summary>
  283. /// Modified from https://codereview.stackexchange.com/a/76891. Faster than float.Parse
  284. /// </summary>
  285. public static float FastFloatParse(string input)
  286. {
  287. input = cleanString(input);
  288. if (input.Contains("e") || input.Contains("E"))
  289. return float.Parse(input, CultureInfo.InvariantCulture);
  290. float result = 0;
  291. int pos = 0;
  292. int len = input.Length;
  293. if (len == 0) return float.NaN;
  294. char c = input[0];
  295. float sign = 1;
  296. if (c == '-')
  297. {
  298. sign = -1;
  299. ++pos;
  300. if (pos >= len) return float.NaN;
  301. }
  302. while (true) // breaks inside on pos >= len or non-digit character
  303. {
  304. if (pos >= len) return sign * result;
  305. c = input[pos++];
  306. if (c < '0' || c > '9') break;
  307. result = (result * 10.0f) + (c - '0');
  308. }
  309. if (c != '.' && c != ',') return float.NaN;
  310. float exp = 0.1f;
  311. while (pos < len)
  312. {
  313. c = input[pos++];
  314. if (c < '0' || c > '9') return float.NaN;
  315. result += (c - '0') * exp;
  316. exp *= 0.1f;
  317. }
  318. return sign * result;
  319. }
  320. /// <summary>
  321. /// Modified from http://cc.davelozinski.com/c-sharp/fastest-way-to-convert-a-string-to-an-int. Faster than int.Parse
  322. /// </summary>
  323. public static int FastIntParse(string input)
  324. {
  325. input = cleanString(input);
  326. int result = 0;
  327. bool isNegative = (input[0] == '-');
  328. for (int i = (isNegative) ? 1 : 0; i < input.Length; i++)
  329. {
  330. result = result * 10 + (input[i] - '0');
  331. }
  332. return (isNegative) ? -result : result;
  333. }
  334. private static string cleanString(string newStr)
  335. {
  336. string tempStr = newStr.Replace((char)13, ' ');
  337. return tempStr.Replace((char)10, ' ').Trim();
  338. }
  339. }
  340. }