diff --git a/gltf/gltfpack.cpp b/gltf/gltfpack.cpp index 4f68ad5cc..3ec5f4c00 100644 --- a/gltf/gltfpack.cpp +++ b/gltf/gltfpack.cpp @@ -393,7 +393,7 @@ static void process(cgltf_data* data, const char* input_path, const char* output { Mesh kinds = {}; Mesh loops = {}; - debugSimplify(mesh, kinds, loops, settings.simplify_debug, settings.simplify_attributes); + debugSimplify(mesh, kinds, loops, settings.simplify_debug, settings.simplify_error, settings.simplify_attributes); debug_meshes.push_back(kinds); debug_meshes.push_back(loops); } @@ -1177,7 +1177,8 @@ Settings defaults() settings.rot_bits = 12; settings.scl_bits = 16; settings.anim_freq = 30; - settings.simplify_threshold = 1.f; + settings.simplify_ratio = 1.f; + settings.simplify_error = 1e-2f; settings.texture_scale = 1.f; for (int kind = 0; kind < TextureKind__Count; ++kind) settings.texture_quality[kind] = 8; @@ -1323,7 +1324,11 @@ int main(int argc, char** argv) } else if (strcmp(arg, "-si") == 0 && i + 1 < argc && isdigit(argv[i + 1][0])) { - settings.simplify_threshold = clamp(float(atof(argv[++i])), 0.f, 1.f); + settings.simplify_ratio = clamp(float(atof(argv[++i])), 0.f, 1.f); + } + else if (strcmp(arg, "-se") == 0 && i + 1 < argc && isdigit(argv[i + 1][0])) + { + settings.simplify_error = clamp(float(atof(argv[++i])), 0.f, 1.f); } else if (strcmp(arg, "-sa") == 0) { @@ -1521,6 +1526,7 @@ int main(int argc, char** argv) fprintf(stderr, "\t... where C is a comma-separated list (no spaces) with valid values color,normal,attrib\n"); fprintf(stderr, "\nSimplification:\n"); fprintf(stderr, "\t-si R: simplify meshes targeting triangle/point count ratio R (default: 1; R should be between 0 and 1)\n"); + fprintf(stderr, "\t-se E: limit simplification error to E (default: 0.01 = 1%% deviation; E should be between 0 and 1)\n"); fprintf(stderr, "\t-sa: aggressively simplify to the target ratio disregarding quality\n"); fprintf(stderr, "\t-sv: take vertex attributes into account when simplifying meshes\n"); fprintf(stderr, "\t-slb: lock border vertices during simplification to avoid gaps on connected meshes\n"); diff --git a/gltf/gltfpack.h b/gltf/gltfpack.h index 10d436d43..0249d265d 100644 --- a/gltf/gltfpack.h +++ b/gltf/gltfpack.h @@ -134,7 +134,8 @@ struct Settings bool mesh_merge; bool mesh_instancing; - float simplify_threshold; + float simplify_ratio; + float simplify_error; bool simplify_aggressive; bool simplify_lock_borders; bool simplify_attributes; @@ -306,7 +307,7 @@ cgltf_data* parseGlb(const void* buffer, size_t size, std::vector& meshes, void processAnimation(Animation& animation, const Settings& settings); void processMesh(Mesh& mesh, const Settings& settings); -void debugSimplify(const Mesh& mesh, Mesh& kinds, Mesh& loops, float ratio, bool attributes); +void debugSimplify(const Mesh& mesh, Mesh& kinds, Mesh& loops, float ratio, float error, bool attributes); void debugMeshlets(const Mesh& mesh, Mesh& meshlets, int max_vertices, bool scan); bool compareMeshTargets(const Mesh& lhs, const Mesh& rhs); diff --git a/gltf/mesh.cpp b/gltf/mesh.cpp index a3dc33941..dc2a18c13 100644 --- a/gltf/mesh.cpp +++ b/gltf/mesh.cpp @@ -527,7 +527,45 @@ static Stream* getStream(Mesh& mesh, cgltf_attribute_type type, int index = 0) return NULL; } -static void simplifyMesh(Mesh& mesh, float threshold, bool attributes, bool aggressive, bool lock_borders, bool debug = false) +static void simplifyAttributes(std::vector& attrs, float* attrw, size_t stride, Mesh& mesh) +{ + assert(stride >= 6); // normal + color + + size_t vertex_count = mesh.streams[0].data.size(); + + attrs.resize(vertex_count * stride); + float* data = attrs.data(); + + if (const Stream* attr = getStream(mesh, cgltf_attribute_type_normal)) + { + const Attr* a = attr->data.data(); + + for (size_t i = 0; i < vertex_count; ++i) + { + data[i * stride + 0] = a[i].f[0]; + data[i * stride + 1] = a[i].f[1]; + data[i * stride + 2] = a[i].f[2]; + } + + attrw[0] = attrw[1] = attrw[2] = 0.5f; + } + + if (const Stream* attr = getStream(mesh, cgltf_attribute_type_color)) + { + const Attr* a = attr->data.data(); + + for (size_t i = 0; i < vertex_count; ++i) + { + data[i * stride + 3] = a[i].f[0] * a[i].f[3]; + data[i * stride + 4] = a[i].f[1] * a[i].f[3]; + data[i * stride + 5] = a[i].f[2] * a[i].f[3]; + } + + attrw[3] = attrw[4] = attrw[5] = 1.0f; + } +} + +static void simplifyMesh(Mesh& mesh, float threshold, float error, bool attributes, bool aggressive, bool lock_borders, bool debug = false) { enum { @@ -546,7 +584,7 @@ static void simplifyMesh(Mesh& mesh, float threshold, bool attributes, bool aggr size_t vertex_count = mesh.streams[0].data.size(); size_t target_index_count = size_t(double(mesh.indices.size() / 3) * threshold) * 3; - float target_error = 1e-2f; + float target_error = error; float target_error_aggressive = 1e-1f; unsigned int options = 0; if (lock_borders) @@ -554,16 +592,21 @@ static void simplifyMesh(Mesh& mesh, float threshold, bool attributes, bool aggr if (debug) options |= meshopt_SimplifyInternalDebug; - const Stream* attr = getStream(mesh, cgltf_attribute_type_color); - attr = attr ? attr : getStream(mesh, cgltf_attribute_type_normal); + std::vector indices(mesh.indices.size()); - const float attrw[3] = {0.5f, 0.5f, 0.5f}; + if (attributes) + { + float attrw[6] = {}; + std::vector attrs; + simplifyAttributes(attrs, attrw, sizeof(attrw) / sizeof(attrw[0]), mesh); - std::vector indices(mesh.indices.size()); - if (attributes && attr) - indices.resize(meshopt_simplifyWithAttributes(&indices[0], &mesh.indices[0], mesh.indices.size(), positions->data[0].f, vertex_count, sizeof(Attr), attr->data[0].f, sizeof(Attr), attrw, 3, NULL, target_index_count, target_error, options)); + indices.resize(meshopt_simplifyWithAttributes(&indices[0], &mesh.indices[0], mesh.indices.size(), positions->data[0].f, vertex_count, sizeof(Attr), attrs.data(), sizeof(attrw), attrw, sizeof(attrw) / sizeof(attrw[0]), NULL, target_index_count, target_error, options)); + } else + { indices.resize(meshopt_simplify(&indices[0], &mesh.indices[0], mesh.indices.size(), positions->data[0].f, vertex_count, sizeof(Attr), target_index_count, target_error, options)); + } + mesh.indices.swap(indices); // Note: if the simplifier got stuck, we can try to reindex without normals/tangents and retry @@ -771,7 +814,7 @@ void processMesh(Mesh& mesh, const Settings& settings) { case cgltf_primitive_type_points: assert(mesh.indices.empty()); - simplifyPointMesh(mesh, settings.simplify_threshold); + simplifyPointMesh(mesh, settings.simplify_ratio); sortPointMesh(mesh); break; @@ -782,8 +825,8 @@ void processMesh(Mesh& mesh, const Settings& settings) filterBones(mesh); reindexMesh(mesh); filterTriangles(mesh); - if (settings.simplify_threshold < 1) - simplifyMesh(mesh, settings.simplify_threshold, settings.simplify_attributes, settings.simplify_aggressive, settings.simplify_lock_borders); + if (settings.simplify_ratio < 1) + simplifyMesh(mesh, settings.simplify_ratio, settings.simplify_error, settings.simplify_attributes, settings.simplify_aggressive, settings.simplify_lock_borders); optimizeMesh(mesh, settings.compressmore); break; @@ -793,7 +836,7 @@ void processMesh(Mesh& mesh, const Settings& settings) } #ifndef NDEBUG -void debugSimplify(const Mesh& source, Mesh& kinds, Mesh& loops, float ratio, bool attributes) +void debugSimplify(const Mesh& source, Mesh& kinds, Mesh& loops, float ratio, float error, bool attributes) { Mesh mesh = source; assert(mesh.type == cgltf_primitive_type_triangles); @@ -806,7 +849,7 @@ void debugSimplify(const Mesh& source, Mesh& kinds, Mesh& loops, float ratio, bo size_t vertex_count = mesh.streams[0].data.size(); - simplifyMesh(mesh, ratio, attributes, /* aggressive= */ false, /* lock_borders= */ false, /* debug= */ true); + simplifyMesh(mesh, ratio, error, attributes, /* aggressive= */ false, /* lock_borders= */ false, /* debug= */ true); // color palette for display static const Attr kPalette[] = {