@@ -429,7 +429,6 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, Array p_bone_transf
429429
430430 unsigned int merged_vertex_count = merged_vertices.size ();
431431 const Vector3 *merged_vertices_ptr = merged_vertices.ptr ();
432- const int32_t *merged_indices_ptr = merged_indices.ptr ();
433432 Vector3 *merged_normals_ptr = merged_normals.ptr ();
434433
435434 {
@@ -474,17 +473,22 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, Array p_bone_transf
474473 }
475474 }
476475
477- unsigned int index_target = 12 ; // Start with the smallest target, 4 triangles
478- unsigned int last_index_count = 0 ;
476+ print_verbose (" LOD Generation: Triangles " + itos (index_count / 3 ) + " , vertices " + itos (vertex_count) + " (merged " + itos (merged_vertex_count) + " )" + (deformable ? " , deformable" : " " ));
479477
480- const float max_mesh_error = 1 .0f ; // we only need LODs that can be selected by error threshold
481- float mesh_error = 0 .0f ;
478+ const float max_mesh_error = 1 .0f ; // We only need LODs that can be selected by error threshold.
479+ const unsigned min_target_indices = 12 ;
480+
481+ LocalVector<int > current_indices = merged_indices;
482+ float current_error = 0 .0f ;
483+
484+ while (current_indices.size () > min_target_indices * 2 ) {
485+ unsigned int current_index_count = current_indices.size ();
486+ unsigned int target_index_count = MAX (((current_index_count / 3 ) / 2 ) * 3 , min_target_indices);
482487
483- while (index_target < index_count) {
484488 PackedInt32Array new_indices;
485- new_indices.resize (index_count );
489+ new_indices.resize (current_index_count );
486490
487- int simplify_options = 0 ;
491+ int simplify_options = SurfaceTool::SIMPLIFY_SPARSE; // Does not change appearance, but speeds up subsequent iterations.
488492
489493 // Lock geometric boundary in case the mesh is composed of multiple material subsets.
490494 simplify_options |= SurfaceTool::SIMPLIFY_LOCK_BORDER;
@@ -494,38 +498,40 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, Array p_bone_transf
494498 simplify_options |= SurfaceTool::SIMPLIFY_REGULARIZE;
495499 }
496500
501+ float step_error = 0 .0f ;
497502 size_t new_index_count = SurfaceTool::simplify_with_attrib_func (
498503 (unsigned int *)new_indices.ptrw (),
499- (const uint32_t *)merged_indices_ptr, index_count ,
504+ (const uint32_t *)current_indices. ptr (), current_index_count ,
500505 merged_vertices_f32.ptr (), merged_vertex_count,
501506 sizeof (float ) * 3 , // Vertex stride
502507 merged_attribs_ptr,
503508 sizeof (float ) * attrib_count, // Attribute stride
504509 attrib_weights, attrib_count,
505510 nullptr , // Vertex lock
506- index_target ,
511+ target_index_count ,
507512 max_mesh_error,
508513 simplify_options,
509- &mesh_error );
514+ &step_error );
510515
511- if (new_index_count < last_index_count * 1 .5f ) {
512- index_target = index_target * 1 .5f ;
513- continue ;
514- }
516+ // Accumulate error over iterations. Usually, it's correct to use step_error as is; however, on coarse LODs, we may start
517+ // getting *smaller* relative error compared to the previous LOD. To make sure the error is monotonic and strictly increasing,
518+ // and to limit the switching (pop) distance, we ensure the error grows by an arbitrary factor each iteration.
519+ current_error = MAX (current_error * 1 .5f , step_error);
520+
521+ new_indices.resize (new_index_count);
522+ current_indices = new_indices;
515523
516- if (new_index_count == 0 || (new_index_count >= (index_count * 0 .75f ))) {
524+ if (new_index_count == 0 || (new_index_count >= current_index_count * 0 .75f )) {
525+ print_verbose (" LOD stop: got " + itos (new_index_count / 3 ) + " triangles when asking for " + itos (target_index_count / 3 ));
517526 break ;
518527 }
519- if (new_index_count > 5000000 ) {
520- // This limit theoretically shouldn't be needed, but it's here
521- // as an ad-hoc fix to prevent a crash with complex meshes.
522- // The crash still happens with limit of 6000000, but 5000000 works.
523- // In the future, identify what's causing that crash and fix it.
524- WARN_PRINT (" Mesh LOD generation failed for mesh " + get_name () + " surface " + itos (i) + " , mesh is too complex. Some automatic LODs were not generated." );
528+
529+ if (current_error > max_mesh_error) {
530+ print_verbose (" LOD stop: reached " + rtos (current_error) + " cumulative error (step error " + rtos (step_error) + " )" );
525531 break ;
526532 }
527533
528- new_indices. resize (new_index_count);
534+ // We need to remap the LOD indices back to the original vertex array; note that we already copied new_indices into current_indices for subsequent iteration.
529535 {
530536 int *ptrw = new_indices.ptrw ();
531537 for (unsigned int j = 0 ; j < new_index_count; j++) {
@@ -534,15 +540,11 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, Array p_bone_transf
534540 }
535541
536542 Surface::LOD lod;
537- lod.distance = MAX (mesh_error * scale, CMP_EPSILON2);
543+ lod.distance = MAX (current_error * scale, CMP_EPSILON2);
538544 lod.indices = new_indices;
539545 surfaces.write [i].lods .push_back (lod);
540- index_target = MAX (new_index_count, index_target) * 2 ;
541- last_index_count = new_index_count;
542546
543- if (mesh_error == 0 .0f ) {
544- break ;
545- }
547+ print_verbose (" LOD " + itos (surfaces.write [i].lods .size ()) + " : " + itos (new_index_count / 3 ) + " triangles, error " + rtos (current_error) + " (step error " + rtos (step_error) + " )" );
546548 }
547549
548550 surfaces.write [i].lods .sort_custom <Surface::LODComparator>();
0 commit comments