diff --git a/3rdparty/meshoptimizer/.github/workflows/build.yml b/3rdparty/meshoptimizer/.github/workflows/build.yml index ab29f999e..18dd693a2 100644 --- a/3rdparty/meshoptimizer/.github/workflows/build.yml +++ b/3rdparty/meshoptimizer/.github/workflows/build.yml @@ -17,6 +17,7 @@ jobs: make -j2 config=debug test make -j2 config=release test make -j2 config=release gltfpack + strip gltfpack - name: make coverage run: | make -j2 config=coverage test @@ -36,7 +37,7 @@ jobs: steps: - uses: actions/checkout@v1 - name: cmake configure - run: cmake . -DBUILD_DEMO=ON -DBUILD_TOOLS=ON -A ${{matrix.arch}} + run: cmake . -DBUILD_DEMO=ON -DBUILD_TOOLS=ON -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded$<$:Debug>" -A ${{matrix.arch}} - name: cmake test shell: bash # necessary for fail-fast run: | @@ -48,3 +49,4 @@ jobs: with: name: gltfpack-windows path: Release/gltfpack.exe + if: matrix.arch == 'x64' diff --git a/3rdparty/meshoptimizer/.travis.yml b/3rdparty/meshoptimizer/.travis.yml index 3c46e945a..038f8af71 100644 --- a/3rdparty/meshoptimizer/.travis.yml +++ b/3rdparty/meshoptimizer/.travis.yml @@ -1,6 +1,7 @@ language: cpp jobs: + include: - os: linux compiler: gcc - os: linux diff --git a/3rdparty/meshoptimizer/CMakeLists.txt b/3rdparty/meshoptimizer/CMakeLists.txt index 2fcba6701..cda378fb1 100644 --- a/3rdparty/meshoptimizer/CMakeLists.txt +++ b/3rdparty/meshoptimizer/CMakeLists.txt @@ -1,5 +1,10 @@ cmake_minimum_required(VERSION 3.0) +if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.15) + cmake_policy(SET CMP0091 NEW) # Enables use of MSVC_RUNTIME_LIBRARY + cmake_policy(SET CMP0092 NEW) # Enables clean /W4 override for MSVC +endif() + project(meshoptimizer VERSION 0.12 LANGUAGES CXX) option(BUILD_DEMO "Build demo" OFF) diff --git a/3rdparty/meshoptimizer/demo/demo.html b/3rdparty/meshoptimizer/demo/demo.html index 2ce3fd3ed..7cc32b89c 100644 --- a/3rdparty/meshoptimizer/demo/demo.html +++ b/3rdparty/meshoptimizer/demo/demo.html @@ -33,6 +33,7 @@ var canvas = document.getElementById("renderCanvas"); BABYLON.GLTF2.GLTFLoader.RegisterExtension("MESHOPT_compression", (loader) => new MESHOPT_compression(loader, MeshoptDecoder)); + BABYLON.GLTF2.GLTFLoader.RegisterExtension("KHR_quantized_geometry", (loader) => ({ name: "KHR_quantized_geometry", enabled: true })); var createScene = function () { var scene = new BABYLON.Scene(engine); diff --git a/3rdparty/meshoptimizer/demo/pirate.glb b/3rdparty/meshoptimizer/demo/pirate.glb index eaf3397c0..9f8a66467 100644 Binary files a/3rdparty/meshoptimizer/demo/pirate.glb and b/3rdparty/meshoptimizer/demo/pirate.glb differ diff --git a/3rdparty/meshoptimizer/tools/gltfpack.cpp b/3rdparty/meshoptimizer/tools/gltfpack.cpp index e939f3f13..22b3664a1 100644 --- a/3rdparty/meshoptimizer/tools/gltfpack.cpp +++ b/3rdparty/meshoptimizer/tools/gltfpack.cpp @@ -73,7 +73,7 @@ struct Settings int pos_bits; int tex_bits; int nrm_bits; - bool nrm_unit; + bool nrm_unnormalized; int anim_freq; bool anim_const; @@ -841,6 +841,29 @@ void reindexMesh(Mesh& mesh) } } +void filterMesh(Mesh& mesh) +{ + unsigned int* indices = &mesh.indices[0]; + size_t total_indices = mesh.indices.size(); + + size_t write = 0; + + for (size_t i = 0; i < total_indices; i += 3) + { + unsigned int a = indices[i + 0], b = indices[i + 1], c = indices[i + 2]; + + if (a != b && a != c && b != c) + { + indices[write + 0] = a; + indices[write + 1] = b; + indices[write + 2] = c; + write += 3; + } + } + + mesh.indices.resize(write); +} + Stream* getStream(Mesh& mesh, cgltf_attribute_type type) { for (size_t i = 0; i < mesh.streams.size(); ++i) @@ -922,6 +945,9 @@ void sortBoneInfluences(Mesh& mesh) if (!joints || !weights) return; + // weights below cutoff can't be represented in quantized 8-bit storage + const float weight_cutoff = 0.5f / 255.f; + size_t vertex_count = mesh.streams[0].data.size(); for (size_t i = 0; i < vertex_count; ++i) @@ -933,7 +959,7 @@ void sortBoneInfluences(Mesh& mesh) int count = 0; for (int k = 0; k < 4; ++k) - if (wa.f[k] > 0.f) + if (wa.f[k] > weight_cutoff) { inf[count].i = ja.f[k]; inf[count].w = wa.f[k]; @@ -1156,20 +1182,51 @@ StreamFormat writeVertexStream(std::string& bin, const Stream& stream, const Qua { float pos_rscale = params.pos_scale == 0.f ? 0.f : 1.f / params.pos_scale; + int maxv = 0; + for (size_t i = 0; i < stream.data.size(); ++i) { const Attr& a = stream.data[i]; - int16_t v[4] = { - int16_t((a.f[0] >= 0.f ? 1 : -1) * meshopt_quantizeUnorm(fabsf(a.f[0]) * pos_rscale, params.pos_bits)), - int16_t((a.f[1] >= 0.f ? 1 : -1) * meshopt_quantizeUnorm(fabsf(a.f[1]) * pos_rscale, params.pos_bits)), - int16_t((a.f[2] >= 0.f ? 1 : -1) * meshopt_quantizeUnorm(fabsf(a.f[2]) * pos_rscale, params.pos_bits)), - 0}; - bin.append(reinterpret_cast(v), sizeof(v)); + maxv = std::max(maxv, meshopt_quantizeUnorm(fabsf(a.f[0]) * pos_rscale, params.pos_bits)); + maxv = std::max(maxv, meshopt_quantizeUnorm(fabsf(a.f[1]) * pos_rscale, params.pos_bits)); + maxv = std::max(maxv, meshopt_quantizeUnorm(fabsf(a.f[2]) * pos_rscale, params.pos_bits)); } - StreamFormat format = {cgltf_type_vec3, cgltf_component_type_r_16, false, 8}; - return format; + if (maxv <= 127) + { + for (size_t i = 0; i < stream.data.size(); ++i) + { + const Attr& a = stream.data[i]; + + int8_t v[4] = { + int8_t((a.f[0] >= 0.f ? 1 : -1) * meshopt_quantizeUnorm(fabsf(a.f[0]) * pos_rscale, params.pos_bits)), + int8_t((a.f[1] >= 0.f ? 1 : -1) * meshopt_quantizeUnorm(fabsf(a.f[1]) * pos_rscale, params.pos_bits)), + int8_t((a.f[2] >= 0.f ? 1 : -1) * meshopt_quantizeUnorm(fabsf(a.f[2]) * pos_rscale, params.pos_bits)), + 0}; + bin.append(reinterpret_cast(v), sizeof(v)); + } + + StreamFormat format = {cgltf_type_vec3, cgltf_component_type_r_8, false, 4}; + return format; + } + else + { + for (size_t i = 0; i < stream.data.size(); ++i) + { + const Attr& a = stream.data[i]; + + int16_t v[4] = { + int16_t((a.f[0] >= 0.f ? 1 : -1) * meshopt_quantizeUnorm(fabsf(a.f[0]) * pos_rscale, params.pos_bits)), + int16_t((a.f[1] >= 0.f ? 1 : -1) * meshopt_quantizeUnorm(fabsf(a.f[1]) * pos_rscale, params.pos_bits)), + int16_t((a.f[2] >= 0.f ? 1 : -1) * meshopt_quantizeUnorm(fabsf(a.f[2]) * pos_rscale, params.pos_bits)), + 0}; + bin.append(reinterpret_cast(v), sizeof(v)); + } + + StreamFormat format = {cgltf_type_vec3, cgltf_component_type_r_16, false, 8}; + return format; + } } } else if (stream.type == cgltf_attribute_type_texcoord) @@ -1195,8 +1252,8 @@ StreamFormat writeVertexStream(std::string& bin, const Stream& stream, const Qua } else if (stream.type == cgltf_attribute_type_normal) { - bool nrm_unit = has_targets || settings.nrm_unit; - int bits = nrm_unit ? (settings.nrm_bits > 8 ? 16 : 8) : settings.nrm_bits; + bool unnormalized = settings.nrm_unnormalized && !has_targets; + int bits = unnormalized ? settings.nrm_bits : (settings.nrm_bits > 8 ? 16 : 8); for (size_t i = 0; i < stream.data.size(); ++i) { @@ -1204,7 +1261,7 @@ StreamFormat writeVertexStream(std::string& bin, const Stream& stream, const Qua float nx = a.f[0], ny = a.f[1], nz = a.f[2]; - if (!nrm_unit) + if (unnormalized) rescaleNormal(nx, ny, nz); if (bits > 8) @@ -1240,8 +1297,8 @@ StreamFormat writeVertexStream(std::string& bin, const Stream& stream, const Qua } else if (stream.type == cgltf_attribute_type_tangent) { - bool nrm_unit = has_targets || settings.nrm_unit; - int bits = nrm_unit ? (settings.nrm_bits > 8 ? 16 : 8) : settings.nrm_bits; + bool unnormalized = settings.nrm_unnormalized && !has_targets; + int bits = unnormalized ? settings.nrm_bits : (settings.nrm_bits > 8 ? 16 : 8); for (size_t i = 0; i < stream.data.size(); ++i) { @@ -1249,7 +1306,7 @@ StreamFormat writeVertexStream(std::string& bin, const Stream& stream, const Qua float nx = a.f[0], ny = a.f[1], nz = a.f[2], nw = a.f[3]; - if (!nrm_unit) + if (unnormalized) rescaleNormal(nx, ny, nz); if (bits > 8) @@ -3143,6 +3200,7 @@ void process(cgltf_data* data, std::vector& meshes, const Settings& settin case cgltf_primitive_type_triangles: reindexMesh(mesh); + filterMesh(mesh); simplifyMesh(mesh, settings.simplify_threshold, settings.simplify_aggressive); optimizeMesh(mesh); sortBoneInfluences(mesh); @@ -3443,8 +3501,9 @@ void process(cgltf_data* data, std::vector& meshes, const Settings& settin json.append(data->json + data->asset.extras.start_offset, data->json + data->asset.extras.end_offset); } append(json, "}"); + append(json, ",\"extensionsUsed\":["); - append(json, "\"MESHOPT_quantized_geometry\""); + append(json, "\"KHR_quantized_geometry\""); if (settings.compress) { comma(json); @@ -3471,16 +3530,15 @@ void process(cgltf_data* data, std::vector& meshes, const Settings& settin append(json, "\"KHR_lights_punctual\""); } append(json, "]"); + + append(json, ",\"extensionsRequired\":["); + append(json, "\"KHR_quantized_geometry\""); if (settings.compress && !settings.fallback) { - append(json, ",\"extensionsRequired\":["); - // Note: ideally we should include MESHOPT_quantized_geometry in the required extension list (regardless of compression) - // This extension *only* allows the use of quantized attributes for positions/normals/etc. This happens to be supported - // by popular JS frameworks, however, Babylon.JS refuses to load files with unsupported required extensions. - // For now we don't include it in the list, which will be fixed at some point once this extension becomes official. + comma(json); append(json, "\"MESHOPT_compression\""); - append(json, "]"); } + append(json, "]"); if (!views.empty()) { @@ -3833,7 +3891,7 @@ int main(int argc, char** argv) } else if (strcmp(arg, "-vu") == 0) { - settings.nrm_unit = true; + settings.nrm_unnormalized = true; } else if (strcmp(arg, "-af") == 0 && i + 1 < argc && isdigit(argv[i + 1][0])) { @@ -3917,7 +3975,7 @@ int main(int argc, char** argv) fprintf(stderr, "-vp N: use N-bit quantization for positions (default: 14; N should be between 1 and 16)\n"); fprintf(stderr, "-vt N: use N-bit quantization for texture corodinates (default: 12; N should be between 1 and 16)\n"); fprintf(stderr, "-vn N: use N-bit quantization for normals and tangents (default: 8; N should be between 1 and 16)\n"); - fprintf(stderr, "-vu: use unit-length normal/tangent vectors (default: off)\n"); + fprintf(stderr, "-vu: use unnormalized normal/tangent vectors to improve compression (default: off)\n"); fprintf(stderr, "-af N: resample animations at N Hz (default: 30)\n"); fprintf(stderr, "-ac: keep constant animation tracks even if they don't modify the node transform\n"); fprintf(stderr, "-kn: keep named nodes and meshes attached to named nodes so that named nodes can be transformed externally\n");