bunnylod.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. /*
  2. * Copyright 2011-2026 Branimir Karadzic. All rights reserved.
  3. * License: https://github.com/bkaradzic/bgfx/blob/master/LICENSE
  4. */
  5. #include <bx/easing.h>
  6. #include <bx/file.h>
  7. #include "common.h"
  8. #include "bgfx_utils.h"
  9. #include "imgui/imgui.h"
  10. extern "C" void ProgressiveMesh(int vert_n, int vert_stride, const float *v, int tri_n, const int *tri, int *map, int *permutation);
  11. namespace
  12. {
  13. class ExampleBunnyLOD : public entry::AppI
  14. {
  15. public:
  16. ExampleBunnyLOD(const char* _name, const char* _description, const char* _url)
  17. : entry::AppI(_name, _description, _url)
  18. {
  19. }
  20. void PermuteMesh(const bgfx::Memory* _vb, const bgfx::Memory* _ib, const bgfx::VertexLayout& _layout)
  21. {
  22. const uint32_t stride = _layout.getStride();
  23. const uint32_t offset = _layout.getOffset(bgfx::Attrib::Position);
  24. const uint32_t numVertices = _vb->size / stride;
  25. const uint32_t numTriangles = _ib->size / (3 * sizeof(uint32_t) );
  26. if (m_cachePermutation == NULL)
  27. {
  28. m_cachePermutation = (uint32_t*)bx::alloc(entry::getAllocator(), numVertices * sizeof(uint32_t) );
  29. m_map = (uint32_t*)bx::alloc(entry::getAllocator(), numVertices * sizeof(uint32_t) );
  30. // It will takes long time if there are too many vertices.
  31. ProgressiveMesh(
  32. numVertices
  33. , stride
  34. , (const float*)(_vb->data + offset)
  35. , numTriangles
  36. , (const int*)_ib->data
  37. , (int*)m_map
  38. , (int*)m_cachePermutation
  39. );
  40. }
  41. // rearrange the vertex Array
  42. char* temp = (char*)bx::alloc(entry::getAllocator(), numVertices * stride);
  43. bx::memCopy(temp, _vb->data, _vb->size);
  44. for (uint32_t ii = 0; ii < numVertices; ++ii)
  45. {
  46. bx::memCopy(_vb->data + m_cachePermutation[ii] * stride , temp + ii * stride, stride);
  47. }
  48. bx::free(entry::getAllocator(), temp);
  49. // update the changes in the entries in the triangle Array
  50. for (uint32_t ii = 0, num = numTriangles*3; ii < num; ++ii)
  51. {
  52. uint32_t* indices = (uint32_t*)(_ib->data + ii * sizeof(uint32_t) );
  53. *indices = m_cachePermutation[*indices];
  54. }
  55. }
  56. static void remapIndices(uint32_t* _indices, uint32_t _num)
  57. {
  58. uint32_t target = 0;
  59. for (uint32_t i = 0; i < _num; i++)
  60. {
  61. uint32_t map = _indices[i];
  62. if (i != map)
  63. {
  64. _indices[i] = _indices[map];
  65. }
  66. else
  67. {
  68. _indices[i] = target;
  69. ++target;
  70. }
  71. }
  72. }
  73. static const bgfx::Memory* mergeVertices(const uint8_t* _vb, uint16_t _stride, const uint32_t* _indices, uint32_t _num, uint32_t _numMerged)
  74. {
  75. const bgfx::Memory* mem = bgfx::alloc(_stride * _numMerged);
  76. for (uint32_t ii = 0; ii < _num; ++ii)
  77. {
  78. bx::memCopy(mem->data + _indices[ii]*_stride, _vb + ii*_stride, _stride);
  79. }
  80. return mem;
  81. }
  82. void loadMesh(Mesh* _mesh)
  83. {
  84. // merge sub mesh
  85. uint32_t numVertices = 0;
  86. uint32_t numIndices = 0;
  87. for (GroupArray::const_iterator it = _mesh->m_groups.begin(), itEnd = _mesh->m_groups.end()
  88. ; it != itEnd
  89. ; ++it
  90. )
  91. {
  92. numVertices += it->m_numVertices;
  93. numIndices += it->m_numIndices;
  94. }
  95. const bgfx::Memory* ib = bgfx::alloc(numIndices * sizeof(uint32_t) );
  96. uint8_t* vbData = (uint8_t*)bx::alloc(entry::getAllocator(), _mesh->m_layout.getSize(numVertices) );
  97. {
  98. uint32_t voffset = 0;
  99. uint32_t ioffset = 0;
  100. uint32_t index = 0;
  101. for (GroupArray::const_iterator it = _mesh->m_groups.begin(), itEnd = _mesh->m_groups.end()
  102. ; it != itEnd
  103. ; ++it
  104. )
  105. {
  106. const uint32_t vsize = _mesh->m_layout.getSize(it->m_numVertices);
  107. bx::memCopy(vbData + voffset, it->m_vertices, vsize);
  108. uint32_t* ibptr = (uint32_t*)(ib->data + ioffset);
  109. for (uint32_t ii = 0, num = it->m_numIndices; ii < num; ++ii)
  110. {
  111. ibptr[ii] = it->m_indices[ii] + index;
  112. }
  113. voffset += vsize;
  114. ioffset += uint32_t(it->m_numIndices * sizeof(uint32_t) );
  115. index += uint32_t(it->m_numVertices);
  116. }
  117. }
  118. bool cacheInvalid = false;
  119. loadCache();
  120. if (m_originalVertices != numVertices
  121. || m_cacheWeld == NULL)
  122. {
  123. cacheInvalid = true;
  124. m_originalVertices = numVertices;
  125. bx::free(entry::getAllocator(), m_cachePermutation);
  126. m_cachePermutation = NULL;
  127. bx::free(entry::getAllocator(), m_cacheWeld);
  128. m_cacheWeld = (uint32_t*)bx::alloc(entry::getAllocator(), numVertices * sizeof(uint32_t) );
  129. m_totalVertices = bgfx::weldVertices(m_cacheWeld, _mesh->m_layout, vbData, numVertices, true, 0.00001f);
  130. remapIndices(m_cacheWeld, numVertices);
  131. }
  132. const bgfx::Memory* vb = mergeVertices(
  133. vbData
  134. , _mesh->m_layout.getStride()
  135. , m_cacheWeld
  136. , numVertices
  137. , m_totalVertices
  138. );
  139. bx::free(entry::getAllocator(), vbData);
  140. {
  141. uint32_t* ibData = (uint32_t*)ib->data;
  142. for (uint32_t ii = 0; ii < numIndices; ++ii)
  143. {
  144. ibData[ii] = m_cacheWeld[ibData[ii] ];
  145. }
  146. }
  147. PermuteMesh(vb, ib, _mesh->m_layout);
  148. if (cacheInvalid)
  149. {
  150. saveCache();
  151. }
  152. m_triangle = (uint32_t*)bx::alloc(entry::getAllocator(), ib->size);
  153. bx::memCopy(m_triangle, ib->data, ib->size);
  154. m_vb = bgfx::createVertexBuffer(vb, _mesh->m_layout);
  155. m_ib = bgfx::createDynamicIndexBuffer(ib, BGFX_BUFFER_INDEX32);
  156. m_numVertices = m_totalVertices;
  157. m_numTriangles = numIndices/3;
  158. m_totalTriangles = m_numTriangles;
  159. }
  160. const bx::FilePath kCacheFilePath = bx::FilePath("temp/bunnylod.cache");
  161. void loadCache()
  162. {
  163. m_cacheWeld = NULL;
  164. m_cachePermutation = NULL;
  165. m_originalVertices = 0;
  166. bx::Error err;
  167. bx::FileReader reader;
  168. if (bx::open(&reader, kCacheFilePath) )
  169. {
  170. bx::read(&reader, m_originalVertices, &err);
  171. bx::read(&reader, m_totalVertices, &err);
  172. m_cacheWeld = (uint32_t*)bx::alloc(entry::getAllocator(), m_originalVertices * sizeof(uint32_t) );
  173. bx::read(&reader, m_cacheWeld, m_originalVertices * sizeof(uint32_t), &err);
  174. m_cachePermutation = (uint32_t*)bx::alloc(entry::getAllocator(), m_totalVertices * sizeof(uint32_t) );
  175. bx::read(&reader, m_cachePermutation, m_totalVertices * sizeof(uint32_t), &err);
  176. m_map = (uint32_t*)bx::alloc(entry::getAllocator(), m_totalVertices * sizeof(uint32_t) );
  177. bx::read(&reader, m_map, m_totalVertices * sizeof(uint32_t), &err);
  178. if (!err.isOk() )
  179. {
  180. // read fail
  181. bx::free(entry::getAllocator(), m_cacheWeld);
  182. m_cacheWeld = NULL;
  183. bx::free(entry::getAllocator(), m_cachePermutation);
  184. m_cachePermutation = NULL;
  185. bx::free(entry::getAllocator(), m_map);
  186. m_map = NULL;
  187. }
  188. bx::close(&reader);
  189. }
  190. }
  191. void saveCache()
  192. {
  193. bx::FileWriter writer;
  194. if (bx::open(&writer, kCacheFilePath) )
  195. {
  196. bx::Error err;
  197. bx::write(&writer, m_originalVertices, &err);
  198. bx::write(&writer, m_totalVertices, &err);
  199. bx::write(&writer, m_cacheWeld, m_originalVertices * sizeof(uint32_t), &err);
  200. bx::write(&writer, m_cachePermutation, m_totalVertices * sizeof(uint32_t), &err);
  201. bx::write(&writer, m_map, m_totalVertices * sizeof(uint32_t), &err);
  202. bx::close(&writer);
  203. }
  204. }
  205. void init(int32_t _argc, const char* const* _argv, uint32_t _width, uint32_t _height) override
  206. {
  207. Args args(_argc, _argv);
  208. m_width = _width;
  209. m_height = _height;
  210. m_debug = BGFX_DEBUG_NONE;
  211. m_reset = BGFX_RESET_VSYNC;
  212. bgfx::Init init;
  213. init.type = args.m_type;
  214. init.vendorId = args.m_pciId;
  215. init.platformData.nwh = entry::getNativeWindowHandle(entry::kDefaultWindowHandle);
  216. init.platformData.ndt = entry::getNativeDisplayHandle();
  217. init.platformData.type = entry::getNativeWindowHandleType();
  218. init.resolution.width = m_width;
  219. init.resolution.height = m_height;
  220. init.resolution.reset = m_reset;
  221. bgfx::init(init);
  222. // Enable debug text.
  223. bgfx::setDebug(m_debug);
  224. // Set view 0 clear state.
  225. bgfx::setViewClear(0
  226. , BGFX_CLEAR_COLOR|BGFX_CLEAR_DEPTH
  227. , 0x303030ff
  228. , 1.0f
  229. , 0
  230. );
  231. // Create program from shaders.
  232. m_program = loadProgram("vs_bunnylod", "fs_bunnylod");
  233. Mesh* mesh = meshLoad("meshes/bunny_patched.bin", true);
  234. loadMesh(mesh);
  235. meshUnload(mesh);
  236. m_LOD = 1.0f;
  237. m_lastLOD = m_LOD;
  238. imguiCreate();
  239. m_frameTime.reset();
  240. }
  241. int shutdown() override
  242. {
  243. imguiDestroy();
  244. // Cleanup.
  245. bgfx::destroy(m_program);
  246. bgfx::destroy(m_vb);
  247. bgfx::destroy(m_ib);
  248. bx::free(entry::getAllocator(), m_map);
  249. bx::free(entry::getAllocator(), m_triangle);
  250. bx::free(entry::getAllocator(), m_cacheWeld);
  251. bx::free(entry::getAllocator(), m_cachePermutation);
  252. // Shutdown bgfx.
  253. bgfx::shutdown();
  254. return 0;
  255. }
  256. void updateIndexBuffer()
  257. {
  258. int verts = int(bx::easeInQuad(m_LOD) * m_totalVertices);
  259. if (verts <= 0)
  260. {
  261. return;
  262. }
  263. int tris = 0;
  264. const bgfx::Memory* ib = bgfx::alloc(m_totalTriangles * 3 * sizeof(uint32_t) );
  265. for (uint32_t ii = 0; ii < m_totalTriangles; ++ii)
  266. {
  267. int v[3];
  268. for (uint32_t jj = 0; jj < 3; ++jj)
  269. {
  270. int idx = m_triangle[ii*3+jj];
  271. while (idx >= verts)
  272. {
  273. idx = m_map[idx];
  274. }
  275. v[jj] = idx;
  276. }
  277. if (v[0] != v[1]
  278. && v[0] != v[2]
  279. && v[1] != v[2])
  280. {
  281. bx::memCopy(ib->data + tris * 3 * sizeof(uint32_t), v, 3 * sizeof(int) );
  282. ++tris;
  283. }
  284. }
  285. m_numTriangles = tris;
  286. m_numVertices = verts;
  287. bgfx::update(m_ib, 0, ib);
  288. }
  289. void submitLod(bgfx::ViewId _viewid, const float* _mtx)
  290. {
  291. bgfx::setTransform(_mtx);
  292. bgfx::setState(0
  293. | BGFX_STATE_WRITE_RGB
  294. | BGFX_STATE_WRITE_A
  295. | BGFX_STATE_WRITE_Z
  296. | BGFX_STATE_DEPTH_TEST_LESS
  297. | BGFX_STATE_CULL_CCW
  298. | BGFX_STATE_MSAA
  299. );
  300. if (m_LOD != m_lastLOD)
  301. {
  302. updateIndexBuffer();
  303. m_lastLOD = m_LOD;
  304. }
  305. bgfx::setIndexBuffer(m_ib, 0, m_numTriangles*3);
  306. bgfx::setVertexBuffer(0, m_vb, 0, m_numVertices);
  307. bgfx::submit(_viewid, m_program);
  308. }
  309. bool update() override
  310. {
  311. if (!entry::processEvents(m_width, m_height, m_debug, m_reset, &m_mouseState) )
  312. {
  313. m_frameTime.frame();
  314. const float time = bx::toSeconds<float>(m_frameTime.getDurationTime() );
  315. imguiBeginFrame(m_mouseState.m_mx
  316. , m_mouseState.m_my
  317. , (m_mouseState.m_buttons[entry::MouseButton::Left ] ? IMGUI_MBUT_LEFT : 0)
  318. | (m_mouseState.m_buttons[entry::MouseButton::Right ] ? IMGUI_MBUT_RIGHT : 0)
  319. | (m_mouseState.m_buttons[entry::MouseButton::Middle] ? IMGUI_MBUT_MIDDLE : 0)
  320. , m_mouseState.m_mz
  321. , uint16_t(m_width)
  322. , uint16_t(m_height)
  323. );
  324. showExampleDialog(this);
  325. ImGui::SetNextWindowPos(
  326. ImVec2(m_width - m_width / 5.0f - 10.0f, 10.0f)
  327. , ImGuiCond_FirstUseEver
  328. );
  329. ImGui::SetNextWindowSize(
  330. ImVec2(m_width / 5.0f, m_height / 2.0f)
  331. , ImGuiCond_FirstUseEver
  332. );
  333. ImGui::Begin("Settings"
  334. , NULL
  335. , 0
  336. );
  337. ImGui::Text("Vertices: %d", m_numVertices);
  338. ImGui::Text("Triangles: %d", m_numTriangles);
  339. ImGui::SliderFloat("LOD Level", &m_LOD, 0.05f, 1.0f);
  340. ImGui::End();
  341. imguiEndFrame();
  342. // Set view 0 default viewport.
  343. bgfx::setViewRect(0, 0, 0, uint16_t(m_width), uint16_t(m_height) );
  344. // This dummy draw call is here to make sure that view 0 is cleared
  345. // if no other draw calls are submitted to view 0.
  346. bgfx::touch(0);
  347. const bx::Vec3 at = { 0.0f, 1.0f, 0.0f };
  348. const bx::Vec3 eye = { 0.0f, 1.0f, -2.5f };
  349. // Set view and projection matrix for view 0.
  350. {
  351. float view[16];
  352. bx::mtxLookAt(view, eye, at);
  353. float proj[16];
  354. bx::mtxProj(proj, 60.0f, float(m_width)/float(m_height), 0.1f, 100.0f, bgfx::getCaps()->homogeneousDepth);
  355. bgfx::setViewTransform(0, view, proj);
  356. // Set view 0 default viewport.
  357. bgfx::setViewRect(0, 0, 0, uint16_t(m_width), uint16_t(m_height) );
  358. }
  359. float mtx[16];
  360. bx::mtxRotateXY(mtx
  361. , 0.0f
  362. , time*0.37f
  363. );
  364. submitLod(0, mtx);
  365. // Advance to next frame. Rendering thread will be kicked to
  366. // process submitted rendering primitives.
  367. bgfx::frame();
  368. return true;
  369. }
  370. return false;
  371. }
  372. entry::MouseState m_mouseState;
  373. uint32_t m_width;
  374. uint32_t m_height;
  375. uint32_t m_debug;
  376. uint32_t m_reset;
  377. float m_lastLOD;
  378. float m_LOD;
  379. uint32_t m_numVertices;
  380. uint32_t m_numTriangles;
  381. uint32_t m_totalVertices;
  382. uint32_t m_totalTriangles;
  383. uint32_t m_originalVertices;
  384. uint32_t* m_map;
  385. uint32_t* m_triangle;
  386. uint32_t* m_cacheWeld;
  387. uint32_t* m_cachePermutation;
  388. bgfx::VertexBufferHandle m_vb;
  389. bgfx::DynamicIndexBufferHandle m_ib;
  390. bgfx::ProgramHandle m_program;
  391. FrameTime m_frameTime;
  392. };
  393. } // namespace
  394. ENTRY_IMPLEMENT_MAIN(
  395. ExampleBunnyLOD
  396. , "42-bunnylod"
  397. , "Progressive Mesh LOD"
  398. , "https://bkaradzic.github.io/bgfx/examples.html#bunnylod"
  399. );