lodviewer.cpp 17 KB

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