Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid reimporting lightmap textures every getter call #77788

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion doc/classes/LightmapGIData.xml
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,12 @@
</method>
</methods>
<members>
<member name="light_texture" type="TextureLayered" setter="set_light_texture" getter="get_light_texture">
<member name="light_texture" type="TextureLayered" setter="set_light_texture" getter="get_light_texture" is_deprecated="true">
The lightmap atlas texture generated by the lightmapper.
[i]Deprecated.[/i] The lightmap atlas can now have multiple textures. See [member lightmap_textures].
</member>
<member name="lightmap_textures" type="TextureLayered[]" setter="set_lightmap_textures" getter="get_lightmap_textures" default="[]">
The lightmap atlas textures generated by the lightmapper.
</member>
</members>
</class>
207 changes: 114 additions & 93 deletions scene/3d/lightmap_gi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,16 @@ Array LightmapGIData::_get_user_data() const {
return ret;
}

void LightmapGIData::_set_light_textures_data(const Array &p_data) {
ERR_FAIL_COND(p_data.is_empty());
void LightmapGIData::set_lightmap_textures(const TypedArray<TextureLayered> &p_data) {
light_textures = p_data;
if (p_data.is_empty()) {
light_texture = Ref<TextureLayered>();
_reset_lightmap_textures();
return;
}

if (p_data.size() == 1) {
set_light_texture(p_data[0]);
light_texture = p_data[0];
} else {
Vector<Ref<Image>> images;
for (int i = 0; i < p_data.size(); i++) {
Expand All @@ -116,73 +121,13 @@ void LightmapGIData::_set_light_textures_data(const Array &p_data) {
combined_texture.instantiate();

combined_texture->create_from_images(images);
set_light_texture(combined_texture);
light_texture = combined_texture;
}
_reset_lightmap_textures();
}

Array LightmapGIData::_get_light_textures_data() const {
Array ret;
if (light_texture.is_null() || light_texture->get_layers() == 0) {
return ret;
}

Vector<Ref<Image>> images;
for (int i = 0; i < light_texture->get_layers(); i++) {
images.push_back(light_texture->get_layer_data(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);

ret.resize(texture_count);

String base_name = get_path().get_basename();

int last_count = slice_count % slices_per_texture;
for (int i = 0; i < texture_count; i++) {
int texture_slice_count = (i == texture_count - 1 && last_count != 0) ? last_count : slices_per_texture;

Ref<Image> texture_image = Image::create_empty(slice_width, slice_height * texture_slice_count, false, images[0]->get_format());

for (int j = 0; j < texture_slice_count; j++) {
texture_image->blit_rect(images[i * slices_per_texture + j], Rect2i(0, 0, slice_width, slice_height), Point2i(0, slice_height * j));
}

String texture_path = texture_count > 1 ? base_name + "_" + itos(i) + ".exr" : base_name + ".exr";

Ref<ConfigFile> config;
config.instantiate();

if (FileAccess::exists(texture_path + ".import")) {
config->load(texture_path + ".import");
}

config->set_value("remap", "importer", "2d_array_texture");
config->set_value("remap", "type", "CompressedTexture2DArray");
if (!config->has_section_key("params", "compress/mode")) {
// User may want another compression, so leave it be, but default to VRAM uncompressed.
config->set_value("params", "compress/mode", 3);
}
config->set_value("params", "compress/channel_pack", 1);
config->set_value("params", "mipmaps/generate", false);
config->set_value("params", "slices/horizontal", 1);
config->set_value("params", "slices/vertical", texture_slice_count);

config->save(texture_path + ".import");

Error err = texture_image->save_exr(texture_path, false);
ERR_FAIL_COND_V(err, ret);
ResourceLoader::import(texture_path);
Ref<TextureLayered> t = ResourceLoader::load(texture_path); //if already loaded, it will be updated on refocus?
ERR_FAIL_COND_V(t.is_null(), ret);
ret[i] = t;
}

return ret;
TypedArray<TextureLayered> LightmapGIData::get_lightmap_textures() const {
return light_textures;
}

RID LightmapGIData::get_rid() const {
Expand All @@ -193,18 +138,13 @@ void LightmapGIData::clear() {
users.clear();
}

void LightmapGIData::set_light_texture(const Ref<TextureLayered> &p_light_texture) {
light_texture = p_light_texture;
void LightmapGIData::_reset_lightmap_textures() {
RS::get_singleton()->lightmap_set_textures(lightmap, light_texture.is_valid() ? light_texture->get_rid() : RID(), uses_spherical_harmonics);
}

Ref<TextureLayered> LightmapGIData::get_light_texture() const {
return light_texture;
}

void LightmapGIData::set_uses_spherical_harmonics(bool p_enable) {
uses_spherical_harmonics = p_enable;
RS::get_singleton()->lightmap_set_textures(lightmap, light_texture.is_valid() ? light_texture->get_rid() : RID(), uses_spherical_harmonics);
_reset_lightmap_textures();
}

bool LightmapGIData::is_using_spherical_harmonics() const {
Expand Down Expand Up @@ -282,15 +222,35 @@ Dictionary LightmapGIData::_get_probe_data() const {
return d;
}

#ifndef DISABLE_DEPRECATED
void LightmapGIData::set_light_texture(const Ref<TextureLayered> &p_light_texture) {
TypedArray<TextureLayered> arr;
arr.append(p_light_texture);
set_lightmap_textures(arr);
}

Ref<TextureLayered> LightmapGIData::get_light_texture() const {
if (light_textures.is_empty()) {
return Ref<TextureLayered>();
}
return light_textures.get(0);
}

void LightmapGIData::_set_light_textures_data(const Array &p_data) {
set_lightmap_textures(p_data);
}

Array LightmapGIData::_get_light_textures_data() const {
return Array(light_textures);
}
#endif

void LightmapGIData::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_user_data", "data"), &LightmapGIData::_set_user_data);
ClassDB::bind_method(D_METHOD("_get_user_data"), &LightmapGIData::_get_user_data);

ClassDB::bind_method(D_METHOD("set_light_texture", "light_texture"), &LightmapGIData::set_light_texture);
ClassDB::bind_method(D_METHOD("get_light_texture"), &LightmapGIData::get_light_texture);

ClassDB::bind_method(D_METHOD("_set_light_textures_data", "data"), &LightmapGIData::_set_light_textures_data);
ClassDB::bind_method(D_METHOD("_get_light_textures_data"), &LightmapGIData::_get_light_textures_data);
ClassDB::bind_method(D_METHOD("set_lightmap_textures", "light_textures"), &LightmapGIData::set_lightmap_textures);
ClassDB::bind_method(D_METHOD("get_lightmap_textures"), &LightmapGIData::get_lightmap_textures);

ClassDB::bind_method(D_METHOD("set_uses_spherical_harmonics", "uses_spherical_harmonics"), &LightmapGIData::set_uses_spherical_harmonics);
ClassDB::bind_method(D_METHOD("is_using_spherical_harmonics"), &LightmapGIData::is_using_spherical_harmonics);
Expand All @@ -303,11 +263,21 @@ void LightmapGIData::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_probe_data", "data"), &LightmapGIData::_set_probe_data);
ClassDB::bind_method(D_METHOD("_get_probe_data"), &LightmapGIData::_get_probe_data);

ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_texture", PROPERTY_HINT_RESOURCE_TYPE, "TextureLayered", PROPERTY_USAGE_EDITOR), "set_light_texture", "get_light_texture"); // property usage default but no save
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "light_textures", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_light_textures_data", "_get_light_textures_data");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "lightmap_textures", PROPERTY_HINT_ARRAY_TYPE, "TextureLayered", PROPERTY_USAGE_NO_EDITOR), "set_lightmap_textures", "get_lightmap_textures");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uses_spherical_harmonics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_uses_spherical_harmonics", "is_using_spherical_harmonics");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "user_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_user_data", "_get_user_data");
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "probe_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_probe_data", "_get_probe_data");

#ifndef DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("set_light_texture", "light_texture"), &LightmapGIData::set_light_texture);
ClassDB::bind_method(D_METHOD("get_light_texture"), &LightmapGIData::get_light_texture);

ClassDB::bind_method(D_METHOD("_set_light_textures_data", "data"), &LightmapGIData::_set_light_textures_data);
ClassDB::bind_method(D_METHOD("_get_light_textures_data"), &LightmapGIData::_get_light_textures_data);

ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_texture", PROPERTY_HINT_RESOURCE_TYPE, "TextureLayered", PROPERTY_USAGE_EDITOR), "set_light_texture", "get_light_texture");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "light_textures", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_light_textures_data", "_get_light_textures_data");
#endif
}

LightmapGIData::LightmapGIData() {
Expand Down Expand Up @@ -1091,6 +1061,68 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
return BAKE_ERROR_MESHES_INVALID;
}

// POSTBAKE: Save Textures.

TypedArray<TextureLayered> textures;
{
Vector<Ref<Image>> images;
images.resize(lightmapper->get_bake_texture_count());
for (int i = 0; i < images.size(); i++) {
images.set(i, lightmapper->get_bake_texture(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);

textures.resize(texture_count);

String base_path = p_image_data_path.get_basename();

int last_count = slice_count % slices_per_texture;
for (int i = 0; i < texture_count; i++) {
int texture_slice_count = (i == texture_count - 1 && last_count != 0) ? last_count : slices_per_texture;

Ref<Image> texture_image = Image::create_empty(slice_width, slice_height * texture_slice_count, false, images[0]->get_format());

for (int j = 0; j < texture_slice_count; j++) {
texture_image->blit_rect(images[i * slices_per_texture + j], Rect2i(0, 0, slice_width, slice_height), Point2i(0, slice_height * j));
}

String texture_path = texture_count > 1 ? base_path + "_" + itos(i) + ".exr" : base_path + ".exr";

Ref<ConfigFile> config;
config.instantiate();

if (FileAccess::exists(texture_path + ".import")) {
config->load(texture_path + ".import");
}

config->set_value("remap", "importer", "2d_array_texture");
config->set_value("remap", "type", "CompressedTexture2DArray");
if (!config->has_section_key("params", "compress/mode")) {
// User may want another compression, so leave it be, but default to VRAM uncompressed.
config->set_value("params", "compress/mode", 3);
}
config->set_value("params", "compress/channel_pack", 1);
config->set_value("params", "mipmaps/generate", false);
config->set_value("params", "slices/horizontal", 1);
config->set_value("params", "slices/vertical", texture_slice_count);

config->save(texture_path + ".import");

Error err = texture_image->save_exr(texture_path, false);
ERR_FAIL_COND_V(err, BAKE_ERROR_CANT_CREATE_IMAGE);
ResourceLoader::import(texture_path);
Ref<TextureLayered> t = ResourceLoader::load(texture_path); // If already loaded, it will be updated on refocus?
ERR_FAIL_COND_V(t.is_null(), BAKE_ERROR_CANT_CREATE_IMAGE);
textures[i] = t;
}
}

/* POSTBAKE: Save Light Data */

Ref<LightmapGIData> gi_data;
Expand All @@ -1102,18 +1134,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
gi_data.instantiate();
}

Ref<Texture2DArray> texture;
{
Vector<Ref<Image>> images;
for (int i = 0; i < lightmapper->get_bake_texture_count(); i++) {
images.push_back(lightmapper->get_bake_texture(i));
}

texture.instantiate();
texture->create_from_images(images);
}

gi_data->set_light_texture(texture);
gi_data->set_lightmap_textures(textures);
gi_data->set_uses_spherical_harmonics(directional);

for (int i = 0; i < lightmapper->get_bake_mesh_count(); i++) {
Expand Down
13 changes: 11 additions & 2 deletions scene/3d/lightmap_gi.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class LightmapGIData : public Resource {
RES_BASE_EXTENSION("lmbake")

Ref<TextureLayered> light_texture;
TypedArray<TextureLayered> light_textures;

bool uses_spherical_harmonics = false;
bool interior = false;
Expand All @@ -65,8 +66,8 @@ class LightmapGIData : public Resource {
Array _get_user_data() const;
void _set_probe_data(const Dictionary &p_data);
Dictionary _get_probe_data() const;
void _set_light_textures_data(const Array &p_data);
Array _get_light_textures_data() const;

void _reset_lightmap_textures();

protected:
static void _bind_methods();
Expand All @@ -80,9 +81,14 @@ class LightmapGIData : public Resource {
int get_user_lightmap_slice_index(int p_user) const;
void clear_users();

#ifndef DISABLE_DEPRECATED
void set_light_texture(const Ref<TextureLayered> &p_light_texture);
Ref<TextureLayered> get_light_texture() const;

void _set_light_textures_data(const Array &p_data);
Array _get_light_textures_data() const;
#endif

void set_uses_spherical_harmonics(bool p_enable);
bool is_using_spherical_harmonics() const;

Expand All @@ -98,6 +104,9 @@ class LightmapGIData : public Resource {

void clear();

void set_lightmap_textures(const TypedArray<TextureLayered> &p_data);
TypedArray<TextureLayered> get_lightmap_textures() const;

virtual RID get_rid() const override;
LightmapGIData();
~LightmapGIData();
Expand Down
Loading