fbx.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. /*
  2. * m3dconv/fbx.h
  3. *
  4. * Copyright (C) 2022 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 Autodesk FBX importer
  27. * https://gitlab.com/bztsrc/model3d
  28. * https://github.com/bqqbarbhg/ufbx
  29. *
  30. * Special thanks to bqqbarbhg for the help he provided in this!
  31. *
  32. */
  33. #ifdef __GNUC__
  34. #pragma GCC diagnostic push
  35. #pragma GCC diagnostic ignored "-Wpedantic"
  36. #endif
  37. #include "ufbx.h"
  38. #ifdef __GNUC__
  39. #pragma GCC diagnostic pop
  40. #endif
  41. #define MAX_BONES 64
  42. #define MAX_BLEND_SHAPES 64
  43. typedef struct skin_vertex {
  44. uint8_t bone_index[4];
  45. uint8_t bone_weight[4];
  46. } skin_vertex;
  47. /**
  48. * This comes mostly from ufbx/examples/viewer.c
  49. */
  50. static void read_mesh(m3d_t *m3d, ufbx_mesh *mesh, ufbx_node *node)
  51. {
  52. size_t max_triangles = 0;
  53. size_t pi, ci, fi, vi;
  54. size_t num_tri_indices = mesh->max_face_triangles * 3;
  55. size_t num_parts = 0;
  56. ufbx_matrix normal_to_world;
  57. ufbx_mesh_material *mesh_mat;
  58. size_t num_blend_shapes = 0;
  59. ufbx_blend_channel *blend_channels[MAX_BLEND_SHAPES];
  60. size_t num_bones = 0;
  61. ufbx_skin_deformer *skin = NULL;
  62. ufbx_skin_cluster *cluster;
  63. uint32_t *tri_indices;
  64. skin_vertex *mesh_skin_vertices;
  65. ufbx_face face;
  66. ufbx_vec3 pos, normal;
  67. ufbx_vec2 uv;
  68. ufbx_vec4 color;
  69. ufbx_real length;
  70. size_t num_tris;
  71. M3D_INDEX mi;
  72. int i;
  73. if(!m3d || !mesh || !node) return;
  74. normal_to_world = ufbx_matrix_for_normals(&node->geometry_to_world);
  75. for (pi = 0; pi < mesh->materials.count; pi++) {
  76. mesh_mat = &mesh->materials.data[pi];
  77. if (mesh_mat->num_triangles == 0) continue;
  78. if(mesh_mat->num_triangles > max_triangles) max_triangles = mesh_mat->num_triangles;
  79. }
  80. tri_indices = calloc(num_tri_indices, sizeof(uint32_t));
  81. mesh_skin_vertices = calloc(mesh->num_vertices, sizeof(skin_vertex));
  82. if(!tri_indices || !mesh_skin_vertices) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  83. /*
  84. TODO: bones and skin
  85. if (mesh->skin_deformers.count > 0) {
  86. skin = mesh->skin_deformers.data[0];
  87. for (ci = 0; ci < skin->clusters.count; ci++) {
  88. cluster = skin->clusters.data[ci];
  89. if (num_bones < MAX_BONES) {
  90. vmesh->bone_indices[num_bones] = (int32_t)cluster->bone_node->typed_id;
  91. vmesh->bone_matrices[num_bones] = ufbx_to_um_mat(cluster->geometry_to_bone);
  92. num_bones++;
  93. }
  94. }
  95. vmesh->num_bones = num_bones;
  96. for (vi = 0; vi < mesh->num_vertices; vi++) {
  97. size_t num_weights = 0;
  98. float total_weight = 0.0f;
  99. float weights[4] = { 0.0f };
  100. uint8_t clusters[4] = { 0 };
  101. ufbx_skin_vertex vertex_weights = skin->vertices.data[vi];
  102. for (size_t wi = 0; wi < vertex_weights.num_weights; wi++) {
  103. if (num_weights >= 4) break;
  104. ufbx_skin_weight weight = skin->weights.data[vertex_weights.weight_begin + wi];
  105. if (weight.cluster_index < MAX_BONES) {
  106. total_weight += (float)weight.weight;
  107. clusters[num_weights] = (uint8_t)weight.cluster_index;
  108. weights[num_weights] = (float)weight.weight;
  109. num_weights++;
  110. }
  111. }
  112. if (total_weight > 0.0f) {
  113. skin_vertex *skin_vert = &mesh_skin_vertices[vi];
  114. uint32_t quantized_sum = 0;
  115. for (size_t i = 0; i < 4; i++) {
  116. uint8_t quantized_weight = (uint8_t)((float)weights[i] / total_weight * 255.0f);
  117. quantized_sum += quantized_weight;
  118. skin_vert->bone_index[i] = clusters[i];
  119. skin_vert->bone_weight[i] = quantized_weight;
  120. }
  121. skin_vert->bone_weight[0] += 255 - quantized_sum;
  122. }
  123. }
  124. }
  125. */
  126. for (pi = 0; pi < mesh->materials.count; pi++) {
  127. mesh_mat = &mesh->materials.data[pi];
  128. if (mesh_mat->num_triangles == 0) continue;
  129. if(mesh_mat->material == NULL) {
  130. mi = -1U;
  131. } else {
  132. for(mi = 0; mi < m3d->nummaterial && strcmp(m3d->material[mi].name, mesh_mat->material->name.data); mi++);
  133. if(mi >= m3d->nummaterial) {
  134. mi = m3d->nummaterial++;
  135. m3d->material = (m3dm_t*)realloc(m3d->material, m3d->nummaterial * sizeof(m3dm_t));
  136. if(!m3d->material) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  137. memset(&m3d->material[mi], 0, sizeof(m3dm_t));
  138. m3d->material[mi].name = (char*)malloc(mesh_mat->material->name.length + 1);
  139. if(!m3d->material[mi].name) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  140. strcpy(m3d->material[mi].name, mesh_mat->material->name.data);
  141. /* TODO: add material props */
  142. }
  143. }
  144. for (fi = 0; fi < mesh_mat->num_faces; fi++) {
  145. face = mesh->faces.data[mesh_mat->face_indices.data[fi]];
  146. num_tris = ufbx_triangulate_face(tri_indices, num_tri_indices, mesh, face);
  147. m3d->face = (m3df_t*)realloc(m3d->face, (m3d->numface + num_tris) * sizeof(m3df_t));
  148. if(!m3d->face) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  149. memset(&m3d->face[m3d->numface], 255, num_tris * sizeof(m3df_t));
  150. m3d->vertex = (m3dv_t*)realloc(m3d->vertex, (m3d->numvertex + num_tris * 6) * sizeof(m3dv_t));
  151. if(!m3d->vertex) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  152. memset(&m3d->vertex[m3d->numvertex], 0, num_tris * 6 * sizeof(m3dv_t));
  153. m3d->tmap = (m3dti_t*)realloc(m3d->tmap, (m3d->numtmap + num_tris * 3) * sizeof(m3dti_t));
  154. if(!m3d->tmap) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  155. memset(&m3d->tmap[m3d->numtmap], 0, num_tris * 3 * sizeof(m3dti_t));
  156. for (vi = 0; vi < num_tris; vi++) {
  157. m3d->face[m3d->numface].materialid = mi;
  158. for(i = 0; i < 3; i++) {
  159. pos = ufbx_get_vertex_vec3(&mesh->vertex_position, tri_indices[3 * vi + i]);
  160. pos = ufbx_transform_position(&node->geometry_to_world, pos);
  161. m3d->vertex[m3d->numvertex].x = pos.x;
  162. m3d->vertex[m3d->numvertex].y = pos.y;
  163. m3d->vertex[m3d->numvertex].z = pos.z;
  164. m3d->vertex[m3d->numvertex].w = 1.0;
  165. m3d->vertex[m3d->numvertex].type = VT_WORLD;
  166. /*
  167. TODO: get skinid
  168. // The skinning vertex stream is pre-calculated above so we just need to
  169. // copy the right one by the vertex index.
  170. if (skin) {
  171. skin_vertices[num_indices] = mesh_skin_vertices[mesh->vertex_indices.data[tri_indices[3 * vi + i]]];
  172. }
  173. */
  174. m3d->vertex[m3d->numvertex].skinid = -1U;
  175. if(mesh->vertex_color.exists) {
  176. color = ufbx_get_vertex_vec4(&mesh->vertex_color, tri_indices[3 * vi + i]);
  177. m3d->vertex[m3d->numvertex].color =
  178. ((uint32_t)(color.w * 255.0) << 24L) |
  179. ((uint32_t)(color.z * 255.0) << 16L) |
  180. ((uint32_t)(color.y * 255.0) << 8L) |
  181. ((uint32_t)(color.x * 255.0));
  182. }
  183. m3d->face[m3d->numface].vertex[i] = m3d->numvertex++;
  184. if(mesh->vertex_normal.exists) {
  185. normal = ufbx_get_vertex_vec3(&mesh->vertex_normal, tri_indices[3 * vi + i]);
  186. normal = ufbx_transform_direction(&normal_to_world, normal);
  187. length = sqrt(normal.x*normal.x + normal.y*normal.y + normal.z*normal.z);
  188. m3d->vertex[m3d->numvertex].x = normal.x / length;
  189. m3d->vertex[m3d->numvertex].y = normal.y / length;
  190. m3d->vertex[m3d->numvertex].z = normal.z / length;
  191. m3d->vertex[m3d->numvertex].w = 1.0;
  192. m3d->vertex[m3d->numvertex].type = VT_NORMAL;
  193. m3d->vertex[m3d->numvertex].skinid = -1U;
  194. m3d->face[m3d->numface].normal[i] = m3d->numvertex++;
  195. }
  196. if(mesh->vertex_uv.exists) {
  197. uv = ufbx_get_vertex_vec2(&mesh->vertex_uv, tri_indices[3 * vi + i]);
  198. m3d->tmap[m3d->numtmap].u = uv.x;
  199. m3d->tmap[m3d->numtmap].v = uv.y;
  200. m3d->face[m3d->numface].texcoord[i] = m3d->numtmap++;
  201. }
  202. }
  203. m3d->numface++;
  204. }
  205. }
  206. }
  207. free(tri_indices);
  208. free(mesh_skin_vertices);
  209. }
  210. /**
  211. * Load a model and convert it's structures into a Model 3D in-memory format
  212. */
  213. m3d_t *fbx_load(unsigned char *data, unsigned int size)
  214. {
  215. m3d_t *m3d;
  216. ufbx_load_opts opts = { 0 };
  217. ufbx_error error = { 0 };
  218. ufbx_scene *scene = NULL;
  219. size_t i, j;
  220. opts.load_external_files = true;
  221. opts.allow_null_material = true;
  222. opts.evaluate_skinning = true;
  223. opts.target_axes.right = UFBX_COORDINATE_AXIS_POSITIVE_X;
  224. opts.target_axes.up = UFBX_COORDINATE_AXIS_POSITIVE_Y;
  225. opts.target_axes.front = UFBX_COORDINATE_AXIS_POSITIVE_Z;
  226. /* load the model using ufbx */
  227. scene = ufbx_load_memory((const void*)data, size, &opts, &error);
  228. if(!scene) {
  229. fprintf(stderr, "m3dconv: ufbx failed to parse model file (err %x, '%s')\n", error.type, error.description.data); exit(1);
  230. }
  231. m3d = (m3d_t*)malloc(sizeof(m3d_t));
  232. if(!m3d) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  233. memset(m3d, 0, sizeof(m3d_t));
  234. m3d->flags = M3D_FLG_FREESTR;
  235. /*
  236. for(i = 0; i < scene->metadata.scene_props.props.count; i++)
  237. printf("%s='%s'\n",scene->metadata.scene_props.props.data[i].name.data,scene->metadata.scene_props.props.data[i].value_str.data);
  238. */
  239. if(scene->metadata.creator.data) {
  240. m3d->author = (char*)malloc(scene->metadata.creator.length + 1);
  241. if(!m3d->author) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  242. strcpy(m3d->author, scene->metadata.creator.data);
  243. }
  244. /* add default position and orientation, may be needed by bones in group statements */
  245. m3d->numvertex = 2;
  246. m3d->vertex = (m3dv_t*)malloc(m3d->numvertex * sizeof(m3dv_t));
  247. if(!m3d->vertex) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  248. memset(m3d->vertex, 0, 2 * sizeof(m3dv_t));
  249. m3d->vertex[0].skinid = -1U;
  250. m3d->vertex[0].type = VT_WORLD;
  251. m3d->vertex[1].skinid = -2U;
  252. m3d->vertex[1].type = VT_QUATERN;
  253. /* get the mesh */
  254. for(i = 0; i < scene->meshes.count; i++)
  255. for(j = 0; j < scene->meshes.data[i]->instances.count; j++)
  256. read_mesh(m3d, scene->meshes.data[i], scene->meshes.data[i]->instances.data[j]);
  257. ufbx_free_scene(scene);
  258. return m3d;
  259. }