Skip to content

Commit

Permalink
Add support for saving multiple Images in BakedLightmap
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
JFonS committed Feb 15, 2022
1 parent f68a6da commit 690f971
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 70 deletions.
44 changes: 22 additions & 22 deletions modules/lightmapper_cpu/lightmapper_cpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,15 +166,13 @@ Error LightmapperCPU::_layout_atlas(int p_max_size, Vector2i *r_atlas_size, int
}

float mem_utilization = static_cast<float>(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) {
Expand Down Expand Up @@ -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 (parameters.bounces > 0) {
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 (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;
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;
}
}
}
}
Expand Down
87 changes: 39 additions & 48 deletions scene/3d/baked_lightmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Image> 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();

int slices_per_texture = Image::MAX_HEIGHT / slice_height;
int texture_count = Math::ceil(slice_count / (float)slices_per_texture);

Ref<TextureLayered> texture;
Vector<Ref<TextureLayered>> 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<Image> 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<ConfigFile> 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);
Expand All @@ -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");

ResourceLoader::import(base_path);
texture = ResourceLoader::load(base_path); //if already loaded, it will be updated on refocus?
} else {
base_path += ".texarr";
Ref<TextureLayered> tex;
bool set_path = true;
if (ResourceCache::has(base_path)) {
tex = Ref<Resource>((Resource *)ResourceCache::get(base_path));
set_path = false;
}

if (!tex.is_valid()) {
tex.instance();
}
config->set_value("params", "slices/vertical", texture_slice_count);

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);
}
config->save(texture_path + ".import");

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++) {
Expand Down

0 comments on commit 690f971

Please sign in to comment.