ObjParser.hx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. package arm.format;
  2. class ObjParser {
  3. public static var splitCode = "o".code; // Object split, "g" for groups, "u"semtl for materials
  4. public var posa: kha.arrays.Int16Array = null;
  5. public var nora: kha.arrays.Int16Array = null;
  6. public var texa: kha.arrays.Int16Array = null;
  7. public var inda: kha.arrays.Uint32Array = null;
  8. public var udims: Array<kha.arrays.Uint32Array> = null; // Indices split per udim tile
  9. public var udimsU = 1; // Number of horizontal udim tiles
  10. public var scalePos = 1.0;
  11. public var scaleTex = 1.0;
  12. public var name = "";
  13. public var hasNext = false; // File contains multiple objects
  14. public var pos = 0;
  15. var posTemp: Array<Float>;
  16. var uvTemp: Array<Float>;
  17. var norTemp: Array<Float>;
  18. var va: kha.arrays.Uint32Array;
  19. var ua: kha.arrays.Uint32Array;
  20. var na: kha.arrays.Uint32Array;
  21. var vi = 0;
  22. var ui = 0;
  23. var ni = 0;
  24. var buf: haxe.io.UInt8Array = null;
  25. static var vindOff = 0;
  26. static var tindOff = 0;
  27. static var nindOff = 0;
  28. static var bytes: haxe.io.Bytes = null;
  29. static var posFirst: Array<Float>;
  30. static var uvFirst: Array<Float>;
  31. static var norFirst: Array<Float>;
  32. public function new(blob: kha.Blob, startPos = 0, udim = false) {
  33. pos = startPos;
  34. var posIndices: Array<Int> = [];
  35. var uvIndices: Array<Int> = [];
  36. var norIndices: Array<Int> = [];
  37. var readingFaces = false;
  38. var readingObject = false;
  39. var fullAttrib = false;
  40. bytes = blob.bytes;
  41. posTemp = [];
  42. uvTemp = [];
  43. norTemp = [];
  44. va = new kha.arrays.Uint32Array(60);
  45. ua = new kha.arrays.Uint32Array(60);
  46. na = new kha.arrays.Uint32Array(60);
  47. buf = new haxe.io.UInt8Array(64);
  48. if (startPos == 0) vindOff = tindOff = nindOff = 0;
  49. if (splitCode == "u".code && startPos > 0) {
  50. posTemp = posFirst;
  51. norTemp = norFirst;
  52. uvTemp = uvFirst;
  53. }
  54. while (true) {
  55. if (pos >= bytes.length) break;
  56. var c0 = bytes.get(pos++);
  57. if (readingObject && readingFaces && (c0 == "v".code || c0 == splitCode)) {
  58. pos--;
  59. hasNext = true;
  60. break;
  61. }
  62. if (c0 == "v".code) {
  63. var c1 = bytes.get(pos++);
  64. if (c1 == " ".code) {
  65. if (bytes.get(pos) == " ".code) pos++; // Some exporters put additional space directly after "v"
  66. posTemp.push(readFloat());
  67. pos++; // Space
  68. posTemp.push(readFloat());
  69. pos++; // Space
  70. posTemp.push(readFloat());
  71. }
  72. else if (c1 == "t".code) {
  73. pos++; // Space
  74. uvTemp.push(readFloat());
  75. pos++; // Space
  76. uvTemp.push(readFloat());
  77. if (norTemp.length > 0) fullAttrib = true;
  78. }
  79. else if (c1 == "n".code) {
  80. pos++; // Space
  81. norTemp.push(readFloat());
  82. pos++; // Space
  83. norTemp.push(readFloat());
  84. pos++; // Space
  85. norTemp.push(readFloat());
  86. if (uvTemp.length > 0) fullAttrib = true;
  87. }
  88. }
  89. else if (c0 == "f".code) {
  90. pos++; // Space
  91. if (bytes.get(pos) == " ".code) pos++; // Some exporters put additional space directly after "f"
  92. readingFaces = true;
  93. vi = ui = ni = 0;
  94. fullAttrib ? readFaceFast() : readFace();
  95. if (vi <= 4) { // Convex, fan triangulation
  96. posIndices.push(va[0]);
  97. posIndices.push(va[1]);
  98. posIndices.push(va[2]);
  99. for (i in 3...vi) {
  100. posIndices.push(va[0]);
  101. posIndices.push(va[i - 1]);
  102. posIndices.push(va[i]);
  103. }
  104. if (uvTemp.length > 0) {
  105. uvIndices.push(ua[0]);
  106. uvIndices.push(ua[1]);
  107. uvIndices.push(ua[2]);
  108. for (i in 3...ui) {
  109. uvIndices.push(ua[0]);
  110. uvIndices.push(ua[i - 1]);
  111. uvIndices.push(ua[i]);
  112. }
  113. }
  114. if (norTemp.length > 0) {
  115. norIndices.push(na[0]);
  116. norIndices.push(na[1]);
  117. norIndices.push(na[2]);
  118. for (i in 3...ni) {
  119. norIndices.push(na[0]);
  120. norIndices.push(na[i - 1]);
  121. norIndices.push(na[i]);
  122. }
  123. }
  124. }
  125. else { // Convex or concave, ear clipping
  126. var vindOff = splitCode == "u".code ? 0 : vindOff;
  127. var nindOff = splitCode == "u".code ? 0 : nindOff;
  128. var nx = 0.0;
  129. var ny = 0.0;
  130. var nz = 0.0;
  131. if (norTemp.length > 0) {
  132. nx = norTemp[(na[0] - nindOff) * 3 ];
  133. ny = norTemp[(na[0] - nindOff) * 3 + 1];
  134. nz = norTemp[(na[0] - nindOff) * 3 + 2];
  135. }
  136. else {
  137. var n = MeshParser.calcNormal(
  138. new iron.math.Vec4(posTemp[(va[0] - vindOff) * 3], posTemp[(va[0] - vindOff) * 3 + 1], posTemp[(va[0] - vindOff) * 3 + 2]),
  139. new iron.math.Vec4(posTemp[(va[1] - vindOff) * 3], posTemp[(va[1] - vindOff) * 3 + 1], posTemp[(va[1] - vindOff) * 3 + 2]),
  140. new iron.math.Vec4(posTemp[(va[2] - vindOff) * 3], posTemp[(va[2] - vindOff) * 3 + 1], posTemp[(va[2] - vindOff) * 3 + 2])
  141. );
  142. nx = n.x;
  143. ny = n.y;
  144. nz = n.z;
  145. }
  146. var nxabs = Math.abs(nx);
  147. var nyabs = Math.abs(ny);
  148. var nzabs = Math.abs(nz);
  149. var flip = nx + ny + nz > 0;
  150. var axis = nxabs > nyabs && nxabs > nzabs ? 0 : nyabs > nxabs && nyabs > nzabs ? 1 : 2;
  151. var axis0 = axis == 0 ? (flip ? 2 : 1) : axis == 1 ? (flip ? 0 : 2) : (flip ? 1 : 0);
  152. var axis1 = axis == 0 ? (flip ? 1 : 2) : axis == 1 ? (flip ? 2 : 0) : (flip ? 0 : 1);
  153. var loops = 0;
  154. var i = -1;
  155. while (vi > 3 && loops++ < vi) {
  156. i = (i + 1) % vi;
  157. var i1 = (i + 1) % vi;
  158. var i2 = (i + 2) % vi;
  159. var vi0 = (va[i ] - vindOff) * 3;
  160. var vi1 = (va[i1] - vindOff) * 3;
  161. var vi2 = (va[i2] - vindOff) * 3;
  162. var v0x = posTemp[vi0 + axis0];
  163. var v0y = posTemp[vi0 + axis1];
  164. var v1x = posTemp[vi1 + axis0];
  165. var v1y = posTemp[vi1 + axis1];
  166. var v2x = posTemp[vi2 + axis0];
  167. var v2y = posTemp[vi2 + axis1];
  168. var e0x = v0x - v1x; // Not an interior vertex
  169. var e0y = v0y - v1y;
  170. var e1x = v2x - v1x;
  171. var e1y = v2y - v1y;
  172. var cross = e0x * e1y - e0y * e1x;
  173. if (cross <= 0) continue;
  174. var overlap = false; // Other vertex found inside this triangle
  175. for (j in 0...vi - 3) {
  176. var j0 = (va[(i + 3 + j) % vi] - vindOff) * 3;
  177. var px = posTemp[j0 + axis0];
  178. var py = posTemp[j0 + axis1];
  179. if (MeshParser.pnpoly(v0x, v0y, v1x, v1y, v2x, v2y, px, py)) {
  180. overlap = true;
  181. break;
  182. }
  183. }
  184. if (overlap) continue;
  185. posIndices.push(va[i ]); // Found ear
  186. posIndices.push(va[i1]);
  187. posIndices.push(va[i2]);
  188. if (uvTemp.length > 0) {
  189. uvIndices.push(ua[i ]);
  190. uvIndices.push(ua[i1]);
  191. uvIndices.push(ua[i2]);
  192. }
  193. if (norTemp.length > 0) {
  194. norIndices.push(na[i ]);
  195. norIndices.push(na[i1]);
  196. norIndices.push(na[i2]);
  197. }
  198. for (j in ((i + 1) % vi)...vi - 1) { // Consume vertex
  199. va[j] = va[j + 1];
  200. ua[j] = ua[j + 1];
  201. na[j] = na[j + 1];
  202. }
  203. vi--;
  204. i--;
  205. loops = 0;
  206. }
  207. posIndices.push(va[0]); // Last one
  208. posIndices.push(va[1]);
  209. posIndices.push(va[2]);
  210. if (uvTemp.length > 0) {
  211. uvIndices.push(ua[0]);
  212. uvIndices.push(ua[1]);
  213. uvIndices.push(ua[2]);
  214. }
  215. if (norTemp.length > 0) {
  216. norIndices.push(na[0]);
  217. norIndices.push(na[1]);
  218. norIndices.push(na[2]);
  219. }
  220. }
  221. }
  222. else if (c0 == splitCode) {
  223. if (splitCode == "u".code) pos += 5; // "u"semtl
  224. pos++; // Space
  225. if (!udim) readingObject = true;
  226. name = readString();
  227. }
  228. nextLine();
  229. }
  230. if (startPos > 0) {
  231. if (splitCode != "u".code) {
  232. for (i in 0...posIndices.length) posIndices[i] -= vindOff;
  233. for (i in 0...uvIndices.length) uvIndices[i] -= tindOff;
  234. for (i in 0...norIndices.length) norIndices[i] -= nindOff;
  235. }
  236. }
  237. else {
  238. if (splitCode == "u".code) {
  239. posFirst = posTemp;
  240. norFirst = norTemp;
  241. uvFirst = uvTemp;
  242. }
  243. }
  244. vindOff += Std.int(posTemp.length / 3); // Assumes separate vertex data per object
  245. tindOff += Std.int(uvTemp.length / 2);
  246. nindOff += Std.int(norTemp.length / 3);
  247. // Pack positions to (-1, 1) range
  248. scalePos = 0.0;
  249. for (i in 0...posTemp.length) {
  250. var f = Math.abs(posTemp[i]);
  251. if (scalePos < f) scalePos = f;
  252. }
  253. var inv = 32767 * (1 / scalePos);
  254. posa = new kha.arrays.Int16Array(posIndices.length * 4);
  255. inda = new kha.arrays.Uint32Array(posIndices.length);
  256. for (i in 0...posIndices.length) {
  257. posa[i * 4 ] = Std.int( posTemp[posIndices[i] * 3 ] * inv);
  258. posa[i * 4 + 1] = Std.int(-posTemp[posIndices[i] * 3 + 2] * inv);
  259. posa[i * 4 + 2] = Std.int( posTemp[posIndices[i] * 3 + 1] * inv);
  260. inda[i] = i;
  261. }
  262. if (norIndices.length > 0) {
  263. nora = new kha.arrays.Int16Array(norIndices.length * 2);
  264. for (i in 0...posIndices.length) {
  265. nora[i * 2 ] = Std.int( norTemp[norIndices[i] * 3 ] * 32767);
  266. nora[i * 2 + 1] = Std.int(-norTemp[norIndices[i] * 3 + 2] * 32767);
  267. posa[i * 4 + 3] = Std.int( norTemp[norIndices[i] * 3 + 1] * 32767);
  268. }
  269. }
  270. else {
  271. // Calc normals
  272. nora = new kha.arrays.Int16Array(inda.length * 2);
  273. var va = new iron.math.Vec4();
  274. var vb = new iron.math.Vec4();
  275. var vc = new iron.math.Vec4();
  276. var cb = new iron.math.Vec4();
  277. var ab = new iron.math.Vec4();
  278. for (i in 0...Std.int(inda.length / 3)) {
  279. var i1 = inda[i * 3 ];
  280. var i2 = inda[i * 3 + 1];
  281. var i3 = inda[i * 3 + 2];
  282. va.set(posa[i1 * 4], posa[i1 * 4 + 1], posa[i1 * 4 + 2]);
  283. vb.set(posa[i2 * 4], posa[i2 * 4 + 1], posa[i2 * 4 + 2]);
  284. vc.set(posa[i3 * 4], posa[i3 * 4 + 1], posa[i3 * 4 + 2]);
  285. cb.subvecs(vc, vb);
  286. ab.subvecs(va, vb);
  287. cb.cross(ab);
  288. cb.normalize();
  289. nora[i1 * 2 ] = Std.int(cb.x * 32767);
  290. nora[i1 * 2 + 1] = Std.int(cb.y * 32767);
  291. posa[i1 * 4 + 3] = Std.int(cb.z * 32767);
  292. nora[i2 * 2 ] = Std.int(cb.x * 32767);
  293. nora[i2 * 2 + 1] = Std.int(cb.y * 32767);
  294. posa[i2 * 4 + 3] = Std.int(cb.z * 32767);
  295. nora[i3 * 2 ] = Std.int(cb.x * 32767);
  296. nora[i3 * 2 + 1] = Std.int(cb.y * 32767);
  297. posa[i3 * 4 + 3] = Std.int(cb.z * 32767);
  298. }
  299. }
  300. if (uvIndices.length > 0) {
  301. if (udim) {
  302. // Find number of tiles
  303. var tilesU = 1;
  304. var tilesV = 1;
  305. for (i in 0...Std.int(uvTemp.length / 2)) {
  306. while (uvTemp[i * 2 ] > tilesU) tilesU++;
  307. while (uvTemp[i * 2 + 1] > tilesV) tilesV++;
  308. }
  309. function getTile(i1: Int, i2: Int, i3: Int): Int {
  310. var u1 = uvTemp[uvIndices[i1] * 2 ];
  311. var v1 = uvTemp[uvIndices[i1] * 2 + 1];
  312. var u2 = uvTemp[uvIndices[i2] * 2 ];
  313. var v2 = uvTemp[uvIndices[i2] * 2 + 1];
  314. var u3 = uvTemp[uvIndices[i3] * 2 ];
  315. var v3 = uvTemp[uvIndices[i3] * 2 + 1];
  316. var tileU = Std.int((u1 + u2 + u3) / 3);
  317. var tileV = Std.int((v1 + v2 + v3) / 3);
  318. return tileU + tileV * tilesU;
  319. }
  320. // Amount of indices pre tile
  321. var num = new kha.arrays.Uint32Array(tilesU * tilesV);
  322. for (i in 0...Std.int(inda.length / 3)) {
  323. var tile = getTile(inda[i * 3], inda[i * 3 + 1], inda[i * 3 + 2]);
  324. num[tile] += 3;
  325. }
  326. // Split indices per tile
  327. udims = [];
  328. udimsU = tilesU;
  329. for (i in 0...tilesU * tilesV) {
  330. udims.push(new kha.arrays.Uint32Array(num[i]));
  331. num[i] = 0;
  332. }
  333. for (i in 0...Std.int(inda.length / 3)) {
  334. var i1 = inda[i * 3 ];
  335. var i2 = inda[i * 3 + 1];
  336. var i3 = inda[i * 3 + 2];
  337. var tile = getTile(i1, i2, i3);
  338. udims[tile][num[tile]++] = i1;
  339. udims[tile][num[tile]++] = i2;
  340. udims[tile][num[tile]++] = i3;
  341. }
  342. // Normalize uvs to 0-1 range
  343. var uvtiles = new kha.arrays.Int16Array(uvTemp.length);
  344. for (i in 0...Std.int(inda.length / 3)) { // TODO: merge loops
  345. var i1 = inda[i * 3 ];
  346. var i2 = inda[i * 3 + 1];
  347. var i3 = inda[i * 3 + 2];
  348. var tile = getTile(i1, i2, i3);
  349. var tileU = tile % tilesU;
  350. var tileV = Std.int(tile / tilesU);
  351. uvtiles[uvIndices[i1] * 2 ] = tileU;
  352. uvtiles[uvIndices[i1] * 2 + 1] = tileV;
  353. uvtiles[uvIndices[i2] * 2 ] = tileU;
  354. uvtiles[uvIndices[i2] * 2 + 1] = tileV;
  355. uvtiles[uvIndices[i3] * 2 ] = tileU;
  356. uvtiles[uvIndices[i3] * 2 + 1] = tileV;
  357. }
  358. for (i in 0...uvtiles.length) uvTemp[i] -= uvtiles[i];
  359. }
  360. texa = new kha.arrays.Int16Array(uvIndices.length * 2);
  361. for (i in 0...posIndices.length) {
  362. var uvx = uvTemp[uvIndices[i] * 2];
  363. if (uvx > 1.0) uvx = uvx - Std.int(uvx);
  364. var uvy = uvTemp[uvIndices[i] * 2 + 1];
  365. if (uvy > 1.0) uvy = uvy - Std.int(uvy);
  366. texa[i * 2 ] = Std.int( uvx * 32767);
  367. texa[i * 2 + 1] = Std.int((1.0 - uvy) * 32767);
  368. }
  369. }
  370. bytes = null;
  371. if (!hasNext) {
  372. posFirst = norFirst = uvFirst = null;
  373. }
  374. }
  375. function readFaceFast() {
  376. while (true) {
  377. va[vi++] = readInt() - 1;
  378. pos++; // "/"
  379. ua[ui++] = readInt() - 1;
  380. pos++; // "/"
  381. na[ni++] = readInt() - 1;
  382. if (bytes.get(pos) == "\n".code || bytes.get(pos) == "\r".code) break;
  383. pos++; // " "
  384. // Some exporters put space at the end of "f" line
  385. if (vi >= 3 && (bytes.get(pos) == "\n".code || bytes.get(pos) == "\r".code)) break;
  386. }
  387. }
  388. function readFace() {
  389. while (true) {
  390. va[vi++] = readInt() - 1;
  391. if (uvTemp.length > 0 || norTemp.length > 0) {
  392. pos++; // "/"
  393. }
  394. if (uvTemp.length > 0) {
  395. ua[ui++] = readInt() - 1;
  396. }
  397. if (norTemp.length > 0) {
  398. pos++; // "/"
  399. na[ni++] = readInt() - 1;
  400. }
  401. if (bytes.get(pos) == "\n".code || bytes.get(pos) == "\r".code) break;
  402. pos++; // " "
  403. // Some exporters put space at the end of "f" line
  404. if (vi >= 3 && (bytes.get(pos) == "\n".code || bytes.get(pos) == "\r".code)) break;
  405. }
  406. }
  407. function readFloat(): Float {
  408. var bi = 0;
  409. while (true) { // Read into buffer
  410. var c = bytes.get(pos);
  411. if (c == " ".code || c == "\n".code || c == "\r".code) break;
  412. if (c == "E".code || c == "e".code) {
  413. pos++;
  414. var first = buf[0] == "-".code ? -(buf[1] - 48) : buf[0] - 48;
  415. var exp = readInt();
  416. var dec = 1;
  417. var loop = exp > 0 ? exp : -exp;
  418. for (i in 0...loop) dec *= 10;
  419. return exp > 0 ? first * dec : first / dec;
  420. }
  421. pos++;
  422. buf[bi++] = c;
  423. }
  424. var res = 0.0; // Parse buffer into float
  425. var dot = 1;
  426. var dec = 1;
  427. var off = buf[0] == "-".code ? 1 : 0;
  428. var len = bi - 1;
  429. for (i in 0...bi - off) {
  430. var c = buf[len - i];
  431. if (c == ".".code) {
  432. dot = dec;
  433. continue;
  434. }
  435. res += (c - 48) * dec;
  436. dec *= 10;
  437. }
  438. off > 0 ? res /= -dot : res /= dot;
  439. return res;
  440. }
  441. function readInt(): Int {
  442. var bi = 0;
  443. while (true) { // Read into buffer
  444. var c = bytes.get(pos);
  445. if (c == "/".code || c == "\n".code || c == "\r".code || c == " ".code) break;
  446. pos++;
  447. buf[bi++] = c;
  448. }
  449. var res = 0; // Parse buffer into int
  450. var dec = 1;
  451. var off = buf[0] == "-".code ? 1 : 0;
  452. var len = bi - 1;
  453. for (i in 0...bi - off) {
  454. res += (buf[len - i] - 48) * dec;
  455. dec *= 10;
  456. }
  457. if (off > 0) res *= -1;
  458. return res;
  459. }
  460. function readString(): String {
  461. var begin = pos;
  462. while (true) {
  463. var c = bytes.get(pos);
  464. if (c == "\n".code || c == "\r".code || c == " ".code) break;
  465. pos++;
  466. }
  467. return bytes.getString(begin, pos - begin);
  468. }
  469. function nextLine() {
  470. while (true) {
  471. var c = bytes.get(pos++);
  472. if (c == "\n".code || pos >= bytes.length) break; // \n, \r\n
  473. }
  474. }
  475. }