ctm.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. var CTM = CTM || {};
  2. CTM.CompressionMethod = {
  3. RAW: 0x00574152,
  4. MG1: 0x0031474d,
  5. MG2: 0x0032474d
  6. };
  7. CTM.Flags = {
  8. NORMALS: 0x00000001
  9. };
  10. CTM.File = function(stream){
  11. this.load(stream);
  12. };
  13. CTM.File.prototype.load = function(stream){
  14. this.header = new CTM.FileHeader(stream);
  15. this.body = new CTM.FileBody(this.header);
  16. this.getReader().read(stream, this.body);
  17. };
  18. CTM.File.prototype.getReader = function(){
  19. var reader;
  20. switch(this.header.compressionMethod){
  21. case CTM.CompressionMethod.RAW:
  22. reader = new CTM.ReaderRAW();
  23. break;
  24. case CTM.CompressionMethod.MG1:
  25. reader = new CTM.ReaderMG1();
  26. break;
  27. case CTM.CompressionMethod.MG2:
  28. reader = new CTM.ReaderMG2();
  29. break;
  30. }
  31. return reader;
  32. };
  33. CTM.FileHeader = function(stream){
  34. stream.readInt32(); //magic "OCTM"
  35. this.fileFormat = stream.readInt32();
  36. this.compressionMethod = stream.readInt32();
  37. this.vertexCount = stream.readInt32();
  38. this.triangleCount = stream.readInt32();
  39. this.uvMapCount = stream.readInt32();
  40. this.attrMapCount = stream.readInt32();
  41. this.flags = stream.readInt32();
  42. this.comment = stream.readString();
  43. };
  44. CTM.FileHeader.prototype.hasNormals = function(){
  45. return this.flags & CTM.Flags.NORMALS;
  46. };
  47. CTM.FileBody = function(header){
  48. var i = header.triangleCount * 3,
  49. v = header.vertexCount * 3,
  50. n = header.hasNormals()? header.vertexCount * 3: 0,
  51. u = header.vertexCount * 2,
  52. a = header.vertexCount * 4,
  53. j = 0;
  54. var data = new ArrayBuffer(
  55. (i + v + n + (u * header.uvMapCount) + (a * header.attrMapCount) ) * 4);
  56. this.indices = new Uint32Array(data, 0, i);
  57. this.vertices = new Float32Array(data, i * 4, v);
  58. if ( header.hasNormals() ){
  59. this.normals = new Float32Array(data, (i + v) * 4, n);
  60. }
  61. if (header.uvMapCount){
  62. this.uvMaps = [];
  63. for (j = 0; j < header.uvMapCount; ++ j){
  64. this.uvMaps[j] = {uv: new Float32Array(data,
  65. (i + v + n + (j * u) ) * 4, u) };
  66. }
  67. }
  68. if (header.attrMapCount){
  69. this.attrMaps = [];
  70. for (j = 0; j < header.attrMapCount; ++ j){
  71. this.attrMaps[j] = {attr: new Float32Array(data,
  72. (i + v + n + (u * header.uvMapCount) + (j * a) ) * 4, a) };
  73. }
  74. }
  75. };
  76. CTM.FileMG2Header = function(stream){
  77. stream.readInt32(); //magic "MG2H"
  78. this.vertexPrecision = stream.readFloat32();
  79. this.normalPrecision = stream.readFloat32();
  80. this.lowerBoundx = stream.readFloat32();
  81. this.lowerBoundy = stream.readFloat32();
  82. this.lowerBoundz = stream.readFloat32();
  83. this.higherBoundx = stream.readFloat32();
  84. this.higherBoundy = stream.readFloat32();
  85. this.higherBoundz = stream.readFloat32();
  86. this.divx = stream.readInt32();
  87. this.divy = stream.readInt32();
  88. this.divz = stream.readInt32();
  89. this.sizex = (this.higherBoundx - this.lowerBoundx) / this.divx;
  90. this.sizey = (this.higherBoundy - this.lowerBoundy) / this.divy;
  91. this.sizez = (this.higherBoundz - this.lowerBoundz) / this.divz;
  92. };
  93. CTM.ReaderRAW = function(){
  94. };
  95. CTM.ReaderRAW.prototype.read = function(stream, body){
  96. this.readIndices(stream, body.indices);
  97. this.readVertices(stream, body.vertices);
  98. if (body.normals){
  99. this.readNormals(stream, body.normals);
  100. }
  101. if (body.uvMaps){
  102. this.readUVMaps(stream, body.uvMaps);
  103. }
  104. if (body.attrMaps){
  105. this.readAttrMaps(stream, body.attrMaps);
  106. }
  107. };
  108. CTM.ReaderRAW.prototype.readIndices = function(stream, indices){
  109. stream.readInt32(); //magic "INDX"
  110. stream.readArrayInt32(indices);
  111. };
  112. CTM.ReaderRAW.prototype.readVertices = function(stream, vertices){
  113. stream.readInt32(); //magic "VERT"
  114. stream.readArrayFloat32(vertices);
  115. };
  116. CTM.ReaderRAW.prototype.readNormals = function(stream, normals){
  117. stream.readInt32(); //magic "NORM"
  118. stream.readArrayFloat32(normals);
  119. };
  120. CTM.ReaderRAW.prototype.readUVMaps = function(stream, uvMaps){
  121. var i = 0;
  122. for (; i < uvMaps.length; ++ i){
  123. stream.readInt32(); //magic "TEXC"
  124. uvMaps[i].name = stream.readString();
  125. uvMaps[i].filename = stream.readString();
  126. stream.readArrayFloat32(uvMaps[i].uv);
  127. }
  128. };
  129. CTM.ReaderRAW.prototype.readAttrMaps = function(stream, attrMaps){
  130. var i = 0;
  131. for (; i < attrMaps.length; ++ i){
  132. stream.readInt32(); //magic "ATTR"
  133. attrMaps[i].name = stream.readString();
  134. stream.readArrayFloat32(attrMaps[i].attr);
  135. }
  136. };
  137. CTM.ReaderMG1 = function(){
  138. };
  139. CTM.ReaderMG1.prototype.read = function(stream, body){
  140. this.readIndices(stream, body.indices);
  141. this.readVertices(stream, body.vertices);
  142. if (body.normals){
  143. this.readNormals(stream, body.normals);
  144. }
  145. if (body.uvMaps){
  146. this.readUVMaps(stream, body.uvMaps);
  147. }
  148. if (body.attrMaps){
  149. this.readAttrMaps(stream, body.attrMaps);
  150. }
  151. };
  152. CTM.ReaderMG1.prototype.readIndices = function(stream, indices){
  153. stream.readInt32(); //magic "INDX"
  154. stream.readInt32(); //packed size
  155. var interleaved = new CTM.InterleavedStream(indices, 3);
  156. LZMA.decompress(stream, stream, interleaved, interleaved.data.length);
  157. CTM.restoreIndices(indices, indices.length);
  158. };
  159. CTM.ReaderMG1.prototype.readVertices = function(stream, vertices){
  160. stream.readInt32(); //magic "VERT"
  161. stream.readInt32(); //packed size
  162. var interleaved = new CTM.InterleavedStream(vertices, 1);
  163. LZMA.decompress(stream, stream, interleaved, interleaved.data.length);
  164. };
  165. CTM.ReaderMG1.prototype.readNormals = function(stream, normals){
  166. stream.readInt32(); //magic "NORM"
  167. stream.readInt32(); //packed size
  168. var interleaved = new CTM.InterleavedStream(normals, 3);
  169. LZMA.decompress(stream, stream, interleaved, interleaved.data.length);
  170. };
  171. CTM.ReaderMG1.prototype.readUVMaps = function(stream, uvMaps){
  172. var i = 0;
  173. for (; i < uvMaps.length; ++ i){
  174. stream.readInt32(); //magic "TEXC"
  175. uvMaps[i].name = stream.readString();
  176. uvMaps[i].filename = stream.readString();
  177. stream.readInt32(); //packed size
  178. var interleaved = new CTM.InterleavedStream(uvMaps[i].uv, 2);
  179. LZMA.decompress(stream, stream, interleaved, interleaved.data.length);
  180. }
  181. };
  182. CTM.ReaderMG1.prototype.readAttrMaps = function(stream, attrMaps){
  183. var i = 0;
  184. for (; i < attrMaps.length; ++ i){
  185. stream.readInt32(); //magic "ATTR"
  186. attrMaps[i].name = stream.readString();
  187. stream.readInt32(); //packed size
  188. var interleaved = new CTM.InterleavedStream(attrMaps[i].attr, 4);
  189. LZMA.decompress(stream, stream, interleaved, interleaved.data.length);
  190. }
  191. };
  192. CTM.ReaderMG2 = function(){
  193. };
  194. CTM.ReaderMG2.prototype.read = function(stream, body){
  195. this.MG2Header = new CTM.FileMG2Header(stream);
  196. this.readVertices(stream, body.vertices);
  197. this.readIndices(stream, body.indices);
  198. if (body.normals){
  199. this.readNormals(stream, body);
  200. }
  201. if (body.uvMaps){
  202. this.readUVMaps(stream, body.uvMaps);
  203. }
  204. if (body.attrMaps){
  205. this.readAttrMaps(stream, body.attrMaps);
  206. }
  207. };
  208. CTM.ReaderMG2.prototype.readVertices = function(stream, vertices){
  209. stream.readInt32(); //magic "VERT"
  210. stream.readInt32(); //packed size
  211. var interleaved = new CTM.InterleavedStream(vertices, 3);
  212. LZMA.decompress(stream, stream, interleaved, interleaved.data.length);
  213. var gridIndices = this.readGridIndices(stream, vertices);
  214. CTM.restoreVertices(vertices, this.MG2Header, gridIndices, this.MG2Header.vertexPrecision);
  215. };
  216. CTM.ReaderMG2.prototype.readGridIndices = function(stream, vertices){
  217. stream.readInt32(); //magic "GIDX"
  218. stream.readInt32(); //packed size
  219. var gridIndices = new Uint32Array(vertices.length / 3);
  220. var interleaved = new CTM.InterleavedStream(gridIndices, 1);
  221. LZMA.decompress(stream, stream, interleaved, interleaved.data.length);
  222. CTM.restoreGridIndices(gridIndices, gridIndices.length);
  223. return gridIndices;
  224. };
  225. CTM.ReaderMG2.prototype.readIndices = function(stream, indices){
  226. stream.readInt32(); //magic "INDX"
  227. stream.readInt32(); //packed size
  228. var interleaved = new CTM.InterleavedStream(indices, 3);
  229. LZMA.decompress(stream, stream, interleaved, interleaved.data.length);
  230. CTM.restoreIndices(indices, indices.length);
  231. };
  232. CTM.ReaderMG2.prototype.readNormals = function(stream, body){
  233. stream.readInt32(); //magic "NORM"
  234. stream.readInt32(); //packed size
  235. var interleaved = new CTM.InterleavedStream(body.normals, 3);
  236. LZMA.decompress(stream, stream, interleaved, interleaved.data.length);
  237. var smooth = CTM.calcSmoothNormals(body.indices, body.vertices);
  238. CTM.restoreNormals(body.normals, smooth, this.MG2Header.normalPrecision);
  239. };
  240. CTM.ReaderMG2.prototype.readUVMaps = function(stream, uvMaps){
  241. var i = 0;
  242. for (; i < uvMaps.length; ++ i){
  243. stream.readInt32(); //magic "TEXC"
  244. uvMaps[i].name = stream.readString();
  245. uvMaps[i].filename = stream.readString();
  246. var precision = stream.readFloat32();
  247. stream.readInt32(); //packed size
  248. var interleaved = new CTM.InterleavedStream(uvMaps[i].uv, 2);
  249. LZMA.decompress(stream, stream, interleaved, interleaved.data.length);
  250. CTM.restoreMap(uvMaps[i].uv, 2, precision);
  251. }
  252. };
  253. CTM.ReaderMG2.prototype.readAttrMaps = function(stream, attrMaps){
  254. var i = 0;
  255. for (; i < attrMaps.length; ++ i){
  256. stream.readInt32(); //magic "ATTR"
  257. attrMaps[i].name = stream.readString();
  258. var precision = stream.readFloat32();
  259. stream.readInt32(); //packed size
  260. var interleaved = new CTM.InterleavedStream(attrMaps[i].attr, 4);
  261. LZMA.decompress(stream, stream, interleaved, interleaved.data.length);
  262. CTM.restoreMap(attrMaps[i].attr, 4, precision);
  263. }
  264. };
  265. CTM.restoreIndices = function(indices, len){
  266. var i = 3;
  267. if (len > 0){
  268. indices[2] += indices[0];
  269. }
  270. for (; i < len; i += 3){
  271. indices[i] += indices[i - 3];
  272. if (indices[i] === indices[i - 3]){
  273. indices[i + 1] += indices[i - 2];
  274. }else{
  275. indices[i + 1] += indices[i];
  276. }
  277. indices[i + 2] += indices[i];
  278. }
  279. };
  280. CTM.restoreGridIndices = function(gridIndices, len){
  281. var i = 1;
  282. for (; i < len; ++ i){
  283. gridIndices[i] += gridIndices[i - 1];
  284. }
  285. };
  286. CTM.restoreVertices = function(vertices, grid, gridIndices, precision){
  287. var gridIdx, delta, x, y, z,
  288. intVertices = new Uint32Array(vertices.buffer, vertices.byteOffset, vertices.length),
  289. ydiv = grid.divx, zdiv = ydiv * grid.divy,
  290. prevGridIdx = 0x7fffffff, prevDelta = 0,
  291. i = 0, j = 0, len = gridIndices.length;
  292. for (; i < len; j += 3){
  293. x = gridIdx = gridIndices[i ++];
  294. z = ~~(x / zdiv);
  295. x -= ~~(z * zdiv);
  296. y = ~~(x / ydiv);
  297. x -= ~~(y * ydiv);
  298. delta = intVertices[j];
  299. if (gridIdx === prevGridIdx){
  300. delta += prevDelta;
  301. }
  302. vertices[j] = grid.lowerBoundx +
  303. x * grid.sizex + precision * delta;
  304. vertices[j + 1] = grid.lowerBoundy +
  305. y * grid.sizey + precision * intVertices[j + 1];
  306. vertices[j + 2] = grid.lowerBoundz +
  307. z * grid.sizez + precision * intVertices[j + 2];
  308. prevGridIdx = gridIdx;
  309. prevDelta = delta;
  310. }
  311. };
  312. CTM.restoreNormals = function(normals, smooth, precision){
  313. var ro, phi, theta, sinPhi,
  314. nx, ny, nz, by, bz, len,
  315. intNormals = new Uint32Array(normals.buffer, normals.byteOffset, normals.length),
  316. i = 0, k = normals.length,
  317. PI_DIV_2 = 3.141592653589793238462643 * 0.5;
  318. for (; i < k; i += 3){
  319. ro = intNormals[i] * precision;
  320. phi = intNormals[i + 1];
  321. if (phi === 0){
  322. normals[i] = smooth[i] * ro;
  323. normals[i + 1] = smooth[i + 1] * ro;
  324. normals[i + 2] = smooth[i + 2] * ro;
  325. }else{
  326. if (phi <= 4){
  327. theta = (intNormals[i + 2] - 2) * PI_DIV_2;
  328. }else{
  329. theta = ( (intNormals[i + 2] * 4 / phi) - 2) * PI_DIV_2;
  330. }
  331. phi *= precision * PI_DIV_2;
  332. sinPhi = ro * Math.sin(phi);
  333. nx = sinPhi * Math.cos(theta);
  334. ny = sinPhi * Math.sin(theta);
  335. nz = ro * Math.cos(phi);
  336. bz = smooth[i + 1];
  337. by = smooth[i] - smooth[i + 2];
  338. len = Math.sqrt(2 * bz * bz + by * by);
  339. if (len > 1e-20){
  340. by /= len;
  341. bz /= len;
  342. }
  343. normals[i] = smooth[i] * nz +
  344. (smooth[i + 1] * bz - smooth[i + 2] * by) * ny - bz * nx;
  345. normals[i + 1] = smooth[i + 1] * nz -
  346. (smooth[i + 2] + smooth[i] ) * bz * ny + by * nx;
  347. normals[i + 2] = smooth[i + 2] * nz +
  348. (smooth[i] * by + smooth[i + 1] * bz) * ny + bz * nx;
  349. }
  350. }
  351. };
  352. CTM.restoreMap = function(map, count, precision){
  353. var delta, value,
  354. intMap = new Uint32Array(map.buffer, map.byteOffset, map.length),
  355. i = 0, j, len = map.length;
  356. for (; i < count; ++ i){
  357. delta = 0;
  358. for (j = i; j < len; j += count){
  359. value = intMap[j];
  360. delta += value & 1? -( (value + 1) >> 1): value >> 1;
  361. map[j] = delta * precision;
  362. }
  363. }
  364. };
  365. CTM.calcSmoothNormals = function(indices, vertices){
  366. var smooth = new Float32Array(vertices.length),
  367. indx, indy, indz, nx, ny, nz,
  368. v1x, v1y, v1z, v2x, v2y, v2z, len,
  369. i, k;
  370. for (i = 0, k = indices.length; i < k;){
  371. indx = indices[i ++] * 3;
  372. indy = indices[i ++] * 3;
  373. indz = indices[i ++] * 3;
  374. v1x = vertices[indy] - vertices[indx];
  375. v2x = vertices[indz] - vertices[indx];
  376. v1y = vertices[indy + 1] - vertices[indx + 1];
  377. v2y = vertices[indz + 1] - vertices[indx + 1];
  378. v1z = vertices[indy + 2] - vertices[indx + 2];
  379. v2z = vertices[indz + 2] - vertices[indx + 2];
  380. nx = v1y * v2z - v1z * v2y;
  381. ny = v1z * v2x - v1x * v2z;
  382. nz = v1x * v2y - v1y * v2x;
  383. len = Math.sqrt(nx * nx + ny * ny + nz * nz);
  384. if (len > 1e-10){
  385. nx /= len;
  386. ny /= len;
  387. nz /= len;
  388. }
  389. smooth[indx] += nx;
  390. smooth[indx + 1] += ny;
  391. smooth[indx + 2] += nz;
  392. smooth[indy] += nx;
  393. smooth[indy + 1] += ny;
  394. smooth[indy + 2] += nz;
  395. smooth[indz] += nx;
  396. smooth[indz + 1] += ny;
  397. smooth[indz + 2] += nz;
  398. }
  399. for (i = 0, k = smooth.length; i < k; i += 3){
  400. len = Math.sqrt(smooth[i] * smooth[i] +
  401. smooth[i + 1] * smooth[i + 1] +
  402. smooth[i + 2] * smooth[i + 2]);
  403. if(len > 1e-10){
  404. smooth[i] /= len;
  405. smooth[i + 1] /= len;
  406. smooth[i + 2] /= len;
  407. }
  408. }
  409. return smooth;
  410. };
  411. CTM.isLittleEndian = (function(){
  412. var buffer = new ArrayBuffer(2),
  413. bytes = new Uint8Array(buffer),
  414. ints = new Uint16Array(buffer);
  415. bytes[0] = 1;
  416. return ints[0] === 1;
  417. }());
  418. CTM.InterleavedStream = function(data, count){
  419. this.data = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
  420. this.offset = CTM.isLittleEndian? 3: 0;
  421. this.count = count * 4;
  422. this.len = this.data.length;
  423. };
  424. CTM.InterleavedStream.prototype.writeByte = function(value){
  425. this.data[this.offset] = value;
  426. this.offset += this.count;
  427. if (this.offset >= this.len){
  428. this.offset -= this.len - 4;
  429. if (this.offset >= this.count){
  430. this.offset -= this.count + (CTM.isLittleEndian? 1: -1);
  431. }
  432. }
  433. };
  434. CTM.Stream = function(data){
  435. this.data = data;
  436. this.offset = 0;
  437. };
  438. CTM.Stream.prototype.TWO_POW_MINUS23 = Math.pow(2, -23);
  439. CTM.Stream.prototype.TWO_POW_MINUS126 = Math.pow(2, -126);
  440. CTM.Stream.prototype.readByte = function(){
  441. return this.data.charCodeAt(this.offset ++) & 0xff;
  442. };
  443. CTM.Stream.prototype.readInt32 = function(){
  444. var i = this.readByte();
  445. i |= this.readByte() << 8;
  446. i |= this.readByte() << 16;
  447. return i | (this.readByte() << 24);
  448. };
  449. CTM.Stream.prototype.readFloat32 = function(){
  450. var m = this.readByte();
  451. m += this.readByte() << 8;
  452. var b1 = this.readByte();
  453. var b2 = this.readByte();
  454. m += (b1 & 0x7f) << 16;
  455. var e = ( (b2 & 0x7f) << 1) | ( (b1 & 0x80) >>> 7);
  456. var s = b2 & 0x80? -1: 1;
  457. if (e === 255){
  458. return m !== 0? NaN: s * Infinity;
  459. }
  460. if (e > 0){
  461. return s * (1 + (m * this.TWO_POW_MINUS23) ) * Math.pow(2, e - 127);
  462. }
  463. if (m !== 0){
  464. return s * m * this.TWO_POW_MINUS126;
  465. }
  466. return s * 0;
  467. };
  468. CTM.Stream.prototype.readString = function(){
  469. var len = this.readInt32();
  470. this.offset += len;
  471. return this.data.substr(this.offset - len, len);
  472. };
  473. CTM.Stream.prototype.readArrayInt32 = function(array){
  474. var i = 0, len = array.length;
  475. while(i < len){
  476. array[i ++] = this.readInt32();
  477. }
  478. return array;
  479. };
  480. CTM.Stream.prototype.readArrayFloat32 = function(array){
  481. var i = 0, len = array.length;
  482. while(i < len){
  483. array[i ++] = this.readFloat32();
  484. }
  485. return array;
  486. };