From 184395401b7aad41f9c1f58babc529b9915f0054 Mon Sep 17 00:00:00 2001
From: BlueCube3310 <53150244+BlueCube3310@users.noreply.github.com>
Date: Fri, 18 Oct 2024 10:13:36 +0200
Subject: LightmapGI: Clean up and improve lightmap atlas storage

---
 scene/3d/lightmap_gi.cpp | 164 ++++++++++++++++++++++++++---------------------
 scene/3d/lightmap_gi.h   |  10 ++-
 2 files changed, 99 insertions(+), 75 deletions(-)

(limited to 'scene/3d')

diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp
index a1f32fccbe..cd47f1c477 100644
--- a/scene/3d/lightmap_gi.cpp
+++ b/scene/3d/lightmap_gi.cpp
@@ -98,15 +98,15 @@ Array LightmapGIData::_get_user_data() const {
 }
 
 void LightmapGIData::set_lightmap_textures(const TypedArray<TextureLayered> &p_data) {
-	light_textures = p_data;
+	storage_light_textures = p_data;
 	if (p_data.is_empty()) {
-		light_texture = Ref<TextureLayered>();
+		combined_light_texture = Ref<TextureLayered>();
 		_reset_lightmap_textures();
 		return;
 	}
 
 	if (p_data.size() == 1) {
-		light_texture = p_data[0];
+		combined_light_texture = p_data[0];
 	} else {
 		Vector<Ref<Image>> images;
 		for (int i = 0; i < p_data.size(); i++) {
@@ -121,13 +121,13 @@ void LightmapGIData::set_lightmap_textures(const TypedArray<TextureLayered> &p_d
 		combined_texture.instantiate();
 
 		combined_texture->create_from_images(images);
-		light_texture = combined_texture;
+		combined_light_texture = combined_texture;
 	}
 	_reset_lightmap_textures();
 }
 
 TypedArray<TextureLayered> LightmapGIData::get_lightmap_textures() const {
-	return light_textures;
+	return storage_light_textures;
 }
 
 RID LightmapGIData::get_rid() const {
@@ -139,7 +139,7 @@ void LightmapGIData::clear() {
 }
 
 void LightmapGIData::_reset_lightmap_textures() {
-	RS::get_singleton()->lightmap_set_textures(lightmap, light_texture.is_valid() ? light_texture->get_rid() : RID(), uses_spherical_harmonics);
+	RS::get_singleton()->lightmap_set_textures(lightmap, combined_light_texture.is_valid() ? combined_light_texture->get_rid() : RID(), uses_spherical_harmonics);
 }
 
 void LightmapGIData::set_uses_spherical_harmonics(bool p_enable) {
@@ -238,10 +238,10 @@ void LightmapGIData::set_light_texture(const Ref<TextureLayered> &p_light_textur
 }
 
 Ref<TextureLayered> LightmapGIData::get_light_texture() const {
-	if (light_textures.is_empty()) {
+	if (storage_light_textures.is_empty()) {
 		return Ref<TextureLayered>();
 	}
-	return light_textures.get(0);
+	return storage_light_textures.get(0);
 }
 
 void LightmapGIData::_set_light_textures_data(const Array &p_data) {
@@ -249,7 +249,7 @@ void LightmapGIData::_set_light_textures_data(const Array &p_data) {
 }
 
 Array LightmapGIData::_get_light_textures_data() const {
-	return Array(light_textures);
+	return Array(storage_light_textures);
 }
 #endif
 
@@ -274,7 +274,7 @@ 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::ARRAY, "lightmap_textures", PROPERTY_HINT_ARRAY_TYPE, "TextureLayered", PROPERTY_USAGE_NO_EDITOR), "set_lightmap_textures", "get_lightmap_textures");
+	ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "lightmap_textures", PROPERTY_HINT_ARRAY_TYPE, "TextureLayered", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY), "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");
@@ -287,8 +287,8 @@ void LightmapGIData::_bind_methods() {
 	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");
+	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_texture", PROPERTY_HINT_RESOURCE_TYPE, "TextureLayered", PROPERTY_USAGE_NONE), "set_light_texture", "get_light_texture");
+	ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "light_textures", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_INTERNAL), "_set_light_textures_data", "_get_light_textures_data");
 #endif
 }
 
@@ -740,6 +740,74 @@ void LightmapGI::_gen_new_positions_from_octree(const GenProbesOctree *p_cell, f
 	}
 }
 
