meshencoder.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. // Converts .obj files to .optmesh files
  2. // Usage: meshencoder [.obj] [.optmesh]
  3. // Data layout:
  4. // Header: 64b
  5. // Object table: 16b * object_count
  6. // Object data
  7. // Vertex data
  8. // Index data
  9. #include "../src/meshoptimizer.h"
  10. #include "objparser.h"
  11. #include <algorithm>
  12. #include <vector>
  13. #include <float.h>
  14. #include <math.h>
  15. #include <stdio.h>
  16. #include <string.h>
  17. struct Header
  18. {
  19. char magic[4]; // OPTM
  20. unsigned int group_count;
  21. unsigned int vertex_count;
  22. unsigned int index_count;
  23. unsigned int vertex_data_size;
  24. unsigned int index_data_size;
  25. float pos_offset[3];
  26. float pos_scale;
  27. float uv_offset[2];
  28. float uv_scale[2];
  29. unsigned int reserved[2];
  30. };
  31. struct Object
  32. {
  33. unsigned int index_offset;
  34. unsigned int index_count;
  35. unsigned int material_length;
  36. unsigned int reserved;
  37. };
  38. struct Vertex
  39. {
  40. unsigned short px, py, pz, pw; // unsigned 16-bit value, use pos_offset/pos_scale to unpack
  41. char nx, ny, nz, nw; // normalized signed 8-bit value
  42. unsigned short tx, ty; // unsigned 16-bit value, use uv_offset/uv_scale to unpack
  43. };
  44. float rcpSafe(float v)
  45. {
  46. return v == 0.f ? 0.f : 1.f / v;
  47. }
  48. int main(int argc, char** argv)
  49. {
  50. if (argc <= 2)
  51. {
  52. printf("Usage: %s [.obj] [.optmesh]\n", argv[0]);
  53. return 1;
  54. }
  55. const char* input = argv[1];
  56. const char* output = argv[2];
  57. ObjFile file;
  58. if (!objParseFile(file, input))
  59. {
  60. printf("Error loading %s: file not found\n", input);
  61. return 2;
  62. }
  63. if (!objValidate(file))
  64. {
  65. printf("Error loading %s: invalid file data\n", input);
  66. return 3;
  67. }
  68. float pos_offset[3] = { FLT_MAX, FLT_MAX, FLT_MAX };
  69. float pos_scale = 0.f;
  70. for (size_t i = 0; i < file.v_size; i += 3)
  71. {
  72. pos_offset[0] = std::min(pos_offset[0], file.v[i + 0]);
  73. pos_offset[1] = std::min(pos_offset[1], file.v[i + 1]);
  74. pos_offset[2] = std::min(pos_offset[2], file.v[i + 2]);
  75. }
  76. for (size_t i = 0; i < file.v_size; i += 3)
  77. {
  78. pos_scale = std::max(pos_scale, file.v[i + 0] - pos_offset[0]);
  79. pos_scale = std::max(pos_scale, file.v[i + 1] - pos_offset[1]);
  80. pos_scale = std::max(pos_scale, file.v[i + 2] - pos_offset[2]);
  81. }
  82. float uv_offset[2] = { FLT_MAX, FLT_MAX };
  83. float uv_scale[2] = { 0, 0 };
  84. for (size_t i = 0; i < file.vt_size; i += 3)
  85. {
  86. uv_offset[0] = std::min(uv_offset[0], file.vt[i + 0]);
  87. uv_offset[1] = std::min(uv_offset[1], file.vt[i + 1]);
  88. }
  89. for (size_t i = 0; i < file.vt_size; i += 3)
  90. {
  91. uv_scale[0] = std::max(uv_scale[0], file.vt[i + 0] - uv_offset[0]);
  92. uv_scale[1] = std::max(uv_scale[1], file.vt[i + 1] - uv_offset[1]);
  93. }
  94. float pos_scale_inverse = rcpSafe(pos_scale);
  95. float uv_scale_inverse[2] = { rcpSafe(uv_scale[0]), rcpSafe(uv_scale[1]) };
  96. size_t total_indices = file.f_size / 3;
  97. std::vector<Vertex> triangles(total_indices);
  98. int pos_bits = 14;
  99. int uv_bits = 12;
  100. for (size_t i = 0; i < total_indices; ++i)
  101. {
  102. int vi = file.f[i * 3 + 0];
  103. int vti = file.f[i * 3 + 1];
  104. int vni = file.f[i * 3 + 2];
  105. // note: we scale the vertices uniformly; this is not the best option wrt compression quality
  106. // however, it means we can scale the mesh uniformly without distorting the normals
  107. // this is helpful for backends like ThreeJS that apply mesh scaling to normals
  108. float px = (file.v[vi * 3 + 0] - pos_offset[0]) * pos_scale_inverse;
  109. float py = (file.v[vi * 3 + 1] - pos_offset[1]) * pos_scale_inverse;
  110. float pz = (file.v[vi * 3 + 2] - pos_offset[2]) * pos_scale_inverse;
  111. // normal is 0 if absent from the mesh
  112. float nx = vni >= 0 ? file.vn[vni * 3 + 0] : 0;
  113. float ny = vni >= 0 ? file.vn[vni * 3 + 1] : 0;
  114. float nz = vni >= 0 ? file.vn[vni * 3 + 2] : 0;
  115. // scale the normal to make sure the largest component is +-1.0
  116. // this reduces the entropy of the normal by ~1.5 bits without losing precision
  117. // it's better to use octahedral encoding but that requires special shader support
  118. float nm = std::max(fabsf(nx), std::max(fabsf(ny), fabsf(nz)));
  119. float ns = nm == 0.f ? 0.f : 1 / nm;
  120. nx *= ns;
  121. ny *= ns;
  122. nz *= ns;
  123. // texture coordinates are 0 if absent, and require a texture matrix to decode
  124. float tx = vti >= 0 ? (file.vt[vti * 3 + 0] - uv_offset[0]) * uv_scale_inverse[0] : 0;
  125. float ty = vti >= 0 ? (file.vt[vti * 3 + 1] - uv_offset[1]) * uv_scale_inverse[1] : 0;
  126. Vertex v =
  127. {
  128. (unsigned short)(meshopt_quantizeUnorm(px, pos_bits)),
  129. (unsigned short)(meshopt_quantizeUnorm(py, pos_bits)),
  130. (unsigned short)(meshopt_quantizeUnorm(pz, pos_bits)),
  131. 0,
  132. char(meshopt_quantizeSnorm(nx, 8)),
  133. char(meshopt_quantizeSnorm(ny, 8)),
  134. char(meshopt_quantizeSnorm(nz, 8)),
  135. 0,
  136. (unsigned short)(meshopt_quantizeUnorm(tx, uv_bits)),
  137. (unsigned short)(meshopt_quantizeUnorm(ty, uv_bits)),
  138. };
  139. triangles[i] = v;
  140. }
  141. std::vector<unsigned int> remap(total_indices);
  142. size_t total_vertices = meshopt_generateVertexRemap(&remap[0], NULL, total_indices, &triangles[0], total_indices, sizeof(Vertex));
  143. std::vector<unsigned int> indices(total_indices);
  144. meshopt_remapIndexBuffer(&indices[0], NULL, total_indices, &remap[0]);
  145. std::vector<Vertex> vertices(total_vertices);
  146. meshopt_remapVertexBuffer(&vertices[0], &triangles[0], total_indices, sizeof(Vertex), &remap[0]);
  147. for (size_t i = 0; i < file.g_size; ++i)
  148. {
  149. ObjGroup& g = file.g[i];
  150. meshopt_optimizeVertexCache(&indices[g.index_offset], &indices[g.index_offset], g.index_count, vertices.size());
  151. }
  152. meshopt_optimizeVertexFetch(&vertices[0], &indices[0], indices.size(), &vertices[0], vertices.size(), sizeof(Vertex));
  153. std::vector<unsigned char> vbuf(meshopt_encodeVertexBufferBound(vertices.size(), sizeof(Vertex)));
  154. vbuf.resize(meshopt_encodeVertexBuffer(&vbuf[0], vbuf.size(), &vertices[0], vertices.size(), sizeof(Vertex)));
  155. std::vector<unsigned char> ibuf(meshopt_encodeIndexBufferBound(indices.size(), vertices.size()));
  156. ibuf.resize(meshopt_encodeIndexBuffer(&ibuf[0], ibuf.size(), &indices[0], indices.size()));
  157. FILE* result = fopen(output, "wb");
  158. if (!result)
  159. {
  160. printf("Error saving %s: can't open file for writing\n", output);
  161. return 4;
  162. }
  163. Header header = {};
  164. memcpy(header.magic, "OPTM", 4);
  165. header.group_count = unsigned(file.g_size);
  166. header.vertex_count = unsigned(vertices.size());
  167. header.index_count = unsigned(indices.size());
  168. header.vertex_data_size = unsigned(vbuf.size());
  169. header.index_data_size = unsigned(ibuf.size());
  170. header.pos_offset[0] = pos_offset[0];
  171. header.pos_offset[1] = pos_offset[1];
  172. header.pos_offset[2] = pos_offset[2];
  173. header.pos_scale = pos_scale / float((1 << pos_bits) - 1);
  174. header.uv_offset[0] = uv_offset[0];
  175. header.uv_offset[1] = uv_offset[1];
  176. header.uv_scale[0] = uv_scale[0] / float((1 << uv_bits) - 1);
  177. header.uv_scale[1] = uv_scale[1] / float((1 << uv_bits) - 1);
  178. fwrite(&header, 1, sizeof(header), result);
  179. for (size_t i = 0; i < file.g_size; ++i)
  180. {
  181. ObjGroup& g = file.g[i];
  182. Object object = {};
  183. object.index_offset = unsigned(g.index_offset);
  184. object.index_count = unsigned(g.index_count);
  185. object.material_length = unsigned(strlen(g.material));
  186. fwrite(&object, 1, sizeof(object), result);
  187. }
  188. for (size_t i = 0; i < file.g_size; ++i)
  189. {
  190. ObjGroup& g = file.g[i];
  191. fwrite(g.material, 1, strlen(g.material), result);
  192. }
  193. fwrite(&vbuf[0], 1, vbuf.size(), result);
  194. fwrite(&ibuf[0], 1, ibuf.size(), result);
  195. fclose(result);
  196. return 0;
  197. }