lodviewer.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include "../src/meshoptimizer.h"
  3. #include "fast_obj.h"
  4. #include "cgltf.h"
  5. #include <algorithm>
  6. #include <cmath>
  7. #include <cstdio>
  8. #include <ctime>
  9. #include <vector>
  10. #include <GLFW/glfw3.h>
  11. #ifdef _WIN32
  12. #pragma comment(lib, "opengl32.lib")
  13. #endif
  14. extern unsigned char* meshopt_simplifyDebugKind;
  15. extern unsigned int* meshopt_simplifyDebugLoop;
  16. #ifndef TRACE
  17. unsigned char* meshopt_simplifyDebugKind;
  18. unsigned int* meshopt_simplifyDebugLoop;
  19. #endif
  20. struct Options
  21. {
  22. bool wireframe;
  23. enum
  24. {
  25. Mode_Default,
  26. Mode_Texture,
  27. Mode_Normals,
  28. Mode_UV,
  29. Mode_Kind,
  30. } mode;
  31. };
  32. struct Vertex
  33. {
  34. float px, py, pz;
  35. float nx, ny, nz;
  36. float tx, ty;
  37. };
  38. struct Mesh
  39. {
  40. std::vector<Vertex> vertices;
  41. std::vector<unsigned int> indices;
  42. // TODO: this is debug only visualization and will go away at some point
  43. std::vector<unsigned char> kinds;
  44. std::vector<unsigned int> loop;
  45. };
  46. Mesh parseObj(const char* path)
  47. {
  48. fastObjMesh* obj = fast_obj_read(path);
  49. if (!obj)
  50. {
  51. printf("Error loading %s: file not found\n", path);
  52. return Mesh();
  53. }
  54. size_t total_indices = 0;
  55. for (unsigned int i = 0; i < obj->face_count; ++i)
  56. total_indices += 3 * (obj->face_vertices[i] - 2);
  57. std::vector<Vertex> vertices(total_indices);
  58. size_t vertex_offset = 0;
  59. size_t index_offset = 0;
  60. for (unsigned int i = 0; i < obj->face_count; ++i)
  61. {
  62. for (unsigned int j = 0; j < obj->face_vertices[i]; ++j)
  63. {
  64. fastObjIndex gi = obj->indices[index_offset + j];
  65. Vertex v =
  66. {
  67. obj->positions[gi.p * 3 + 0],
  68. obj->positions[gi.p * 3 + 1],
  69. obj->positions[gi.p * 3 + 2],
  70. obj->normals[gi.n * 3 + 0],
  71. obj->normals[gi.n * 3 + 1],
  72. obj->normals[gi.n * 3 + 2],
  73. obj->texcoords[gi.t * 2 + 0],
  74. obj->texcoords[gi.t * 2 + 1],
  75. };
  76. // triangulate polygon on the fly; offset-3 is always the first polygon vertex
  77. if (j >= 3)
  78. {
  79. vertices[vertex_offset + 0] = vertices[vertex_offset - 3];
  80. vertices[vertex_offset + 1] = vertices[vertex_offset - 1];
  81. vertex_offset += 2;
  82. }
  83. vertices[vertex_offset] = v;
  84. vertex_offset++;
  85. }
  86. index_offset += obj->face_vertices[i];
  87. }
  88. fast_obj_destroy(obj);
  89. Mesh result;
  90. std::vector<unsigned int> remap(total_indices);
  91. size_t total_vertices = meshopt_generateVertexRemap(&remap[0], NULL, total_indices, &vertices[0], total_indices, sizeof(Vertex));
  92. result.indices.resize(total_indices);
  93. meshopt_remapIndexBuffer(&result.indices[0], NULL, total_indices, &remap[0]);
  94. result.vertices.resize(total_vertices);
  95. meshopt_remapVertexBuffer(&result.vertices[0], &vertices[0], total_indices, sizeof(Vertex), &remap[0]);
  96. return result;
  97. }
  98. cgltf_accessor* getAccessor(const cgltf_attribute* attributes, size_t attribute_count, cgltf_attribute_type type, int index = 0)
  99. {
  100. for (size_t i = 0; i < attribute_count; ++i)
  101. if (attributes[i].type == type && attributes[i].index == index)
  102. return attributes[i].data;
  103. return 0;
  104. }
  105. Mesh parseGltf(const char* path)
  106. {
  107. cgltf_options options = {};
  108. cgltf_data* data = 0;
  109. cgltf_result res = cgltf_parse_file(&options, path, &data);
  110. if (res != cgltf_result_success)
  111. {
  112. return Mesh();
  113. }
  114. res = cgltf_load_buffers(&options, data, path);
  115. if (res != cgltf_result_success)
  116. {
  117. cgltf_free(data);
  118. return Mesh();
  119. }
  120. res = cgltf_validate(data);
  121. if (res != cgltf_result_success)
  122. {
  123. cgltf_free(data);
  124. return Mesh();
  125. }
  126. size_t total_vertices = 0;
  127. size_t total_indices = 0;
  128. for (size_t ni = 0; ni < data->nodes_count; ++ni)
  129. {
  130. if (!data->nodes[ni].mesh)
  131. continue;
  132. const cgltf_mesh& mesh = *data->nodes[ni].mesh;
  133. for (size_t pi = 0; pi < mesh.primitives_count; ++pi)
  134. {
  135. const cgltf_primitive& primitive = mesh.primitives[pi];
  136. cgltf_accessor* ai = primitive.indices;
  137. cgltf_accessor* ap = getAccessor(primitive.attributes, primitive.attributes_count, cgltf_attribute_type_position);
  138. if (!ai || !ap)
  139. continue;
  140. total_vertices += ap->count;
  141. total_indices += ai->count;
  142. }
  143. }
  144. Mesh result;
  145. result.vertices.resize(total_vertices);
  146. result.indices.resize(total_indices);
  147. size_t vertex_offset = 0;
  148. size_t index_offset = 0;
  149. for (size_t ni = 0; ni < data->nodes_count; ++ni)
  150. {
  151. if (!data->nodes[ni].mesh)
  152. continue;
  153. const cgltf_mesh& mesh = *data->nodes[ni].mesh;
  154. float transform[16];
  155. cgltf_node_transform_world(&data->nodes[ni], transform);
  156. for (size_t pi = 0; pi < mesh.primitives_count; ++pi)
  157. {
  158. const cgltf_primitive& primitive = mesh.primitives[pi];
  159. cgltf_accessor* ai = primitive.indices;
  160. cgltf_accessor* ap = getAccessor(primitive.attributes, primitive.attributes_count, cgltf_attribute_type_position);
  161. if (!ai || !ap)
  162. continue;
  163. for (size_t i = 0; i < ai->count; ++i)
  164. result.indices[index_offset + i] = unsigned(vertex_offset + cgltf_accessor_read_index(ai, i));
  165. {
  166. for (size_t i = 0; i < ap->count; ++i)
  167. {
  168. float ptr[3];
  169. cgltf_accessor_read_float(ap, i, ptr, 3);
  170. result.vertices[vertex_offset + i].px = ptr[0] * transform[0] + ptr[1] * transform[4] + ptr[2] * transform[8] + transform[12];
  171. result.vertices[vertex_offset + i].py = ptr[0] * transform[1] + ptr[1] * transform[5] + ptr[2] * transform[9] + transform[13];
  172. result.vertices[vertex_offset + i].pz = ptr[0] * transform[2] + ptr[1] * transform[6] + ptr[2] * transform[10] + transform[14];
  173. }
  174. }
  175. if (cgltf_accessor* an = getAccessor(primitive.attributes, primitive.attributes_count, cgltf_attribute_type_normal))
  176. {
  177. for (size_t i = 0; i < ap->count; ++i)
  178. {
  179. float ptr[3];
  180. cgltf_accessor_read_float(an, i, ptr, 3);
  181. result.vertices[vertex_offset + i].nx = ptr[0] * transform[0] + ptr[1] * transform[4] + ptr[2] * transform[8];
  182. result.vertices[vertex_offset + i].ny = ptr[0] * transform[1] + ptr[1] * transform[5] + ptr[2] * transform[9];
  183. result.vertices[vertex_offset + i].nz = ptr[0] * transform[2] + ptr[1] * transform[6] + ptr[2] * transform[10];
  184. }
  185. }
  186. if (cgltf_accessor* at = getAccessor(primitive.attributes, primitive.attributes_count, cgltf_attribute_type_texcoord))
  187. {
  188. for (size_t i = 0; i < ap->count; ++i)
  189. {
  190. float ptr[2];
  191. cgltf_accessor_read_float(at, i, ptr, 2);
  192. result.vertices[vertex_offset + i].tx = ptr[0];
  193. result.vertices[vertex_offset + i].ty = ptr[1];
  194. }
  195. }
  196. vertex_offset += ap->count;
  197. index_offset += ai->count;
  198. }
  199. }
  200. std::vector<unsigned int> remap(total_indices);
  201. size_t unique_vertices = meshopt_generateVertexRemap(&remap[0], &result.indices[0], total_indices, &result.vertices[0], total_vertices, sizeof(Vertex));
  202. meshopt_remapIndexBuffer(&result.indices[0], &result.indices[0], total_indices, &remap[0]);
  203. meshopt_remapVertexBuffer(&result.vertices[0], &result.vertices[0], total_vertices, sizeof(Vertex), &remap[0]);
  204. result.vertices.resize(unique_vertices);
  205. cgltf_free(data);
  206. return result;
  207. }
  208. Mesh loadMesh(const char* path)
  209. {
  210. if (strstr(path, ".obj"))
  211. return parseObj(path);
  212. if (strstr(path, ".gltf") || strstr(path, ".glb"))
  213. return parseGltf(path);
  214. return Mesh();
  215. }
  216. bool saveObj(const Mesh& mesh, const char* path)
  217. {
  218. std::vector<Vertex> verts = mesh.vertices;
  219. std::vector<unsigned int> tris = mesh.indices;
  220. size_t vertcount = meshopt_optimizeVertexFetch(verts.data(), tris.data(), tris.size(), verts.data(), verts.size(), sizeof(Vertex));
  221. FILE* obj = fopen(path, "w");
  222. if (!obj)
  223. return false;
  224. for (size_t i = 0; i < vertcount; ++i)
  225. {
  226. fprintf(obj, "v %f %f %f\n", verts[i].px, verts[i].py, verts[i].pz);
  227. fprintf(obj, "vn %f %f %f\n", verts[i].nx, verts[i].ny, verts[i].nz);
  228. fprintf(obj, "vt %f %f %f\n", verts[i].tx, verts[i].ty, 0.f);
  229. }
  230. for (size_t i = 0; i < tris.size(); i += 3)
  231. {
  232. unsigned int i0 = tris[i + 0] + 1;
  233. unsigned int i1 = tris[i + 1] + 1;
  234. unsigned int i2 = tris[i + 2] + 1;
  235. fprintf(obj, "f %d/%d/%d %d/%d/%d %d/%d/%d\n", i0, i0, i0, i1, i1, i1, i2, i2, i2);
  236. }
  237. fclose(obj);
  238. return true;
  239. }
  240. Mesh optimize(const Mesh& mesh, int lod)
  241. {
  242. float threshold = powf(0.5f, float(lod));
  243. size_t target_index_count = size_t(mesh.indices.size() * threshold);
  244. float target_error = 1e-2f;
  245. Mesh result = mesh;
  246. result.kinds.resize(result.vertices.size());
  247. result.loop.resize(result.vertices.size());
  248. meshopt_simplifyDebugKind = &result.kinds[0];
  249. meshopt_simplifyDebugLoop = &result.loop[0];
  250. result.indices.resize(meshopt_simplify(&result.indices[0], &result.indices[0], mesh.indices.size(), &mesh.vertices[0].px, mesh.vertices.size(), sizeof(Vertex), target_index_count, target_error));
  251. return result;
  252. }
  253. void display(int x, int y, int width, int height, const Mesh& mesh, const Options& options)
  254. {
  255. glViewport(x, y, width, height);
  256. glEnable(GL_DEPTH_TEST);
  257. glDepthFunc(GL_LESS);
  258. glDepthMask(GL_TRUE);
  259. glMatrixMode(GL_MODELVIEW);
  260. glLoadIdentity();
  261. glRotatef(0.f, 0.f, 1.f, 0.f);
  262. glPolygonMode(GL_FRONT_AND_BACK, options.wireframe ? GL_LINE : GL_FILL);
  263. float centerx = 0;
  264. float centery = 0;
  265. float centerz = 0;
  266. float centeru = 0;
  267. float centerv = 0;
  268. for (size_t i = 0; i < mesh.vertices.size(); ++i)
  269. {
  270. const Vertex& v = mesh.vertices[i];
  271. centerx += v.px;
  272. centery += v.py;
  273. centerz += v.pz;
  274. centeru += v.tx;
  275. centerv += v.ty;
  276. }
  277. centerx /= float(mesh.vertices.size());
  278. centery /= float(mesh.vertices.size());
  279. centerz /= float(mesh.vertices.size());
  280. centeru /= float(mesh.vertices.size());
  281. centerv /= float(mesh.vertices.size());
  282. float extent = 0;
  283. float extentuv = 0;
  284. for (size_t i = 0; i < mesh.vertices.size(); ++i)
  285. {
  286. const Vertex& v = mesh.vertices[i];
  287. extent = std::max(extent, fabsf(v.px - centerx));
  288. extent = std::max(extent, fabsf(v.py - centery));
  289. extent = std::max(extent, fabsf(v.pz - centerz));
  290. extentuv = std::max(extentuv, fabsf(v.tx - centeru));
  291. extentuv = std::max(extentuv, fabsf(v.ty - centerv));
  292. }
  293. extent *= 1.1f;
  294. extentuv *= 1.1f;
  295. float scalex = width > height ? float(height) / float(width) : 1;
  296. float scaley = height > width ? float(width) / float(height) : 1;
  297. glBegin(GL_TRIANGLES);
  298. for (size_t i = 0; i < mesh.indices.size(); ++i)
  299. {
  300. const Vertex& v = mesh.vertices[mesh.indices[i]];
  301. float intensity = -(v.pz - centerz) / extent * 0.5f + 0.5f;
  302. switch (options.mode)
  303. {
  304. case Options::Mode_UV:
  305. glColor3f(intensity, intensity, intensity);
  306. glVertex3f((v.tx - centeru) / extentuv * scalex, (v.ty - centerv) / extentuv * scaley, 0);
  307. break;
  308. case Options::Mode_Texture:
  309. glColor3f(v.tx - floorf(v.tx), v.ty - floorf(v.ty), 0.5f);
  310. glVertex3f((v.px - centerx) / extent * scalex, (v.py - centery) / extent * scaley, (v.pz - centerz) / extent);
  311. break;
  312. case Options::Mode_Normals:
  313. glColor3f(v.nx * 0.5f + 0.5f, v.ny * 0.5f + 0.5f, v.nz * 0.5f + 0.5f);
  314. glVertex3f((v.px - centerx) / extent * scalex, (v.py - centery) / extent * scaley, (v.pz - centerz) / extent);
  315. break;
  316. default:
  317. glColor3f(intensity, intensity, intensity);
  318. glVertex3f((v.px - centerx) / extent * scalex, (v.py - centery) / extent * scaley, (v.pz - centerz) / extent);
  319. }
  320. }
  321. glEnd();
  322. float zbias = 1e-3f;
  323. if (options.mode == Options::Mode_Kind && !mesh.kinds.empty() && !mesh.loop.empty())
  324. {
  325. glLineWidth(1);
  326. glBegin(GL_LINES);
  327. for (size_t i = 0; i < mesh.indices.size(); ++i)
  328. {
  329. unsigned int a = mesh.indices[i];
  330. unsigned int b = mesh.loop[a];
  331. if (b != ~0u)
  332. {
  333. const Vertex& v0 = mesh.vertices[a];
  334. const Vertex& v1 = mesh.vertices[b];
  335. unsigned char kind = mesh.kinds[a];
  336. glColor3f(kind == 0 || kind == 4, kind == 0 || kind == 2 || kind == 3, kind == 0 || kind == 1 || kind == 3);
  337. glVertex3f((v0.px - centerx) / extent * scalex, (v0.py - centery) / extent * scaley, (v0.pz - centerz) / extent - zbias);
  338. glVertex3f((v1.px - centerx) / extent * scalex, (v1.py - centery) / extent * scaley, (v1.pz - centerz) / extent - zbias);
  339. }
  340. }
  341. glEnd();
  342. glPointSize(3);
  343. glBegin(GL_POINTS);
  344. for (size_t i = 0; i < mesh.indices.size(); ++i)
  345. {
  346. const Vertex& v = mesh.vertices[mesh.indices[i]];
  347. unsigned char kind = mesh.kinds[mesh.indices[i]];
  348. if (kind != 0)
  349. {
  350. glColor3f(kind == 0 || kind == 4, kind == 0 || kind == 2 || kind == 3, kind == 0 || kind == 1 || kind == 3);
  351. glVertex3f((v.px - centerx) / extent * scalex, (v.py - centery) / extent * scaley, (v.pz - centerz) / extent - zbias * 2);
  352. }
  353. }
  354. glEnd();
  355. }
  356. }
  357. void stats(GLFWwindow* window, const char* path, unsigned int triangles, int lod, double time)
  358. {
  359. char title[256];
  360. snprintf(title, sizeof(title), "%s: LOD %d - %d triangles (%.1f msec)", path, lod, triangles, time * 1000);
  361. glfwSetWindowTitle(window, title);
  362. }
  363. struct File
  364. {
  365. Mesh basemesh;
  366. Mesh lodmesh;
  367. const char* path;
  368. };
  369. std::vector<File> files;
  370. Options options;
  371. bool redraw;
  372. void keyhandler(GLFWwindow* window, int key, int scancode, int action, int mods)
  373. {
  374. if (action == GLFW_PRESS)
  375. {
  376. if (key == GLFW_KEY_W)
  377. {
  378. options.wireframe = !options.wireframe;
  379. redraw = true;
  380. }
  381. else if (key == GLFW_KEY_T)
  382. {
  383. options.mode = options.mode == Options::Mode_Texture ? Options::Mode_Default : Options::Mode_Texture;
  384. redraw = true;
  385. }
  386. else if (key == GLFW_KEY_N)
  387. {
  388. options.mode = options.mode == Options::Mode_Normals ? Options::Mode_Default : Options::Mode_Normals;
  389. redraw = true;
  390. }
  391. else if (key == GLFW_KEY_U)
  392. {
  393. options.mode = options.mode == Options::Mode_UV ? Options::Mode_Default : Options::Mode_UV;
  394. redraw = true;
  395. }
  396. else if (key == GLFW_KEY_K)
  397. {
  398. options.mode = options.mode == Options::Mode_Kind ? Options::Mode_Default : Options::Mode_Kind;
  399. redraw = true;
  400. }
  401. else if (key >= GLFW_KEY_0 && key <= GLFW_KEY_9)
  402. {
  403. int lod = int(key - GLFW_KEY_0);
  404. unsigned int triangles = 0;
  405. clock_t start = clock();
  406. for (auto& f : files)
  407. {
  408. f.lodmesh = optimize(f.basemesh, lod);
  409. triangles += unsigned(f.lodmesh.indices.size() / 3);
  410. }
  411. clock_t end = clock();
  412. stats(window, files[0].path, triangles, lod, double(end - start) / CLOCKS_PER_SEC);
  413. redraw = true;
  414. }
  415. else if (key == GLFW_KEY_S)
  416. {
  417. int i = 0;
  418. for (auto& f : files)
  419. {
  420. char path[32];
  421. sprintf(path, "result%d.obj", i);
  422. saveObj(f.lodmesh, path);
  423. printf("Saved LOD of %s to %s\n", f.path, path);
  424. }
  425. }
  426. }
  427. }
  428. void sizehandler(GLFWwindow* window, int width, int height)
  429. {
  430. redraw = true;
  431. }
  432. int main(int argc, char** argv)
  433. {
  434. if (argc <= 1)
  435. {
  436. printf("Usage: %s [.obj files]\n", argv[0]);
  437. return 0;
  438. }
  439. unsigned int basetriangles = 0;
  440. for (int i = 1; i < argc; ++i)
  441. {
  442. files.emplace_back();
  443. File& f = files.back();
  444. f.path = argv[i];
  445. f.basemesh = loadMesh(f.path);
  446. f.lodmesh = optimize(f.basemesh, 0);
  447. basetriangles += unsigned(f.basemesh.indices.size() / 3);
  448. }
  449. glfwInit();
  450. GLFWwindow* window = glfwCreateWindow(640, 480, "Simple example", NULL, NULL);
  451. glfwMakeContextCurrent(window);
  452. stats(window, files[0].path, basetriangles, 0, 0);
  453. glfwSetKeyCallback(window, keyhandler);
  454. glfwSetWindowSizeCallback(window, sizehandler);
  455. redraw = true;
  456. while (!glfwWindowShouldClose(window))
  457. {
  458. if (redraw)
  459. {
  460. redraw = false;
  461. int width, height;
  462. glfwGetFramebufferSize(window, &width, &height);
  463. glViewport(0, 0, width, height);
  464. glClearDepth(1.f);
  465. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  466. int cols = int(ceil(sqrt(double(files.size()))));
  467. int rows = int(ceil(double(files.size()) / cols));
  468. int tilew = width / cols;
  469. int tileh = height / rows;
  470. for (size_t i = 0; i < files.size(); ++i)
  471. {
  472. File& f = files[i];
  473. int x = int(i) % cols;
  474. int y = int(i) / cols;
  475. display(x * tilew, y * tileh, tilew, tileh, f.lodmesh, options);
  476. }
  477. glfwSwapBuffers(window);
  478. }
  479. glfwWaitEvents();
  480. }
  481. }