m3dconv.c 60 KB

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