m3d_vox.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  1. /*
  2. * Model 3D voxel-only reader / writer
  3. * https://bztsrc.gitlab.io/model3d
  4. *
  5. * Copyright (C) 2023 bzt (bztsrc@gitlab), MIT license
  6. *
  7. * Permission is hereby granted, free of charge, to any person obtaining a copy
  8. * of this software and associated documentation files (the "Software"), to
  9. * deal in the Software without restriction, including without limitation the
  10. * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  11. * sell copies of the Software, and to permit persons to whom the Software is
  12. * furnished to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be included in
  15. * all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY
  20. * DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  21. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
  22. * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  23. */
  24. /*
  25. * This is the same code as the src/formats/m3d.c Goxel plugin, except I have removed
  26. * all the Goxel API, so this version can be, and is MIT licensed (Goxel is GPL). It
  27. * might be a little more readable as well, good start to implement your own voxel-only
  28. * M3D importer / exporter. Note that this only handles static models; rigged and animated
  29. * voxel models are only supported by the M3D SDK. The exporter is also limited to 2^16-2
  30. * different voxel types, however the importer supports up to 2^32-2, just like the SDK.
  31. */
  32. #include <stdint.h>
  33. #include <stdlib.h>
  34. #include <stdio.h>
  35. #include <string.h>
  36. #include <errno.h>
  37. /* special values in voxlayers[].data */
  38. #define VOX_AIR -1U /* this means that voxel is empty (air), so keep original world's voxel on load */
  39. #define VOX_CLEAR -2U /* this means that original world's voxel must be set to empty (air) */
  40. /* -------- abstract structure to store voxel types and layers. Replace this with your own implementation -------- */
  41. typedef struct {
  42. uint32_t diffuse; /* diffuse color (packed RGBA) */
  43. uint32_t ambient; /* ambient color (packed RGBA) */
  44. uint32_t specular; /* specular color (packed RGBA) */
  45. uint32_t emission; /* emission color (packed RGBA) */
  46. uint32_t transmis; /* transmission color (packed RGBA) */
  47. float emisexp; /* emission exponent */
  48. float metallic; /* metallic factor */
  49. float roughness; /* roughness factor */
  50. char *name; /* material name (optional) */
  51. } m3d_voxel_material_t;
  52. /* array to store materials */
  53. m3d_voxel_material_t *voxmaterials = NULL;
  54. int numvoxmaterials;
  55. typedef struct {
  56. uint32_t color; /* voxel color if no material is assigned */
  57. uint32_t materialid; /* material index, -1U if there's none */
  58. char *name; /* name of the voxel type (optional) */
  59. } m3d_voxel_type_t;
  60. /* array to store voxel types */
  61. m3d_voxel_type_t *voxtypes = NULL;
  62. int numvoxtypes;
  63. typedef struct {
  64. int x, y, z; /* position of the layer */
  65. int w, h, d; /* dimensions */
  66. uint32_t *data; /* layer data, voxtypes indeces */
  67. } m3d_voxel_layer_t;
  68. /* array to store voxel data */
  69. m3d_voxel_layer_t *voxlayers = NULL;
  70. int numvoxlayers;
  71. /* -------- end of abstract structures -------- */
  72. // get stbi_zlib_compress(), you can use the official zlib library as well
  73. #define STB_IMAGE_WRITE_IMPLEMENTATION
  74. #include "stb_image_write.h"
  75. // get stbi_zlib_decompress(), you can use the official zlib library as well
  76. #define STB_IMAGE_IMPLEMENTATION
  77. #define STBI_ONLY_PNG
  78. #define STBI_NO_STDIO
  79. #define STBI_NO_GIF
  80. #include "stb_image.h"
  81. // material types
  82. enum {
  83. m3dp_Kd = 0, /* scalar display properties */
  84. m3dp_Ka,
  85. m3dp_Ks,
  86. m3dp_Ns,
  87. m3dp_Ke,
  88. m3dp_Tf,
  89. m3dp_Km,
  90. m3dp_d,
  91. m3dp_il,
  92. m3dp_Pr = 64, /* scalar physical properties */
  93. m3dp_Pm,
  94. m3dp_Ps,
  95. m3dp_Ni,
  96. m3dp_Nt,
  97. m3dp_map_Kd = 128, /* textured display map properties */
  98. m3dp_map_Ka,
  99. m3dp_map_Ks,
  100. m3dp_map_Ns,
  101. m3dp_map_Ke,
  102. m3dp_map_Tf,
  103. m3dp_map_Km, /* bump map */
  104. m3dp_map_D,
  105. m3dp_map_N, /* normal map */
  106. m3dp_map_Pr = 192, /* textured physical map properties */
  107. m3dp_map_Pm,
  108. m3dp_map_Ps,
  109. m3dp_map_Ni,
  110. m3dp_map_Nt
  111. };
  112. /*** m3d reader ***/
  113. /**
  114. * Import a Model 3D binary into the abstract structs
  115. */
  116. static int import_as_m3d(const char *path)
  117. {
  118. FILE *file;
  119. long int size;
  120. unsigned char *data, *buf, *s, *e, *chunk;
  121. char *strtbl, *n;
  122. int i, j, k, l, ci_s, si_s, sk_s, vd_s, vp_s;
  123. int sx, sy, sz, px, py, pz;
  124. uint32_t *cmap = NULL, C;
  125. // read in file data
  126. file = fopen(path, "rb");
  127. if(!file) {
  128. ferr: fprintf(stderr, "Cannot load from %s: %s", path, strerror(errno));
  129. return -1;
  130. }
  131. fseek(file, 0, SEEK_END);
  132. size = (long int)ftell(file);
  133. if(size < 8) { fclose(file); goto ferr; }
  134. fseek(file, 0, SEEK_SET);
  135. data = (unsigned char*)malloc(size);
  136. if(!data) {
  137. fclose(file);
  138. merr: fprintf(stderr, "Memory allocation error %s: %ld bytes", path, size);
  139. return -1;
  140. }
  141. if(fread(data, 1, size, file) != size) {
  142. fclose(file);
  143. free(data);
  144. goto ferr;
  145. }
  146. fclose(file);
  147. // check magic and uncompress
  148. if(memcmp(data, "3DMO", 4)) {
  149. free(data);
  150. fprintf(stderr, "Bad file format %s", path);
  151. return -1;
  152. }
  153. // skip over file header
  154. s = data + 8;
  155. e = data + size;
  156. size -= 8;
  157. // skip over optional preview chunk if it exists
  158. if(!memcmp(s, "PRVW", 4)) {
  159. size -= *((uint32_t*)(s + 4));
  160. s += *((uint32_t*)(s + 4));
  161. }
  162. // check if it's a header chunk, if not, then file is stream compressed
  163. if(memcmp(s, "HEAD", 4)) {
  164. buf = (unsigned char *)stbi_zlib_decode_malloc_guesssize_headerflag(
  165. (const char*)s, size, 4096, &l, 1);
  166. free(data);
  167. if(!buf || !l || memcmp(buf, "HEAD", 4)) {
  168. if(buf) free(buf);
  169. fprintf(stderr, "Uncompression error %s", path);
  170. return -1;
  171. }
  172. data = s = buf;
  173. e = data + l;
  174. }
  175. // decode item sizes from model header
  176. strtbl = (char*)s + 16; // string table
  177. si_s = 1 << ((s[12] >> 4) & 3); // string index (offset) size
  178. ci_s = 1 << ((s[12] >> 6) & 3); // color index size
  179. sk_s = 1 << ((s[13] >> 6) & 3); // skin index size
  180. vd_s = 1 << ((s[14] >> 6) & 3); // voxel data size
  181. vp_s = 1 << ((s[15] >> 0) & 3); // voxel position size
  182. // some are optional. To simplify calculations, use 0 for these
  183. if(ci_s == 8) ci_s = 0;
  184. if(sk_s == 8) sk_s = 0;
  185. if(si_s == 8) si_s = 0;
  186. // voxel type and position size must be specified with this importer
  187. if(vd_s == 8 || vp_s == 8) {
  188. free(data);
  189. fprintf(stderr, "Bad model header %s", path);
  190. return -1;
  191. }
  192. // just to be on the safe side, free buffers in case they are already allocated (they shouldn't be)
  193. if(voxtypes) { free(voxtypes); voxtypes = NULL; }
  194. if(voxlayers) { free(voxlayers); voxlayers = NULL; }
  195. if(voxmaterials) { free(voxmaterials); voxmaterials = NULL; }
  196. numvoxtypes = numvoxlayers = numvoxmaterials = 0;
  197. // iterate on chunks, simply skip those we don't care about
  198. for(chunk = s; chunk < e && memcmp(chunk, "OMD3", 4);) {
  199. // decode chunk header and adjust to the next chunk
  200. s = chunk;
  201. l = *((uint32_t*)(chunk + 4));
  202. chunk += l;
  203. if(l < 8 || chunk >= e) break;
  204. l -= 8;
  205. // if it's a color map (not saved, but m3d files might have it)
  206. if(!memcmp(s, "CMAP", 4)) { cmap = (uint32_t*)(s + 8); } else
  207. // material chunk
  208. if(!memcmp(s, "MTRL", 4)) {
  209. // allocate memory for a new material
  210. voxmaterials = (m3d_voxel_material_t*)realloc(voxmaterials, (numvoxmaterials + 1) * sizeof(m3d_voxel_material_t));
  211. if(!voxmaterials) goto merr;
  212. memset(&voxmaterials[numvoxmaterials], 0, sizeof(m3d_voxel_material_t));
  213. // get material's name
  214. s += 8; n = NULL;
  215. switch(si_s) {
  216. case 1: n = strtbl + s[0]; break;
  217. case 2: n = strtbl + *((uint16_t*)s); break;
  218. case 4: n = strtbl + *((uint32_t*)s); break;
  219. }
  220. s += si_s;
  221. if(n && *n)
  222. voxmaterials[numvoxmaterials].name = strdup(n);
  223. // parse material properties
  224. while(s < chunk) {
  225. switch(s[0]) {
  226. /* emission exponent, float */
  227. case m3dp_Ns:
  228. memcpy(&voxmaterials[numvoxmaterials].emisexp, s + 1, 4);
  229. s += 5;
  230. break;
  231. /* metallic, float */
  232. case m3dp_Pm:
  233. memcpy(&voxmaterials[numvoxmaterials].metallic, s + 1, 4);
  234. s += 5;
  235. break;
  236. /* roughness, float */
  237. case m3dp_Pr:
  238. memcpy(&voxmaterials[numvoxmaterials].roughness, s + 1, 4);
  239. s += 5;
  240. break;
  241. /* get various colors */
  242. case m3dp_Kd: case m3dp_Ka: case m3dp_Ks: case m3dp_Ke: case m3dp_Tf:
  243. /* decode color into a packed RGBA pixel */
  244. j = *s++; C = 0;
  245. switch(ci_s) {
  246. case 1: C = cmap ? cmap[s[0]] : 0; s++; break;
  247. case 2: C = cmap ? cmap[*((uint16_t*)s)] : 0; s += 2; break;
  248. case 4: C = *((uint32_t*)s); s += 4; break;
  249. }
  250. /* store the decoded color in the appropriate material field */
  251. switch(j) {
  252. case m3dp_Kd: voxmaterials[numvoxmaterials].diffuse = C; break;
  253. case m3dp_Ka: voxmaterials[numvoxmaterials].ambient = C; break;
  254. case m3dp_Ks: voxmaterials[numvoxmaterials].specular = C; break;
  255. case m3dp_Ke: voxmaterials[numvoxmaterials].emission = C; break;
  256. case m3dp_Tf: voxmaterials[numvoxmaterials].transmis = C; break;
  257. }
  258. break;
  259. /* skip over properties we don't care about, most notably texture identifiers */
  260. case m3dp_il: s += 2; break;
  261. default: s += 1 + (s[0] >= 128 ? si_s : 4); break;
  262. }
  263. }
  264. numvoxmaterials++;
  265. } else
  266. // voxel types
  267. if(!memcmp(s, "VOXT", 4)) {
  268. s += 8;
  269. // this will get an upper bound of number of types
  270. numvoxtypes = l / (ci_s + si_s + 3 + sk_s);
  271. voxtypes = (m3d_voxel_type_t*)realloc(voxtypes, numvoxtypes * sizeof(m3d_voxel_type_t));
  272. if(!voxtypes) goto merr;
  273. memset(voxtypes, 0, numvoxtypes * sizeof(m3d_voxel_material_t));
  274. // get voxel types
  275. for(i = 0; i < numvoxtypes && s < chunk; i++) {
  276. // diffuse color (when there's no material, each voxel might use a different voxel color)
  277. C = 0;
  278. switch(ci_s) {
  279. case 1: C = cmap ? cmap[s[0]] : 0; s++; break;
  280. case 2: C = cmap ? cmap[*((uint16_t*)s)] : 0; s += 2; break;
  281. case 4: C = *((uint32_t*)s); s += 4; break;
  282. }
  283. voxtypes[i].color = C;
  284. // voxel type's name
  285. n = NULL;
  286. switch(si_s) {
  287. case 1: n = strtbl + s[0]; break;
  288. case 2: n = strtbl + *((uint16_t*)s); break;
  289. case 4: n = strtbl + *((uint32_t*)s); break;
  290. }
  291. s += si_s;
  292. // material index
  293. voxtypes[i].materialid = -1U;
  294. if(n && *n) {
  295. voxtypes[i].name = strdup(n);
  296. // if there's a material with the same name as the voxel type then set the materialid
  297. for(j = 0; j < numvoxmaterials && strcmp(voxmaterials[j].name, n); j++);
  298. if(j < numvoxmaterials)
  299. voxtypes[i].materialid = j;
  300. }
  301. // skip over other additional attributes (rigging and such)
  302. s += 2;
  303. j = *s;
  304. s += 1 + sk_s + j * (2 + si_s);
  305. }
  306. // if we actually have less types than the upper bound, free the unused memory
  307. if(i != numvoxtypes) {
  308. numvoxtypes = i;
  309. voxtypes = (m3d_voxel_type_t *)realloc(voxtypes, numvoxtypes * sizeof(m3d_voxel_type_t));
  310. if(!voxtypes) goto merr;
  311. }
  312. } else
  313. // voxel data
  314. if(!memcmp(s, "VOXD", 4)) {
  315. voxlayers = (m3d_voxel_layer_t*)realloc(voxlayers, (numvoxlayers + 1) * sizeof(m3d_voxel_layer_t));
  316. if(!voxlayers) goto merr;
  317. memset(&voxlayers[numvoxlayers], 0, sizeof(m3d_voxel_layer_t));
  318. // layer name
  319. s += 8; n = NULL;
  320. switch(si_s) {
  321. case 1: n = strtbl + s[0]; break;
  322. case 2: n = strtbl + *((uint16_t*)s); break;
  323. case 4: n = strtbl + *((uint32_t*)s); break;
  324. }
  325. s += si_s;
  326. if(n && *n)
  327. voxlayers[numvoxlayers].name = strdup(n);
  328. // get layer dimensions
  329. px = py = pz = sx = sy = sz = 0;
  330. switch(vd_s) {
  331. case 1:
  332. px = (int8_t)s[0]; py = (int8_t)s[1]; pz = (int8_t)s[2];
  333. sx = (int8_t)s[3]; sy = (int8_t)s[4]; sz = (int8_t)s[5];
  334. s += 6;
  335. break;
  336. case 2:
  337. px = *((int16_t*)(s+0)); py = *((int16_t*)(s+2)); pz = *((int16_t*)(s+4));
  338. sx = *((int16_t*)(s+6)); sy = *((int16_t*)(s+8)); sz = *((int16_t*)(s+10));
  339. s += 12;
  340. break;
  341. case 4:
  342. px = *((int32_t*)(s+0)); py = *((int32_t*)(s+4)); pz = *((int32_t*)(s+8));
  343. sx = *((int32_t*)(s+12)); sy = *((int32_t*)(s+16)); sz = *((int32_t*)(s+20));
  344. s += 24;
  345. break;
  346. }
  347. voxlayers[numvoxlayers].x = px;
  348. voxlayers[numvoxlayers].y = py;
  349. voxlayers[numvoxlayers].z = pz;
  350. voxlayers[numvoxlayers].w = sx;
  351. voxlayers[numvoxlayers].h = py;
  352. voxlayers[numvoxlayers].d = sz;
  353. voxlayers[numvoxlayers].data = (uint32_t *)malloc(sx * sy * sz * sizeof(uint32_t));
  354. if(!voxlayers[numvoxlayers].data) goto merr;
  355. // decompress RLE layer data
  356. for(s += 2, i = 0; s < chunk && i < sx * sy * sz;) {
  357. // get RLE packet length
  358. l = ((*s++) & 0x7F) + 1;
  359. // check packet's type
  360. if(s[-1] & 0x80) {
  361. // get voxel type index
  362. switch(vp_s) {
  363. case 1: k = *s++; break;
  364. case 2: k = *((uint16_t*)s); s += 2; break;
  365. case 4: k = *((uint32_t*)s); s += 4; break;
  366. default: k = -1; break;
  367. }
  368. // repeat it l times
  369. for(j = 0; j < l; j++, i++)
  370. voxlayers[numvoxlayers].data[i] = k;
  371. } else {
  372. // each index in the l sized packet is a different voxel type
  373. for(j = 0; j < l; j++, i++) {
  374. // get voxel type index
  375. switch(vp_s) {
  376. case 1: k = *s++; break;
  377. case 2: k = *((uint16_t*)s); s += 2; break;
  378. case 4: k = *((uint32_t*)s); s += 4; break;
  379. default: k = -1; break;
  380. }
  381. voxlayers[numvoxlayers].data[i] = k;
  382. }
  383. }
  384. }
  385. numvoxlayers++;
  386. }
  387. }
  388. free(data);
  389. return 0;
  390. }
  391. /*** m3d writer ***/
  392. /* string table stuff. */
  393. typedef struct {
  394. char *data; // concatenated, escape-safe string buffer with zero terminated UTF-8 strings
  395. int len, num; // string table length and number of strings in table
  396. int *str; // string table offsets
  397. } m3d_strtable_t;
  398. /* remove unsafe characters from identifiers (tab, newline, directory separators etc.) */
  399. /* for the morelines argument, see M3D SDK documentation. */
  400. char *m3d_safestr(const char *in, int morelines)
  401. {
  402. char *out, *o, *i = (char*)in;
  403. int l;
  404. if(!in || !*in) {
  405. out = (char*)malloc(1);
  406. if(!out) return NULL;
  407. out[0] = 0;
  408. } else {
  409. for(o = (char*)in, l = 0; *o && ((morelines & 1) || (*o != '\r' && *o != '\n')) && l < 256; o++, l++);
  410. out = o = (char*)malloc(l+1);
  411. if(!out) return NULL;
  412. while(*i == ' ' || *i == '\t' || *i == '\r' || (morelines && *i == '\n')) i++;
  413. for(; *i && (morelines || (*i != '\r' && *i != '\n')); i++) {
  414. if(*i == '\r') continue;
  415. if(*i == '\n') {
  416. if(morelines >= 3 && o > out && *(o-1) == '\n') break;
  417. if(i > in && *(i-1) == '\n') continue;
  418. if(morelines & 1) {
  419. if(morelines == 1) *o++ = '\r';
  420. *o++ = '\n';
  421. } else
  422. break;
  423. } else
  424. if(*i == ' ' || *i == '\t') {
  425. *o++ = morelines? ' ' : '_';
  426. } else
  427. *o++ = !morelines && (*i == '/' || *i == '\\') ? '_' : *i;
  428. }
  429. for(; o > out && (*(o-1) == ' ' || *(o-1) == '\t' || *(o-1) == '\r' || *(o-1) == '\n'); o--);
  430. *o = 0;
  431. out = (char*)realloc(out, (uintptr_t)o - (uintptr_t)out + 1);
  432. }
  433. return out;
  434. }
  435. /* add a string to string table (with de-duplication) */
  436. void m3d_addstr(m3d_strtable_t *tbl, const char *str)
  437. {
  438. int i, l;
  439. char *safe = m3d_safestr(str, 0);
  440. if(!safe) return;
  441. // first 4 strings are mandatory: title, license, author, comment (in this order. If not specified, must be saved as 4 zeros)
  442. if(tbl->num > 3) {
  443. for(i = 4; i < tbl->num && tbl->data && strcmp(tbl->data + tbl->str[i], safe); i++);
  444. if(tbl->data && i < tbl->num) { free(safe); return; }
  445. }
  446. l = strlen(safe) + 1;
  447. tbl->data = (char*)realloc(tbl->data, tbl->len + l);
  448. if(!tbl->data) { free(safe); return; }
  449. i = tbl->num++;
  450. tbl->str = (int*)realloc(tbl->str, tbl->num * sizeof(int));
  451. if(!tbl->str) { free(safe); return; }
  452. tbl->str[i] = tbl->len;
  453. memcpy(tbl->data + tbl->len, safe, l);
  454. tbl->len += l;
  455. free(safe);
  456. }
  457. /* return string table offset for a string */
  458. int m3d_getstr(m3d_strtable_t *tbl, const char *str)
  459. {
  460. int i;
  461. char *safe;
  462. if(!tbl || !tbl->data || !tbl->str || !str || !*str) return -1;
  463. safe = m3d_safestr(str, 0);
  464. if(!safe) return -1;
  465. for(i = 0; i < tbl->num && strcmp(tbl->data + tbl->str[i], safe); i++);
  466. free(safe);
  467. return i < tbl->num ? tbl->str[i] : -1;
  468. }
  469. /* free string table */
  470. void m3d_freestr(m3d_strtable_t *tbl)
  471. {
  472. if(tbl->data) free(tbl->data);
  473. if(tbl->str) free(tbl->str);
  474. }
  475. /**
  476. * Export the abstract structs into a Model 3D binary
  477. */
  478. static int export_as_m3d(const char *path)
  479. {
  480. FILE *out;
  481. uint8_t *data = NULL, *comp = NULL, *chunk, *ptr;
  482. uint16_t *blk;
  483. uint32_t *intp, *sizp;
  484. m3d_strtable_t str;
  485. int size = 0, i, j, k, l, m, n, o;
  486. // the M3D format supports 2^32-2 voxel types, and the importer works with that many, however this exporter
  487. // unconditionally saves 16 bit voxel data, therefore limited to 65534 different voxel types (reasonable).
  488. // 2 is substracted because of VOX_AIR (0xffff) and VOX_CLEAR (0xfffe)
  489. if(numvoxtypes >= 65534) {
  490. fprintf(stderr, "Cannot save, too many palette entries (max 65534) %s: %d", path, numvoxtypes);
  491. return -1;
  492. }
  493. // write out file
  494. out = fopen(path, "wb");
  495. if (!out) {
  496. fprintf(stderr, "Cannot save to %s: %s", path, strerror(errno));
  497. return -1;
  498. }
  499. fwrite("3DMO", 4, 1, out);
  500. // construct the string table
  501. memset(&str, 0, sizeof(m3d_strtable_t));
  502. // these are not stored by goxel, but we must save them
  503. m3d_addstr(&str, ""); // title, model name
  504. m3d_addstr(&str, ""); // license
  505. m3d_addstr(&str, ""); // author
  506. m3d_addstr(&str, ""); // comment
  507. // iterate through materials and add their names to string table
  508. for(i = 0; i < numvoxmaterials; i++)
  509. m3d_addstr(&str, voxmaterials[i].name);
  510. // iterate through voxel types and add their names to string table (table is de-duplicated in case they match material name)
  511. for(i = 0; i < numvoxtypes; i++)
  512. m3d_addstr(&str, voxtypes[i].name);
  513. // iterate through layers and add their names to string table
  514. for(i = 0; i < numvoxlayers; i++)
  515. m3d_addstr(&str, voxlayers[i].name);
  516. size = 16 + str.len + /* header size */
  517. 8 + numvoxtypes * 11 + /* voxel type chunk size */
  518. numvoxmaterials * 64; /* total size of material chunks */
  519. /* voxel data chunk reallocated and size calculated dynamically */
  520. data = calloc(size, 1);
  521. if(!data) {
  522. merr: fprintf(stderr, "Memory allocation error %s: %ld bytes", path, size);
  523. return -1;
  524. }
  525. // construct model header
  526. intp = (uint32_t*)(data + 0);
  527. memcpy(data + 0, "HEAD", 4); // chunk magic
  528. intp[1] = 16 + str.len; // chunk size
  529. intp[2] = 0x3F800000; // scale (1 SI meters)
  530. intp[3] = 0x018FCFA0; // flags (all index sizes 32 bits, except voxel data, that's 16 bits)
  531. chunk = data + 16 + str.len;
  532. if(str.data)
  533. memcpy(data + 16, str.data, str.len);
  534. // materials
  535. for(i = 0; i < numvoxmaterials; i++) {
  536. intp = (uint32_t*)(chunk);
  537. memcpy(chunk, "MTRL", 4); chunk += 8;
  538. /* material name index (actually, byte offset within the string table) */
  539. j = m3d_getstr(&str, voxmaterials[i].name);
  540. memcpy(chunk, &j, 4); chunk += 4;
  541. /* diffuse color (transparency for glass effect is stored in its alpha channel) */
  542. if(voxmaterials[i].diffuse) {
  543. *chunk++ = m3dp_Kd;
  544. memcpy(chunk, &voxmaterials[i].diffuse, 4); chunk += 4;
  545. }
  546. /* ambient color */
  547. if(voxmaterials[i].ambient) {
  548. *chunk++ = m3dp_Ka;
  549. memcpy(chunk, &voxmaterials[i].ambient, 4); chunk += 4;
  550. }
  551. /* specular color */
  552. if(voxmaterials[i].specular) {
  553. *chunk++ = m3dp_Ks;
  554. memcpy(chunk, &voxmaterials[i].specular, 4); chunk += 4;
  555. }
  556. /* emission color */
  557. if(voxmaterials[i].emission) {
  558. *chunk++ = m3dp_Ke;
  559. memcpy(chunk, &voxmaterials[i].emission, 4); chunk += 4;
  560. *chunk++ = m3dp_Ns;
  561. memcpy(chunk, &voxmaterials[i].emisexp, 4); chunk += 4;
  562. }
  563. /* transmission color */
  564. if(voxmaterials[i].transmis) {
  565. *chunk++ = m3dp_Tf;
  566. memcpy(chunk, &voxmaterials[i].transmis, 4); chunk += 4;
  567. }
  568. /* metallic, float */
  569. if(voxmaterials[i].metallic != 0.0f) {
  570. *chunk++ = m3dp_Pm;
  571. memcpy(chunk, &voxmaterials[i].metallic, 4); chunk += 4;
  572. }
  573. /* roughness, float */
  574. if(voxmaterials[i].roughness != 0.0f) {
  575. *chunk++ = m3dp_Pr;
  576. memcpy(chunk, &voxmaterials[i].roughness, 4); chunk += 4;
  577. }
  578. /* set the size of the chunk in the chunk header */
  579. intp[1] = chunk - (uint8_t*)intp;
  580. }
  581. // voxel types chunk
  582. memcpy(chunk, "VOXT", 4);
  583. i = 8 + 11 * numvoxtypes; memcpy(chunk + 4, &i, 4);
  584. chunk += 8;
  585. for(i = 0; i < numvoxtypes; i++, chunk += 11) {
  586. /* diffuse voxel color (should be used when there's no material associated) */
  587. memcpy(chunk, &voxtypes[i].color, 4);
  588. /* if we have a materialid, then use the material's name, otherwise fallback to voxel type name */
  589. j = m3d_getstr(&str, voxtypes[i].materialid != -1U ? voxmaterials[voxtypes[i].materialid].name : voxtypes[i].name);
  590. memcpy(chunk + 4, &j, 4);
  591. }
  592. // because we've allocated memory for the worst case, now recalculate how much memory we've actually used so far
  593. size = (uintptr_t)chunk - (uintptr_t)data;
  594. // iterate through layers to get voxel data chunks
  595. for(i = 0; i < numvoxlayers; i++) {
  596. n = voxtypes[i].w * voxtypes[i].h * voxtypes[i].d * sizeof(uint16_t);
  597. // allocate memory for the worst case, when every voxel is a packet (38: min voxd chunk length)
  598. chunk -= (uintptr_t)data;
  599. data = realloc(data, size + 38 + n * 3);
  600. if(!data) goto merr;
  601. chunk += (uintptr_t)data;
  602. /* set pointer to the start of the new chunk, and write chunk magic */
  603. intp = (uint32_t*)(chunk);
  604. memset(chunk, 0, 38);
  605. memcpy(chunk, "VOXD", 4);
  606. /* voxel layer name index */
  607. j = m3d_getstr(&str, voxlayers[i].name);
  608. memcpy(chunk + 8, &j, 4); chunk += 4;
  609. /* save position and dimensions */
  610. memcpy(chunk + 12, &voxlayers[i].x, 4);
  611. memcpy(chunk + 16, &voxlayers[i].y, 4);
  612. memcpy(chunk + 20, &voxlayers[i].z, 4);
  613. memcpy(chunk + 24, &voxlayers[i].w, 4);
  614. memcpy(chunk + 28, &voxlayers[i].h, 4);
  615. memcpy(chunk + 32, &voxlayers[i].d, 4);
  616. // RLE compression
  617. ptr = chunk + 38;
  618. k = o = 0; ptr[o++] = 0;
  619. for(m = 0; m < n; i++) {
  620. for(l = 1; l < 128 && m + l < n && voxlayers[i].data[m] == voxlayers[i].data[m + l]; l++);
  621. if(l > 1) {
  622. l--;
  623. if(ptr[k]) { ptr[k]--; ptr[o++] = 0x80 | l; }
  624. else ptr[k] = 0x80 | l;
  625. memcpy(ptr + o, &voxlayers[i].data[m], 2);
  626. o += 2;
  627. k = o; ptr[o++] = 0;
  628. m += l;
  629. continue;
  630. }
  631. ptr[k]++;
  632. memcpy(ptr + o, &voxlayers[i].data[m], 2);
  633. o += 2;
  634. if(ptr[k] > 127) { ptr[k]--; k = o; ptr[o++] = 0; }
  635. }
  636. if(!(ptr[k] & 0x80)) { if(ptr[k]) ptr[k]--; else o--; }
  637. intp[1] = ptr + o - chunk; // chunk size
  638. size += intp[1];
  639. chunk = data + size;
  640. }
  641. m3d_freestr(&str);
  642. // end chunk
  643. data = realloc(data, size + 4);
  644. memcpy(data + size, "OMD3", 4);
  645. size += 4;
  646. // compress payload
  647. comp = stbi_zlib_compress(data, size, &i, 9);
  648. if(comp && i) {
  649. free(data);
  650. data = comp;
  651. size = i;
  652. }
  653. // write out compressed size (plus file header's size, including file magic and this size field itself)
  654. i = size + 8;
  655. fwrite(&i, 1, sizeof(uint32_t), out);
  656. // write out compressed chunks
  657. if(data) {
  658. fwrite(data, 1, size, out);
  659. free(data);
  660. }
  661. fclose(out);
  662. return 0;
  663. }