diff options
Diffstat (limited to '3rdparty/bgfx/3rdparty/meshoptimizer/tools/lodviewer.cpp')
-rw-r--r-- | 3rdparty/bgfx/3rdparty/meshoptimizer/tools/lodviewer.cpp | 618 |
1 files changed, 618 insertions, 0 deletions
diff --git a/3rdparty/bgfx/3rdparty/meshoptimizer/tools/lodviewer.cpp b/3rdparty/bgfx/3rdparty/meshoptimizer/tools/lodviewer.cpp new file mode 100644 index 00000000000..54dc4c90967 --- /dev/null +++ b/3rdparty/bgfx/3rdparty/meshoptimizer/tools/lodviewer.cpp @@ -0,0 +1,618 @@ +#define _CRT_SECURE_NO_WARNINGS + +#include "../src/meshoptimizer.h" +#include "fast_obj.h" +#include "cgltf.h" + +#include <algorithm> +#include <cmath> +#include <cstdio> +#include <ctime> +#include <vector> + +#include <GLFW/glfw3.h> + +#ifdef _WIN32 +#pragma comment(lib, "opengl32.lib") +#endif + +extern unsigned char* meshopt_simplifyDebugKind; +extern unsigned int* meshopt_simplifyDebugLoop; + +#ifndef TRACE +unsigned char* meshopt_simplifyDebugKind; +unsigned int* meshopt_simplifyDebugLoop; +#endif + +struct Options +{ + bool wireframe; + enum + { + Mode_Default, + Mode_Texture, + Mode_Normals, + Mode_UV, + Mode_Kind, + } mode; +}; + +struct Vertex +{ + float px, py, pz; + float nx, ny, nz; + float tx, ty; +}; + +struct Mesh +{ + std::vector<Vertex> vertices; + std::vector<unsigned int> indices; + + // TODO: this is debug only visualization and will go away at some point + std::vector<unsigned char> kinds; + std::vector<unsigned int> loop; +}; + +Mesh parseObj(const char* path) +{ + fastObjMesh* obj = fast_obj_read(path); + if (!obj) + { + printf("Error loading %s: file not found\n", path); + return Mesh(); + } + + size_t total_indices = 0; + + for (unsigned int i = 0; i < obj->face_count; ++i) + total_indices += 3 * (obj->face_vertices[i] - 2); + + std::vector<Vertex> vertices(total_indices); + + size_t vertex_offset = 0; + size_t index_offset = 0; + + for (unsigned int i = 0; i < obj->face_count; ++i) + { + for (unsigned int j = 0; j < obj->face_vertices[i]; ++j) + { + fastObjIndex gi = obj->indices[index_offset + j]; + + Vertex v = + { + obj->positions[gi.p * 3 + 0], + obj->positions[gi.p * 3 + 1], + obj->positions[gi.p * 3 + 2], + obj->normals[gi.n * 3 + 0], + obj->normals[gi.n * 3 + 1], + obj->normals[gi.n * 3 + 2], + obj->texcoords[gi.t * 2 + 0], + obj->texcoords[gi.t * 2 + 1], + }; + + // triangulate polygon on the fly; offset-3 is always the first polygon vertex + if (j >= 3) + { + vertices[vertex_offset + 0] = vertices[vertex_offset - 3]; + vertices[vertex_offset + 1] = vertices[vertex_offset - 1]; + vertex_offset += 2; + } + + vertices[vertex_offset] = v; + vertex_offset++; + } + + index_offset += obj->face_vertices[i]; + } + + fast_obj_destroy(obj); + + Mesh result; + + std::vector<unsigned int> remap(total_indices); + size_t total_vertices = meshopt_generateVertexRemap(&remap[0], NULL, total_indices, &vertices[0], total_indices, sizeof(Vertex)); + + result.indices.resize(total_indices); + meshopt_remapIndexBuffer(&result.indices[0], NULL, total_indices, &remap[0]); + + result.vertices.resize(total_vertices); + meshopt_remapVertexBuffer(&result.vertices[0], &vertices[0], total_indices, sizeof(Vertex), &remap[0]); + + return result; +} + +cgltf_accessor* getAccessor(const cgltf_attribute* attributes, size_t attribute_count, cgltf_attribute_type type, int index = 0) +{ + for (size_t i = 0; i < attribute_count; ++i) + if (attributes[i].type == type && attributes[i].index == index) + return attributes[i].data; + + return 0; +} + +Mesh parseGltf(const char* path) +{ + cgltf_options options = {}; + cgltf_data* data = 0; + cgltf_result res = cgltf_parse_file(&options, path, &data); + + if (res != cgltf_result_success) + { + return Mesh(); + } + + res = cgltf_load_buffers(&options, data, path); + if (res != cgltf_result_success) + { + cgltf_free(data); + return Mesh(); + } + + res = cgltf_validate(data); + if (res != cgltf_result_success) + { + cgltf_free(data); + return Mesh(); + } + + size_t total_vertices = 0; + size_t total_indices = 0; + + for (size_t ni = 0; ni < data->nodes_count; ++ni) + { + if (!data->nodes[ni].mesh) + continue; + + const cgltf_mesh& mesh = *data->nodes[ni].mesh; + + for (size_t pi = 0; pi < mesh.primitives_count; ++pi) + { + const cgltf_primitive& primitive = mesh.primitives[pi]; + + cgltf_accessor* ai = primitive.indices; + cgltf_accessor* ap = getAccessor(primitive.attributes, primitive.attributes_count, cgltf_attribute_type_position); + + if (!ai || !ap) + continue; + + total_vertices += ap->count; + total_indices += ai->count; + } + } + + Mesh result; + result.vertices.resize(total_vertices); + result.indices.resize(total_indices); + + size_t vertex_offset = 0; + size_t index_offset = 0; + + for (size_t ni = 0; ni < data->nodes_count; ++ni) + { + if (!data->nodes[ni].mesh) + continue; + + const cgltf_mesh& mesh = *data->nodes[ni].mesh; + + float transform[16]; + cgltf_node_transform_world(&data->nodes[ni], transform); + + for (size_t pi = 0; pi < mesh.primitives_count; ++pi) + { + const cgltf_primitive& primitive = mesh.primitives[pi]; + + cgltf_accessor* ai = primitive.indices; + cgltf_accessor* ap = getAccessor(primitive.attributes, primitive.attributes_count, cgltf_attribute_type_position); + + if (!ai || !ap) + continue; + + for (size_t i = 0; i < ai->count; ++i) + result.indices[index_offset + i] = unsigned(vertex_offset + cgltf_accessor_read_index(ai, i)); + + { + for (size_t i = 0; i < ap->count; ++i) + { + float ptr[3]; + cgltf_accessor_read_float(ap, i, ptr, 3); + + result.vertices[vertex_offset + i].px = ptr[0] * transform[0] + ptr[1] * transform[4] + ptr[2] * transform[8] + transform[12]; + result.vertices[vertex_offset + i].py = ptr[0] * transform[1] + ptr[1] * transform[5] + ptr[2] * transform[9] + transform[13]; + result.vertices[vertex_offset + i].pz = ptr[0] * transform[2] + ptr[1] * transform[6] + ptr[2] * transform[10] + transform[14]; + } + } + + if (cgltf_accessor* an = getAccessor(primitive.attributes, primitive.attributes_count, cgltf_attribute_type_normal)) + { + for (size_t i = 0; i < ap->count; ++i) + { + float ptr[3]; + cgltf_accessor_read_float(an, i, ptr, 3); + + result.vertices[vertex_offset + i].nx = ptr[0] * transform[0] + ptr[1] * transform[4] + ptr[2] * transform[8]; + result.vertices[vertex_offset + i].ny = ptr[0] * transform[1] + ptr[1] * transform[5] + ptr[2] * transform[9]; + result.vertices[vertex_offset + i].nz = ptr[0] * transform[2] + ptr[1] * transform[6] + ptr[2] * transform[10]; + } + } + + if (cgltf_accessor* at = getAccessor(primitive.attributes, primitive.attributes_count, cgltf_attribute_type_texcoord)) + { + for (size_t i = 0; i < ap->count; ++i) + { + float ptr[2]; + cgltf_accessor_read_float(at, i, ptr, 2); + + result.vertices[vertex_offset + i].tx = ptr[0]; + result.vertices[vertex_offset + i].ty = ptr[1]; + } + } + + vertex_offset += ap->count; + index_offset += ai->count; + } + } + + std::vector<unsigned int> remap(total_indices); + size_t unique_vertices = meshopt_generateVertexRemap(&remap[0], &result.indices[0], total_indices, &result.vertices[0], total_vertices, sizeof(Vertex)); + + meshopt_remapIndexBuffer(&result.indices[0], &result.indices[0], total_indices, &remap[0]); + meshopt_remapVertexBuffer(&result.vertices[0], &result.vertices[0], total_vertices, sizeof(Vertex), &remap[0]); + + result.vertices.resize(unique_vertices); + + cgltf_free(data); + + return result; +} + +Mesh loadMesh(const char* path) +{ + if (strstr(path, ".obj")) + return parseObj(path); + + if (strstr(path, ".gltf") || strstr(path, ".glb")) + return parseGltf(path); + + return Mesh(); +} + +bool saveObj(const Mesh& mesh, const char* path) +{ + std::vector<Vertex> verts = mesh.vertices; + std::vector<unsigned int> tris = mesh.indices; + size_t vertcount = meshopt_optimizeVertexFetch(verts.data(), tris.data(), tris.size(), verts.data(), verts.size(), sizeof(Vertex)); + + FILE* obj = fopen(path, "w"); + if (!obj) + return false; + + for (size_t i = 0; i < vertcount; ++i) + { + fprintf(obj, "v %f %f %f\n", verts[i].px, verts[i].py, verts[i].pz); + fprintf(obj, "vn %f %f %f\n", verts[i].nx, verts[i].ny, verts[i].nz); + fprintf(obj, "vt %f %f %f\n", verts[i].tx, verts[i].ty, 0.f); + } + + for (size_t i = 0; i < tris.size(); i += 3) + { + unsigned int i0 = tris[i + 0] + 1; + unsigned int i1 = tris[i + 1] + 1; + unsigned int i2 = tris[i + 2] + 1; + + fprintf(obj, "f %d/%d/%d %d/%d/%d %d/%d/%d\n", i0, i0, i0, i1, i1, i1, i2, i2, i2); + } + + fclose(obj); + + return true; +} + +Mesh optimize(const Mesh& mesh, int lod) +{ + float threshold = powf(0.5f, float(lod)); + size_t target_index_count = size_t(mesh.indices.size() * threshold); + float target_error = 1e-2f; + + Mesh result = mesh; + result.kinds.resize(result.vertices.size()); + result.loop.resize(result.vertices.size()); + meshopt_simplifyDebugKind = &result.kinds[0]; + meshopt_simplifyDebugLoop = &result.loop[0]; + 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)); + + return result; +} + +void display(int x, int y, int width, int height, const Mesh& mesh, const Options& options) +{ + glViewport(x, y, width, height); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + glDepthMask(GL_TRUE); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glRotatef(0.f, 0.f, 1.f, 0.f); + + glPolygonMode(GL_FRONT_AND_BACK, options.wireframe ? GL_LINE : GL_FILL); + + float centerx = 0; + float centery = 0; + float centerz = 0; + float centeru = 0; + float centerv = 0; + + for (size_t i = 0; i < mesh.vertices.size(); ++i) + { + const Vertex& v = mesh.vertices[i]; + + centerx += v.px; + centery += v.py; + centerz += v.pz; + centeru += v.tx; + centerv += v.ty; + } + + centerx /= float(mesh.vertices.size()); + centery /= float(mesh.vertices.size()); + centerz /= float(mesh.vertices.size()); + centeru /= float(mesh.vertices.size()); + centerv /= float(mesh.vertices.size()); + + float extent = 0; + float extentuv = 0; + + for (size_t i = 0; i < mesh.vertices.size(); ++i) + { + const Vertex& v = mesh.vertices[i]; + + extent = std::max(extent, fabsf(v.px - centerx)); + extent = std::max(extent, fabsf(v.py - centery)); + extent = std::max(extent, fabsf(v.pz - centerz)); + extentuv = std::max(extentuv, fabsf(v.tx - centeru)); + extentuv = std::max(extentuv, fabsf(v.ty - centerv)); + } + + extent *= 1.1f; + extentuv *= 1.1f; + + float scalex = width > height ? float(height) / float(width) : 1; + float scaley = height > width ? float(width) / float(height) : 1; + + glBegin(GL_TRIANGLES); + + for (size_t i = 0; i < mesh.indices.size(); ++i) + { + const Vertex& v = mesh.vertices[mesh.indices[i]]; + + float intensity = -(v.pz - centerz) / extent * 0.5f + 0.5f; + + switch (options.mode) + { + case Options::Mode_UV: + glColor3f(intensity, intensity, intensity); + glVertex3f((v.tx - centeru) / extentuv * scalex, (v.ty - centerv) / extentuv * scaley, 0); + break; + + case Options::Mode_Texture: + glColor3f(v.tx - floorf(v.tx), v.ty - floorf(v.ty), 0.5f); + glVertex3f((v.px - centerx) / extent * scalex, (v.py - centery) / extent * scaley, (v.pz - centerz) / extent); + break; + + case Options::Mode_Normals: + glColor3f(v.nx * 0.5f + 0.5f, v.ny * 0.5f + 0.5f, v.nz * 0.5f + 0.5f); + glVertex3f((v.px - centerx) / extent * scalex, (v.py - centery) / extent * scaley, (v.pz - centerz) / extent); + break; + + default: + glColor3f(intensity, intensity, intensity); + glVertex3f((v.px - centerx) / extent * scalex, (v.py - centery) / extent * scaley, (v.pz - centerz) / extent); + } + } + + glEnd(); + + float zbias = 1e-3f; + + if (options.mode == Options::Mode_Kind && !mesh.kinds.empty() && !mesh.loop.empty()) + { + glLineWidth(1); + + glBegin(GL_LINES); + + for (size_t i = 0; i < mesh.indices.size(); ++i) + { + unsigned int a = mesh.indices[i]; + unsigned int b = mesh.loop[a]; + + if (b != ~0u) + { + const Vertex& v0 = mesh.vertices[a]; + const Vertex& v1 = mesh.vertices[b]; + + unsigned char kind = mesh.kinds[a]; + + glColor3f(kind == 0 || kind == 4, kind == 0 || kind == 2 || kind == 3, kind == 0 || kind == 1 || kind == 3); + glVertex3f((v0.px - centerx) / extent * scalex, (v0.py - centery) / extent * scaley, (v0.pz - centerz) / extent - zbias); + glVertex3f((v1.px - centerx) / extent * scalex, (v1.py - centery) / extent * scaley, (v1.pz - centerz) / extent - zbias); + } + } + + glEnd(); + + glPointSize(3); + + glBegin(GL_POINTS); + + for (size_t i = 0; i < mesh.indices.size(); ++i) + { + const Vertex& v = mesh.vertices[mesh.indices[i]]; + unsigned char kind = mesh.kinds[mesh.indices[i]]; + + if (kind != 0) + { + glColor3f(kind == 0 || kind == 4, kind == 0 || kind == 2 || kind == 3, kind == 0 || kind == 1 || kind == 3); + glVertex3f((v.px - centerx) / extent * scalex, (v.py - centery) / extent * scaley, (v.pz - centerz) / extent - zbias * 2); + } + } + + glEnd(); + } +} + +void stats(GLFWwindow* window, const char* path, unsigned int triangles, int lod, double time) +{ + char title[256]; + snprintf(title, sizeof(title), "%s: LOD %d - %d triangles (%.1f msec)", path, lod, triangles, time * 1000); + + glfwSetWindowTitle(window, title); +} + +struct File +{ + Mesh basemesh; + Mesh lodmesh; + const char* path; +}; + +std::vector<File> files; +Options options; +bool redraw; + +void keyhandler(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (action == GLFW_PRESS) + { + if (key == GLFW_KEY_W) + { + options.wireframe = !options.wireframe; + redraw = true; + } + else if (key == GLFW_KEY_T) + { + options.mode = options.mode == Options::Mode_Texture ? Options::Mode_Default : Options::Mode_Texture; + redraw = true; + } + else if (key == GLFW_KEY_N) + { + options.mode = options.mode == Options::Mode_Normals ? Options::Mode_Default : Options::Mode_Normals; + redraw = true; + } + else if (key == GLFW_KEY_U) + { + options.mode = options.mode == Options::Mode_UV ? Options::Mode_Default : Options::Mode_UV; + redraw = true; + } + else if (key == GLFW_KEY_K) + { + options.mode = options.mode == Options::Mode_Kind ? Options::Mode_Default : Options::Mode_Kind; + redraw = true; + } + else if (key >= GLFW_KEY_0 && key <= GLFW_KEY_9) + { + int lod = int(key - GLFW_KEY_0); + + unsigned int triangles = 0; + + clock_t start = clock(); + for (auto& f : files) + { + f.lodmesh = optimize(f.basemesh, lod); + triangles += unsigned(f.lodmesh.indices.size() / 3); + } + clock_t end = clock(); + + stats(window, files[0].path, triangles, lod, double(end - start) / CLOCKS_PER_SEC); + redraw = true; + } + else if (key == GLFW_KEY_S) + { + int i = 0; + + for (auto& f : files) + { + char path[32]; + sprintf(path, "result%d.obj", i); + + saveObj(f.lodmesh, path); + + printf("Saved LOD of %s to %s\n", f.path, path); + } + } + } +} + +void sizehandler(GLFWwindow* window, int width, int height) +{ + redraw = true; +} + +int main(int argc, char** argv) +{ + if (argc <= 1) + { + printf("Usage: %s [.obj files]\n", argv[0]); + return 0; + } + + unsigned int basetriangles = 0; + + for (int i = 1; i < argc; ++i) + { + files.emplace_back(); + File& f = files.back(); + + f.path = argv[i]; + f.basemesh = loadMesh(f.path); + f.lodmesh = optimize(f.basemesh, 0); + + basetriangles += unsigned(f.basemesh.indices.size() / 3); + } + + glfwInit(); + + GLFWwindow* window = glfwCreateWindow(640, 480, "Simple example", NULL, NULL); + glfwMakeContextCurrent(window); + + stats(window, files[0].path, basetriangles, 0, 0); + + glfwSetKeyCallback(window, keyhandler); + glfwSetWindowSizeCallback(window, sizehandler); + + redraw = true; + + while (!glfwWindowShouldClose(window)) + { + if (redraw) + { + redraw = false; + + int width, height; + glfwGetFramebufferSize(window, &width, &height); + + glViewport(0, 0, width, height); + glClearDepth(1.f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + int cols = int(ceil(sqrt(double(files.size())))); + int rows = int(ceil(double(files.size()) / cols)); + + int tilew = width / cols; + int tileh = height / rows; + + for (size_t i = 0; i < files.size(); ++i) + { + File& f = files[i]; + int x = int(i) % cols; + int y = int(i) / cols; + + display(x * tilew, y * tileh, tilew, tileh, f.lodmesh, options); + } + + glfwSwapBuffers(window); + } + + glfwWaitEvents(); + } +} |