From 690f9716faac7b9466d42aee0bc0d9d2f60ae0ef Mon Sep 17 00:00:00 2001 From: JFonS Date: Mon, 14 Feb 2022 14:47:02 +0100 Subject: [PATCH] Add support for saving multiple Images in BakedLightmap Instead of fitting all atlas slices into a single image, which meant there was a hard limit on the size, BakedLightmap will now save as many images as needed to fit all the slices generated by the lightmapper. --- modules/lightmapper_cpu/lightmapper_cpu.cpp | 46 +++++------ scene/3d/baked_lightmap.cpp | 87 +++++++++------------ 2 files changed, 62 insertions(+), 71 deletions(-) diff --git a/modules/lightmapper_cpu/lightmapper_cpu.cpp b/modules/lightmapper_cpu/lightmapper_cpu.cpp index 4f6093d7443..486cf955f95 100644 --- a/modules/lightmapper_cpu/lightmapper_cpu.cpp +++ b/modules/lightmapper_cpu/lightmapper_cpu.cpp @@ -166,15 +166,13 @@ Error LightmapperCPU::_layout_atlas(int p_max_size, Vector2i *r_atlas_size, int } float mem_utilization = static_cast(mem_occupied) / mem_used; - if (slices * atlas_size.y <= 16384) { // Maximum Image size - if (mem_used < best_atlas_memory || (mem_used == best_atlas_memory && mem_utilization > best_atlas_mem_utilization)) { - best_atlas_size = atlas_size; - best_atlas_offsets = curr_atlas_offsets; - best_atlas_slices = slices; - best_atlas_memory = mem_used; - best_atlas_mem_utilization = mem_utilization; - best_scaled_sizes = scaled_sizes; - } + if (mem_used < best_atlas_memory || (mem_used == best_atlas_memory && mem_utilization > best_atlas_mem_utilization)) { + best_atlas_size = atlas_size; + best_atlas_offsets = curr_atlas_offsets; + best_atlas_slices = slices; + best_atlas_memory = mem_used; + best_atlas_mem_utilization = mem_utilization; + best_scaled_sizes = scaled_sizes; } if (recovery_percent == 0) { @@ -1430,22 +1428,24 @@ LightmapperCPU::BakeError LightmapperCPU::bake(BakeQuality p_quality, bool p_use parameters.environment_panorama->lock(); } - for (unsigned int i = 0; i < mesh_instances.size(); i++) { - if (!mesh_instances[i].generate_lightmap) { - continue; - } - - if (p_step_function) { - float p = float(i) / n_lit_meshes; - bool cancelled = p_step_function(0.4 + p * 0.4, vformat("%s (%d/%d)", TTR("Indirect lighting"), i, mesh_instances.size()), p_bake_userdata, false); - if (cancelled) { - return BAKE_ERROR_USER_ABORTED; + if (parameters.bounces > 0) { + for (unsigned int i = 0; i < mesh_instances.size(); i++) { + if (!mesh_instances[i].generate_lightmap) { + continue; } - } - if (!scene_lightmaps[i].empty()) { - if (_parallel_run(scene_lightmaps[i].size(), "Computing indirect light", &LightmapperCPU::_compute_indirect_light, scene_lightmaps[i].ptr(), p_substep_function)) { - return BAKE_ERROR_USER_ABORTED; + if (p_step_function) { + float p = float(i) / n_lit_meshes; + bool cancelled = p_step_function(0.4 + p * 0.4, vformat("%s (%d/%d)", TTR("Indirect lighting"), i, mesh_instances.size()), p_bake_userdata, false); + if (cancelled) { + return BAKE_ERROR_USER_ABORTED; + } + } + + if (!scene_lightmaps[i].empty()) { + if (_parallel_run(scene_lightmaps[i].size(), "Computing indirect light", &LightmapperCPU::_compute_indirect_light, scene_lightmaps[i].ptr(), p_substep_function)) { + return BAKE_ERROR_USER_ABORTED; + } } } } diff --git a/scene/3d/baked_lightmap.cpp b/scene/3d/baked_lightmap.cpp index 2c2c1877f78..7927b9d660e 100644 --- a/scene/3d/baked_lightmap.cpp +++ b/scene/3d/baked_lightmap.cpp @@ -954,23 +954,35 @@ BakedLightmap::BakeError BakedLightmap::bake(Node *p_from_node, String p_data_sa bool use_srgb = use_color && !use_hdr; if (gen_atlas) { - Ref large_image; - large_image.instance(); - large_image->create(images[0]->get_width(), images[0]->get_height() * images.size(), false, images[0]->get_format()); - for (int i = 0; i < images.size(); i++) { - large_image->blit_rect(images[i], Rect2(0, 0, images[0]->get_width(), images[0]->get_height()), Point2(0, images[0]->get_height() * i)); - } + int slice_count = images.size(); + int slice_width = images[0]->get_width(); + int slice_height = images[0]->get_height(); - Ref texture; + int slices_per_texture = Image::MAX_HEIGHT / slice_height; + int texture_count = Math::ceil(slice_count / (float)slices_per_texture); + + Vector> textures; + textures.resize(texture_count); String base_path = p_data_save_path.get_basename(); - if (ResourceLoader::import) { - _save_image(base_path, large_image, use_srgb); + int last_count = slice_count % slices_per_texture; + for (int i = 0; i < texture_count; i++) { + String texture_path = texture_count > 1 ? base_path + "_" + itos(i) : base_path; + int texture_slice_count = (i == texture_count - 1 && last_count != 0) ? last_count : slices_per_texture; + + Ref large_image; + large_image.instance(); + large_image->create(slice_width, slice_height * texture_slice_count, false, images[0]->get_format()); + + for (int j = 0; j < texture_slice_count; j++) { + large_image->blit_rect(images[i * slices_per_texture + j], Rect2(0, 0, slice_width, slice_height), Point2(0, slice_height * j)); + } + _save_image(texture_path, large_image, use_srgb); Ref config; config.instance(); - if (FileAccess::exists(base_path + ".import")) { - config->load(base_path + ".import"); + if (FileAccess::exists(texture_path + ".import")) { + config->load(texture_path + ".import"); } else { // Set only if settings don't exist, to keep user choice config->set_value("params", "compress/mode", 0); @@ -983,49 +995,28 @@ BakedLightmap::BakeError BakedLightmap::bake(Node *p_from_node, String p_data_sa config->set_value("params", "flags/mipmaps", false); config->set_value("params", "flags/srgb", use_srgb); config->set_value("params", "slices/horizontal", 1); - config->set_value("params", "slices/vertical", images.size()); - config->save(base_path + ".import"); + config->set_value("params", "slices/vertical", texture_slice_count); - ResourceLoader::import(base_path); - texture = ResourceLoader::load(base_path); //if already loaded, it will be updated on refocus? - } else { - base_path += ".texarr"; - Ref tex; - bool set_path = true; - if (ResourceCache::has(base_path)) { - tex = Ref((Resource *)ResourceCache::get(base_path)); - set_path = false; - } + config->save(texture_path + ".import"); - if (!tex.is_valid()) { - tex.instance(); - } - - tex->create(images[0]->get_width(), images[0]->get_height(), images.size(), images[0]->get_format(), Texture::FLAGS_DEFAULT); - for (int i = 0; i < images.size(); i++) { - tex->set_layer_data(images[i], i); - } - - ResourceSaver::save(base_path, tex, ResourceSaver::FLAG_CHANGE_PATH); - if (set_path) { - tex->set_path(base_path); - } - texture = tex; + ResourceLoader::import(texture_path); + textures.write[i] = ResourceLoader::load(texture_path); //if already loaded, it will be updated on refocus? } for (int i = 0; i < lightmapper->get_bake_mesh_count(); i++) { - if (meshes_found[i].generate_lightmap) { - Dictionary d = lightmapper->get_bake_mesh_userdata(i); - NodePath np = d["path"]; - int32_t subindex = -1; - if (d.has("subindex")) { - subindex = d["subindex"]; - } - - Rect2 uv_rect = lightmapper->get_bake_mesh_uv_scale(i); - int slice_index = lightmapper->get_bake_mesh_texture_slice(i); - data->add_user(np, texture, slice_index, uv_rect, subindex); + if (!meshes_found[i].generate_lightmap) { + continue; } + Dictionary d = lightmapper->get_bake_mesh_userdata(i); + NodePath np = d["path"]; + int32_t subindex = -1; + if (d.has("subindex")) { + subindex = d["subindex"]; + } + + Rect2 uv_rect = lightmapper->get_bake_mesh_uv_scale(i); + int slice_index = lightmapper->get_bake_mesh_texture_slice(i); + data->add_user(np, textures[slice_index / slices_per_texture], slice_index % slices_per_texture, uv_rect, subindex); } } else { for (int i = 0; i < lightmapper->get_bake_mesh_count(); i++) {