+LightmapGI::BakeError LightmapGI::_save_and_reimport_atlas_textures(const Ref<Lightmapper> p_lightmapper, const String &p_base_name, TypedArray<TextureLayered> &r_textures, bool p_compress) const {
+	Vector<Ref<Image>> images;
+	images.resize(p_lightmapper->get_bake_texture_count());
+
+	for (int i = 0; i < images.size(); i++) {
+		images.set(i, p_lightmapper->get_bake_texture(i));
+	}
+
+	const int slice_count = images.size();
+	const int slice_width = images[0]->get_width();
+	const int slice_height = images[0]->get_height();
+
+	const int slices_per_texture = Image::MAX_HEIGHT / slice_height;
+	const int texture_count = Math::ceil(slice_count / (float)slices_per_texture);
+	const int last_count = slice_count % slices_per_texture;
+
+	r_textures.resize(texture_count);
+
+	for (int i = 0; i < texture_count; i++) {
+		const 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));
+		}
+
+		const String atlas_path = (texture_count > 1 ? p_base_name + "_" + itos(i) : p_base_name) + ".exr";
+		const String config_path = atlas_path + ".import";
+
+		Ref<ConfigFile> config;
+		config.instantiate();
+
+		// Load an import configuration if present.
+		if (FileAccess::exists(config_path)) {
+			config->load(config_path);
+		}
+
+		config->set_value("remap", "importer", "2d_array_texture");
+		config->set_value("remap", "type", "CompressedTexture2DArray");
+		if (!config->has_section_key("params", "compress/mode")) {
+			// Do not override an existing compression mode.
+			config->set_value("params", "compress/mode", p_compress ? 2 : 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(config_path);
+
+		// Save the file.
+		Error save_err = texture_image->save_exr(atlas_path, false);
+
+		ERR_FAIL_COND_V(save_err, LightmapGI::BAKE_ERROR_CANT_CREATE_IMAGE);
+
+		// Reimport the file.
+		ResourceLoader::import(atlas_path);
+		Ref<TextureLayered> t = ResourceLoader::load(atlas_path); // If already loaded, it will be updated on refocus?
+		ERR_FAIL_COND_V(t.is_null(), LightmapGI::BAKE_ERROR_CANT_CREATE_IMAGE);
+
+		// Store the atlas in the array.
+		r_textures[i] = t;
+	}
+
+	return LightmapGI::BAKE_ERROR_OK;
+}
+
 LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_path, Lightmapper::BakeStepFunc p_bake_step, void *p_bake_userdata) {
 	if (p_image_data_path.is_empty()) {
 		if (get_light_data().is_null()) {
@@ -1127,80 +1195,30 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
 
 	// 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";
+	TypedArray<TextureLayered> lightmap_textures;
 
-			Ref<ConfigFile> config;
-			config.instantiate();
+	const String texture_filename = p_image_data_path.get_basename();
 
-			if (FileAccess::exists(texture_path + ".import")) {
-				config->load(texture_path + ".import");
-			}
+	// Save the lightmap atlases.
+	BakeError save_err = _save_and_reimport_atlas_textures(lightmapper, texture_filename, lightmap_textures, false);
+	ERR_FAIL_COND_V(save_err != BAKE_ERROR_OK, save_err);
 
-			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 */
+	// POSTBAKE: Save Light Data.
 
 	Ref<LightmapGIData> gi_data;
+
 	if (get_light_data().is_valid()) {
 		gi_data = get_light_data();
-		set_light_data(Ref<LightmapGIData>()); //clear
+		set_light_data(Ref<LightmapGIData>()); // Clear.
 		gi_data->clear();
+
 	} else {
 		gi_data.instantiate();
 	}
 
-	gi_data->set_lightmap_textures(textures);
-	gi_data->_set_uses_packed_directional(directional); // New SH lightmaps are packed automatically.
+	gi_data->set_lightmap_textures(lightmap_textures);
 	gi_data->set_uses_spherical_harmonics(directional);
+	gi_data->_set_uses_packed_directional(directional); // New SH lightmaps are packed automatically.
 
 	for (int i = 0; i < lightmapper->get_bake_mesh_count(); i++) {
 		Dictionary d = lightmapper->get_bake_mesh_userdata(i);
diff --git a/scene/3d/lightmap_gi.h b/scene/3d/lightmap_gi.h
index 6377c420d1..0476061c60 100644
--- a/scene/3d/lightmap_gi.h
+++ b/scene/3d/lightmap_gi.h
@@ -43,8 +43,12 @@ class LightmapGIData : public Resource {
 	GDCLASS(LightmapGIData, Resource);
 	RES_BASE_EXTENSION("lmbake")
 
-	Ref<TextureLayered> light_texture;
-	TypedArray<TextureLayered> light_textures;
+	// The 'merged' texture atlases actually used by the renderer.
+	Ref<TextureLayered> combined_light_texture;
+
+	// The temporary texture atlas arrays which are used for storage.
+	// If a single atlas is too large, it's split and recombined during loading.
+	TypedArray<TextureLayered> storage_light_textures;
 
 	bool uses_spherical_harmonics = false;
 	bool interior = false;
@@ -245,6 +249,8 @@ private:
 	void _plot_triangle_into_octree(GenProbesOctree *p_cell, float p_cell_size, const Vector3 *p_triangle);
 	void _gen_new_positions_from_octree(const GenProbesOctree *p_cell, float p_cell_size, const Vector<Vector3> &probe_positions, LocalVector<Vector3> &new_probe_positions, HashMap<Vector3i, bool> &positions_used, const AABB &p_bounds);
 
+	BakeError _save_and_reimport_atlas_textures(const Ref<Lightmapper> p_lightmapper, const String &p_base_name, TypedArray<TextureLayered> &r_textures, bool p_compress = false) const;
+
 protected:
 	void _validate_property(PropertyInfo &p_property) const;
 	static void _bind_methods();
-- 
cgit v1.2.3


From 0d350e71086fffce0553811739aae9f6ad66136c Mon Sep 17 00:00:00 2001
From: Adam Scott <ascott.ca@gmail.com>
Date: Mon, 7 Oct 2024 10:57:21 -0400
Subject: Set clang-format `RemoveSemicolon` rule to `true`

- Set clang-format `Standard` rule to `c++20`
---
 scene/3d/camera_3d.cpp            |  8 ++++----
 scene/3d/navigation_obstacle_3d.h |  4 ++--
 scene/3d/node_3d.h                |  4 ++--
 scene/3d/xr_nodes.cpp             | 14 +++++++-------
 4 files changed, 15 insertions(+), 15 deletions(-)

(limited to 'scene/3d')

diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp
index c70fa3ca2e..aafc2141af 100644
--- a/scene/3d/camera_3d.cpp
+++ b/scene/3d/camera_3d.cpp
@@ -377,7 +377,7 @@ void Camera3D::set_projection(ProjectionType p_mode) {
 
 RID Camera3D::get_camera() const {
 	return camera;
-};
+}
 
 void Camera3D::make_current() {
 	current = true;
@@ -423,7 +423,7 @@ bool Camera3D::is_current() const {
 Vector3 Camera3D::project_ray_normal(const Point2 &p_pos) const {
 	Vector3 ray = project_local_ray_normal(p_pos);
 	return get_camera_transform().basis.xform(ray).normalized();
-};
+}
 
 Vector3 Camera3D::project_local_ray_normal(const Point2 &p_pos) const {
 	ERR_FAIL_COND_V_MSG(!is_inside_tree(), Vector3(), "Camera is not inside scene.");
@@ -441,7 +441,7 @@ Vector3 Camera3D::project_local_ray_normal(const Point2 &p_pos) const {
 	}
 
 	return ray;
-};
+}
 
 Vector3 Camera3D::project_ray_origin(const Point2 &p_pos) const {
 	ERR_FAIL_COND_V_MSG(!is_inside_tree(), Vector3(), "Camera is not inside scene.");
@@ -470,7 +470,7 @@ Vector3 Camera3D::project_ray_origin(const Point2 &p_pos) const {
 	} else {
 		return get_camera_transform().origin;
 	};
-};
+}
 
 bool Camera3D::is_position_behind(const Vector3 &p_pos) const {
 	Transform3D t = get_global_transform();
diff --git a/scene/3d/navigation_obstacle_3d.h b/scene/3d/navigation_obstacle_3d.h
index e9a4669fa2..99288fc59e 100644
--- a/scene/3d/navigation_obstacle_3d.h
+++ b/scene/3d/navigation_obstacle_3d.h
@@ -95,7 +95,7 @@ public:
 	real_t get_height() const { return height; }
 
 	void set_vertices(const Vector<Vector3> &p_vertices);
-	const Vector<Vector3> &get_vertices() const { return vertices; };
+	const Vector<Vector3> &get_vertices() const { return vertices; }
 
 	void set_avoidance_layers(uint32_t p_layers);
 	uint32_t get_avoidance_layers() const;
@@ -107,7 +107,7 @@ public:
 	bool get_use_3d_avoidance() const { return use_3d_avoidance; }
 
 	void set_velocity(const Vector3 p_velocity);
-	Vector3 get_velocity() const { return velocity; };
+	Vector3 get_velocity() const { return velocity; }
 
 	void _avoidance_done(Vector3 p_new_velocity); // Dummy
 
diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h
index 217ee28cf1..20288fad3a 100644
--- a/scene/3d/node_3d.h
+++ b/scene/3d/node_3d.h
@@ -233,8 +233,8 @@ public:
 #ifdef TOOLS_ENABLED
 	virtual Transform3D get_global_gizmo_transform() const;
 	virtual Transform3D get_local_gizmo_transform() const;
-	virtual void set_transform_gizmo_visible(bool p_enabled) { data.transform_gizmo_visible = p_enabled; };
-	virtual bool is_transform_gizmo_visible() const { return data.transform_gizmo_visible; };
+	virtual void set_transform_gizmo_visible(bool p_enabled) { data.transform_gizmo_visible = p_enabled; }
+	virtual bool is_transform_gizmo_visible() const { return data.transform_gizmo_visible; }
 #endif
 	virtual void reparent(Node *p_parent, bool p_keep_global_transform = true) override;
 
diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp
index 214c1f77ca..9869b241d3 100644
--- a/scene/3d/xr_nodes.cpp
+++ b/scene/3d/xr_nodes.cpp
@@ -89,7 +89,7 @@ PackedStringArray XRCamera3D::get_configuration_warnings() const {
 	}
 
 	return warnings;
-};
+}
 
 Vector3 XRCamera3D::project_local_ray_normal(const Point2 &p_pos) const {
 	// get our XRServer
@@ -114,7 +114,7 @@ Vector3 XRCamera3D::project_local_ray_normal(const Point2 &p_pos) const {
 	ray = Vector3(((cpos.x / viewport_size.width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (cpos.y / viewport_size.height)) * 2.0 - 1.0) * screen_he.y, -get_near()).normalized();
 
 	return ray;
-};
+}
 
 Point2 XRCamera3D::unproject_position(const Vector3 &p_pos) const {
 	// get our XRServer
@@ -144,7 +144,7 @@ Point2 XRCamera3D::unproject_position(const Vector3 &p_pos) const {
 	res.y = (-p.normal.y * 0.5 + 0.5) * viewport_size.y;
 
 	return res;
-};
+}
 
 Vector3 XRCamera3D::project_position(const Point2 &p_point, real_t p_z_depth) const {
 	// get our XRServer
@@ -174,7 +174,7 @@ Vector3 XRCamera3D::project_position(const Point2 &p_point, real_t p_z_depth) co
 	Vector3 p(point.x, point.y, -p_z_depth);
 
 	return get_camera_transform().xform(p);
-};
+}
 
 Vector<Plane> XRCamera3D::get_frustum() const {
 	// get our XRServer
@@ -193,7 +193,7 @@ Vector<Plane> XRCamera3D::get_frustum() const {
 	// TODO Just use the first view for now, this is mostly for debugging so we may look into using our combined projection here.
 	Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
 	return cm.get_projection_planes(get_camera_transform());
-};
+}
 
 XRCamera3D::XRCamera3D() {
 	XRServer *xr_server = XRServer::get_singleton();
@@ -240,7 +240,7 @@ void XRNode3D::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("trigger_haptic_pulse", "action_name", "frequency", "amplitude", "duration_sec", "delay_sec"), &XRNode3D::trigger_haptic_pulse);
 
 	ADD_SIGNAL(MethodInfo("tracking_changed", PropertyInfo(Variant::BOOL, "tracking")));
-};
+}
 
 void XRNode3D::_validate_property(PropertyInfo &p_property) const {
 	XRServer *xr_server = XRServer::get_singleton();
@@ -499,7 +499,7 @@ void XRController3D::_bind_methods() {
 	ADD_SIGNAL(MethodInfo("input_float_changed", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::FLOAT, "value")));
 	ADD_SIGNAL(MethodInfo("input_vector2_changed", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::VECTOR2, "value")));
 	ADD_SIGNAL(MethodInfo("profile_changed", PropertyInfo(Variant::STRING, "role")));
-};
+}
 
 void XRController3D::_bind_tracker() {
 	XRNode3D::_bind_tracker();
-- 
cgit v1.2.3