From 60a255a0d06d723a4143f5dc526b6251518c4750 Mon Sep 17 00:00:00 2001 From: BlueCube3310 <53150244+BlueCube3310@users.noreply.github.com> Date: Thu, 11 Jul 2024 23:42:52 +0200 Subject: [PATCH] Lightmapper: Prevent infinite loop when blitting lightmaps into an atlas --- doc/classes/LightmapGI.xml | 6 ++++++ editor/plugins/lightmap_gi_editor_plugin.cpp | 3 +++ modules/lightmapper_rd/lightmapper_rd.cpp | 14 +++++++++++--- scene/3d/lightmap_gi.cpp | 6 +++++- scene/3d/lightmap_gi.h | 1 + scene/3d/lightmapper.h | 5 +++-- 6 files changed, 29 insertions(+), 6 deletions(-) diff --git a/doc/classes/LightmapGI.xml b/doc/classes/LightmapGI.xml index 6fb15e4d21e..e7d44411efd 100644 --- a/doc/classes/LightmapGI.xml +++ b/doc/classes/LightmapGI.xml @@ -137,6 +137,12 @@ Lightmap baking failed as the maximum texture size is too small to fit some of the meshes marked for baking. + + Lightmap baking failed as the lightmap is too small. + + + Lightmap baking failed as the lightmap was unable to fit into an atlas. + Ignore environment lighting when baking lightmaps. diff --git a/editor/plugins/lightmap_gi_editor_plugin.cpp b/editor/plugins/lightmap_gi_editor_plugin.cpp index 2289105d78c..1c17d99d0dc 100644 --- a/editor/plugins/lightmap_gi_editor_plugin.cpp +++ b/editor/plugins/lightmap_gi_editor_plugin.cpp @@ -110,6 +110,9 @@ void LightmapGIEditorPlugin::_bake_select_file(const String &p_file) { case LightmapGI::BAKE_ERROR_LIGHTMAP_TOO_SMALL: { EditorNode::get_singleton()->show_warning(TTR("Failed creating lightmap images. Make sure all meshes selected to bake have `lightmap_size_hint` value set high enough, and `texel_scale` value of LightmapGI is not too low.")); } break; + case LightmapGI::BAKE_ERROR_ATLAS_TOO_SMALL: { + EditorNode::get_singleton()->show_warning(TTR("Failed fitting a lightmap image into an atlas. This should never happen and should be reported.")); + } break; default: { } break; } diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp index 0a848af9984..33b0b0d0152 100644 --- a/modules/lightmapper_rd/lightmapper_rd.cpp +++ b/modules/lightmapper_rd/lightmapper_rd.cpp @@ -240,7 +240,7 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_ max = MAX(max, nearest_power_of_2_templated(atlas_size.height)); if (max > p_max_texture_size) { - return BAKE_ERROR_LIGHTMAP_TOO_SMALL; + return BAKE_ERROR_TEXTURE_EXCEEDS_MAX_SIZE; } if (p_step_function) { @@ -254,19 +254,27 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_ int best_atlas_memory = 0x7FFFFFFF; Vector best_atlas_offsets; - //determine best texture array atlas size by bruteforce fitting + // Determine best texture array atlas size by bruteforce fitting. while (atlas_size.x <= p_max_texture_size && atlas_size.y <= p_max_texture_size) { Vector source_sizes; Vector source_indices; source_sizes.resize(sizes.size()); source_indices.resize(sizes.size()); for (int i = 0; i < source_indices.size(); i++) { - source_sizes.write[i] = sizes[i] + Vector2i(2, 2).maxi(p_denoiser_range); // Add padding between lightmaps + source_sizes.write[i] = sizes[i] + Vector2i(2, 2).maxi(p_denoiser_range); // Add padding between lightmaps. source_indices.write[i] = i; } Vector atlas_offsets; atlas_offsets.resize(source_sizes.size()); + // Ensure the sizes can all fit into a single atlas layer. + // This should always happen, and this check is only in place to prevent an infinite loop. + for (int i = 0; i < source_sizes.size(); i++) { + if (source_sizes[i] > atlas_size) { + return BAKE_ERROR_ATLAS_TOO_SMALL; + } + } + int slices = 0; while (source_sizes.size() > 0) { diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index ef492a69941..77c5d5a4999 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -1104,10 +1104,12 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa Lightmapper::BakeError bake_err = lightmapper->bake(Lightmapper::BakeQuality(bake_quality), use_denoiser, denoiser_strength, denoiser_range, bounces, bounce_indirect_energy, bias, max_texture_size, directional, use_texture_for_bounces, Lightmapper::GenerateProbes(gen_probes), environment_image, environment_transform, _lightmap_bake_step_function, &bsud, exposure_normalization); - if (bake_err == Lightmapper::BAKE_ERROR_LIGHTMAP_TOO_SMALL) { + if (bake_err == Lightmapper::BAKE_ERROR_TEXTURE_EXCEEDS_MAX_SIZE) { return BAKE_ERROR_TEXTURE_SIZE_TOO_SMALL; } else if (bake_err == Lightmapper::BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES) { return BAKE_ERROR_MESHES_INVALID; + } else if (bake_err == Lightmapper::BAKE_ERROR_ATLAS_TOO_SMALL) { + return BAKE_ERROR_ATLAS_TOO_SMALL; } // POSTBAKE: Save Textures. @@ -1711,6 +1713,8 @@ void LightmapGI::_bind_methods() { BIND_ENUM_CONSTANT(BAKE_ERROR_CANT_CREATE_IMAGE); BIND_ENUM_CONSTANT(BAKE_ERROR_USER_ABORTED); BIND_ENUM_CONSTANT(BAKE_ERROR_TEXTURE_SIZE_TOO_SMALL); + BIND_ENUM_CONSTANT(BAKE_ERROR_LIGHTMAP_TOO_SMALL); + BIND_ENUM_CONSTANT(BAKE_ERROR_ATLAS_TOO_SMALL); BIND_ENUM_CONSTANT(ENVIRONMENT_MODE_DISABLED); BIND_ENUM_CONSTANT(ENVIRONMENT_MODE_SCENE); diff --git a/scene/3d/lightmap_gi.h b/scene/3d/lightmap_gi.h index 527667177b4..67480132b6f 100644 --- a/scene/3d/lightmap_gi.h +++ b/scene/3d/lightmap_gi.h @@ -143,6 +143,7 @@ public: BAKE_ERROR_USER_ABORTED, BAKE_ERROR_TEXTURE_SIZE_TOO_SMALL, BAKE_ERROR_LIGHTMAP_TOO_SMALL, + BAKE_ERROR_ATLAS_TOO_SMALL, }; enum EnvironmentMode { diff --git a/scene/3d/lightmapper.h b/scene/3d/lightmapper.h index 39181ad9a2e..9aa8ef8ccbc 100644 --- a/scene/3d/lightmapper.h +++ b/scene/3d/lightmapper.h @@ -143,9 +143,10 @@ public: }; enum BakeError { - BAKE_ERROR_LIGHTMAP_TOO_SMALL, + BAKE_OK, + BAKE_ERROR_TEXTURE_EXCEEDS_MAX_SIZE, BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES, - BAKE_OK + BAKE_ERROR_ATLAS_TOO_SMALL, }; enum BakeQuality {