m3dconv.c 65 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318
  1. /*
  2. * m3dconv/m3dconv.c
  3. *
  4. * Copyright (C) 2019 bzt (bztsrc@gitlab)
  5. *
  6. * Permission is hereby granted, free of charge, to any person
  7. * obtaining a copy of this software and associated documentation
  8. * files (the "Software"), to deal in the Software without
  9. * restriction, including without limitation the rights to use, copy,
  10. * modify, merge, publish, distribute, sublicense, and/or sell copies
  11. * 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
  15. * included in all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  18. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  19. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  20. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  21. * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  22. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  24. * DEALINGS IN THE SOFTWARE.
  25. *
  26. * @brief simple 3D model to M3D converter
  27. * https://gitlab.com/bztsrc/model3d
  28. *
  29. */
  30. #include <stdlib.h>
  31. #include <stdio.h>
  32. #include <math.h>
  33. #ifndef NOASSIMP
  34. #include <assimp/cimport.h>
  35. #else
  36. /* implement that few function from assimp that we actually need */
  37. typedef float ai_real;
  38. struct aiMatrix3x3 {
  39. ai_real a1, a2, a3;
  40. ai_real b1, b2, b3;
  41. ai_real c1, c2, c3;
  42. };
  43. struct aiMatrix4x4 {
  44. ai_real a1, a2, a3, a4;
  45. ai_real b1, b2, b3, b4;
  46. ai_real c1, c2, c3, c4;
  47. ai_real d1, d2, d3, d4;
  48. };
  49. struct aiQuaternion {
  50. ai_real w, x, y, z;
  51. };
  52. struct aiVector3D {
  53. ai_real x, y, z;
  54. };
  55. #endif
  56. #ifdef PROFILING
  57. #include <sys/time.h>
  58. #endif
  59. /* command line parameters */
  60. float arg_scale = 0.0, arg_framedelay = 1.0, arg_tri = 0.0;
  61. char *arg_name=NULL, *arg_license=NULL, *arg_author=NULL, *arg_comment=NULL, *errstr = NULL, arg_rot[64];
  62. int zip = 1, ascii = 0, doinline = 0, storeinline = 0, doextract = 0, doworld = 1, matlib = 0, quality = -1, dump = 0;
  63. int right = 0, norm = 1, domesh = 0, withoutnorm = 0, withoutuv = 0, verbose = 0, flip = 0, notourimp = 0, arg_vox = 0;
  64. int forcevoxpal = 0;
  65. #define M3D_IMPLEMENTATION
  66. #define M3D_EXPORTER
  67. #define M3D_ASCII
  68. #define M3D_LOG(x) do{if(verbose>1)printf(" %s\n",x);}while(0)
  69. #define M3D_VERTEXTYPE
  70. #define M3D_VERTEXMAX
  71. #define M3D_NOVOXELS
  72. #include <m3d.h>
  73. /* vertex types */
  74. #define VT_WORLD 0 /* vertex in world space */
  75. #define VT_NORMAL 1 /* normal vector coordinate */
  76. #define VT_RELATIVE 2 /* vertex relative to bone parent */
  77. #define VT_QUATERN 3 /* vertex actually encodes a quaternion */
  78. /* variables to the material library creator */
  79. uint32_t nummaterial = 0;
  80. m3dm_t *material = NULL;
  81. uint32_t numinlined = 0;
  82. m3di_t *inlined = NULL;
  83. /* specific voxel palette */
  84. m3d_t *voxpal = NULL;
  85. /*** matrix helpers ***/
  86. /* print an assimp matrix */
  87. void _assimp_prt(struct aiMatrix4x4 *m)
  88. {
  89. printf("%g %g %g %g\n", m->a1,m->a2,m->a3,m->a4);
  90. printf("%g %g %g %g\n", m->b1,m->b2,m->b3,m->b4);
  91. printf("%g %g %g %g\n", m->c1,m->c2,m->c3,m->c4);
  92. printf("%g %g %g %g\n\n", m->d1,m->d2,m->d3,m->d4);
  93. }
  94. /* fix minus zeros in matrix */
  95. void _assimp_fixmat(struct aiMatrix4x4 *m)
  96. {
  97. if(m->a1 > -M3D_EPSILON && m->a1 < M3D_EPSILON) m->a1 = 0.0;
  98. if(m->a2 > -M3D_EPSILON && m->a2 < M3D_EPSILON) m->a2 = 0.0;
  99. if(m->a3 > -M3D_EPSILON && m->a3 < M3D_EPSILON) m->a3 = 0.0;
  100. if(m->a4 > -M3D_EPSILON && m->a4 < M3D_EPSILON) m->a4 = 0.0;
  101. if(m->b1 > -M3D_EPSILON && m->b1 < M3D_EPSILON) m->b1 = 0.0;
  102. if(m->b2 > -M3D_EPSILON && m->b2 < M3D_EPSILON) m->b2 = 0.0;
  103. if(m->b3 > -M3D_EPSILON && m->b3 < M3D_EPSILON) m->b3 = 0.0;
  104. if(m->b4 > -M3D_EPSILON && m->b4 < M3D_EPSILON) m->b4 = 0.0;
  105. if(m->c1 > -M3D_EPSILON && m->c1 < M3D_EPSILON) m->c1 = 0.0;
  106. if(m->c2 > -M3D_EPSILON && m->c2 < M3D_EPSILON) m->c2 = 0.0;
  107. if(m->c3 > -M3D_EPSILON && m->c3 < M3D_EPSILON) m->c3 = 0.0;
  108. if(m->c4 > -M3D_EPSILON && m->c4 < M3D_EPSILON) m->c4 = 0.0;
  109. if(m->d1 > -M3D_EPSILON && m->d1 < M3D_EPSILON) m->d1 = 0.0;
  110. if(m->d2 > -M3D_EPSILON && m->d2 < M3D_EPSILON) m->d2 = 0.0;
  111. if(m->d3 > -M3D_EPSILON && m->d3 < M3D_EPSILON) m->d3 = 0.0;
  112. if(m->d4 > -M3D_EPSILON && m->d4 < M3D_EPSILON) m->d4 = 0.0;
  113. }
  114. /* get determinant */
  115. float _assimp_determinant(const struct aiMatrix4x4 *m)
  116. {
  117. float det =
  118. m->a1*m->b2*m->c3*m->d4 - m->a1*m->b2*m->c4*m->d3 + m->a1*m->b3*m->c4*m->d2 - m->a1*m->b3*m->c2*m->d4
  119. + m->a1*m->b4*m->c2*m->d3 - m->a1*m->b4*m->c3*m->d2 - m->a2*m->b3*m->c4*m->d1 + m->a2*m->b3*m->c1*m->d4
  120. - m->a2*m->b4*m->c1*m->d3 + m->a2*m->b4*m->c3*m->d1 - m->a2*m->b1*m->c3*m->d4 + m->a2*m->b1*m->c4*m->d3
  121. + m->a3*m->b4*m->c1*m->d2 - m->a3*m->b4*m->c2*m->d1 + m->a3*m->b1*m->c2*m->d4 - m->a3*m->b1*m->c4*m->d2
  122. + m->a3*m->b2*m->c4*m->d1 - m->a3*m->b2*m->c1*m->d4 - m->a4*m->b1*m->c2*m->d3 + m->a4*m->b1*m->c3*m->d2
  123. - m->a4*m->b2*m->c3*m->d1 + m->a4*m->b2*m->c1*m->d3 - m->a4*m->b3*m->c1*m->d2 + m->a4*m->b3*m->c2*m->d1;
  124. if(det == 0.0 || det == -0.0) det = 1.0; else det = 1.0 / det;
  125. return det;
  126. }
  127. /* those lazy assimp devs forgot to expose aiInverseMatrix4() in C API */
  128. void _assimp_inverse(struct aiMatrix4x4 *m)
  129. {
  130. struct aiMatrix4x4 ret;
  131. float det = _assimp_determinant(m);
  132. ret.a1 = det *(m->b2*(m->c3*m->d4 - m->c4*m->d3) + m->b3*(m->c4*m->d2 - m->c2*m->d4) + m->b4*(m->c2*m->d3 - m->c3*m->d2));
  133. ret.a2 = -det*(m->a2*(m->c3*m->d4 - m->c4*m->d3) + m->a3*(m->c4*m->d2 - m->c2*m->d4) + m->a4*(m->c2*m->d3 - m->c3*m->d2));
  134. ret.a3 = det *(m->a2*(m->b3*m->d4 - m->b4*m->d3) + m->a3*(m->b4*m->d2 - m->b2*m->d4) + m->a4*(m->b2*m->d3 - m->b3*m->d2));
  135. ret.a4 = -det*(m->a2*(m->b3*m->c4 - m->b4*m->c3) + m->a3*(m->b4*m->c2 - m->b2*m->c4) + m->a4*(m->b2*m->c3 - m->b3*m->c2));
  136. ret.b1 = -det*(m->b1*(m->c3*m->d4 - m->c4*m->d3) + m->b3*(m->c4*m->d1 - m->c1*m->d4) + m->b4*(m->c1*m->d3 - m->c3*m->d1));
  137. ret.b2 = det *(m->a1*(m->c3*m->d4 - m->c4*m->d3) + m->a3*(m->c4*m->d1 - m->c1*m->d4) + m->a4*(m->c1*m->d3 - m->c3*m->d1));
  138. ret.b3 = -det*(m->a1*(m->b3*m->d4 - m->b4*m->d3) + m->a3*(m->b4*m->d1 - m->b1*m->d4) + m->a4*(m->b1*m->d3 - m->b3*m->d1));
  139. ret.b4 = det *(m->a1*(m->b3*m->c4 - m->b4*m->c3) + m->a3*(m->b4*m->c1 - m->b1*m->c4) + m->a4*(m->b1*m->c3 - m->b3*m->c1));
  140. ret.c1 = det *(m->b1*(m->c2*m->d4 - m->c4*m->d2) + m->b2*(m->c4*m->d1 - m->c1*m->d4) + m->b4*(m->c1*m->d2 - m->c2*m->d1));
  141. ret.c2 = -det*(m->a1*(m->c2*m->d4 - m->c4*m->d2) + m->a2*(m->c4*m->d1 - m->c1*m->d4) + m->a4*(m->c1*m->d2 - m->c2*m->d1));
  142. ret.c3 = det *(m->a1*(m->b2*m->d4 - m->b4*m->d2) + m->a2*(m->b4*m->d1 - m->b1*m->d4) + m->a4*(m->b1*m->d2 - m->b2*m->d1));
  143. ret.c4 = -det*(m->a1*(m->b2*m->c4 - m->b4*m->c2) + m->a2*(m->b4*m->c1 - m->b1*m->c4) + m->a4*(m->b1*m->c2 - m->b2*m->c1));
  144. ret.d1 = -det*(m->b1*(m->c2*m->d3 - m->c3*m->d2) + m->b2*(m->c3*m->d1 - m->c1*m->d3) + m->b3*(m->c1*m->d2 - m->c2*m->d1));
  145. ret.d2 = det *(m->a1*(m->c2*m->d3 - m->c3*m->d2) + m->a2*(m->c3*m->d1 - m->c1*m->d3) + m->a3*(m->c1*m->d2 - m->c2*m->d1));
  146. ret.d3 = -det*(m->a1*(m->b2*m->d3 - m->b3*m->d2) + m->a2*(m->b3*m->d1 - m->b1*m->d3) + m->a3*(m->b1*m->d2 - m->b2*m->d1));
  147. ret.d4 = det *(m->a1*(m->b2*m->c3 - m->b3*m->c2) + m->a2*(m->b3*m->c1 - m->b1*m->c3) + m->a3*(m->b1*m->c2 - m->b2*m->c1));
  148. memcpy(m, &ret, sizeof(struct aiMatrix4x4));
  149. }
  150. /* get rotation matrix */
  151. void _assimp_extract3x3(struct aiMatrix3x3 *m3, struct aiMatrix4x4 *m4)
  152. {
  153. m3->a1 = m4->a1; m3->a2 = m4->a2; m3->a3 = m4->a3;
  154. m3->b1 = m4->b1; m3->b2 = m4->b2; m3->b3 = m4->b3;
  155. m3->c1 = m4->c1; m3->c2 = m4->c2; m3->c3 = m4->c3;
  156. }
  157. /* identity matrix */
  158. void _assimp_identity4x4(struct aiMatrix4x4 *mat)
  159. {
  160. memset(mat, 0, sizeof(struct aiMatrix4x4));
  161. mat->a1 = mat->b2 = mat->c3 = mat->d4 = 1.0;
  162. }
  163. /* matrix multiplication */
  164. void _assimp_multiply4x4(struct aiMatrix4x4 *dst, const struct aiMatrix4x4 *src)
  165. {
  166. struct aiMatrix4x4 old;
  167. memcpy(&old, dst, sizeof(struct aiMatrix4x4));
  168. dst->a1 = src->a1 * old.a1 + src->b1 * old.a2 + src->c1 * old.a3 + src->d1 * old.a4;
  169. dst->a2 = src->a2 * old.a1 + src->b2 * old.a2 + src->c2 * old.a3 + src->d2 * old.a4;
  170. dst->a3 = src->a3 * old.a1 + src->b3 * old.a2 + src->c3 * old.a3 + src->d3 * old.a4;
  171. dst->a4 = src->a4 * old.a1 + src->b4 * old.a2 + src->c4 * old.a3 + src->d4 * old.a4;
  172. dst->b1 = src->a1 * old.b1 + src->b1 * old.b2 + src->c1 * old.b3 + src->d1 * old.b4;
  173. dst->b2 = src->a2 * old.b1 + src->b2 * old.b2 + src->c2 * old.b3 + src->d2 * old.b4;
  174. dst->b3 = src->a3 * old.b1 + src->b3 * old.b2 + src->c3 * old.b3 + src->d3 * old.b4;
  175. dst->b4 = src->a4 * old.b1 + src->b4 * old.b2 + src->c4 * old.b3 + src->d4 * old.b4;
  176. dst->c1 = src->a1 * old.c1 + src->b1 * old.c2 + src->c1 * old.c3 + src->d1 * old.c4;
  177. dst->c2 = src->a2 * old.c1 + src->b2 * old.c2 + src->c2 * old.c3 + src->d2 * old.c4;
  178. dst->c3 = src->a3 * old.c1 + src->b3 * old.c2 + src->c3 * old.c3 + src->d3 * old.c4;
  179. dst->c4 = src->a4 * old.c1 + src->b4 * old.c2 + src->c4 * old.c3 + src->d4 * old.c4;
  180. dst->d1 = src->a1 * old.d1 + src->b1 * old.d2 + src->c1 * old.d3 + src->d1 * old.d4;
  181. dst->d2 = src->a2 * old.d1 + src->b2 * old.d2 + src->c2 * old.d3 + src->d2 * old.d4;
  182. dst->d3 = src->a3 * old.d1 + src->b3 * old.d2 + src->c3 * old.d3 + src->d3 * old.d4;
  183. dst->d4 = src->a4 * old.d1 + src->b4 * old.d2 + src->c4 * old.d3 + src->d4 * old.d4;
  184. }
  185. /* compose a transformation matrix */
  186. void _assimp_composematrix(struct aiMatrix4x4 *m, struct aiVector3D *p, struct aiQuaternion *q, struct aiVector3D *s)
  187. {
  188. /* quat to rotation matrix */
  189. if(q->x == 0.0 && q->y == 0.0 && q->z >= 0.7071065 && q->z <= 0.7071075 && q->w == 0.0) {
  190. m->a2 = m->a3 = m->b1 = m->b3 = m->c1 = m->c2 = 0.0;
  191. m->a1 = m->b2 = m->c3 = -1.0;
  192. } else {
  193. m->a1 = 1 - 2 * (q->y * q->y + q->z * q->z); if(m->a1 > -M3D_EPSILON && m->a1 < M3D_EPSILON) m->a1 = 0.0;
  194. m->a2 = 2 * (q->x * q->y - q->z * q->w); if(m->a2 > -M3D_EPSILON && m->a2 < M3D_EPSILON) m->a2 = 0.0;
  195. m->a3 = 2 * (q->x * q->z + q->y * q->w); if(m->a3 > -M3D_EPSILON && m->a3 < M3D_EPSILON) m->a3 = 0.0;
  196. m->b1 = 2 * (q->x * q->y + q->z * q->w); if(m->b1 > -M3D_EPSILON && m->b1 < M3D_EPSILON) m->b1 = 0.0;
  197. m->b2 = 1 - 2 * (q->x * q->x + q->z * q->z); if(m->b2 > -M3D_EPSILON && m->b2 < M3D_EPSILON) m->b2 = 0.0;
  198. m->b3 = 2 * (q->y * q->z - q->x * q->w); if(m->b3 > -M3D_EPSILON && m->b3 < M3D_EPSILON) m->b3 = 0.0;
  199. m->c1 = 2 * (q->x * q->z - q->y * q->w); if(m->c1 > -M3D_EPSILON && m->c1 < M3D_EPSILON) m->c1 = 0.0;
  200. m->c2 = 2 * (q->y * q->z + q->x * q->w); if(m->c2 > -M3D_EPSILON && m->c2 < M3D_EPSILON) m->c2 = 0.0;
  201. m->c3 = 1 - 2 * (q->x * q->x + q->y * q->y); if(m->c3 > -M3D_EPSILON && m->c3 < M3D_EPSILON) m->c3 = 0.0;
  202. }
  203. /* scale matrix */
  204. if(s) {
  205. m->a1 *= s->x; m->a2 *= s->x; m->a3 *= s->x;
  206. m->b1 *= s->y; m->b2 *= s->y; m->b3 *= s->y;
  207. m->c1 *= s->z; m->c2 *= s->z; m->c3 *= s->z;
  208. }
  209. /* set translation */
  210. m->a4 = p->x; m->b4 = p->y; m->c4 = p->z;
  211. m->d1 = 0; m->d2 = 0; m->d3 = 0; m->d4 = 1;
  212. }
  213. /* decompose a transformation matrix */
  214. void _assimp_decomposematrix(const struct aiMatrix4x4 *mat, struct aiVector3D *scaling, struct aiQuaternion *rotation, struct aiVector3D *position)
  215. {
  216. ai_real t, s;
  217. struct aiVector3D vCols[3];
  218. struct aiMatrix3x3 rot;
  219. /* extract translation */
  220. position->x = mat->a4;
  221. position->y = mat->b4;
  222. position->z = mat->c4;
  223. /* extract the columns of the matrix. */
  224. vCols[0].x = mat->a1; vCols[0].y = mat->b1; vCols[0].z = mat->c1;
  225. vCols[1].x = mat->a2; vCols[1].y = mat->b2; vCols[1].z = mat->c2;
  226. vCols[2].x = mat->a3; vCols[2].y = mat->b3; vCols[2].z = mat->c3;
  227. /* extract the scaling factors */
  228. scaling->x = sqrt(vCols[0].x * vCols[0].x + vCols[0].y * vCols[0].y + vCols[0].z * vCols[0].z);
  229. scaling->y = sqrt(vCols[1].x * vCols[1].x + vCols[1].y * vCols[1].y + vCols[1].z * vCols[1].z);
  230. scaling->z = sqrt(vCols[2].x * vCols[2].x + vCols[2].y * vCols[2].y + vCols[2].z * vCols[2].z);
  231. /* and the sign of the scaling */
  232. if (_assimp_determinant(mat) < 0) { scaling->x = -scaling->x; scaling->y = -scaling->y; scaling->z = -scaling->z; }
  233. /* and remove all scaling from the matrix */
  234. if(scaling->x) { vCols[0].x /= scaling->x; vCols[0].y /= scaling->x; vCols[0].z /= scaling->x; }
  235. if(scaling->y) { vCols[1].x /= scaling->y; vCols[1].y /= scaling->y; vCols[1].z /= scaling->y; }
  236. if(scaling->z) { vCols[2].x /= scaling->z; vCols[2].y /= scaling->z; vCols[2].z /= scaling->z; }
  237. rot.a1 = vCols[0].x; rot.a2 = vCols[1].x; rot.a3 = vCols[2].x;
  238. rot.b1 = vCols[0].y; rot.b2 = vCols[1].y; rot.b3 = vCols[2].y;
  239. rot.c1 = vCols[0].z; rot.c2 = vCols[1].z; rot.c3 = vCols[2].z;
  240. t = rot.a1 + rot.b2 + rot.c3;
  241. /* large enough */
  242. if( t > 0.0)
  243. {
  244. s = sqrt(1 + t) * 2.0;
  245. rotation->x = (rot.c2 - rot.b3) / s;
  246. rotation->y = (rot.a3 - rot.c1) / s;
  247. rotation->z = (rot.b1 - rot.a2) / s;
  248. rotation->w = 0.25 * s;
  249. } /* else we have to check several cases */
  250. else if( rot.a1 > rot.b2 && rot.a1 > rot.c3 )
  251. {
  252. /* Column 0: */
  253. s = sqrt( 1.0 + rot.a1 - rot.b2 - rot.c3) * 2.0;
  254. rotation->x = 0.25 * s;
  255. rotation->y = (rot.b1 + rot.a2) / s;
  256. rotation->z = (rot.a3 + rot.c1) / s;
  257. rotation->w = (rot.c2 - rot.b3) / s;
  258. }
  259. else if( rot.b2 > rot.c3)
  260. {
  261. /* Column 1: */
  262. s = sqrt( 1.0 + rot.b2 - rot.a1 - rot.c3) * 2.0;
  263. rotation->x = (rot.b1 + rot.a2) / s;
  264. rotation->y = 0.25 * s;
  265. rotation->z = (rot.c2 + rot.b3) / s;
  266. rotation->w = (rot.a3 - rot.c1) / s;
  267. } else
  268. {
  269. /* Column 2: */
  270. s = sqrt( 1.0 + rot.c3 - rot.a1 - rot.b2) * 2.0;
  271. rotation->x = (rot.a3 + rot.c1) / s;
  272. rotation->y = (rot.c2 + rot.b3) / s;
  273. rotation->z = 0.25 * s;
  274. rotation->w = (rot.b1 - rot.a2) / s;
  275. }
  276. }
  277. /* add a vertex to list */
  278. m3dv_t *_assimp_addvrtx(m3dv_t *vrtx, uint32_t *numvrtx, m3dv_t *v, uint32_t *idx)
  279. {
  280. if(v->x == (M3D_FLOAT)-0.0) v->x = (M3D_FLOAT)0.0;
  281. if(v->y == (M3D_FLOAT)-0.0) v->y = (M3D_FLOAT)0.0;
  282. if(v->z == (M3D_FLOAT)-0.0) v->z = (M3D_FLOAT)0.0;
  283. if(v->w == (M3D_FLOAT)-0.0) v->w = (M3D_FLOAT)0.0;
  284. vrtx = (m3dv_t*)M3D_REALLOC(vrtx, ((*numvrtx) + 1) * sizeof(m3dv_t));
  285. memcpy(&vrtx[*numvrtx], v, sizeof(m3dv_t));
  286. *idx = *numvrtx;
  287. (*numvrtx)++;
  288. return vrtx;
  289. }
  290. /* add a space (position + orientation + scaling) to vertex list */
  291. static m3dv_t *_assimp_addspace(m3dv_t *vrtx, uint32_t *numvrtx, struct aiMatrix4x4 *m, uint32_t type, uint32_t *idx)
  292. {
  293. struct aiVector3D p, s;
  294. struct aiQuaternion q;
  295. _assimp_fixmat(m);
  296. _assimp_decomposematrix(m, &s, &q, &p);
  297. if(s.x == (M3D_FLOAT)-0.0) s.x = (M3D_FLOAT)0.0;
  298. if(s.y == (M3D_FLOAT)-0.0) s.y = (M3D_FLOAT)0.0;
  299. if(s.z == (M3D_FLOAT)-0.0) s.z = (M3D_FLOAT)0.0;
  300. if(p.x == (M3D_FLOAT)-0.0) p.x = (M3D_FLOAT)0.0;
  301. if(p.y == (M3D_FLOAT)-0.0) p.y = (M3D_FLOAT)0.0;
  302. if(p.z == (M3D_FLOAT)-0.0) p.z = (M3D_FLOAT)0.0;
  303. if(q.x == (M3D_FLOAT)-0.0) q.x = (M3D_FLOAT)0.0;
  304. if(q.y == (M3D_FLOAT)-0.0) q.y = (M3D_FLOAT)0.0;
  305. if(q.z == (M3D_FLOAT)-0.0) q.z = (M3D_FLOAT)0.0;
  306. if(q.w == (M3D_FLOAT)-0.0) q.w = (M3D_FLOAT)0.0;
  307. vrtx = (m3dv_t*)M3D_REALLOC(vrtx, ((*numvrtx) + 3) * sizeof(m3dv_t));
  308. memset(&vrtx[*numvrtx], 0, 3 * sizeof(m3dv_t));
  309. *idx = *numvrtx;
  310. vrtx[*numvrtx].x = p.x;
  311. vrtx[*numvrtx].y = p.y;
  312. vrtx[*numvrtx].z = p.z;
  313. vrtx[*numvrtx].w = (M3D_FLOAT)1.0;
  314. vrtx[*numvrtx].skinid = (M3D_INDEX)-1U;
  315. vrtx[*numvrtx].type = type;
  316. (*numvrtx)++;
  317. vrtx[*numvrtx].x = q.x;
  318. vrtx[*numvrtx].y = q.y;
  319. vrtx[*numvrtx].z = q.z;
  320. vrtx[*numvrtx].w = q.w;
  321. vrtx[*numvrtx].skinid = (M3D_INDEX)-2U;
  322. vrtx[*numvrtx].type = VT_QUATERN;
  323. (*numvrtx)++;
  324. vrtx[*numvrtx].x = s.x;
  325. vrtx[*numvrtx].y = s.y;
  326. vrtx[*numvrtx].z = s.z;
  327. vrtx[*numvrtx].w = (M3D_FLOAT)1.0;
  328. vrtx[*numvrtx].skinid = (M3D_INDEX)-1U;
  329. vrtx[*numvrtx].type = VT_WORLD;
  330. (*numvrtx)++;
  331. return vrtx;
  332. }
  333. /*** other useful helpers ***/
  334. /**
  335. * Convert euler radians into a quaternion
  336. */
  337. void _m3d_euler_to_quat(M3D_FLOAT x, M3D_FLOAT y, M3D_FLOAT z, m3dv_t *q)
  338. {
  339. M3D_FLOAT sr = (M3D_FLOAT)sin(x*0.5), cr = (M3D_FLOAT)cos(x*0.5);
  340. M3D_FLOAT sp = (M3D_FLOAT)sin(y*0.5), cp = (M3D_FLOAT)cos(y*0.5);
  341. M3D_FLOAT sy = (M3D_FLOAT)sin(z*0.5), cy = (M3D_FLOAT)cos(z*0.5);
  342. q->x = sr * cp * cy - cr * sp * sy;
  343. q->y = cr * sp * cy + sr * cp * sy;
  344. q->z = cr * cp * sy - sr * sp * cy;
  345. q->w = cr * cp * cy + sr * sp * sy;
  346. }
  347. /**
  348. * Add to animation frame
  349. */
  350. void _m3d_addframe(m3da_t *action, uint32_t t, unsigned int bone, unsigned int pos, unsigned int ori)
  351. {
  352. unsigned int i, j;
  353. if(t > action->durationmsec) action->durationmsec = t;
  354. /* find frame in action */
  355. for(i = 0; i < action->numframe && action->frame[i].msec < t; i++);
  356. if(i >= action->numframe || action->frame[i].msec > t) {
  357. action->frame = (m3dfr_t*)realloc(action->frame, (++action->numframe) * sizeof(m3dfr_t));
  358. if(!action->frame) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  359. if(action->numframe - 1 > i)
  360. memmove(&action->frame[i + 1], &action->frame[i], (action->numframe - i - 1) * sizeof(m3dfr_t));
  361. memset(&action->frame[i], 0, sizeof(m3dfr_t));
  362. action->frame[i].msec = t;
  363. }
  364. /* find transformation for the bone in that frame */
  365. for(j = 0; j < action->frame[i].numtransform && action->frame[i].transform[j].boneid != bone; j++);
  366. if(j >= action->frame[i].numtransform) {
  367. action->frame[i].transform = (m3dtr_t*)realloc(action->frame[i].transform, (++action->frame[i].numtransform) * sizeof(m3dtr_t));
  368. if(!action->frame[i].transform) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  369. action->frame[i].transform[j].boneid = bone;
  370. }
  371. action->frame[i].transform[j].pos = pos;
  372. action->frame[i].transform[j].ori = ori;
  373. }
  374. /**
  375. * External asset reader for the readfile callback. Also used to load the model itself
  376. */
  377. unsigned char *readfile(char *fn, unsigned int *size)
  378. {
  379. FILE *f;
  380. unsigned char *ret = NULL, *buff = NULL;
  381. char *in, *p;
  382. int i, j;
  383. *size = 0;
  384. f = fopen(fn, "rb");
  385. if(f) {
  386. fseek(f, 0L, SEEK_END);
  387. *size = (unsigned int)ftell(f);
  388. fseek(f, 0L, SEEK_SET);
  389. ret = (unsigned char*)malloc(*size + 1);
  390. if(ret) {
  391. fread(ret, *size, 1, f);
  392. /* if it's gzip compressed, uncompress it first */
  393. if(ret[0] == 0x1f && ret[1] == 0x8b) {
  394. /* skip over gzip header */
  395. buff = ret + 3;
  396. i = *buff++; buff += 6;
  397. if(i & 4) { j = *buff++; j += (*buff++ << 8); buff += j; }
  398. if(i & 8) { while(*buff++ != 0); }
  399. if(i & 16) { while(*buff++ != 0); }
  400. if(i & 2) buff += 2;
  401. *size -= (int)(buff - ret);
  402. buff = (uint8_t*)_m3dstbi_zlib_decode_malloc_guesssize_headerflag((const char*)buff, *size, 4096, &i, 0);
  403. if(buff) { free(ret); ret = buff; buff = NULL; *size = (unsigned int)i; }
  404. }
  405. ret[*size] = 0;
  406. } else
  407. *size = 0;
  408. fclose(f);
  409. if(storeinline) {
  410. inlined = (m3di_t*)realloc(inlined, (numinlined+1) * sizeof(m3di_t));
  411. if(!inlined) { fprintf(stderr, "m3dconv: memory allocation error\n"); exit(3); }
  412. in = strrchr(fn, '/');
  413. if(!in) in = strrchr(fn, '\\');
  414. if(!in) in = fn; else in++;
  415. in = _m3d_safestr(in, 0);
  416. p = strrchr(in, '.');
  417. if(p && (p[1] == 'p' || p[1] == 'P') && (p[2] == 'n' || p[2] == 'N') && (p[3] == 'g' || p[3] == 'G')) *p = 0;
  418. inlined[numinlined].name = in;
  419. inlined[numinlined].length = *size;
  420. inlined[numinlined].data = ret;
  421. numinlined++;
  422. }
  423. }
  424. return ret;
  425. }
  426. /* include M3D dump */
  427. #include "dump.h"
  428. /* include triangulation routines */
  429. #include "tri.h"
  430. /* include assimp related code */
  431. #include "assimp.h"
  432. /* include ISO-10303 related code */
  433. #include "step.h"
  434. /* include OBJ with NURBS related code */
  435. #include "objnurbs.h"
  436. /* include Blender related code */
  437. #include "blend.h"
  438. /* Voxel image format importers */
  439. #include "voxel.h"
  440. /* include MilkShape 3D related code */
  441. #include "ms3d.h"
  442. /* include FBX related code */
  443. #include "fbx.h"
  444. /* include Polygon Model eXtended related code */
  445. #include "pmx.h"
  446. /**
  447. * Parse materials from a model, collect them in global variables and remove them from the file
  448. */
  449. void parse_material(char *fn)
  450. {
  451. unsigned int size, i, j, k, l;
  452. unsigned char *out, *data = readfile(fn, &size);
  453. m3d_t *m3d;
  454. FILE *f;
  455. if(data) {
  456. storeinline = 1;
  457. m3d = m3d_load(data, readfile, free, NULL);
  458. if(m3d && m3d->nummaterial) {
  459. for(i = 0; i < m3d->nummaterial; i++) {
  460. for(j = 0, k = -1U; j < nummaterial; j++)
  461. if(!strcmp(m3d->material[i].name, material[j].name)) { k = j; break; }
  462. if(k == -1U) {
  463. k = nummaterial++;
  464. material = (m3dm_t*)realloc(material, nummaterial * sizeof(m3dm_t));
  465. if(!material) { fprintf(stderr, "m3dconv: memory allocation error\n"); exit(3); }
  466. material[k].name = m3d->material[i].name;
  467. material[k].numprop = m3d->material[i].numprop;
  468. material[k].prop = m3d->material[i].prop;
  469. /* replace textureids with the index in the global inlined array */
  470. for(j = 0; j < material[k].numprop; j++)
  471. if(material[k].prop[j].type >= 128 && material[k].prop[j].value.textureid < m3d->numtexture)
  472. for(l = 0; l < numinlined; l++)
  473. if(!strcmp(m3d->texture[material[k].prop[j].value.textureid].name, inlined[l].name)) {
  474. material[k].prop[j].value.textureid = l;
  475. break;
  476. }
  477. }
  478. }
  479. /* if it was a binary model, remove materials and save back */
  480. if(M3D_CHUNKMAGIC(data, '3','D','M','O')) {
  481. /* remove materials and inlined textures (but not inlined scripts) */
  482. for(i = 0; i < m3d->numinlined; i++)
  483. for(j = 0; j < m3d->numtexture; j++)
  484. if(!strcmp(m3d->inlined[i].name, m3d->texture[j].name))
  485. m3d->inlined[i].name = NULL;
  486. m3d->nummaterial = 0;
  487. m3d->material = NULL;
  488. m3d->numtexture = 0;
  489. m3d->texture = NULL;
  490. out = m3d_save(m3d, m3d->vc_s == 1? 0 : (m3d->vc_s == 2 ? 1 : (m3d->vc_s == 8 ? 3 : 2)),
  491. (m3d->flags & M3D_FLG_FREERAW ? 0 : M3D_EXP_NOZLIB) | (m3d->inlined ? M3D_EXP_INLINE : 0) |
  492. M3D_EXP_NOVRTMAX, &size);
  493. if(out) {
  494. f = fopen(fn, "wb");
  495. if(f) {
  496. fwrite(out, size, 1, f);
  497. fclose(f);
  498. }
  499. free(out);
  500. }
  501. }
  502. }
  503. /* don't free data and m3d, because material array still has pointers into them */
  504. }
  505. }
  506. /**
  507. * Get the bounding cube of the model
  508. */
  509. M3D_FLOAT bcube(m3d_t *m3d, int type, M3D_FLOAT *min_x, M3D_FLOAT *max_x, M3D_FLOAT *min_y, M3D_FLOAT *max_y,
  510. M3D_FLOAT *min_z, M3D_FLOAT *max_z)
  511. {
  512. unsigned int i;
  513. M3D_FLOAT mi_x, ma_x, mi_y, ma_y, mi_z, ma_z, scale;
  514. *min_x = *min_y = *min_z = (M3D_FLOAT)1e10;
  515. *max_x = *max_y = *max_z = (M3D_FLOAT)-1e10;
  516. for(i = 0; i < m3d->numvertex; i++) {
  517. if((!type && m3d->vertex[i].type != VT_WORLD) || (type && m3d->vertex[i].type != VT_NORMAL)) continue;
  518. if(m3d->vertex[i].x > *max_x) *max_x = m3d->vertex[i].x;
  519. if(m3d->vertex[i].x < *min_x) *min_x = m3d->vertex[i].x;
  520. if(m3d->vertex[i].y > *max_y) *max_y = m3d->vertex[i].y;
  521. if(m3d->vertex[i].y < *min_y) *min_y = m3d->vertex[i].y;
  522. if(m3d->vertex[i].z > *max_z) *max_z = m3d->vertex[i].z;
  523. if(m3d->vertex[i].z < *min_z) *min_z = m3d->vertex[i].z;
  524. }
  525. mi_x = *min_x < (M3D_FLOAT)0.0 ? -*min_x : *min_x;
  526. ma_x = *max_x < (M3D_FLOAT)0.0 ? -*max_x : *max_x;
  527. mi_y = *min_y < (M3D_FLOAT)0.0 ? -*min_y : *min_y;
  528. ma_y = *max_y < (M3D_FLOAT)0.0 ? -*max_y : *max_y;
  529. mi_z = *min_z < (M3D_FLOAT)0.0 ? -*min_z : *min_z;
  530. ma_z = *max_z < (M3D_FLOAT)0.0 ? -*max_z : *max_z;
  531. scale = mi_x;
  532. if(ma_x > scale) scale = ma_x;
  533. if(mi_y > scale) scale = mi_y;
  534. if(ma_y > scale) scale = ma_y;
  535. if(mi_z > scale) scale = mi_z;
  536. if(ma_z > scale) scale = ma_z;
  537. if(scale == (M3D_FLOAT)0.0 || !m3d->numvertex) scale = (M3D_FLOAT)1.0;
  538. return scale;
  539. }
  540. /**
  541. * Get the bounding cube for a voxel image
  542. */
  543. int bvox(m3d_t *m3d, int *min_x, int *max_x, int *min_y, int *max_y, int *min_z, int *max_z)
  544. {
  545. unsigned int i;
  546. if(!m3d->numvoxel || !m3d->voxel) return 0;
  547. *min_x = *min_y = *min_z = 2147483647L;
  548. *max_x = *max_y = *max_z = -2147483648L;
  549. for(i = 0; i < m3d->numvoxel; i++) {
  550. if((int)m3d->voxel[i].x + (int)m3d->voxel[i].w > *max_x) *max_x = m3d->voxel[i].x + (int)m3d->voxel[i].w;
  551. if((int)m3d->voxel[i].x < *min_x) *min_x = m3d->voxel[i].x;
  552. if((int)m3d->voxel[i].y + (int)m3d->voxel[i].h > *max_y) *max_y = m3d->voxel[i].y + (int)m3d->voxel[i].h;
  553. if((int)m3d->voxel[i].y < *min_y) *min_y = m3d->voxel[i].y;
  554. if((int)m3d->voxel[i].z + (int)m3d->voxel[i].d > *max_z) *max_z = m3d->voxel[i].z + (int)m3d->voxel[i].d;
  555. if((int)m3d->voxel[i].z < *min_z) *min_z = m3d->voxel[i].z;
  556. }
  557. return 1;
  558. }
  559. /**
  560. * Mark vertices by type, this is an extra check
  561. */
  562. void markvertices(m3d_t *m3d)
  563. {
  564. unsigned int i, j, k, l, n;
  565. m3dcd_t *cd;
  566. if(!m3d->vertex) return;
  567. for(k = 0; k < m3d->numvertex; k++)
  568. m3d->vertex[k].type = 127;
  569. if(m3d->face)
  570. for(k = 0; k < m3d->numface; k++) {
  571. for(j = 0; j < 3; j++) {
  572. if(m3d->face[k].vertex[j] >= m3d->numvertex) { fprintf(stderr, "m3dconv: invalid vertex index in mesh\n"); exit(1); }
  573. if(m3d->face[k].texcoord[j] != M3D_UNDEF && m3d->face[k].texcoord[j] >= m3d->numvertex) { fprintf(stderr, "m3dconv: invalid UV index in mesh\n"); exit(1); }
  574. m3d->vertex[m3d->face[k].vertex[j]].type = VT_WORLD;
  575. }
  576. }
  577. if(m3d->shape)
  578. for(i = 0; i < m3d->numshape; i++)
  579. for(j = 0; j < m3d->shape[i].numcmd; j++) {
  580. if(m3d->shape[i].cmd[j].type >= (unsigned int)(sizeof(m3d_commandtypes)/sizeof(m3d_commandtypes[0])) ||
  581. !m3d->shape[i].cmd[j].arg) continue;
  582. cd = &m3d_commandtypes[m3d->shape[i].cmd[j].type];
  583. for(k = n = 0, l = cd->p; k < l; k++) {
  584. switch(cd->a[((k - n) % (cd->p - n)) + n]) {
  585. case m3dcp_vi_t:
  586. if(m3d->shape[i].cmd[j].arg[k] >= m3d->numvertex) { fprintf(stderr, "m3dconv: invalid vertex index in shape\n"); exit(1); }
  587. m3d->vertex[m3d->shape[i].cmd[j].arg[k]].type = VT_WORLD;
  588. break;
  589. case m3dcp_qi_t:
  590. if(m3d->shape[i].cmd[j].arg[k] >= m3d->numvertex) { fprintf(stderr, "m3dconv: invalid vertex index in shape\n"); exit(1); }
  591. m3d->vertex[m3d->shape[i].cmd[j].arg[k]].type = VT_QUATERN;
  592. break;
  593. case m3dcp_va_t: n = k + 1; l += (m3d->shape[i].cmd[j].arg[k] - 1) * (cd->p - k - 1); break;
  594. }
  595. }
  596. }
  597. if(m3d->bone)
  598. for(k = 0; k < m3d->numbone; k++) {
  599. if(m3d->bone[k].pos >= m3d->numvertex || m3d->bone[k].ori >= m3d->numvertex) { fprintf(stderr, "m3dconv: invalid vertex index in bone\n"); exit(1); }
  600. m3d->vertex[m3d->bone[k].pos].type = m3d->bone[k].parent != -1U ? VT_RELATIVE : VT_WORLD;
  601. m3d->vertex[m3d->bone[k].ori].type = VT_QUATERN;
  602. }
  603. if(m3d->action)
  604. for(k = 0; k < m3d->numaction; k++)
  605. for(j = 0; j < m3d->action[k].numframe; j++)
  606. for(i = 0; i < m3d->action[k].frame[j].numtransform; i++) {
  607. if(m3d->action[k].frame[j].transform[i].pos >= m3d->numvertex || m3d->action[k].frame[j].transform[i].ori >= m3d->numvertex) {
  608. fprintf(stderr, "m3dconv: invalid vertex index in bone\n"); exit(1);
  609. }
  610. m3d->vertex[m3d->action[k].frame[j].transform[i].pos].type =
  611. m3d->bone[m3d->action[k].frame[j].transform[i].boneid].parent != -1U ? VT_RELATIVE : VT_WORLD;
  612. m3d->vertex[m3d->action[k].frame[j].transform[i].ori].type = VT_QUATERN;
  613. }
  614. if(m3d->flags & M3D_FLG_GENNORM)
  615. for(k = 0; k < m3d->numface; k++) {
  616. for(j = 0; j < 3; j++) {
  617. if(m3d->face[k].normal[j] >= m3d->numvertex) { fprintf(stderr, "m3dconv: invalid normal index in mesh\n"); exit(1); }
  618. m3d->vertex[m3d->face[k].normal[j]].type = VT_NORMAL;
  619. }
  620. }
  621. else {
  622. for(k = 0; k < m3d->numface; k++) {
  623. for(j = 0; j < 3; j++) {
  624. if(m3d->face[k].normal[j] >= m3d->numvertex) { fprintf(stderr, "m3dconv: invalid normal index in mesh\n"); exit(1); }
  625. if(m3d->vertex[m3d->face[k].normal[j]].type == VT_NORMAL) continue;
  626. if(m3d->vertex[m3d->face[k].normal[j]].type != 127) {
  627. i = m3d->numvertex++;
  628. m3d->vertex = (m3dv_t*)realloc(m3d->vertex, m3d->numvertex * sizeof(m3dv_t));
  629. if(!m3d->vertex) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  630. memcpy(&m3d->vertex[i], &m3d->vertex[m3d->face[k].normal[j]], sizeof(m3dv_t));
  631. m3d->face[k].normal[j] = i;
  632. m3d->vertex[i].type = VT_NORMAL;
  633. }
  634. }
  635. }
  636. }
  637. for(k = 0; k < m3d->numvertex; k++)
  638. if(m3d->vertex[k].type == 127) {
  639. fprintf(stderr, "m3dconv: unreferenced vertices?\n");
  640. break;
  641. }
  642. }
  643. /**
  644. * Usage instructions
  645. */
  646. void usage()
  647. {
  648. printf("Model 3D Converter by bzt Copyright (C) 2019-2022 MIT license\n\n"
  649. "./m3dconv [-0|-1|-2|-3] [-u|-A] [-i|-e] [-t <n>] [-V <n>] [-F] [-x|-X] [-y|-Y]\n"
  650. " [-z|-Y] [-R] [-r] [-g|-G] [-w] [-C] [-N] [-U] [-M] [-f <delay>] [-s <size>]\n"
  651. " [-n <name>] [-l <lic>] [-a <author>] [-c <comment>] [-v|-vv] <in> <out.m3d>\n"
  652. "./m3dconv -m [-u|-A] [-i] [-n <name>] [-l <license>] [-a <author>]\n"
  653. " [-c <comment>] [[-p|-P] <pal>] <in1.m3d> [<in2.m3d> ...] <out.m3d>\n"
  654. "./m3dconv [-d|-dd|-ddd|-D] <in.m3d>\n\n");
  655. printf(" -0: use 8 bit coordinate precision (int8_t 1/256, default)\n"
  656. " -1: use 16 bit coordinate precision (int16_t 1/65536)\n"
  657. " -2: use 32 bit coordinate precision (float, default for NURBS)\n"
  658. " -3: use 64 bit coordinate precision (double)\n"
  659. " -u: save uncompressed binary model\n"
  660. " -A: save in ASCII format\n"
  661. " -i: inline assets (like textures)\n"
  662. " -e: extract inlined assets\n"
  663. " -t: triangulate shapes and voxels\n"
  664. " -V: voxelize mesh\n"
  665. " -x: rotate model +90 degrees clock-wise around its X axis in place\n");
  666. printf(" -X: rotate model +90 degrees clock-wise around the X axis\n"
  667. " -y: rotate model +90 degrees clock-wise around its Y axis in place\n"
  668. " -Y: rotate model +90 degrees clock-wise around the Y axis\n"
  669. " -z: rotate model +90 degrees clock-wise around its Z axis in place\n"
  670. " -Z: rotate model +90 degrees clock-wise around the Z axis\n"
  671. " -R: mirror coordinates on Z axis\n"
  672. " -r: convert to right-handed coordinate system\n"
  673. " -g: move model to ground (only positive Y, and centered X, Z)\n");
  674. printf(" -G: move model global, to absolute center\n"
  675. " -w: don't use world transformation\n"
  676. " -C: don't normalize coordinates (only for debug purposes)\n"
  677. " -N: don't save normal vectors\n"
  678. " -U: don't save texture UV coordinates\n"
  679. " -F: flip UV\n"
  680. " -f: set framedelay multiplier\n"
  681. " -M: export mesh only, no skeleton or animation\n"
  682. " -s: set model's size in SI meters (float)\n"
  683. " -n: set model's name\n"
  684. " -l: set model's license string (\"MIT\", \"CC-0\", \"GPL\" etc.)\n");
  685. printf(" -a: set author's name, contact (email address or git repo etc.)\n"
  686. " -c: set comment on model\n"
  687. " -m: create material library\n"
  688. " -O: don't use our importers, use Assimp's (does not support NURBS)\n"
  689. " -v: verbose\n"
  690. " -d: dump contents of a M3D file\n"
  691. " -D: dump the m3d_t in-memory structure\n"
  692. " -p: use voxel palette\n"
  693. " -P: force voxel palette\n"
  694. " in: input file(s), any format that assimp can handle\n"
  695. " out: output M3D filename\n\n");
  696. exit(0);
  697. }
  698. /**
  699. * Main function
  700. */
  701. int main(int argc, char **argv)
  702. {
  703. unsigned char *data = NULL, *out = NULL;
  704. char *infile = NULL, *outfile = NULL;
  705. unsigned int orig, size, k, n;
  706. int i, j, l, rot = 0, ret = 0, mi_x, ma_x, mi_y, ma_y, mi_z, ma_z, x, y, z, sx, sy, sz;
  707. long int ratio;
  708. FILE *f;
  709. m3d_t *m3d = NULL;
  710. M3D_FLOAT scale, t, min_x, max_x, min_y, max_y, min_z, max_z;
  711. M3D_VOXEL *vox = NULL;
  712. m3dv_t *normal = NULL, *v0, *v1, *v2, va, vb;
  713. #ifdef PROFILING
  714. struct timeval tv0, tv1, tvd;
  715. #endif
  716. /* parse flags and arguments */
  717. if(argc<3) usage();
  718. memset(&arg_rot, 0, sizeof(arg_rot));
  719. for(i=1;argv[i];i++){
  720. if(argv[i][0] == '-') {
  721. switch(argv[i][1]) {
  722. case 'n': if(++i>=argc) { usage(); } arg_name = argv[i]; continue;
  723. case 'l': if(++i>=argc) { usage(); } arg_license = argv[i]; continue;
  724. case 'a': if(++i>=argc) { usage(); } arg_author = argv[i]; continue;
  725. case 'c': if(++i>=argc) { usage(); } arg_comment = argv[i]; continue;
  726. case 's': if(++i>=argc) { usage(); } arg_scale = atof(argv[i]); continue;
  727. case 't': if(++i>=argc) { usage(); } arg_tri = atof(argv[i]); continue;
  728. case 'V': if(++i>=argc) { usage(); } arg_vox = atoi(argv[i]); continue;
  729. case 'f': if(++i>=argc) { usage(); } arg_framedelay = atof(argv[i]); continue;
  730. case 'p': case 'P':
  731. forcevoxpal = argv[i][1] == 'P';
  732. if(++i>=argc || voxpal) { usage(); }
  733. data = readfile(argv[i], &orig);
  734. if(data && orig) voxpal = m3d_load(data, NULL, NULL, NULL);
  735. continue;
  736. default:
  737. for(j=1;argv[i][j];j++) {
  738. switch(argv[i][j]) {
  739. case 'd': dump++; break;
  740. case 'D': dump = 99; break;
  741. case 'r': right = 1; break;
  742. case 'C': norm = 0; break;
  743. case 'u': zip = 0; break;
  744. case 'm': matlib = 1; break;
  745. case 'i': doinline = 1; break;
  746. case 'e': doextract = 1; break;
  747. case 'A': ascii = 1; break;
  748. case 'M': domesh = 1; break;
  749. case 'N': withoutnorm = 1; break;
  750. case 'U': withoutuv = 1; break;
  751. case 'v': verbose++; break;
  752. case 'w': doworld = 0; break;
  753. case 'F': flip = 1; break;
  754. case 'O': notourimp = 1; break;
  755. case 'g':
  756. case 'G':
  757. case 'R':
  758. case 'x':
  759. case 'X':
  760. case 'y':
  761. case 'Y':
  762. case 'z':
  763. case 'Z': if(rot < (int)sizeof(arg_rot)-1) { arg_rot[rot++] = argv[i][j]; } break;
  764. case '3':
  765. case '2':
  766. case '1':
  767. case '0': quality = argv[i][j] - '0'; break;
  768. default: fprintf(stderr, "unknown flag '%c'\n", argv[i][j]); return 1;
  769. }
  770. }
  771. break;
  772. }
  773. } else {
  774. if(!infile && !matlib) { infile = argv[i]; continue; }
  775. if(argv[i+1]) {
  776. if(matlib)
  777. parse_material(argv[i]);
  778. } else outfile = argv[i];
  779. }
  780. }
  781. if(!dump && !outfile) usage();
  782. /* do the thing */
  783. if(matlib) {
  784. /* create a fake model from global material data and save it */
  785. m3d = (m3d_t*)malloc(sizeof(m3d_t));
  786. if(!m3d) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  787. memset(m3d, 0, sizeof(m3d_t));
  788. m3d->name = arg_name && *arg_name ? arg_name : "MaterialLibrary";
  789. m3d->license = arg_license;
  790. m3d->author = arg_author;
  791. m3d->desc = arg_comment;
  792. m3d->scale = 1.0;
  793. m3d->nummaterial = nummaterial;
  794. m3d->material = material;
  795. /* use the inlined list for texture names, those must match anyway */
  796. m3d->numtexture = numinlined;
  797. m3d->texture = (m3dtx_t*)inlined;
  798. goto savem3d;
  799. } else {
  800. data = readfile(infile, &orig);
  801. if(data && orig > 0) {
  802. if(dump && dump < 99) {
  803. /* print out and explain every single byte in the model file */
  804. ret = dump_file(data, orig, dump);
  805. } else {
  806. #ifdef PROFILING
  807. gettimeofday(&tv0, NULL);
  808. #endif
  809. /* parse a (probably foreign) model into M3D in-memory structure */
  810. if(verbose) printf("Parsing model (%d bytes)\n", orig);
  811. setlocale(LC_NUMERIC, "C");
  812. storeinline = doinline;
  813. if(M3D_CHUNKMAGIC(data, '3','D','M','O') || M3D_CHUNKMAGIC(data, '3','d','m','o')) {
  814. if(verbose > 1) printf(" M3D model\n");
  815. /* if it's a binary or ASCII Model 3D file, use our own library */
  816. m3d = m3d_load(data, readfile, free, NULL);
  817. /* mark the vertices */
  818. markvertices(m3d);
  819. } else
  820. if(!memcmp(data, "ISO-10303-2", 11)) {
  821. if(verbose > 1) printf(" STEP model\n");
  822. /* if it's a STEP file */
  823. m3d = step_load((char*)data);
  824. } else if(!memcmp(data + 3, "Schematic", 9)) {
  825. if(verbose > 1) printf(" Minecraft Schematic\n");
  826. /* Minecraft NBT file */
  827. m3d = schem_load(data, orig);
  828. } else if(!memcmp(data, "VOX ", 4)) {
  829. if(verbose > 1) printf(" Magicavoxel\n");
  830. /* Magicavoxel VOX file */
  831. m3d = vox_load(data, orig);
  832. } else if(!memcmp(data, "#binvox", 7)) {
  833. if(verbose > 1) printf(" binvox\n");
  834. /* BINVOX file */
  835. m3d = binvox_load(data, orig);
  836. } else if(!memcmp(data, "\001\001\000\000", 4)) {
  837. if(verbose > 1) printf(" Qubicle\n");
  838. /* Qubicle QB file */
  839. m3d = qb_load(data, orig);
  840. } else if(!memcmp(data, "// MilkShape", 12)) {
  841. if(verbose > 1) printf(" MilkShape ASCII model\n");
  842. /* if it's a MilkShape 3D ASCII file */
  843. m3d = ms3d_ascii_load((char*)data);
  844. #if 0
  845. } else if(!memcmp(data, "MS3D000000\004", 11) && !notourimp) {
  846. if(verbose > 1) printf(" MilkShape binary model\n");
  847. /* if it's a MilkShape 3D bin file */
  848. m3d = ms3d_bin_load(data, orig);
  849. #endif
  850. } else if(!memcmp(data, "PMX ", 4)) {
  851. if(verbose > 1) printf(" Polygon Model eXtended model\n");
  852. /* if it's a Polygon Model eXtended file */
  853. m3d = pmx_load(data, orig);
  854. } else if(!memcmp(data, "BLENDER", 7) && !notourimp) {
  855. if(verbose > 1) printf(" Blender\n");
  856. /* Blender file */
  857. m3d = blend_load(data, orig);
  858. } else {
  859. /* detect Wavefront OBJ and Autodesk FBX */
  860. for(k = 0, i = 0; *data && k < orig && k < 65536; k++) {
  861. if( !memcmp(data + k, "mtllib", 6) ||
  862. !memcmp(data + k, "usemtl", 6) ||
  863. !memcmp(data + k, "cstype", 6) ||
  864. !memcmp(data + k, "v ", 2) ||
  865. !memcmp(data + k, "vt ", 3) ||
  866. !memcmp(data + k, "vn ", 3)) {
  867. i = 1; break;
  868. }
  869. if( !memcmp(data + k, "Kaydara FBX Binary", 18) ||
  870. !memcmp(data + k, "FBXVersion", 10)) {
  871. i = 2; break;
  872. }
  873. }
  874. if(i == 1 && !notourimp) {
  875. /* use our OBJ loader because Assimp does not load curves and NURBS */
  876. if(verbose > 1) printf(" Wavefront OBJ model\n");
  877. m3d = obj_load((char*)data, infile);
  878. } else
  879. if(i == 2 && !notourimp) {
  880. /* use UFBX because Assimp is buggy as hell */
  881. if(verbose > 1) printf(" Autodesk FBX model\n");
  882. m3d = fbx_load(data, orig);
  883. }
  884. #ifndef NOASSIMP
  885. else {
  886. /* use Assimp for all the other formats */
  887. if(verbose > 1) printf(" Fallback to Assimp\n");
  888. free(data);
  889. data = NULL;
  890. m3d = assimp_load(infile);
  891. }
  892. #endif
  893. }
  894. if(m3d) {
  895. #ifdef PROFILING
  896. gettimeofday(&tv1, NULL);
  897. tvd.tv_sec = tv1.tv_sec - tv0.tv_sec;
  898. tvd.tv_usec = tv1.tv_usec - tv0.tv_usec;
  899. if(tvd.tv_usec < 0) { tvd.tv_sec--; tvd.tv_usec += 1000000L; }
  900. printf("Imported in %ld.%06ld sec\n", tvd.tv_sec, tvd.tv_usec);
  901. #endif
  902. /* do a lot of funcky post-processing stuff with the model */
  903. if(verbose) printf("Got model numvertex %d numface %d numshape %d numvoxtype %d numvoxel %d nummaterial %d\n",
  904. m3d->numvertex, m3d->numface, m3d->numshape, m3d->numvoxtype, m3d->numvoxel, m3d->nummaterial);
  905. /* if we need to voxelize the model */
  906. if(arg_vox > 0) {
  907. if(verbose) printf(" Voxelizing model (volume %d x %d x %d)\n", arg_vox, arg_vox, arg_vox);
  908. if(m3d->numshape) voxelize_shapes(arg_vox, m3d);
  909. if(m3d->numface) voxelize_face(arg_vox, m3d);
  910. } else
  911. /* if we need to triangulate the model */
  912. if(arg_tri > 0.0) {
  913. if(verbose) printf(" Triangulating model (sampling %f)\n", arg_tri);
  914. if(m3d->numshape) tri_shapes(arg_tri, m3d);
  915. if(m3d->numvoxel) tri_voxels(arg_tri, m3d);
  916. }
  917. /* if we have to rotate or move the model */
  918. if(rot) {
  919. for(j = 0; j < rot; j++) {
  920. if(verbose) {
  921. switch(arg_rot[j]) {
  922. case 'g': printf(" Move model to ground\n"); break;
  923. case 'G': printf(" Move model globally centered\n"); break;
  924. case 'R': printf(" Make model right-handed\n"); break;
  925. default: printf(" Rotating model (%c)\n", arg_rot[j]); break;
  926. }
  927. }
  928. bcube(m3d, 0, &min_x, &max_x, &min_y, &max_y, &min_z, &max_z);
  929. max_x -= min_x; max_x /= 2; max_x += min_x;
  930. max_y -= min_y; max_y /= 2; max_y += min_y;
  931. max_z -= min_z; max_z /= 2; max_z += min_z;
  932. for(k = 0; k < m3d->numvertex; k++) {
  933. switch(arg_rot[j]) {
  934. case 'g':
  935. case 'G':
  936. if(m3d->vertex[k].type == VT_WORLD) {
  937. m3d->vertex[k].x -= max_x;
  938. m3d->vertex[k].y -= arg_rot[j] == 'g' ? min_y : max_y;
  939. m3d->vertex[k].z -= max_z;
  940. }
  941. break;
  942. case 'R':
  943. m3d->vertex[k].z = -m3d->vertex[k].z;
  944. break;
  945. case 'X':
  946. t = m3d->vertex[k].z; m3d->vertex[k].z = -m3d->vertex[k].y; m3d->vertex[k].y = t;
  947. break;
  948. case 'x':
  949. if(m3d->vertex[k].type == VT_WORLD)
  950. { m3d->vertex[k].y -= max_y; m3d->vertex[k].z -= max_z; }
  951. t = m3d->vertex[k].z; m3d->vertex[k].z = -m3d->vertex[k].y; m3d->vertex[k].y = t;
  952. if(m3d->vertex[k].type == VT_WORLD)
  953. { m3d->vertex[k].y += max_y; m3d->vertex[k].z += max_z; }
  954. break;
  955. case 'Y':
  956. t = m3d->vertex[k].z; m3d->vertex[k].z = m3d->vertex[k].x; m3d->vertex[k].x = -t;
  957. break;
  958. case 'y':
  959. if(m3d->vertex[k].type == VT_WORLD)
  960. { m3d->vertex[k].x -= max_x; m3d->vertex[k].z -= max_z; }
  961. t = m3d->vertex[k].z; m3d->vertex[k].z = m3d->vertex[k].x; m3d->vertex[k].x = -t;
  962. if(m3d->vertex[k].type == VT_WORLD)
  963. { m3d->vertex[k].x += max_x; m3d->vertex[k].z += max_z; }
  964. break;
  965. case 'Z':
  966. t = m3d->vertex[k].y; m3d->vertex[k].y = -m3d->vertex[k].x; m3d->vertex[k].x = t;
  967. break;
  968. case 'z':
  969. if(m3d->vertex[k].type == VT_WORLD)
  970. { m3d->vertex[k].x -= max_x; m3d->vertex[k].y -= max_y; }
  971. t = m3d->vertex[k].y; m3d->vertex[k].y = -m3d->vertex[k].x; m3d->vertex[k].x = t;
  972. if(m3d->vertex[k].type == VT_WORLD)
  973. { m3d->vertex[k].x += max_x; m3d->vertex[k].y += max_y; }
  974. break;
  975. }
  976. }
  977. bvox(m3d, &mi_x, &ma_x, &mi_y, &ma_y, &mi_z, &ma_z);
  978. for(k = 0; k < m3d->numvoxel; k++) {
  979. if(!m3d->voxel[k].w || !m3d->voxel[k].h || !m3d->voxel[k].d || !m3d->voxel[k].data) continue;
  980. sx = m3d->voxel[k].w; sy = m3d->voxel[k].h; sz = m3d->voxel[k].d;
  981. vox = (M3D_VOXEL*)malloc(sx * sy * sz * sizeof(M3D_VOXEL));
  982. if(!vox) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  983. switch(arg_rot[j]) {
  984. case 'g':
  985. case 'G':
  986. m3d->voxel[k].x -= (ma_x - mi_x)/2 + mi_x;
  987. m3d->voxel[k].y -= arg_rot[j] == 'g' ? (mi_y > 0 ? mi_y : 0) : (ma_y-mi_y)/2 + mi_y;
  988. m3d->voxel[k].z -= (ma_z - mi_z)/2 + mi_z;
  989. free(vox); vox = NULL;
  990. break;
  991. case 'R':
  992. for(y = l = 0; y < sy; y++)
  993. for(z = 0; z < sz; z++)
  994. for(x = 0; x < sx; x++)
  995. vox[l++] = m3d->voxel[k].data[x + (sz-z-1) * sx + y * sx * sz];
  996. m3d->voxel[k].z = mi_z + ma_z - m3d->voxel[k].z - sz;
  997. break;
  998. case 'y':
  999. case 'Y':
  1000. for(y = l = 0; y < sy; y++)
  1001. for(x = 0; x < sx; x++)
  1002. for(z = 0; z < sz; z++)
  1003. vox[l++] = m3d->voxel[k].data[x + (sz-z-1) * sx + y * sx * sz];
  1004. m3d->voxel[k].w = sz;
  1005. m3d->voxel[k].d = sx;
  1006. l = m3d->voxel[k].z;
  1007. m3d->voxel[k].z = m3d->voxel[k].x - mi_x + mi_z;
  1008. m3d->voxel[k].x = ma_z - l - sz + mi_x;
  1009. break;
  1010. case 'z':
  1011. case 'Z':
  1012. for(x = l = 0; x < sx; x++)
  1013. for(z = 0; z < sz; z++)
  1014. for(y = 0; y < sy; y++)
  1015. vox[l++] = m3d->voxel[k].data[(sx-x-1) + z * sx + y * sx * sz];
  1016. m3d->voxel[k].w = sy;
  1017. m3d->voxel[k].h = sx;
  1018. l = m3d->voxel[k].y;
  1019. m3d->voxel[k].y = m3d->voxel[k].x - mi_x + mi_y;
  1020. m3d->voxel[k].x = ma_y - l - sy + mi_x;
  1021. break;
  1022. case 'x':
  1023. case 'X':
  1024. for(z = l = 0; z < sz; z++)
  1025. for(y = 0; y < sy; y++)
  1026. for(x = 0; x < sx; x++)
  1027. vox[l++] = m3d->voxel[k].data[x + z * sx + (sy-y-1) * sx * sz];
  1028. m3d->voxel[k].d = sy;
  1029. m3d->voxel[k].h = sz;
  1030. l = m3d->voxel[k].y;
  1031. m3d->voxel[k].y = m3d->voxel[k].z - mi_z + mi_y;
  1032. m3d->voxel[k].z = ma_y - l - sy + mi_z;
  1033. break;
  1034. }
  1035. if(vox) {
  1036. free(m3d->voxel[k].data);
  1037. m3d->voxel[k].data = vox;
  1038. }
  1039. }
  1040. }
  1041. }
  1042. /* generate smooth normals. Assimp has done this for us, but not the other importers */
  1043. if(!withoutnorm && m3d->numface && m3d->face) {
  1044. /* if they are missing, calculate triangle normals into a temporary buffer */
  1045. for(k = 0, n = m3d->numvertex; k < m3d->numface; k++)
  1046. if(m3d->face[k].normal[0] == -1U) {
  1047. v0 = &m3d->vertex[m3d->face[k].vertex[0]];
  1048. v1 = &m3d->vertex[m3d->face[k].vertex[1]];
  1049. v2 = &m3d->vertex[m3d->face[k].vertex[2]];
  1050. va.x = v1->x - v0->x; va.y = v1->y - v0->y; va.z = v1->z - v0->z;
  1051. vb.x = v2->x - v0->x; vb.y = v2->y - v0->y; vb.z = v2->z - v0->z;
  1052. if(!normal) {
  1053. normal = (m3dv_t*)malloc(m3d->numface * sizeof(m3dv_t));
  1054. if(!normal) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  1055. }
  1056. v0 = &normal[k];
  1057. v0->x = (va.y * vb.z) - (va.z * vb.y);
  1058. v0->y = (va.z * vb.x) - (va.x * vb.z);
  1059. v0->z = (va.x * vb.y) - (va.y * vb.x);
  1060. t = _m3d_rsq((v0->x * v0->x) + (v0->y * v0->y) + (v0->z * v0->z));
  1061. v0->x *= t; v0->y *= t; v0->z *= t;
  1062. m3d->face[k].normal[0] = m3d->face[k].vertex[0] + n;
  1063. m3d->face[k].normal[1] = m3d->face[k].vertex[1] + n;
  1064. m3d->face[k].normal[2] = m3d->face[k].vertex[2] + n;
  1065. }
  1066. /* this is the fast way, we don't care if a normal is repeated in m3d->vertex */
  1067. if(normal) {
  1068. if(verbose) printf(" Generating normals");
  1069. m3d->numvertex <<= 1;
  1070. m3d->vertex = (m3dv_t*)realloc(m3d->vertex, m3d->numvertex * sizeof(m3dv_t));
  1071. if(!m3d->vertex) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  1072. memset(&m3d->vertex[n], 0, n * sizeof(m3dv_t));
  1073. for(k = 0; k < m3d->numface; k++)
  1074. for(j = 0; j < 3; j++) {
  1075. v0 = &m3d->vertex[m3d->face[k].vertex[j] + n];
  1076. v0->x += normal[k].x;
  1077. v0->y += normal[k].y;
  1078. v0->z += normal[k].z;
  1079. }
  1080. /* for each vertex, take the average of the temporary normals and use that */
  1081. for(k = 0, v0 = &m3d->vertex[n]; k < n; k++, v0++) {
  1082. t = _m3d_rsq((v0->x * v0->x) + (v0->y * v0->y) + (v0->z * v0->z));
  1083. v0->x *= t; v0->y *= t; v0->z *= t;
  1084. v0->skinid = -1U;
  1085. }
  1086. free(normal);
  1087. }
  1088. }
  1089. /* normalize coordinates. Don't rely on m3d_save, use M3D_EXP_NORECALC because
  1090. * we might need this with dump_cstruct() too */
  1091. scale = bcube(m3d, VT_WORLD, &min_x, &max_x, &min_y, &max_y, &min_z, &max_z);
  1092. if(norm) {
  1093. if(scale == 0.0f) scale = 1.0f;
  1094. if(verbose) printf(" Normalizing model %f\n", scale);
  1095. for(k = 0; k < m3d->numvertex; k++) {
  1096. if(m3d->vertex[k].type != VT_WORLD) continue;
  1097. m3d->vertex[k].x /= scale;
  1098. m3d->vertex[k].y /= scale;
  1099. m3d->vertex[k].z /= scale;
  1100. }
  1101. m3d->scale = scale;
  1102. scale = bcube(m3d, VT_NORMAL, &min_x, &max_x, &min_y, &max_y, &min_z, &max_z);
  1103. for(k = 0; k < m3d->numvertex; k++) {
  1104. if(m3d->vertex[k].type != VT_NORMAL) continue;
  1105. m3d->vertex[k].x /= scale;
  1106. m3d->vertex[k].y /= scale;
  1107. m3d->vertex[k].z /= scale;
  1108. }
  1109. /* normalize UV coordinates */
  1110. if(!withoutuv && m3d->numtmap && m3d->tmap) {
  1111. min_x = min_y = (M3D_FLOAT)1e10;
  1112. max_x = max_y = (M3D_FLOAT)0.0;
  1113. for(k = 0; k < m3d->numtmap; k++) {
  1114. if(m3d->tmap[k].u < (M3D_FLOAT)0.0) m3d->tmap[k].u = (M3D_FLOAT)1.0 - m3d->tmap[k].u;
  1115. if(m3d->tmap[k].v < (M3D_FLOAT)0.0) m3d->tmap[k].v = (M3D_FLOAT)1.0 - m3d->tmap[k].v;
  1116. if(m3d->tmap[k].u > max_x) max_x = m3d->tmap[k].u;
  1117. if(m3d->tmap[k].u < min_x) min_x = m3d->tmap[k].u;
  1118. if(m3d->tmap[k].v > max_y) max_y = m3d->tmap[k].v;
  1119. if(m3d->tmap[k].v < min_y) min_y = m3d->tmap[k].v;
  1120. }
  1121. scale = min_x;
  1122. if(max_x > scale) scale = max_x;
  1123. if(min_y > scale) scale = min_y;
  1124. if(max_y > scale) scale = max_y;
  1125. if(scale > (M3D_FLOAT)1.0)
  1126. for(k = 0; k < m3d->numtmap; k++) {
  1127. m3d->tmap[k].u /= scale;
  1128. m3d->tmap[k].v /= scale;
  1129. }
  1130. }
  1131. } else
  1132. /* int8 and int16 can only store normalized coordinates, when they are between -1.0 and 1.0 */
  1133. if(scale > 1.0f && quality < 2)
  1134. quality = 2;
  1135. /* convert to right-handed coordinate system. Don't use M3D_EXP_IDOSUCK flag
  1136. * here, because we might need this with dump_cstruct() too */
  1137. if(right) {
  1138. if(verbose) printf(" CCW conversion\n");
  1139. for(k = 0; k < m3d->numface; k++) {
  1140. /* swap 2nd and 3rd indices in triangles */
  1141. j = m3d->face[k].vertex[1];
  1142. m3d->face[k].vertex[1] = m3d->face[k].vertex[2];
  1143. m3d->face[k].vertex[2] = j;
  1144. j = m3d->face[k].texcoord[1];
  1145. m3d->face[k].texcoord[1] = m3d->face[k].texcoord[2];
  1146. m3d->face[k].texcoord[2] = j;
  1147. j = m3d->face[k].normal[1];
  1148. m3d->face[k].normal[1] = m3d->face[k].normal[2];
  1149. m3d->face[k].normal[2] = j;
  1150. }
  1151. }
  1152. /* override the default scale factor with command line argument if specified */
  1153. if(arg_scale > 0.0f) m3d->scale = (M3D_FLOAT)arg_scale;
  1154. if(m3d->scale <= (M3D_FLOAT)0.0) m3d->scale = (M3D_FLOAT)1.0;
  1155. if(arg_name) m3d->name = _m3d_safestr(arg_name, 2);
  1156. if(arg_license) m3d->license = _m3d_safestr(arg_license, 2);
  1157. if(arg_author) m3d->author = _m3d_safestr(arg_author, 2);
  1158. if(arg_comment) m3d->desc = _m3d_safestr(arg_comment, 3);
  1159. #ifdef PROFILING
  1160. gettimeofday(&tv0, NULL);
  1161. tvd.tv_sec = tv0.tv_sec - tv1.tv_sec;
  1162. tvd.tv_usec = tv0.tv_usec - tv1.tv_usec;
  1163. if(tvd.tv_usec < 0) { tvd.tv_sec--; tvd.tv_usec += 1000000L; }
  1164. printf("Converted in %ld.%06ld sec\n", tvd.tv_sec, tvd.tv_usec);
  1165. #endif
  1166. savem3d: if(dump >= 99) {
  1167. /* print out the structure we have in memory */
  1168. dump_cstruct(m3d);
  1169. } else {
  1170. /* save output */
  1171. if(doextract) {
  1172. if(verbose) printf("Extracting assets (%d)\n", m3d->numinlined);
  1173. for(k = 0; k < m3d->numinlined; k++) {
  1174. if(m3d->inlined[k].name && m3d->inlined[k].length && m3d->inlined[k].data) {
  1175. infile = (char*)malloc(strlen(m3d->inlined[k].name) + 5);
  1176. if(!infile) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  1177. strcpy(infile, m3d->inlined[k].name);
  1178. if(m3d->inlined[k].data[1] == 'P' && m3d->inlined[k].data[2] == 'N' &&
  1179. m3d->inlined[k].data[3] == 'G')
  1180. strcat(infile, ".png");
  1181. if(verbose>1) printf(" Extracting '%s'\n", infile);
  1182. f = fopen(infile, "wb");
  1183. if(f) {
  1184. fwrite(m3d->inlined[k].data, m3d->inlined[k].length, 1, f);
  1185. fclose(f);
  1186. } else {
  1187. fprintf(stderr, "m3dconv: unable to write file: %s\n", infile);
  1188. }
  1189. free(infile);
  1190. }
  1191. }
  1192. if(quality == -1)
  1193. quality = m3d->vc_s == 1 ? 0 : (m3d->vc_s == 2 ? 1 : (m3d->vc_s == 8 ? 3 : 2));
  1194. m3d->numinlined = 0;
  1195. doinline = 0;
  1196. }
  1197. if(doinline && numinlined) {
  1198. if(verbose) printf("Inlining assets (%d)\n", numinlined);
  1199. m3d->inlined = (m3di_t*)realloc(m3d->inlined, (m3d->numinlined + numinlined) * sizeof(m3di_t));
  1200. memcpy(&m3d->inlined[m3d->numinlined], inlined, numinlined * sizeof(m3di_t));
  1201. m3d->numinlined += numinlined;
  1202. }
  1203. if(quality < 0) quality = m3d->numshape ? 2 : 0;
  1204. if(verbose) printf("Generating m3d (quality %d bits)\n", 1 << (quality + 3));
  1205. out = m3d_save(m3d, quality, (zip ? 0 : M3D_EXP_NOZLIB) | (ascii ? M3D_EXP_ASCII : 0) |
  1206. (doinline ? M3D_EXP_INLINE : 0) | M3D_EXP_NORECALC | (flip ? M3D_EXP_FLIPTXTCRD : 0) |
  1207. (withoutnorm ? M3D_EXP_NONORMAL : 0 ) | (withoutuv ? M3D_EXP_NOTXTCRD : 0) |
  1208. (domesh ? M3D_EXP_NOBONE | M3D_EXP_NOACTION : 0) | M3D_EXP_NOVRTMAX, &size);
  1209. if(verbose) {
  1210. ratio = size * 10000L / orig;
  1211. printf("Compression ratio %ld.%ld%%\n", ratio / 100, ratio % 100);
  1212. }
  1213. #ifdef PROFILING
  1214. gettimeofday(&tv1, NULL);
  1215. tvd.tv_sec = tv1.tv_sec - tv0.tv_sec;
  1216. tvd.tv_usec = tv1.tv_usec - tv0.tv_usec;
  1217. if(tvd.tv_usec < 0) { tvd.tv_sec--; tvd.tv_usec += 1000000L; }
  1218. printf("Exported in %ld.%06ld sec\n", tvd.tv_sec, tvd.tv_usec);
  1219. #endif
  1220. if(out) {
  1221. if(verbose) printf("Writing model (%d bytes)\n", size);
  1222. f = fopen(outfile, "wb");
  1223. if(f) {
  1224. fwrite(out, size, 1, f);
  1225. fclose(f);
  1226. } else {
  1227. fprintf(stderr, "m3dconv: unable to write file: %s\n", outfile);
  1228. ret = 2;
  1229. }
  1230. } else {
  1231. fprintf(stderr, "m3dconv: unable to create model binary\n");
  1232. ret = 2;
  1233. }
  1234. }
  1235. } else {
  1236. fprintf(stderr, "m3dconv: unable to parse model: %s\n%s\n", infile, errstr ? errstr : "");
  1237. ret = 1;
  1238. }
  1239. }
  1240. } else {
  1241. fprintf(stderr, "m3dconv: unable to read file: %s\n", infile);
  1242. ret = 1;
  1243. }
  1244. }
  1245. /* clean up */
  1246. if(m3d) {
  1247. if(voxpal && voxpal->voxtype == m3d->voxtype)
  1248. voxpal->voxtype = NULL;
  1249. m3d_free(m3d);
  1250. }
  1251. if(voxpal) m3d_free(voxpal);
  1252. if(data) free(data);
  1253. if(out) free(out);
  1254. if(inlined) free(inlined);
  1255. return ret;
  1256. }