From a65ee242a45563020009177ff9d649d350d5cf63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?DESKTOP-17VKIKK=5C=E5=A4=A7=E5=A3=AE=E4=BB=96=E5=93=A5?= <1989punk@gmail.com> Date: Sat, 28 Feb 2026 21:08:49 +0800 Subject: [PATCH] one cluster multiple support works --- demo/clusterlod.h | 83 ++++++++++++++++++++++++++++++++++++++------- src/clusterizer.cpp | 19 ++++++++--- src/meshoptimizer.h | 19 +++++++++++ src/simplifier.cpp | 23 +++++++++---- 4 files changed, 121 insertions(+), 23 deletions(-) diff --git a/demo/clusterlod.h b/demo/clusterlod.h index 7263b48ab..95103669e 100644 --- a/demo/clusterlod.h +++ b/demo/clusterlod.h @@ -88,6 +88,10 @@ struct clodMesh // attribute mask to flag attribute discontinuities for permissive simplification; mask (1< indices; + std::vector materials; // per-triangle material IDs (size = indices.size() / 3) int group; int refined; @@ -231,7 +239,7 @@ static clodBounds boundsMerge(const std::vector& clusters, const std::v return result; } -static std::vector clusterize(const clodConfig& config, const clodMesh& mesh, const unsigned int* indices, size_t index_count) +static std::vector clusterize(const clodConfig& config, const clodMesh& mesh, const unsigned int* indices, size_t index_count, const unsigned int* triangle_materials = NULL) { size_t max_meshlets = meshopt_buildMeshletsBound(index_count, config.max_vertices, config.min_triangles); @@ -244,14 +252,28 @@ static std::vector clusterize(const clodConfig& config, const clodMesh& std::vector meshlet_triangles(index_count); #endif - if (config.cluster_spatial) + // use Flex with triangle mapping when materials are provided (spatial doesn't support mapping) + std::vector meshlet_triangle_map; + + if (triangle_materials) + { + meshlet_triangle_map.resize(index_count / 3); + meshlets.resize(meshopt_buildMeshletsFlexWithMapping(meshlets.data(), meshlet_vertices.data(), meshlet_triangles.data(), meshlet_triangle_map.data(), indices, index_count, + mesh.vertex_positions, mesh.vertex_count, mesh.vertex_positions_stride, + config.max_vertices, config.min_triangles, config.max_triangles, 0.f, config.cluster_split_factor)); + } + else if (config.cluster_spatial) + { meshlets.resize(meshopt_buildMeshletsSpatial(meshlets.data(), meshlet_vertices.data(), meshlet_triangles.data(), indices, index_count, mesh.vertex_positions, mesh.vertex_count, mesh.vertex_positions_stride, config.max_vertices, config.min_triangles, config.max_triangles, config.cluster_fill_weight)); + } else + { meshlets.resize(meshopt_buildMeshletsFlex(meshlets.data(), meshlet_vertices.data(), meshlet_triangles.data(), indices, index_count, mesh.vertex_positions, mesh.vertex_count, mesh.vertex_positions_stride, config.max_vertices, config.min_triangles, config.max_triangles, 0.f, config.cluster_split_factor)); + } std::vector clusters(meshlets.size()); @@ -269,6 +291,13 @@ static std::vector clusterize(const clodConfig& config, const clodMesh& for (size_t j = 0; j < meshlet.triangle_count * 3; ++j) clusters[i].indices[j] = meshlet_vertices[meshlet.vertex_offset + meshlet_triangles[meshlet.triangle_offset + j]]; + if (triangle_materials) + { + clusters[i].materials.resize(meshlet.triangle_count); + for (size_t j = 0; j < meshlet.triangle_count; ++j) + clusters[i].materials[j] = triangle_materials[meshlet_triangle_map[meshlet.triangle_offset / 3 + j]]; + } + clusters[i].group = -1; clusters[i].refined = -1; } @@ -421,7 +450,7 @@ static void simplifyFallback(std::vector& lod, const clodMesh& mes lod[i] = subset[lod[i]].id; } -static std::vector simplify(const clodConfig& config, const clodMesh& mesh, const std::vector& indices, const std::vector& locks, size_t target_count, float* error) +static std::vector simplify(const clodConfig& config, const clodMesh& mesh, const std::vector& indices, const std::vector& locks, size_t target_count, float* error, std::vector* inout_materials = NULL) { if (target_count > indices.size()) return indices; @@ -430,19 +459,33 @@ static std::vector simplify(const clodConfig& config, const clodMe unsigned int options = meshopt_SimplifySparse | meshopt_SimplifyErrorAbsolute | (config.simplify_permissive ? meshopt_SimplifyPermissive : 0) | (config.simplify_regularize ? meshopt_SimplifyRegularize : 0); - lod.resize(meshopt_simplifyWithAttributes(&lod[0], &indices[0], indices.size(), + // copy materials for permissive retry + std::vector lod_materials; + unsigned int* lod_materials_ptr = NULL; + if (inout_materials && !inout_materials->empty()) + { + lod_materials = *inout_materials; + lod_materials_ptr = &lod_materials[0]; + } + + lod.resize(meshopt_simplifyWithAttributesAndMaterials(&lod[0], &indices[0], indices.size(), mesh.vertex_positions, mesh.vertex_count, mesh.vertex_positions_stride, mesh.vertex_attributes, mesh.vertex_attributes_stride, mesh.attribute_weights, mesh.attribute_count, - &locks[0], target_count, FLT_MAX, options, error)); + &locks[0], lod_materials_ptr, target_count, FLT_MAX, options, error)); if (lod.size() > target_count && config.simplify_fallback_permissive && !config.simplify_permissive) - lod.resize(meshopt_simplifyWithAttributes(&lod[0], &indices[0], indices.size(), + { + if (lod_materials_ptr) + lod_materials = *inout_materials; // reset materials for retry + + lod.resize(meshopt_simplifyWithAttributesAndMaterials(&lod[0], &indices[0], indices.size(), mesh.vertex_positions, mesh.vertex_count, mesh.vertex_positions_stride, mesh.vertex_attributes, mesh.vertex_attributes_stride, mesh.attribute_weights, mesh.attribute_count, - &locks[0], target_count, FLT_MAX, options | meshopt_SimplifyPermissive, error)); + &locks[0], lod_materials_ptr, target_count, FLT_MAX, options | meshopt_SimplifyPermissive, error)); + } - // while it's possible to call simplifySloppy directly, it doesn't support sparsity or absolute error, so we need to do some extra work - if (lod.size() > target_count && config.simplify_fallback_sloppy) + // while it's possible to call simplifySloppy directly, it doesn't support sparsity, absolute error, or material tracking + if (lod.size() > target_count && config.simplify_fallback_sloppy && !inout_materials) { simplifyFallback(lod, mesh, indices, locks, target_count, error); *error *= config.simplify_error_factor_sloppy; // scale error up to account for appearance degradation @@ -478,6 +521,12 @@ static std::vector simplify(const clodConfig& config, const clodMe *error = std::min(*error, sqrtf(max_edge_sq) * config.simplify_error_edge_limit); } + if (lod_materials_ptr) + { + lod_materials.resize(lod.size() / 3); + *inout_materials = lod_materials; + } + return lod; } @@ -495,6 +544,7 @@ static int outputGroup(const clodConfig& config, const clodMesh& mesh, const std result.indices = cluster.indices.data(); result.index_count = cluster.indices.size(); result.vertex_count = cluster.vertices; + result.materials = cluster.materials.empty() ? NULL : cluster.materials.data(); } return output_callback ? output_callback(output_context, {depth, simplified}, group_clusters.data(), group_clusters.size()) : -1; @@ -580,7 +630,7 @@ size_t clodBuild(clodConfig config, clodMesh mesh, void* output_context, clodOut } // initial clusterization splits the original mesh - std::vector clusters = clusterize(config, mesh, mesh.indices, mesh.index_count); + std::vector clusters = clusterize(config, mesh, mesh.indices, mesh.index_count, mesh.triangle_materials); // compute initial precise bounds; subsequent bounds will be using group-merged bounds for (Cluster& cluster : clusters) @@ -607,8 +657,17 @@ size_t clodBuild(clodConfig config, clodMesh mesh, void* output_context, clodOut { std::vector merged; merged.reserve(groups[i].size() * config.max_triangles * 3); + + std::vector merged_materials; + if (mesh.triangle_materials) + merged_materials.reserve(groups[i].size() * config.max_triangles); + for (size_t j = 0; j < groups[i].size(); ++j) + { merged.insert(merged.end(), clusters[groups[i][j]].indices.begin(), clusters[groups[i][j]].indices.end()); + if (mesh.triangle_materials) + merged_materials.insert(merged_materials.end(), clusters[groups[i][j]].materials.begin(), clusters[groups[i][j]].materials.end()); + } size_t target_size = size_t((merged.size() / 3) * config.simplify_ratio) * 3; @@ -617,7 +676,7 @@ size_t clodBuild(clodConfig config, clodMesh mesh, void* output_context, clodOut clodBounds bounds = boundsMerge(clusters, groups[i]); float error = 0.f; - std::vector simplified = simplify(config, mesh, merged, locks, target_size, &error); + std::vector simplified = simplify(config, mesh, merged, locks, target_size, &error, merged_materials.empty() ? NULL : &merged_materials); if (simplified.size() > merged.size() * config.simplify_threshold) { bounds.error = FLT_MAX; // terminal group, won't simplify further @@ -635,7 +694,7 @@ size_t clodBuild(clodConfig config, clodMesh mesh, void* output_context, clodOut for (size_t j = 0; j < groups[i].size(); ++j) clusters[groups[i][j]].indices = std::vector(); - std::vector split = clusterize(config, mesh, simplified.data(), simplified.size()); + std::vector split = clusterize(config, mesh, simplified.data(), simplified.size(), merged_materials.empty() ? NULL : merged_materials.data()); for (Cluster& cluster : split) { diff --git a/src/clusterizer.cpp b/src/clusterizer.cpp index c2ebd8c5a..09cdcaab1 100644 --- a/src/clusterizer.cpp +++ b/src/clusterizer.cpp @@ -240,7 +240,7 @@ static float computeTriangleCones(Cone* triangles, const unsigned int* indices, return mesh_area; } -static bool appendMeshlet(meshopt_Meshlet& meshlet, unsigned int a, unsigned int b, unsigned int c, short* used, meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, size_t meshlet_offset, size_t max_vertices, size_t max_triangles, bool split = false) +static bool appendMeshlet(meshopt_Meshlet& meshlet, unsigned int a, unsigned int b, unsigned int c, unsigned int triangle_index, short* used, meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, unsigned int* meshlet_triangle_map, size_t meshlet_offset, size_t max_vertices, size_t max_triangles, bool split = false) { short& av = used[a]; short& bv = used[b]; @@ -286,6 +286,10 @@ static bool appendMeshlet(meshopt_Meshlet& meshlet, unsigned int a, unsigned int meshlet_triangles[meshlet.triangle_offset + meshlet.triangle_count * 3 + 0] = (unsigned char)av; meshlet_triangles[meshlet.triangle_offset + meshlet.triangle_count * 3 + 1] = (unsigned char)bv; meshlet_triangles[meshlet.triangle_offset + meshlet.triangle_count * 3 + 2] = (unsigned char)cv; + + if (meshlet_triangle_map) + meshlet_triangle_map[meshlet.triangle_offset / 3 + meshlet.triangle_count] = triangle_index; + meshlet.triangle_count++; return result; @@ -1024,7 +1028,7 @@ size_t meshopt_buildMeshletsBound(size_t index_count, size_t max_vertices, size_ return meshlet_limit_vertices > meshlet_limit_triangles ? meshlet_limit_vertices : meshlet_limit_triangles; } -size_t meshopt_buildMeshletsFlex(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t min_triangles, size_t max_triangles, float cone_weight, float split_factor) +size_t meshopt_buildMeshletsFlexWithMapping(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, unsigned int* meshlet_triangle_map, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t min_triangles, size_t max_triangles, float cone_weight, float split_factor) { using namespace meshopt; @@ -1166,7 +1170,7 @@ size_t meshopt_buildMeshletsFlex(meshopt_Meshlet* meshlets, unsigned int* meshle assert(a < vertex_count && b < vertex_count && c < vertex_count); // add meshlet to the output; when the current meshlet is full we reset the accumulated bounds - if (appendMeshlet(meshlet, a, b, c, used, meshlets, meshlet_vertices, meshlet_triangles, meshlet_offset, max_vertices, max_triangles, split)) + if (appendMeshlet(meshlet, a, b, c, best_triangle, used, meshlets, meshlet_vertices, meshlet_triangles, meshlet_triangle_map, meshlet_offset, max_vertices, max_triangles, split)) { meshlet_offset++; memset(&meshlet_cone_acc, 0, sizeof(meshlet_cone_acc)); @@ -1215,6 +1219,11 @@ size_t meshopt_buildMeshletsFlex(meshopt_Meshlet* meshlets, unsigned int* meshle return meshlet_offset; } +size_t meshopt_buildMeshletsFlex(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t min_triangles, size_t max_triangles, float cone_weight, float split_factor) +{ + return meshopt_buildMeshletsFlexWithMapping(meshlets, meshlet_vertices, meshlet_triangles, NULL, indices, index_count, vertex_positions, vertex_count, vertex_positions_stride, max_vertices, min_triangles, max_triangles, cone_weight, split_factor); +} + size_t meshopt_buildMeshlets(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t max_triangles, float cone_weight) { return meshopt_buildMeshletsFlex(meshlets, meshlet_vertices, meshlet_triangles, indices, index_count, vertex_positions, vertex_count, vertex_positions_stride, max_vertices, max_triangles, max_triangles, cone_weight, 0.0f); @@ -1244,7 +1253,7 @@ size_t meshopt_buildMeshletsScan(meshopt_Meshlet* meshlets, unsigned int* meshle assert(a < vertex_count && b < vertex_count && c < vertex_count); // appends triangle to the meshlet and writes previous meshlet to the output if full - meshlet_offset += appendMeshlet(meshlet, a, b, c, used, meshlets, meshlet_vertices, meshlet_triangles, meshlet_offset, max_vertices, max_triangles); + meshlet_offset += appendMeshlet(meshlet, a, b, c, unsigned(i / 3), used, meshlets, meshlet_vertices, meshlet_triangles, NULL, meshlet_offset, max_vertices, max_triangles); } if (meshlet.triangle_count) @@ -1342,7 +1351,7 @@ size_t meshopt_buildMeshletsSpatial(struct meshopt_Meshlet* meshlets, unsigned i unsigned int a = indices[index * 3 + 0], b = indices[index * 3 + 1], c = indices[index * 3 + 2]; // appends triangle to the meshlet and writes previous meshlet to the output if full - meshlet_offset += appendMeshlet(meshlet, a, b, c, used, meshlets, meshlet_vertices, meshlet_triangles, meshlet_offset, max_vertices, max_triangles, split); + meshlet_offset += appendMeshlet(meshlet, a, b, c, index, used, meshlets, meshlet_vertices, meshlet_triangles, NULL, meshlet_offset, max_vertices, max_triangles, split); meshlet_pending -= boundary[i]; } diff --git a/src/meshoptimizer.h b/src/meshoptimizer.h index 06e9d9e3a..b04f550fd 100644 --- a/src/meshoptimizer.h +++ b/src/meshoptimizer.h @@ -509,6 +509,15 @@ MESHOPTIMIZER_API size_t meshopt_simplify(unsigned int* destination, const unsig */ MESHOPTIMIZER_API size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, const unsigned char* vertex_lock, size_t target_index_count, float target_error, unsigned int options, float* result_error); +/** + * Mesh simplifier with attribute metric and per-triangle material tracking + * Similar to meshopt_simplifyWithAttributes, but also tracks per-triangle material IDs through simplification. + * + * triangle_materials can be NULL; when it's not NULL, must contain index_count/3 material IDs (one per triangle), modified in-place during simplification + * All other parameters are the same as meshopt_simplifyWithAttributes + */ +MESHOPTIMIZER_API size_t meshopt_simplifyWithAttributesAndMaterials(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, const unsigned char* vertex_lock, unsigned int* triangle_materials, size_t target_index_count, float target_error, unsigned int options, float* result_error); + /** * Mesh simplifier with position/attribute update * Reduces the number of triangles in the mesh, attempting to preserve mesh appearance as much as possible. @@ -716,6 +725,16 @@ MESHOPTIMIZER_API size_t meshopt_buildMeshletsBound(size_t index_count, size_t m */ MESHOPTIMIZER_API size_t meshopt_buildMeshletsFlex(struct meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t min_triangles, size_t max_triangles, float cone_weight, float split_factor); +/** + * Meshlet builder with triangle mapping support + * Similar to meshopt_buildMeshletsFlex, but also outputs a mapping from meshlet triangles to original triangles. + * + * meshlet_triangle_map can be NULL; when it's not NULL, must contain enough space for index_count/3 elements + * meshlet_triangle_map[i] will contain the original triangle index for the i-th triangle in the meshlet output + * All other parameters are the same as meshopt_buildMeshletsFlex + */ +MESHOPTIMIZER_API size_t meshopt_buildMeshletsFlexWithMapping(struct meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, unsigned int* meshlet_triangle_map, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t min_triangles, size_t max_triangles, float cone_weight, float split_factor); + /** * Meshlet builder that produces clusters optimized for raytracing * Splits the mesh into a set of meshlets, similarly to meshopt_buildMeshlets, but optimizes cluster subdivision for raytracing and allows to specify minimum and maximum number of triangles per meshlet. diff --git a/src/simplifier.cpp b/src/simplifier.cpp index 14d4d42fe..3d37e0226 100644 --- a/src/simplifier.cpp +++ b/src/simplifier.cpp @@ -1804,7 +1804,7 @@ static void solveAttributes(Vector3* vertex_positions, float* vertex_attributes, } } -static size_t remapIndexBuffer(unsigned int* indices, size_t index_count, const unsigned int* collapse_remap, const unsigned int* remap) +static size_t remapIndexBuffer(unsigned int* indices, size_t index_count, const unsigned int* collapse_remap, const unsigned int* remap, unsigned int* triangle_materials = NULL) { size_t write = 0; @@ -1831,6 +1831,10 @@ static size_t remapIndexBuffer(unsigned int* indices, size_t index_count, const indices[write + 0] = v0; indices[write + 1] = v1; indices[write + 2] = v2; + + if (triangle_materials) + triangle_materials[write / 3] = triangle_materials[i / 3]; + write += 3; } } @@ -2337,7 +2341,7 @@ enum meshopt_SimplifyInternalDebug = 1 << 30 }; -size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes_data, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, const unsigned char* vertex_lock, size_t target_index_count, float target_error, unsigned int options, float* out_result_error) +size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes_data, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, const unsigned char* vertex_lock, unsigned int* triangle_materials, size_t target_index_count, float target_error, unsigned int options, float* out_result_error) { using namespace meshopt; @@ -2531,7 +2535,7 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic remapEdgeLoops(loop, vertex_count, collapse_remap); remapEdgeLoops(loopback, vertex_count, collapse_remap); - result_count = remapIndexBuffer(result, result_count, collapse_remap, remap); + result_count = remapIndexBuffer(result, result_count, collapse_remap, remap, triangle_materials); if ((options & meshopt_SimplifyPrune) && result_count > target_index_count && component_nexterror <= vertex_error) result_count = pruneComponents(result, result_count, components, component_errors, component_count, vertex_error, component_nexterror); @@ -2626,19 +2630,26 @@ size_t meshopt_simplify(unsigned int* destination, const unsigned int* indices, { assert((options & meshopt_SimplifyInternalSolve) == 0); // use meshopt_simplifyWithUpdate instead - return meshopt_simplifyEdge(destination, indices, index_count, vertex_positions_data, vertex_count, vertex_positions_stride, NULL, 0, NULL, 0, NULL, target_index_count, target_error, options, out_result_error); + return meshopt_simplifyEdge(destination, indices, index_count, vertex_positions_data, vertex_count, vertex_positions_stride, NULL, 0, NULL, 0, NULL, NULL, target_index_count, target_error, options, out_result_error); } size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes_data, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, const unsigned char* vertex_lock, size_t target_index_count, float target_error, unsigned int options, float* out_result_error) { assert((options & meshopt_SimplifyInternalSolve) == 0); // use meshopt_simplifyWithUpdate instead - return meshopt_simplifyEdge(destination, indices, index_count, vertex_positions_data, vertex_count, vertex_positions_stride, vertex_attributes_data, vertex_attributes_stride, attribute_weights, attribute_count, vertex_lock, target_index_count, target_error, options, out_result_error); + return meshopt_simplifyEdge(destination, indices, index_count, vertex_positions_data, vertex_count, vertex_positions_stride, vertex_attributes_data, vertex_attributes_stride, attribute_weights, attribute_count, vertex_lock, NULL, target_index_count, target_error, options, out_result_error); +} + +size_t meshopt_simplifyWithAttributesAndMaterials(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes_data, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, const unsigned char* vertex_lock, unsigned int* triangle_materials, size_t target_index_count, float target_error, unsigned int options, float* out_result_error) +{ + assert((options & meshopt_SimplifyInternalSolve) == 0); // use meshopt_simplifyWithUpdate instead + + return meshopt_simplifyEdge(destination, indices, index_count, vertex_positions_data, vertex_count, vertex_positions_stride, vertex_attributes_data, vertex_attributes_stride, attribute_weights, attribute_count, vertex_lock, triangle_materials, target_index_count, target_error, options, out_result_error); } size_t meshopt_simplifyWithUpdate(unsigned int* indices, size_t index_count, float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, float* vertex_attributes_data, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, const unsigned char* vertex_lock, size_t target_index_count, float target_error, unsigned int options, float* out_result_error) { - return meshopt_simplifyEdge(indices, indices, index_count, vertex_positions_data, vertex_count, vertex_positions_stride, vertex_attributes_data, vertex_attributes_stride, attribute_weights, attribute_count, vertex_lock, target_index_count, target_error, options | meshopt_SimplifyInternalSolve, out_result_error); + return meshopt_simplifyEdge(indices, indices, index_count, vertex_positions_data, vertex_count, vertex_positions_stride, vertex_attributes_data, vertex_attributes_stride, attribute_weights, attribute_count, vertex_lock, NULL, target_index_count, target_error, options | meshopt_SimplifyInternalSolve, out_result_error); } size_t meshopt_simplifySloppy(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, const unsigned char* vertex_lock, size_t target_index_count, float target_error, float* out_result_error)