Browse Source

New lightmapper

-Added LocalVector (needed it)
-Added stb_rect_pack (It's pretty cool, we could probably use it for other stuff too)
-Fixes and changes all around the place
-Added library for 128 bits fixed point (required for Delaunay3D)
Juan Linietsky 5 years ago
parent
commit
1bea8e1eac
100 changed files with 8939 additions and 1629 deletions
  1. 7 0
      SConstruct
  2. 18 18
      core/error_macros.h
  3. 4 0
      core/image.cpp
  4. 2 0
      core/image.h
  5. 15 9
      core/io/resource_format_binary.cpp
  6. 1 1
      core/io/resource_format_binary.h
  7. 1 1
      core/io/resource_importer.cpp
  8. 246 0
      core/local_vector.h
  9. 111 0
      core/math/basis.cpp
  10. 1 0
      core/math/basis.h
  11. 16 0
      core/math/camera_matrix.cpp
  12. 1 0
      core/math/camera_matrix.h
  13. 0 2
      core/math/delaunay.h
  14. 386 0
      core/math/delaunay_3d.h
  15. 194 0
      core/math/geometry.cpp
  16. 243 0
      core/math/geometry.h
  17. 4 0
      core/math/plane.cpp
  18. 1 0
      core/math/plane.h
  19. 2 0
      core/math/r128.cpp
  20. 12 4
      core/ustring.cpp
  21. 1 1
      core/ustring.h
  22. 8 0
      core/vector.h
  23. 9 1
      drivers/vulkan/rendering_device_vulkan.cpp
  24. 2 0
      drivers/vulkan/rendering_device_vulkan.h
  25. 9 0
      drivers/vulkan/vulkan_context.cpp
  26. 12 4
      editor/editor_node.cpp
  27. 171 150
      editor/import/resource_importer_layered_texture.cpp
  28. 21 13
      editor/import/resource_importer_layered_texture.h
  29. 4 3
      editor/import/resource_importer_scene.cpp
  30. 1 1
      editor/import/resource_importer_shader_file.cpp
  31. 62 51
      editor/import/resource_importer_texture.cpp
  32. 6 6
      editor/import/resource_importer_texture.h
  33. 238 77
      editor/node_3d_editor_gizmos.cpp
  34. 24 7
      editor/node_3d_editor_gizmos.h
  35. 43 26
      editor/plugins/baked_lightmap_editor_plugin.cpp
  36. 3 4
      editor/plugins/baked_lightmap_editor_plugin.h
  37. 1 1
      editor/plugins/canvas_item_editor_plugin.cpp
  38. 2 1
      editor/plugins/node_3d_editor_plugin.cpp
  39. 3 3
      editor/plugins/texture_editor_plugin.cpp
  40. 286 0
      editor/plugins/texture_layered_editor_plugin.cpp
  41. 95 0
      editor/plugins/texture_layered_editor_plugin.h
  42. 64 0
      gles_builders.py
  43. 5 3
      main/main.cpp
  44. 51 2
      main/tests/test_math.cpp
  45. 119 0
      modules/denoise/SCsub
  46. 6 0
      modules/denoise/config.py
  47. 34 0
      modules/denoise/denoise_wrapper.cpp
  48. 8 0
      modules/denoise/denoise_wrapper.h
  49. 63 0
      modules/denoise/lightmap_denoiser.cpp
  50. 57 0
      modules/denoise/lightmap_denoiser.h
  51. 41 0
      modules/denoise/register_types.cpp
  52. 32 0
      modules/denoise/register_types.h
  53. 70 0
      modules/denoise/resource_to_cpp.py
  54. 12 0
      modules/lightmapper_rd/SCsub
  55. 6 0
      modules/lightmapper_rd/config.py
  56. 1754 0
      modules/lightmapper_rd/lightmapper_rd.cpp
  57. 229 0
      modules/lightmapper_rd/lightmapper_rd.h
  58. 117 0
      modules/lightmapper_rd/lm_blendseams.glsl
  59. 92 0
      modules/lightmapper_rd/lm_common_inc.glsl
  60. 657 0
      modules/lightmapper_rd/lm_compute.glsl
  61. 170 0
      modules/lightmapper_rd/lm_raster.glsl
  62. 64 0
      modules/lightmapper_rd/register_types.cpp
  63. 37 0
      modules/lightmapper_rd/register_types.h
  64. 12 4
      modules/tinyexr/image_saver_tinyexr.cpp
  65. 1 0
      modules/xatlas_unwrap/register_types.cpp
  66. 23 23
      platform/uwp/export/export.cpp
  67. 972 403
      scene/3d/baked_lightmap.cpp
  68. 155 87
      scene/3d/baked_lightmap.h
  69. 1 1
      scene/3d/gi_probe.cpp
  70. 4 0
      scene/3d/lightmap_probe.cpp
  71. 12 0
      scene/3d/lightmap_probe.h
  72. 37 0
      scene/3d/lightmapper.cpp
  73. 90 0
      scene/3d/lightmapper.h
  74. 64 29
      scene/3d/visual_instance_3d.cpp
  75. 24 12
      scene/3d/visual_instance_3d.h
  76. 5 200
      scene/3d/voxelizer.cpp
  77. 13 5
      scene/register_scene_types.cpp
  78. 3 3
      scene/resources/mesh.cpp
  79. 3 3
      scene/resources/mesh.h
  80. 273 208
      scene/resources/texture.cpp
  81. 135 31
      scene/resources/texture.h
  82. 5 0
      scene/scene_string_names.cpp
  83. 4 0
      scene/scene_string_names.h
  84. 34 31
      servers/rendering/rasterizer.h
  85. 2 2
      servers/rendering/rasterizer_rd/rasterizer_canvas_rd.cpp
  86. 29 1
      servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp
  87. 3 0
      servers/rendering/rasterizer_rd/rasterizer_effects_rd.h
  88. 5 1
      servers/rendering/rasterizer_rd/rasterizer_rd.cpp
  89. 5 2
      servers/rendering/rasterizer_rd/rasterizer_rd.h
  90. 193 28
      servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp
  91. 33 3
      servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h
  92. 171 2
      servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp
  93. 8 2
      servers/rendering/rasterizer_rd/rasterizer_scene_rd.h
  94. 456 108
      servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp
  95. 77 16
      servers/rendering/rasterizer_rd/rasterizer_storage_rd.h
  96. 6 2
      servers/rendering/rasterizer_rd/render_pipeline_vertex_format_cache_rd.cpp
  97. 5 4
      servers/rendering/rasterizer_rd/render_pipeline_vertex_format_cache_rd.h
  98. 5 0
      servers/rendering/rasterizer_rd/shader_compiler_rd.cpp
  99. 28 1
      servers/rendering/rasterizer_rd/shaders/copy.glsl
  100. 89 28
      servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl

+ 7 - 0
SConstruct

@@ -598,6 +598,13 @@ if selected_platform in platform_list:
                 )
                 )
             }
             }
         )
         )
+        env.Append(
+            BUILDERS={
+                "GLSL_HEADER": env.Builder(
+                    action=run_in_subprocess(gles_builders.build_raw_headers), suffix="glsl.gen.h", src_suffix=".glsl"
+                )
+            }
+        )
 
 
     scons_cache_path = os.environ.get("SCONS_CACHE")
     scons_cache_path = os.environ.get("SCONS_CACHE")
     if scons_cache_path != None:
     if scons_cache_path != None:

+ 18 - 18
core/error_macros.h

@@ -530,19 +530,19 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
  * Prints `m_msg`.
  * Prints `m_msg`.
  */
  */
 #define ERR_PRINT(m_msg) \
 #define ERR_PRINT(m_msg) \
-	_err_print_error(FUNCTION_STR, __FILE__, __LINE__, DEBUG_STR(m_msg))
+	_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg)
 
 
 /**
 /**
  * Prints `m_msg` once during the application lifetime.
  * Prints `m_msg` once during the application lifetime.
  */
  */
-#define ERR_PRINT_ONCE(m_msg)                                                     \
-	if (1) {                                                                      \
-		static bool first_print = true;                                           \
-		if (first_print) {                                                        \
-			_err_print_error(FUNCTION_STR, __FILE__, __LINE__, DEBUG_STR(m_msg)); \
-			first_print = false;                                                  \
-		}                                                                         \
-	} else                                                                        \
+#define ERR_PRINT_ONCE(m_msg)                                          \
+	if (1) {                                                           \
+		static bool first_print = true;                                \
+		if (first_print) {                                             \
+			_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg); \
+			first_print = false;                                       \
+		}                                                              \
+	} else                                                             \
 		((void)0)
 		((void)0)
 
 
 // Print warning message macros.
 // Print warning message macros.
@@ -553,21 +553,21 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
  * If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead.
  * If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead.
  */
  */
 #define WARN_PRINT(m_msg) \
 #define WARN_PRINT(m_msg) \
-	_err_print_error(FUNCTION_STR, __FILE__, __LINE__, DEBUG_STR(m_msg), ERR_HANDLER_WARNING)
+	_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, ERR_HANDLER_WARNING)
 
 
 /**
 /**
  * Prints `m_msg` once during the application lifetime.
  * Prints `m_msg` once during the application lifetime.
  *
  *
  * If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead.
  * If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead.
  */
  */
-#define WARN_PRINT_ONCE(m_msg)                                                                         \
-	if (1) {                                                                                           \
-		static bool first_print = true;                                                                \
-		if (first_print) {                                                                             \
-			_err_print_error(FUNCTION_STR, __FILE__, __LINE__, DEBUG_STR(m_msg), ERR_HANDLER_WARNING); \
-			first_print = false;                                                                       \
-		}                                                                                              \
-	} else                                                                                             \
+#define WARN_PRINT_ONCE(m_msg)                                                              \
+	if (1) {                                                                                \
+		static bool first_print = true;                                                     \
+		if (first_print) {                                                                  \
+			_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, ERR_HANDLER_WARNING); \
+			first_print = false;                                                            \
+		}                                                                                   \
+	} else                                                                                  \
 		((void)0)
 		((void)0)
 
 
 // Print deprecated warning message macros.
 // Print deprecated warning message macros.

+ 4 - 0
core/image.cpp

@@ -3668,6 +3668,10 @@ Ref<Resource> Image::duplicate(bool p_subresources) const {
 	return copy;
 	return copy;
 }
 }
 
 
+void Image::set_as_black() {
+	zeromem(data.ptrw(), data.size());
+}
+
 Image::Image() {
 Image::Image() {
 
 
 	width = 0;
 	width = 0;

+ 2 - 0
core/image.h

@@ -376,6 +376,8 @@ public:
 	void set_pixelv(const Point2 &p_dst, const Color &p_color);
 	void set_pixelv(const Point2 &p_dst, const Color &p_color);
 	void set_pixel(int p_x, int p_y, const Color &p_color);
 	void set_pixel(int p_x, int p_y, const Color &p_color);
 
 
+	void set_as_black();
+
 	void copy_internals_from(const Ref<Image> &p_image) {
 	void copy_internals_from(const Ref<Image> &p_image) {
 		ERR_FAIL_COND_MSG(p_image.is_null(), "It's not a reference to a valid Image object.");
 		ERR_FAIL_COND_MSG(p_image.is_null(), "It's not a reference to a valid Image object.");
 		format = p_image->format;
 		format = p_image->format;

+ 15 - 9
core/io/resource_format_binary.cpp

@@ -337,10 +337,14 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) {
 				} break;
 				} break;
 				case OBJECT_INTERNAL_RESOURCE: {
 				case OBJECT_INTERNAL_RESOURCE: {
 					uint32_t index = f->get_32();
 					uint32_t index = f->get_32();
+					String path = res_path + "::" + itos(index);
+
 					if (use_nocache) {
 					if (use_nocache) {
-						r_v = internal_resources[index].cache;
+						if (!internal_index_cache.has(path)) {
+							WARN_PRINT(String("Couldn't load resource (no cache): " + path).utf8().get_data());
+						}
+						r_v = internal_index_cache[path];
 					} else {
 					} else {
-						String path = res_path + "::" + itos(index);
 						RES res = ResourceLoader::load(path);
 						RES res = ResourceLoader::load(path);
 						if (res.is_null()) {
 						if (res.is_null()) {
 							WARN_PRINT(String("Couldn't load resource: " + path).utf8().get_data());
 							WARN_PRINT(String("Couldn't load resource: " + path).utf8().get_data());
@@ -720,13 +724,15 @@ Error ResourceLoaderBinary::load() {
 
 
 		if (!main) {
 		if (!main) {
 
 
+			path = internal_resources[i].path;
+
+			if (path.begins_with("local://")) {
+				path = path.replace_first("local://", "");
+				subindex = path.to_int();
+				path = res_path + "::" + path;
+			}
+
 			if (!use_nocache) {
 			if (!use_nocache) {
-				path = internal_resources[i].path;
-				if (path.begins_with("local://")) {
-					path = path.replace_first("local://", "");
-					subindex = path.to_int();
-					path = res_path + "::" + path;
-				}
 
 
 				if (ResourceCache::has(path)) {
 				if (ResourceCache::has(path)) {
 					//already loaded, don't do anything
 					//already loaded, don't do anything
@@ -769,7 +775,7 @@ Error ResourceLoaderBinary::load() {
 		r->set_subindex(subindex);
 		r->set_subindex(subindex);
 
 
 		if (!main) {
 		if (!main) {
-			internal_resources.write[i].cache = res;
+			internal_index_cache[path] = res;
 		}
 		}
 
 
 		int pc = f->get_32();
 		int pc = f->get_32();

+ 1 - 1
core/io/resource_format_binary.h

@@ -68,10 +68,10 @@ class ResourceLoaderBinary {
 	struct IntResource {
 	struct IntResource {
 		String path;
 		String path;
 		uint64_t offset;
 		uint64_t offset;
-		RES cache;
 	};
 	};
 
 
 	Vector<IntResource> internal_resources;
 	Vector<IntResource> internal_resources;
+	Map<String, RES> internal_index_cache;
 
 
 	String get_unicode_string();
 	String get_unicode_string();
 	void _advance_padding(uint32_t p_len);
 	void _advance_padding(uint32_t p_len);

+ 1 - 1
core/io/resource_importer.cpp

@@ -91,7 +91,7 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy
 				r_path_and_type.path = value;
 				r_path_and_type.path = value;
 				path_found = true; //first match must have priority
 				path_found = true; //first match must have priority
 			} else if (assign == "type") {
 			} else if (assign == "type") {
-				r_path_and_type.type = value;
+				r_path_and_type.type = ClassDB::get_compatibility_remapped_class(value);
 			} else if (assign == "importer") {
 			} else if (assign == "importer") {
 				r_path_and_type.importer = value;
 				r_path_and_type.importer = value;
 			} else if (assign == "group_file") {
 			} else if (assign == "group_file") {

+ 246 - 0
core/local_vector.h

@@ -0,0 +1,246 @@
+/*************************************************************************/
+/*  local_vector.h                                                       */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef LOCAL_VECTOR_H
+#define LOCAL_VECTOR_H
+
+#include "core/error_macros.h"
+#include "core/os/copymem.h"
+#include "core/os/memory.h"
+#include "core/sort_array.h"
+#include "core/vector.h"
+
+template <class T, class U = uint32_t, bool force_trivial = false>
+class LocalVector {
+private:
+	U count = 0;
+	U capacity = 0;
+	T *data = nullptr;
+
+public:
+	_FORCE_INLINE_ void push_back(T p_elem) {
+		if (unlikely(count == capacity)) {
+			if (capacity == 0) {
+				capacity = 1;
+			} else {
+				capacity <<= 1;
+			}
+			data = (T *)memrealloc(data, capacity * sizeof(T));
+			CRASH_COND_MSG(!data, "Out of memory");
+		}
+
+		if (!__has_trivial_constructor(T) && !force_trivial) {
+			memnew_placement(&data[count++], T(p_elem));
+		} else {
+			data[count++] = p_elem;
+		}
+	}
+
+	void remove(U p_index) {
+		ERR_FAIL_UNSIGNED_INDEX(p_index, count);
+		for (U i = p_index; i < count; i++) {
+			data[i] = data[i + 1];
+		}
+		count--;
+		if (!__has_trivial_destructor(T) && !force_trivial) {
+			data[count].~T();
+		}
+	}
+
+	void erase(const T &p_val) {
+		U idx = find(p_val);
+		if (idx >= 0)
+			remove(idx);
+	}
+
+	void invert() {
+
+		for (U i = 0; i < count / 2; i++) {
+			SWAP(data[i], data[count - i - 1]);
+		}
+	}
+
+	_FORCE_INLINE_ void clear() { resize(0); }
+	_FORCE_INLINE_ void reset() {
+		clear();
+		if (data) {
+			memfree(data);
+			data = nullptr;
+			capacity = 0;
+		}
+	}
+	_FORCE_INLINE_ bool empty() const { return count == 0; }
+	_FORCE_INLINE_ void reserve(U p_size) {
+		p_size = nearest_power_of_2_templated(p_size);
+		if (p_size > capacity) {
+			capacity = p_size;
+			data = (T *)memrealloc(data, capacity * sizeof(T));
+			CRASH_COND_MSG(!data, "Out of memory");
+		}
+	}
+
+	_FORCE_INLINE_ U size() const { return count; }
+	void resize(U p_size) {
+
+		if (p_size < count) {
+
+			if (!__has_trivial_destructor(T) && !force_trivial) {
+				for (U i = p_size; i < count; i++) {
+					data[i].~T();
+				}
+			}
+			count = p_size;
+		} else if (p_size > count) {
+
+			if (unlikely(p_size > capacity)) {
+				if (capacity == 0) {
+					capacity = 1;
+				}
+				while (capacity < p_size) {
+					capacity <<= 1;
+				}
+				data = (T *)memrealloc(data, capacity * sizeof(T));
+				CRASH_COND_MSG(!data, "Out of memory");
+			}
+			if (!__has_trivial_constructor(T) && !force_trivial) {
+				for (U i = count; i < p_size; i++) {
+					memnew_placement(&data[i], T);
+				}
+			}
+			count = p_size;
+		}
+	}
+	_FORCE_INLINE_ const T &operator[](U p_index) const {
+		CRASH_BAD_UNSIGNED_INDEX(p_index, count);
+		return data[p_index];
+	}
+	_FORCE_INLINE_ T &operator[](U p_index) {
+		CRASH_BAD_UNSIGNED_INDEX(p_index, count);
+		return data[p_index];
+	}
+
+	void insert(U p_pos, T p_val) {
+		ERR_FAIL_UNSIGNED_INDEX(p_pos, count + 1);
+		if (p_pos == count) {
+			push_back(p_val);
+		} else {
+			resize(count + 1);
+			for (U i = count; i > p_pos; i--) {
+				data[i] = data[i - 1];
+			}
+			data[p_pos] = p_val;
+		}
+	}
+
+	int64_t find(const T &p_val, U p_from = 0) const {
+
+		for (U i = 0; i < count; i++) {
+			if (data[i] == p_val) {
+				return int64_t(i);
+			}
+		}
+		return -1;
+	}
+
+	template <class C>
+	void sort_custom() {
+
+		U len = count;
+		if (len == 0)
+			return;
+
+		SortArray<T, C> sorter;
+		sorter.sort(data, len);
+	}
+
+	void sort() {
+
+		sort_custom<_DefaultComparator<T>>();
+	}
+
+	void ordered_insert(T p_val) {
+
+		U i;
+		for (i = 0; i < count; i++) {
+
+			if (p_val < data[i]) {
+				break;
+			};
+		};
+		insert(i, p_val);
+	}
+
+	operator Vector<T>() const {
+		Vector<T> ret;
+		ret.resize(size());
+		T *w = ret.ptrw();
+		copymem(w, data, sizeof(T) * count);
+		return ret;
+	}
+
+	Vector<uint8_t> to_byte_array() const { //useful to pass stuff to gpu or variant
+		Vector<uint8_t> ret;
+		ret.resize(count * sizeof(T));
+		uint8_t *w = ret.ptrw();
+		copymem(w, data, sizeof(T) * count);
+		return ret;
+	}
+
+	_FORCE_INLINE_ LocalVector() {}
+	_FORCE_INLINE_ LocalVector(const LocalVector &p_from) {
+		resize(p_from.size());
+		for (U i = 0; i < p_from.count; i++) {
+			data[i] = p_from.data[i];
+		}
+	}
+	inline LocalVector &operator=(const LocalVector &p_from) {
+		resize(p_from.size());
+		for (U i = 0; i < p_from.count; i++) {
+			data[i] = p_from.data[i];
+		}
+		return *this;
+	}
+	inline LocalVector &operator=(const Vector<T> &p_from) {
+		resize(p_from.size());
+		for (U i = 0; i < count; i++) {
+			data[i] = p_from[i];
+		}
+		return *this;
+	}
+
+	_FORCE_INLINE_ ~LocalVector() {
+
+		if (data) {
+			reset();
+		}
+	}
+};
+
+#endif // LOCAL_VECTOR_H

+ 111 - 0
core/math/basis.cpp

@@ -878,3 +878,114 @@ Basis Basis::slerp(const Basis &target, const real_t &t) const {
 
 
 	return b;
 	return b;
 }
 }
+
+void Basis::rotate_sh(real_t *p_values) {
+
+	// code by John Hable
+	// http://filmicworlds.com/blog/simple-and-fast-spherical-harmonic-rotation/
+	// this code is Public Domain
+
+	const static real_t s_c3 = 0.94617469575; // (3*sqrt(5))/(4*sqrt(pi))
+	const static real_t s_c4 = -0.31539156525; // (-sqrt(5))/(4*sqrt(pi))
+	const static real_t s_c5 = 0.54627421529; // (sqrt(15))/(4*sqrt(pi))
+
+	const static real_t s_c_scale = 1.0 / 0.91529123286551084;
+	const static real_t s_c_scale_inv = 0.91529123286551084;
+
+	const static real_t s_rc2 = 1.5853309190550713 * s_c_scale;
+	const static real_t s_c4_div_c3 = s_c4 / s_c3;
+	const static real_t s_c4_div_c3_x2 = (s_c4 / s_c3) * 2.0;
+
+	const static real_t s_scale_dst2 = s_c3 * s_c_scale_inv;
+	const static real_t s_scale_dst4 = s_c5 * s_c_scale_inv;
+
+	real_t src[9] = { p_values[0], p_values[1], p_values[2], p_values[3], p_values[4], p_values[5], p_values[6], p_values[7], p_values[8] };
+
+	real_t m00 = elements[0][0];
+	real_t m01 = elements[0][1];
+	real_t m02 = elements[0][2];
+	real_t m10 = elements[1][0];
+	real_t m11 = elements[1][1];
+	real_t m12 = elements[1][2];
+	real_t m20 = elements[2][0];
+	real_t m21 = elements[2][1];
+	real_t m22 = elements[2][2];
+
+	p_values[0] = src[0];
+	p_values[1] = m11 * src[1] - m12 * src[2] + m10 * src[3];
+	p_values[2] = -m21 * src[1] + m22 * src[2] - m20 * src[3];
+	p_values[3] = m01 * src[1] - m02 * src[2] + m00 * src[3];
+
+	real_t sh0 = src[7] + src[8] + src[8] - src[5];
+	real_t sh1 = src[4] + s_rc2 * src[6] + src[7] + src[8];
+	real_t sh2 = src[4];
+	real_t sh3 = -src[7];
+	real_t sh4 = -src[5];
+
+	// Rotations.  R0 and R1 just use the raw matrix columns
+	real_t r2x = m00 + m01;
+	real_t r2y = m10 + m11;
+	real_t r2z = m20 + m21;
+
+	real_t r3x = m00 + m02;
+	real_t r3y = m10 + m12;
+	real_t r3z = m20 + m22;
+
+	real_t r4x = m01 + m02;
+	real_t r4y = m11 + m12;
+	real_t r4z = m21 + m22;
+
+	// dense matrix multiplication one column at a time
+
+	// column 0
+	real_t sh0_x = sh0 * m00;
+	real_t sh0_y = sh0 * m10;
+	real_t d0 = sh0_x * m10;
+	real_t d1 = sh0_y * m20;
+	real_t d2 = sh0 * (m20 * m20 + s_c4_div_c3);
+	real_t d3 = sh0_x * m20;
+	real_t d4 = sh0_x * m00 - sh0_y * m10;
+
+	// column 1
+	real_t sh1_x = sh1 * m02;
+	real_t sh1_y = sh1 * m12;
+	d0 += sh1_x * m12;
+	d1 += sh1_y * m22;
+	d2 += sh1 * (m22 * m22 + s_c4_div_c3);
+	d3 += sh1_x * m22;
+	d4 += sh1_x * m02 - sh1_y * m12;
+
+	// column 2
+	real_t sh2_x = sh2 * r2x;
+	real_t sh2_y = sh2 * r2y;
+	d0 += sh2_x * r2y;
+	d1 += sh2_y * r2z;
+	d2 += sh2 * (r2z * r2z + s_c4_div_c3_x2);
+	d3 += sh2_x * r2z;
+	d4 += sh2_x * r2x - sh2_y * r2y;
+
+	// column 3
+	real_t sh3_x = sh3 * r3x;
+	real_t sh3_y = sh3 * r3y;
+	d0 += sh3_x * r3y;
+	d1 += sh3_y * r3z;
+	d2 += sh3 * (r3z * r3z + s_c4_div_c3_x2);
+	d3 += sh3_x * r3z;
+	d4 += sh3_x * r3x - sh3_y * r3y;
+
+	// column 4
+	real_t sh4_x = sh4 * r4x;
+	real_t sh4_y = sh4 * r4y;
+	d0 += sh4_x * r4y;
+	d1 += sh4_y * r4z;
+	d2 += sh4 * (r4z * r4z + s_c4_div_c3_x2);
+	d3 += sh4_x * r4z;
+	d4 += sh4_x * r4x - sh4_y * r4y;
+
+	// extra multipliers
+	p_values[4] = d0;
+	p_values[5] = -d1;
+	p_values[6] = d2 * s_scale_dst2;
+	p_values[7] = -d3;
+	p_values[8] = d4 * s_scale_dst4;
+}

+ 1 - 0
core/math/basis.h

@@ -159,6 +159,7 @@ public:
 	bool is_rotation() const;
 	bool is_rotation() const;
 
 
 	Basis slerp(const Basis &target, const real_t &t) const;
 	Basis slerp(const Basis &target, const real_t &t) const;
+	void rotate_sh(real_t *p_values);
 
 
 	operator String() const;
 	operator String() const;
 
 

+ 16 - 0
core/math/camera_matrix.cpp

@@ -33,6 +33,22 @@
 #include "core/math/math_funcs.h"
 #include "core/math/math_funcs.h"
 #include "core/print_string.h"
 #include "core/print_string.h"
 
 
+float CameraMatrix::determinant() const {
+
+	return matrix[0][3] * matrix[1][2] * matrix[2][1] * matrix[3][0] - matrix[0][2] * matrix[1][3] * matrix[2][1] * matrix[3][0] -
+		   matrix[0][3] * matrix[1][1] * matrix[2][2] * matrix[3][0] + matrix[0][1] * matrix[1][3] * matrix[2][2] * matrix[3][0] +
+		   matrix[0][2] * matrix[1][1] * matrix[2][3] * matrix[3][0] - matrix[0][1] * matrix[1][2] * matrix[2][3] * matrix[3][0] -
+		   matrix[0][3] * matrix[1][2] * matrix[2][0] * matrix[3][1] + matrix[0][2] * matrix[1][3] * matrix[2][0] * matrix[3][1] +
+		   matrix[0][3] * matrix[1][0] * matrix[2][2] * matrix[3][1] - matrix[0][0] * matrix[1][3] * matrix[2][2] * matrix[3][1] -
+		   matrix[0][2] * matrix[1][0] * matrix[2][3] * matrix[3][1] + matrix[0][0] * matrix[1][2] * matrix[2][3] * matrix[3][1] +
+		   matrix[0][3] * matrix[1][1] * matrix[2][0] * matrix[3][2] - matrix[0][1] * matrix[1][3] * matrix[2][0] * matrix[3][2] -
+		   matrix[0][3] * matrix[1][0] * matrix[2][1] * matrix[3][2] + matrix[0][0] * matrix[1][3] * matrix[2][1] * matrix[3][2] +
+		   matrix[0][1] * matrix[1][0] * matrix[2][3] * matrix[3][2] - matrix[0][0] * matrix[1][1] * matrix[2][3] * matrix[3][2] -
+		   matrix[0][2] * matrix[1][1] * matrix[2][0] * matrix[3][3] + matrix[0][1] * matrix[1][2] * matrix[2][0] * matrix[3][3] +
+		   matrix[0][2] * matrix[1][0] * matrix[2][1] * matrix[3][3] - matrix[0][0] * matrix[1][2] * matrix[2][1] * matrix[3][3] -
+		   matrix[0][1] * matrix[1][0] * matrix[2][2] * matrix[3][3] + matrix[0][0] * matrix[1][1] * matrix[2][2] * matrix[3][3];
+}
+
 void CameraMatrix::set_identity() {
 void CameraMatrix::set_identity() {
 
 
 	for (int i = 0; i < 4; i++) {
 	for (int i = 0; i < 4; i++) {

+ 1 - 0
core/math/camera_matrix.h

@@ -47,6 +47,7 @@ struct CameraMatrix {
 
 
 	real_t matrix[4][4];
 	real_t matrix[4][4];
 
 
+	float determinant() const;
 	void set_identity();
 	void set_identity();
 	void set_zero();
 	void set_zero();
 	void set_light_bias();
 	void set_light_bias();

+ 0 - 2
core/math/delaunay.h

@@ -115,8 +115,6 @@ public:
 		triangles.push_back(Triangle(p_points.size() + 0, p_points.size() + 1, p_points.size() + 2));
 		triangles.push_back(Triangle(p_points.size() + 0, p_points.size() + 1, p_points.size() + 2));
 
 
 		for (int i = 0; i < p_points.size(); i++) {
 		for (int i = 0; i < p_points.size(); i++) {
-			//std::cout << "Traitement du point " << *p << std::endl;
-			//std::cout << "_triangles contains " << _triangles.size() << " elements" << std::endl;
 
 
 			Vector<Edge> polygon;
 			Vector<Edge> polygon;
 
 

+ 386 - 0
core/math/delaunay_3d.h

@@ -0,0 +1,386 @@
+#ifndef DELAUNAY_3D_H
+#define DELAUNAY_3D_H
+
+#include "core/local_vector.h"
+#include "core/math/aabb.h"
+#include "core/math/camera_matrix.h"
+#include "core/math/vector3.h"
+#include "core/oa_hash_map.h"
+#include "core/os/file_access.h"
+#include "core/print_string.h"
+#include "core/variant.h"
+#include "core/vector.h"
+#include "thirdparty/r128/r128.h"
+
+class Delaunay3D {
+	struct Simplex;
+
+	enum {
+		ACCEL_GRID_SIZE = 16
+	};
+	struct GridPos {
+		Vector3i pos;
+		List<Simplex *>::Element *E = nullptr;
+	};
+
+	struct Simplex {
+
+		uint32_t points[4];
+		R128 circum_center_x;
+		R128 circum_center_y;
+		R128 circum_center_z;
+		R128 circum_r2;
+		LocalVector<GridPos> grid_positions;
+		List<Simplex *>::Element *SE = nullptr;
+
+		_FORCE_INLINE_ Simplex() {}
+		_FORCE_INLINE_ Simplex(uint32_t p_a, uint32_t p_b, uint32_t p_c, uint32_t p_d) {
+			points[0] = p_a;
+			points[1] = p_b;
+			points[2] = p_c;
+			points[3] = p_d;
+		}
+	};
+
+	struct Triangle {
+		uint32_t triangle[3];
+		bool bad;
+		_FORCE_INLINE_ bool operator==(const Triangle &p_triangle) const {
+			return triangle[0] == p_triangle.triangle[0] && triangle[1] == p_triangle.triangle[1] && triangle[2] == p_triangle.triangle[2];
+		}
+
+		_FORCE_INLINE_ Triangle() { bad = false; }
+		_FORCE_INLINE_ Triangle(uint32_t p_a, uint32_t p_b, uint32_t p_c) {
+			if (p_a > p_b)
+				SWAP(p_a, p_b);
+			if (p_b > p_c)
+				SWAP(p_b, p_c);
+			if (p_a > p_b)
+				SWAP(p_a, p_b);
+
+			bad = false;
+			triangle[0] = p_a;
+			triangle[1] = p_b;
+			triangle[2] = p_c;
+		}
+	};
+
+	struct TriangleHasher {
+		_FORCE_INLINE_ static uint32_t hash(const Triangle &p_triangle) {
+			uint32_t h = hash_djb2_one_32(p_triangle.triangle[0]);
+			h = hash_djb2_one_32(p_triangle.triangle[1], h);
+			return hash_djb2_one_32(p_triangle.triangle[2], h);
+		}
+	};
+
+	struct FPVal {
+	};
+
+	_FORCE_INLINE_ static void circum_sphere_compute(const Vector3 *p_points, Simplex *p_simplex) {
+
+		// the only part in the algorithm where there may be precision errors is this one, so ensure that
+		// we do it as maximum precision as possible
+
+		R128 v0_x = p_points[p_simplex->points[0]].x;
+		R128 v0_y = p_points[p_simplex->points[0]].y;
+		R128 v0_z = p_points[p_simplex->points[0]].z;
+		R128 v1_x = p_points[p_simplex->points[1]].x;
+		R128 v1_y = p_points[p_simplex->points[1]].y;
+		R128 v1_z = p_points[p_simplex->points[1]].z;
+		R128 v2_x = p_points[p_simplex->points[2]].x;
+		R128 v2_y = p_points[p_simplex->points[2]].y;
+		R128 v2_z = p_points[p_simplex->points[2]].z;
+		R128 v3_x = p_points[p_simplex->points[3]].x;
+		R128 v3_y = p_points[p_simplex->points[3]].y;
+		R128 v3_z = p_points[p_simplex->points[3]].z;
+
+		//Create the rows of our "unrolled" 3x3 matrix
+		R128 row1_x = v1_x - v0_x;
+		R128 row1_y = v1_y - v0_y;
+		R128 row1_z = v1_z - v0_z;
+
+		R128 row2_x = v2_x - v0_x;
+		R128 row2_y = v2_y - v0_y;
+		R128 row2_z = v2_z - v0_z;
+
+		R128 row3_x = v3_x - v0_x;
+		R128 row3_y = v3_y - v0_y;
+		R128 row3_z = v3_z - v0_z;
+
+		R128 sq_lenght1 = row1_x * row1_x + row1_y * row1_y + row1_z * row1_z;
+		R128 sq_lenght2 = row2_x * row2_x + row2_y * row2_y + row2_z * row2_z;
+		R128 sq_lenght3 = row3_x * row3_x + row3_y * row3_y + row3_z * row3_z;
+
+		//Compute the determinant of said matrix
+		R128 determinant = row1_x * (row2_y * row3_z - row3_y * row2_z) - row2_x * (row1_y * row3_z - row3_y * row1_z) + row3_x * (row1_y * row2_z - row2_y * row1_z);
+
+		// Compute the volume of the tetrahedron, and precompute a scalar quantity for re-use in the formula
+		R128 volume = determinant / R128(6.f);
+		R128 i12volume = R128(1.f) / (volume * R128(12.f));
+
+		R128 center_x = v0_x + i12volume * ((row2_y * row3_z - row3_y * row2_z) * sq_lenght1 - (row1_y * row3_z - row3_y * row1_z) * sq_lenght2 + (row1_y * row2_z - row2_y * row1_z) * sq_lenght3);
+		R128 center_y = v0_y + i12volume * (-(row2_x * row3_z - row3_x * row2_z) * sq_lenght1 + (row1_x * row3_z - row3_x * row1_z) * sq_lenght2 - (row1_x * row2_z - row2_x * row1_z) * sq_lenght3);
+		R128 center_z = v0_z + i12volume * ((row2_x * row3_y - row3_x * row2_y) * sq_lenght1 - (row1_x * row3_y - row3_x * row1_y) * sq_lenght2 + (row1_x * row2_y - row2_x * row1_y) * sq_lenght3);
+
+		//Once we know the center, the radius is clearly the distance to any vertex
+
+		R128 rel1_x = center_x - v0_x;
+		R128 rel1_y = center_y - v0_y;
+		R128 rel1_z = center_z - v0_z;
+
+		R128 radius1 = rel1_x * rel1_x + rel1_y * rel1_y + rel1_z * rel1_z;
+
+		p_simplex->circum_center_x = center_x;
+		p_simplex->circum_center_y = center_y;
+		p_simplex->circum_center_z = center_z;
+		p_simplex->circum_r2 = radius1;
+	}
+
+	_FORCE_INLINE_ static bool simplex_contains(const Vector3 *p_points, const Simplex &p_simplex, uint32_t p_vertex) {
+
+		R128 v_x = p_points[p_vertex].x;
+		R128 v_y = p_points[p_vertex].y;
+		R128 v_z = p_points[p_vertex].z;
+
+		R128 rel2_x = p_simplex.circum_center_x - v_x;
+		R128 rel2_y = p_simplex.circum_center_y - v_y;
+		R128 rel2_z = p_simplex.circum_center_z - v_z;
+
+		R128 radius2 = rel2_x * rel2_x + rel2_y * rel2_y + rel2_z * rel2_z;
+
+		return radius2 < (p_simplex.circum_r2 - R128(0.00001));
+	}
+
+	static bool simplex_is_coplanar(const Vector3 *p_points, const Simplex &p_simplex) {
+
+		Plane p(p_points[p_simplex.points[0]], p_points[p_simplex.points[1]], p_points[p_simplex.points[2]]);
+		if (ABS(p.distance_to(p_points[p_simplex.points[3]])) < CMP_EPSILON) {
+			return true;
+		}
+
+		CameraMatrix cm;
+
+		cm.matrix[0][0] = p_points[p_simplex.points[0]].x;
+		cm.matrix[0][1] = p_points[p_simplex.points[1]].x;
+		cm.matrix[0][2] = p_points[p_simplex.points[2]].x;
+		cm.matrix[0][3] = p_points[p_simplex.points[3]].x;
+
+		cm.matrix[1][0] = p_points[p_simplex.points[0]].y;
+		cm.matrix[1][1] = p_points[p_simplex.points[1]].y;
+		cm.matrix[1][2] = p_points[p_simplex.points[2]].y;
+		cm.matrix[1][3] = p_points[p_simplex.points[3]].y;
+
+		cm.matrix[2][0] = p_points[p_simplex.points[0]].z;
+		cm.matrix[2][1] = p_points[p_simplex.points[1]].z;
+		cm.matrix[2][2] = p_points[p_simplex.points[2]].z;
+		cm.matrix[2][3] = p_points[p_simplex.points[3]].z;
+
+		cm.matrix[3][0] = 1.0;
+		cm.matrix[3][1] = 1.0;
+		cm.matrix[3][2] = 1.0;
+		cm.matrix[3][3] = 1.0;
+
+		return ABS(cm.determinant()) <= CMP_EPSILON;
+	}
+
+public:
+	struct OutputSimplex {
+		uint32_t points[4];
+	};
+
+	static Vector<OutputSimplex> tetrahedralize(const Vector<Vector3> &p_points) {
+
+		uint32_t point_count = p_points.size();
+		Vector3 *points = (Vector3 *)memalloc(sizeof(Vector3) * (point_count + 4));
+
+		{
+			const Vector3 *src_points = p_points.ptr();
+			AABB rect;
+			for (uint32_t i = 0; i < point_count; i++) {
+				Vector3 point = src_points[i];
+				if (i == 0) {
+					rect.position = point;
+				} else {
+					rect.expand_to(point);
+				}
+				points[i] = point;
+			}
+
+			for (uint32_t i = 0; i < point_count; i++) {
+				points[i] = (points[i] - rect.position) / rect.size;
+			}
+
+			float delta_max = Math::sqrt(2.0) * 20.0;
+			Vector3 center = Vector3(0.5, 0.5, 0.5);
+
+			// any simplex that contains everything is good
+			points[point_count + 0] = center + Vector3(0, 1, 0) * delta_max;
+			points[point_count + 1] = center + Vector3(0, -1, 1) * delta_max;
+			points[point_count + 2] = center + Vector3(1, -1, -1) * delta_max;
+			points[point_count + 3] = center + Vector3(-1, -1, -1) * delta_max;
+		}
+
+		List<Simplex *> acceleration_grid[ACCEL_GRID_SIZE][ACCEL_GRID_SIZE][ACCEL_GRID_SIZE];
+
+		List<Simplex *> simplex_list;
+		{
+			//create root simplex
+			Simplex *root = memnew(Simplex(point_count + 0, point_count + 1, point_count + 2, point_count + 3));
+			root->SE = simplex_list.push_back(root);
+
+			for (uint32_t i = 0; i < ACCEL_GRID_SIZE; i++) {
+				for (uint32_t j = 0; j < ACCEL_GRID_SIZE; j++) {
+					for (uint32_t k = 0; k < ACCEL_GRID_SIZE; k++) {
+						GridPos gp;
+						gp.E = acceleration_grid[i][j][k].push_back(root);
+						gp.pos = Vector3i(i, j, k);
+						root->grid_positions.push_back(gp);
+					}
+				}
+			}
+
+			circum_sphere_compute(points, root);
+		}
+
+		OAHashMap<Triangle, uint32_t, TriangleHasher> triangles_inserted;
+		LocalVector<Triangle> triangles;
+
+		for (uint32_t i = 0; i < point_count; i++) {
+
+			bool unique = true;
+			for (uint32_t j = i + 1; j < point_count; j++) {
+				if (points[i].is_equal_approx(points[j])) {
+					unique = false;
+					break;
+				}
+			}
+			if (!unique) {
+				continue;
+			}
+
+			Vector3i grid_pos = Vector3i(points[i] * ACCEL_GRID_SIZE);
+			grid_pos.x = CLAMP(grid_pos.x, 0, ACCEL_GRID_SIZE - 1);
+			grid_pos.y = CLAMP(grid_pos.y, 0, ACCEL_GRID_SIZE - 1);
+			grid_pos.z = CLAMP(grid_pos.z, 0, ACCEL_GRID_SIZE - 1);
+
+			for (List<Simplex *>::Element *E = acceleration_grid[grid_pos.x][grid_pos.y][grid_pos.z].front(); E;) {
+				List<Simplex *>::Element *N = E->next(); //may be deleted
+
+				Simplex *simplex = E->get();
+
+				if (simplex_contains(points, *simplex, i)) {
+
+					static const uint32_t triangle_order[4][3] = {
+						{ 0, 1, 2 },
+						{ 0, 1, 3 },
+						{ 0, 2, 3 },
+						{ 1, 2, 3 },
+					};
+
+					for (uint32_t k = 0; k < 4; k++) {
+						Triangle t = Triangle(simplex->points[triangle_order[k][0]], simplex->points[triangle_order[k][1]], simplex->points[triangle_order[k][2]]);
+						uint32_t *p = triangles_inserted.lookup_ptr(t);
+						if (p) {
+							triangles[*p].bad = true;
+						} else {
+							triangles_inserted.insert(t, triangles.size());
+							triangles.push_back(t);
+						}
+					}
+
+					//remove simplex and continue
+					simplex_list.erase(simplex->SE);
+
+					for (uint32_t k = 0; k < simplex->grid_positions.size(); k++) {
+						Vector3i p = simplex->grid_positions[k].pos;
+						acceleration_grid[p.x][p.y][p.z].erase(simplex->grid_positions[k].E);
+					}
+					memdelete(simplex);
+				}
+				E = N;
+			}
+
+			uint32_t good_triangles = 0;
+			for (uint32_t j = 0; j < triangles.size(); j++) {
+
+				if (triangles[j].bad) {
+					continue;
+				}
+				Simplex *new_simplex = memnew(Simplex(triangles[j].triangle[0], triangles[j].triangle[1], triangles[j].triangle[2], i));
+				circum_sphere_compute(points, new_simplex);
+				new_simplex->SE = simplex_list.push_back(new_simplex);
+				{
+					Vector3 center;
+					center.x = double(new_simplex->circum_center_x);
+					center.y = double(new_simplex->circum_center_y);
+					center.z = double(new_simplex->circum_center_z);
+
+					float radius2 = Math::sqrt(double(new_simplex->circum_r2));
+					radius2 += 0.0001; //
+					Vector3 extents = Vector3(radius2, radius2, radius2);
+					Vector3i from = Vector3i((center - extents) * ACCEL_GRID_SIZE);
+					Vector3i to = Vector3i((center + extents) * ACCEL_GRID_SIZE);
+					from.x = CLAMP(from.x, 0, ACCEL_GRID_SIZE - 1);
+					from.y = CLAMP(from.y, 0, ACCEL_GRID_SIZE - 1);
+					from.z = CLAMP(from.z, 0, ACCEL_GRID_SIZE - 1);
+					to.x = CLAMP(to.x, 0, ACCEL_GRID_SIZE - 1);
+					to.y = CLAMP(to.y, 0, ACCEL_GRID_SIZE - 1);
+					to.z = CLAMP(to.z, 0, ACCEL_GRID_SIZE - 1);
+
+					for (int32_t x = from.x; x <= to.x; x++) {
+						for (int32_t y = from.y; y <= to.y; y++) {
+							for (int32_t z = from.z; z <= to.z; z++) {
+								GridPos gp;
+								gp.pos = Vector3(x, y, z);
+								gp.E = acceleration_grid[x][y][z].push_back(new_simplex);
+								new_simplex->grid_positions.push_back(gp);
+							}
+						}
+					}
+				}
+
+				good_triangles++;
+			}
+
+			//print_line("at point " + itos(i) + "/" + itos(point_count) + " simplices added " + itos(good_triangles) + "/" + itos(simplex_list.size()) + " - triangles: " + itos(triangles.size()));
+			triangles.clear();
+			triangles_inserted.clear();
+		}
+
+		//print_line("end with simplices: " + itos(simplex_list.size()));
+		Vector<OutputSimplex> ret_simplices;
+		ret_simplices.resize(simplex_list.size());
+		OutputSimplex *ret_simplicesw = ret_simplices.ptrw();
+		uint32_t simplices_written = 0;
+
+		for (List<Simplex *>::Element *E = simplex_list.front(); E; E = E->next()) {
+			Simplex *simplex = E->get();
+			bool invalid = false;
+			for (int j = 0; j < 4; j++) {
+				if (simplex->points[j] >= point_count) {
+					invalid = true;
+					break;
+				}
+			}
+			if (invalid || simplex_is_coplanar(points, *simplex)) {
+				memdelete(simplex);
+				continue;
+			}
+
+			ret_simplicesw[simplices_written].points[0] = simplex->points[0];
+			ret_simplicesw[simplices_written].points[1] = simplex->points[1];
+			ret_simplicesw[simplices_written].points[2] = simplex->points[2];
+			ret_simplicesw[simplices_written].points[3] = simplex->points[3];
+			simplices_written++;
+			memdelete(simplex);
+		}
+
+		ret_simplices.resize(simplices_written);
+
+		memfree(points);
+
+		return ret_simplices;
+	}
+};
+
+#endif // DELAUNAY_3D_H

+ 194 - 0
core/math/geometry.cpp

@@ -33,6 +33,8 @@
 #include "core/print_string.h"
 #include "core/print_string.h"
 #include "thirdparty/misc/clipper.hpp"
 #include "thirdparty/misc/clipper.hpp"
 #include "thirdparty/misc/triangulator.h"
 #include "thirdparty/misc/triangulator.h"
+#define STB_RECT_PACK_IMPLEMENTATION
+#include "thirdparty/stb_rect_pack/stb_rect_pack.h"
 
 
 #define SCALE_FACTOR 100000.0 // Based on CMP_EPSILON.
 #define SCALE_FACTOR 100000.0 // Based on CMP_EPSILON.
 
 
@@ -1242,3 +1244,195 @@ Vector<Vector3> Geometry::compute_convex_mesh_points(const Plane *p_planes, int
 
 
 	return points;
 	return points;
 }
 }
+
+Vector<Point2i> Geometry::pack_rects(const Vector<Size2i> &p_sizes, const Size2i &p_atlas_size) {
+
+	Vector<stbrp_node> nodes;
+	nodes.resize(p_atlas_size.width);
+
+	stbrp_context context;
+	stbrp_init_target(&context, p_atlas_size.width, p_atlas_size.height, nodes.ptrw(), p_atlas_size.width);
+
+	Vector<stbrp_rect> rects;
+	rects.resize(p_sizes.size());
+
+	for (int i = 0; i < p_sizes.size(); i++) {
+		rects.write[i].id = 0;
+		rects.write[i].w = p_sizes[i].width;
+		rects.write[i].h = p_sizes[i].height;
+		rects.write[i].x = 0;
+		rects.write[i].y = 0;
+		rects.write[i].was_packed = 0;
+	}
+
+	int res = stbrp_pack_rects(&context, rects.ptrw(), rects.size());
+	if (res == 0) { //pack failed
+		return Vector<Point2i>();
+	}
+
+	Vector<Point2i> ret;
+	ret.resize(p_sizes.size());
+
+	for (int i = 0; i < p_sizes.size(); i++) {
+		Point2i r(rects[i].x, rects[i].y);
+		ret.write[i] = r;
+	}
+
+	return ret;
+}
+
+Vector<Vector3i> Geometry::partial_pack_rects(const Vector<Vector2i> &p_sizes, const Size2i &p_atlas_size) {
+
+	Vector<stbrp_node> nodes;
+	nodes.resize(p_atlas_size.width);
+	zeromem(nodes.ptrw(), sizeof(stbrp_node) * nodes.size());
+
+	stbrp_context context;
+	stbrp_init_target(&context, p_atlas_size.width, p_atlas_size.height, nodes.ptrw(), p_atlas_size.width);
+
+	Vector<stbrp_rect> rects;
+	rects.resize(p_sizes.size());
+
+	for (int i = 0; i < p_sizes.size(); i++) {
+		rects.write[i].id = i;
+		rects.write[i].w = p_sizes[i].width;
+		rects.write[i].h = p_sizes[i].height;
+		rects.write[i].x = 0;
+		rects.write[i].y = 0;
+		rects.write[i].was_packed = 0;
+	}
+
+	stbrp_pack_rects(&context, rects.ptrw(), rects.size());
+
+	Vector<Vector3i> ret;
+	ret.resize(p_sizes.size());
+
+	for (int i = 0; i < p_sizes.size(); i++) {
+		ret.write[rects[i].id] = Vector3i(rects[i].x, rects[i].y, rects[i].was_packed != 0 ? 1 : 0);
+	}
+
+	return ret;
+}
+
+#define square(m_s) ((m_s) * (m_s))
+#define INF 1e20
+
+/* dt of 1d function using squared distance */
+static void edt(float *f, int stride, int n) {
+
+	float *d = (float *)alloca(sizeof(float) * n + sizeof(int) * n + sizeof(float) * (n + 1));
+	int *v = (int *)&(d[n]);
+	float *z = (float *)&v[n];
+
+	int k = 0;
+	v[0] = 0;
+	z[0] = -INF;
+	z[1] = +INF;
+	for (int q = 1; q <= n - 1; q++) {
+		float s = ((f[q * stride] + square(q)) - (f[v[k] * stride] + square(v[k]))) / (2 * q - 2 * v[k]);
+		while (s <= z[k]) {
+			k--;
+			s = ((f[q * stride] + square(q)) - (f[v[k] * stride] + square(v[k]))) / (2 * q - 2 * v[k]);
+		}
+		k++;
+		v[k] = q;
+
+		z[k] = s;
+		z[k + 1] = +INF;
+	}
+
+	k = 0;
+	for (int q = 0; q <= n - 1; q++) {
+		while (z[k + 1] < q)
+			k++;
+		d[q] = square(q - v[k]) + f[v[k] * stride];
+	}
+
+	for (int i = 0; i < n; i++) {
+		f[i * stride] = d[i];
+	}
+}
+
+#undef square
+
+Vector<uint32_t> Geometry::generate_edf(const Vector<bool> &p_voxels, const Vector3i &p_size, bool p_negative) {
+
+	uint32_t float_count = p_size.x * p_size.y * p_size.z;
+
+	ERR_FAIL_COND_V((uint32_t)p_voxels.size() != float_count, Vector<uint32_t>());
+
+	float *work_memory = memnew_arr(float, float_count);
+	for (uint32_t i = 0; i < float_count; i++) {
+		work_memory[i] = INF;
+	}
+
+	uint32_t y_mult = p_size.x;
+	uint32_t z_mult = y_mult * p_size.y;
+
+	//plot solid cells
+	{
+		const bool *voxr = p_voxels.ptr();
+		for (uint32_t i = 0; i < float_count; i++) {
+
+			bool plot = voxr[i];
+			if (p_negative) {
+				plot = !plot;
+			}
+			if (plot) {
+				work_memory[i] = 0;
+			}
+		}
+	}
+
+	//process in each direction
+
+	//xy->z
+
+	for (int i = 0; i < p_size.x; i++) {
+		for (int j = 0; j < p_size.y; j++) {
+			edt(&work_memory[i + j * y_mult], z_mult, p_size.z);
+		}
+	}
+
+	//xz->y
+
+	for (int i = 0; i < p_size.x; i++) {
+		for (int j = 0; j < p_size.z; j++) {
+			edt(&work_memory[i + j * z_mult], y_mult, p_size.y);
+		}
+	}
+
+	//yz->x
+	for (int i = 0; i < p_size.y; i++) {
+		for (int j = 0; j < p_size.z; j++) {
+			edt(&work_memory[i * y_mult + j * z_mult], 1, p_size.x);
+		}
+	}
+
+	Vector<uint32_t> ret;
+	ret.resize(float_count);
+	{
+		uint32_t *w = ret.ptrw();
+		for (uint32_t i = 0; i < float_count; i++) {
+			w[i] = uint32_t(Math::sqrt(work_memory[i]));
+		}
+	}
+
+	return ret;
+}
+
+Vector<int8_t> Geometry::generate_sdf8(const Vector<uint32_t> &p_positive, const Vector<uint32_t> &p_negative) {
+	ERR_FAIL_COND_V(p_positive.size() != p_negative.size(), Vector<int8_t>());
+	Vector<int8_t> sdf8;
+	int s = p_positive.size();
+	sdf8.resize(s);
+
+	const uint32_t *rpos = p_positive.ptr();
+	const uint32_t *rneg = p_negative.ptr();
+	int8_t *wsdf = sdf8.ptrw();
+	for (int i = 0; i < s; i++) {
+		int32_t diff = int32_t(rpos[i]) - int32_t(rneg[i]);
+		wsdf[i] = CLAMP(diff, -128, 127);
+	}
+	return sdf8;
+}

+ 243 - 0
core/math/geometry.h

@@ -1024,6 +1024,249 @@ public:
 
 
 	static Vector<Vector3> compute_convex_mesh_points(const Plane *p_planes, int p_plane_count);
 	static Vector<Vector3> compute_convex_mesh_points(const Plane *p_planes, int p_plane_count);
 
 
+#define FINDMINMAX(x0, x1, x2, min, max) \
+	min = max = x0;                      \
+	if (x1 < min)                        \
+		min = x1;                        \
+	if (x1 > max)                        \
+		max = x1;                        \
+	if (x2 < min)                        \
+		min = x2;                        \
+	if (x2 > max)                        \
+		max = x2;
+
+	_FORCE_INLINE_ static bool planeBoxOverlap(Vector3 normal, float d, Vector3 maxbox) {
+		int q;
+		Vector3 vmin, vmax;
+		for (q = 0; q <= 2; q++) {
+			if (normal[q] > 0.0f) {
+				vmin[q] = -maxbox[q];
+				vmax[q] = maxbox[q];
+			} else {
+				vmin[q] = maxbox[q];
+				vmax[q] = -maxbox[q];
+			}
+		}
+		if (normal.dot(vmin) + d > 0.0f)
+			return false;
+		if (normal.dot(vmax) + d >= 0.0f)
+			return true;
+
+		return false;
+	}
+
+/*======================== X-tests ========================*/
+#define AXISTEST_X01(a, b, fa, fb)                 \
+	p0 = a * v0.y - b * v0.z;                      \
+	p2 = a * v2.y - b * v2.z;                      \
+	if (p0 < p2) {                                 \
+		min = p0;                                  \
+		max = p2;                                  \
+	} else {                                       \
+		min = p2;                                  \
+		max = p0;                                  \
+	}                                              \
+	rad = fa * boxhalfsize.y + fb * boxhalfsize.z; \
+	if (min > rad || max < -rad)                   \
+		return false;
+
+#define AXISTEST_X2(a, b, fa, fb)                  \
+	p0 = a * v0.y - b * v0.z;                      \
+	p1 = a * v1.y - b * v1.z;                      \
+	if (p0 < p1) {                                 \
+		min = p0;                                  \
+		max = p1;                                  \
+	} else {                                       \
+		min = p1;                                  \
+		max = p0;                                  \
+	}                                              \
+	rad = fa * boxhalfsize.y + fb * boxhalfsize.z; \
+	if (min > rad || max < -rad)                   \
+		return false;
+
+/*======================== Y-tests ========================*/
+#define AXISTEST_Y02(a, b, fa, fb)                 \
+	p0 = -a * v0.x + b * v0.z;                     \
+	p2 = -a * v2.x + b * v2.z;                     \
+	if (p0 < p2) {                                 \
+		min = p0;                                  \
+		max = p2;                                  \
+	} else {                                       \
+		min = p2;                                  \
+		max = p0;                                  \
+	}                                              \
+	rad = fa * boxhalfsize.x + fb * boxhalfsize.z; \
+	if (min > rad || max < -rad)                   \
+		return false;
+
+#define AXISTEST_Y1(a, b, fa, fb)                  \
+	p0 = -a * v0.x + b * v0.z;                     \
+	p1 = -a * v1.x + b * v1.z;                     \
+	if (p0 < p1) {                                 \
+		min = p0;                                  \
+		max = p1;                                  \
+	} else {                                       \
+		min = p1;                                  \
+		max = p0;                                  \
+	}                                              \
+	rad = fa * boxhalfsize.x + fb * boxhalfsize.z; \
+	if (min > rad || max < -rad)                   \
+		return false;
+
+	/*======================== Z-tests ========================*/
+
+#define AXISTEST_Z12(a, b, fa, fb)                 \
+	p1 = a * v1.x - b * v1.y;                      \
+	p2 = a * v2.x - b * v2.y;                      \
+	if (p2 < p1) {                                 \
+		min = p2;                                  \
+		max = p1;                                  \
+	} else {                                       \
+		min = p1;                                  \
+		max = p2;                                  \
+	}                                              \
+	rad = fa * boxhalfsize.x + fb * boxhalfsize.y; \
+	if (min > rad || max < -rad)                   \
+		return false;
+
+#define AXISTEST_Z0(a, b, fa, fb)                  \
+	p0 = a * v0.x - b * v0.y;                      \
+	p1 = a * v1.x - b * v1.y;                      \
+	if (p0 < p1) {                                 \
+		min = p0;                                  \
+		max = p1;                                  \
+	} else {                                       \
+		min = p1;                                  \
+		max = p0;                                  \
+	}                                              \
+	rad = fa * boxhalfsize.x + fb * boxhalfsize.y; \
+	if (min > rad || max < -rad)                   \
+		return false;
+
+	_FORCE_INLINE_ static bool triangle_box_overlap(const Vector3 &boxcenter, const Vector3 boxhalfsize, const Vector3 *triverts) {
+
+		/*    use separating axis theorem to test overlap between triangle and box */
+		/*    need to test for overlap in these directions: */
+		/*    1) the {x,y,z}-directions (actually, since we use the AABB of the triangle */
+		/*       we do not even need to test these) */
+		/*    2) normal of the triangle */
+		/*    3) crossproduct(edge from tri, {x,y,z}-directin) */
+		/*       this gives 3x3=9 more tests */
+		Vector3 v0, v1, v2;
+		float min, max, d, p0, p1, p2, rad, fex, fey, fez;
+		Vector3 normal, e0, e1, e2;
+
+		/* This is the fastest branch on Sun */
+		/* move everything so that the boxcenter is in (0,0,0) */
+
+		v0 = triverts[0] - boxcenter;
+		v1 = triverts[1] - boxcenter;
+		v2 = triverts[2] - boxcenter;
+
+		/* compute triangle edges */
+		e0 = v1 - v0; /* tri edge 0 */
+		e1 = v2 - v1; /* tri edge 1 */
+		e2 = v0 - v2; /* tri edge 2 */
+
+		/* Bullet 3:  */
+		/*  test the 9 tests first (this was faster) */
+		fex = Math::abs(e0.x);
+		fey = Math::abs(e0.y);
+		fez = Math::abs(e0.z);
+		AXISTEST_X01(e0.z, e0.y, fez, fey);
+		AXISTEST_Y02(e0.z, e0.x, fez, fex);
+		AXISTEST_Z12(e0.y, e0.x, fey, fex);
+
+		fex = Math::abs(e1.x);
+		fey = Math::abs(e1.y);
+		fez = Math::abs(e1.z);
+		AXISTEST_X01(e1.z, e1.y, fez, fey);
+		AXISTEST_Y02(e1.z, e1.x, fez, fex);
+		AXISTEST_Z0(e1.y, e1.x, fey, fex);
+
+		fex = Math::abs(e2.x);
+		fey = Math::abs(e2.y);
+		fez = Math::abs(e2.z);
+		AXISTEST_X2(e2.z, e2.y, fez, fey);
+		AXISTEST_Y1(e2.z, e2.x, fez, fex);
+		AXISTEST_Z12(e2.y, e2.x, fey, fex);
+
+		/* Bullet 1: */
+		/*  first test overlap in the {x,y,z}-directions */
+		/*  find min, max of the triangle each direction, and test for overlap in */
+		/*  that direction -- this is equivalent to testing a minimal AABB around */
+		/*  the triangle against the AABB */
+
+		/* test in X-direction */
+		FINDMINMAX(v0.x, v1.x, v2.x, min, max);
+		if (min > boxhalfsize.x || max < -boxhalfsize.x)
+			return false;
+
+		/* test in Y-direction */
+		FINDMINMAX(v0.y, v1.y, v2.y, min, max);
+		if (min > boxhalfsize.y || max < -boxhalfsize.y)
+			return false;
+
+		/* test in Z-direction */
+		FINDMINMAX(v0.z, v1.z, v2.z, min, max);
+		if (min > boxhalfsize.z || max < -boxhalfsize.z)
+			return false;
+
+		/* Bullet 2: */
+		/*  test if the box intersects the plane of the triangle */
+		/*  compute plane equation of triangle: normal*x+d=0 */
+		normal = e0.cross(e1);
+		d = -normal.dot(v0); /* plane eq: normal.x+d=0 */
+		return planeBoxOverlap(normal, d, boxhalfsize); /* if true, box and triangle overlaps */
+	}
+
+	static Vector<Point2i> pack_rects(const Vector<Size2i> &p_sizes, const Size2i &p_atlas_size);
+	static Vector<Vector3i> partial_pack_rects(const Vector<Vector2i> &p_sizes, const Size2i &p_atlas_size);
+
+	static Vector<uint32_t> generate_edf(const Vector<bool> &p_voxels, const Vector3i &p_size, bool p_negative);
+	static Vector<int8_t> generate_sdf8(const Vector<uint32_t> &p_positive, const Vector<uint32_t> &p_negative);
+
+	static Vector3 triangle_get_barycentric_coords(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, const Vector3 &p_pos) {
+		Vector3 v0 = p_b - p_a;
+		Vector3 v1 = p_c - p_a;
+		Vector3 v2 = p_pos - p_a;
+
+		float d00 = v0.dot(v0);
+		float d01 = v0.dot(v1);
+		float d11 = v1.dot(v1);
+		float d20 = v2.dot(v0);
+		float d21 = v2.dot(v1);
+		float denom = (d00 * d11 - d01 * d01);
+		if (denom == 0) {
+			return Vector3(); //invalid triangle, return empty
+		}
+		float v = (d11 * d20 - d01 * d21) / denom;
+		float w = (d00 * d21 - d01 * d20) / denom;
+		float u = 1.0f - v - w;
+		return Vector3(u, v, w);
+	}
+
+	static Color tetrahedron_get_barycentric_coords(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, const Vector3 &p_d, const Vector3 &p_pos) {
+		Vector3 vap = p_pos - p_a;
+		Vector3 vbp = p_pos - p_b;
+
+		Vector3 vab = p_b - p_a;
+		Vector3 vac = p_c - p_a;
+		Vector3 vad = p_d - p_a;
+
+		Vector3 vbc = p_c - p_b;
+		Vector3 vbd = p_d - p_b;
+		// ScTP computes the scalar triple product
+#define STP(m_a, m_b, m_c) ((m_a).dot((m_b).cross((m_c))))
+		float va6 = STP(vbp, vbd, vbc);
+		float vb6 = STP(vap, vac, vad);
+		float vc6 = STP(vap, vad, vab);
+		float vd6 = STP(vap, vab, vac);
+		float v6 = 1 / STP(vab, vac, vad);
+		return Color(va6 * v6, vb6 * v6, vc6 * v6, vd6 * v6);
+#undef STP
+	}
+
 private:
 private:
 	static Vector<Vector<Point2>> _polypaths_do_operation(PolyBooleanOperation p_op, const Vector<Point2> &p_polypath_a, const Vector<Point2> &p_polypath_b, bool is_a_open = false);
 	static Vector<Vector<Point2>> _polypaths_do_operation(PolyBooleanOperation p_op, const Vector<Point2> &p_polypath_a, const Vector<Point2> &p_polypath_b, bool is_a_open = false);
 	static Vector<Vector<Point2>> _polypath_offset(const Vector<Point2> &p_polypath, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type);
 	static Vector<Vector<Point2>> _polypath_offset(const Vector<Point2> &p_polypath, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type);

+ 4 - 0
core/math/plane.cpp

@@ -153,6 +153,10 @@ bool Plane::intersects_segment(const Vector3 &p_begin, const Vector3 &p_end, Vec
 
 
 /* misc */
 /* misc */
 
 
+bool Plane::is_equal_approx_any_side(const Plane &p_plane) const {
+	return (normal.is_equal_approx(p_plane.normal) && Math::is_equal_approx(d, p_plane.d)) || (normal.is_equal_approx(-p_plane.normal) && Math::is_equal_approx(d, -p_plane.d));
+}
+
 bool Plane::is_equal_approx(const Plane &p_plane) const {
 bool Plane::is_equal_approx(const Plane &p_plane) const {
 
 
 	return normal.is_equal_approx(p_plane.normal) && Math::is_equal_approx(d, p_plane.d);
 	return normal.is_equal_approx(p_plane.normal) && Math::is_equal_approx(d, p_plane.d);

+ 1 - 0
core/math/plane.h

@@ -69,6 +69,7 @@ public:
 
 
 	Plane operator-() const { return Plane(-normal, -d); }
 	Plane operator-() const { return Plane(-normal, -d); }
 	bool is_equal_approx(const Plane &p_plane) const;
 	bool is_equal_approx(const Plane &p_plane) const;
+	bool is_equal_approx_any_side(const Plane &p_plane) const;
 
 
 	_FORCE_INLINE_ bool operator==(const Plane &p_plane) const;
 	_FORCE_INLINE_ bool operator==(const Plane &p_plane) const;
 	_FORCE_INLINE_ bool operator!=(const Plane &p_plane) const;
 	_FORCE_INLINE_ bool operator!=(const Plane &p_plane) const;

+ 2 - 0
core/math/r128.cpp

@@ -0,0 +1,2 @@
+#define R128_IMPLEMENTATION
+#include "thirdparty/r128/r128.h"

+ 12 - 4
core/ustring.cpp

@@ -548,8 +548,8 @@ signed char String::naturalnocasecmp_to(const String &p_str) const {
 					return -1;
 					return -1;
 
 
 				/* Compare the numbers */
 				/* Compare the numbers */
-				this_int = to_int(this_str);
-				that_int = to_int(that_str);
+				this_int = to_int(this_str, -1, true);
+				that_int = to_int(that_str, -1, true);
 
 
 				if (this_int < that_int)
 				if (this_int < that_int)
 					return -1;
 					return -1;
@@ -2138,7 +2138,7 @@ double String::to_double(const CharType *p_str, const CharType **r_end) {
 	return built_in_strtod<CharType>(p_str, (CharType **)r_end);
 	return built_in_strtod<CharType>(p_str, (CharType **)r_end);
 }
 }
 
 
-int64_t String::to_int(const CharType *p_str, int p_len) {
+int64_t String::to_int(const CharType *p_str, int p_len, bool p_clamp) {
 
 
 	if (p_len == 0 || !p_str[0])
 	if (p_len == 0 || !p_str[0])
 		return 0;
 		return 0;
@@ -2182,7 +2182,15 @@ int64_t String::to_int(const CharType *p_str, int p_len) {
 						while (*str && str != limit) {
 						while (*str && str != limit) {
 							number += *(str++);
 							number += *(str++);
 						}
 						}
-						ERR_FAIL_V_MSG(sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + number + " as integer, provided value is " + (sign == 1 ? "too big." : "too small."));
+						if (p_clamp) {
+							if (sign == 1) {
+								return INT64_MAX;
+							} else {
+								return INT64_MIN;
+							}
+						} else {
+							ERR_FAIL_V_MSG(sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + number + " as integer, provided value is " + (sign == 1 ? "too big." : "too small."));
+						}
 					}
 					}
 					integer *= 10;
 					integer *= 10;
 					integer += c - '0';
 					integer += c - '0';

+ 1 - 1
core/ustring.h

@@ -254,7 +254,7 @@ public:
 	static int to_int(const char *p_str, int p_len = -1);
 	static int to_int(const char *p_str, int p_len = -1);
 	static double to_double(const char *p_str);
 	static double to_double(const char *p_str);
 	static double to_double(const CharType *p_str, const CharType **r_end = nullptr);
 	static double to_double(const CharType *p_str, const CharType **r_end = nullptr);
-	static int64_t to_int(const CharType *p_str, int p_len = -1);
+	static int64_t to_int(const CharType *p_str, int p_len = -1, bool p_clamp = false);
 	String capitalize() const;
 	String capitalize() const;
 	String camelcase_to_underscore(bool lowercase = true) const;
 	String camelcase_to_underscore(bool lowercase = true) const;
 
 

+ 8 - 0
core/vector.h

@@ -39,6 +39,7 @@
 
 
 #include "core/cowdata.h"
 #include "core/cowdata.h"
 #include "core/error_macros.h"
 #include "core/error_macros.h"
+#include "core/os/copymem.h"
 #include "core/os/memory.h"
 #include "core/os/memory.h"
 #include "core/sort_array.h"
 #include "core/sort_array.h"
 
 
@@ -125,6 +126,13 @@ public:
 		return *this;
 		return *this;
 	}
 	}
 
 
+	Vector<uint8_t> to_byte_array() const {
+		Vector<uint8_t> ret;
+		ret.resize(size() * sizeof(T));
+		copymem(ret.ptrw(), ptr(), sizeof(T) * size());
+		return ret;
+	}
+
 	Vector<T> subarray(int p_from, int p_to) const {
 	Vector<T> subarray(int p_from, int p_to) const {
 
 
 		if (p_from < 0) {
 		if (p_from < 0) {

+ 9 - 1
drivers/vulkan/rendering_device_vulkan.cpp

@@ -2453,7 +2453,7 @@ Vector<uint8_t> RenderingDeviceVulkan::texture_get_data(RID p_texture, uint32_t
 		uint32_t buffer_size = get_image_format_required_size(tex->format, tex->width, tex->height, tex->depth, tex->mipmaps, &width, &height, &depth);
 		uint32_t buffer_size = get_image_format_required_size(tex->format, tex->width, tex->height, tex->depth, tex->mipmaps, &width, &height, &depth);
 
 
 		//allocate buffer
 		//allocate buffer
-		VkCommandBuffer command_buffer = frames[frame].setup_command_buffer;
+		VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; //makes more sense to retrieve
 		Buffer tmp_buffer;
 		Buffer tmp_buffer;
 		_buffer_allocate(&tmp_buffer, buffer_size, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_CPU_ONLY);
 		_buffer_allocate(&tmp_buffer, buffer_size, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_CPU_ONLY);
 
 
@@ -6859,6 +6859,7 @@ void RenderingDeviceVulkan::sync() {
 
 
 	context->local_device_sync(local_device);
 	context->local_device_sync(local_device);
 	_begin_frame();
 	_begin_frame();
+	local_device_processing = false;
 }
 }
 
 
 void RenderingDeviceVulkan::_free_pending_resources(int p_frame) {
 void RenderingDeviceVulkan::_free_pending_resources(int p_frame) {
@@ -6975,6 +6976,12 @@ uint32_t RenderingDeviceVulkan::get_frame_delay() const {
 	return frame_count;
 	return frame_count;
 }
 }
 
 
+uint64_t RenderingDeviceVulkan::get_memory_usage() const {
+	VmaStats stats;
+	vmaCalculateStats(allocator, &stats);
+	return stats.total.usedBytes;
+}
+
 void RenderingDeviceVulkan::_flush(bool p_current_frame) {
 void RenderingDeviceVulkan::_flush(bool p_current_frame) {
 
 
 	if (local_device.is_valid() && !p_current_frame) {
 	if (local_device.is_valid() && !p_current_frame) {
@@ -7039,6 +7046,7 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de
 	if (p_local_device) {
 	if (p_local_device) {
 		frame_count = 1;
 		frame_count = 1;
 		local_device = p_context->local_device_create();
 		local_device = p_context->local_device_create();
+		device = p_context->local_device_get_vk_device(local_device);
 	} else {
 	} else {
 		frame_count = p_context->get_swapchain_image_count() + 1; //always need one extra to ensure it's unused at any time, without having to use a fence for this.
 		frame_count = p_context->get_swapchain_image_count() + 1; //always need one extra to ensure it's unused at any time, without having to use a fence for this.
 	}
 	}

+ 2 - 0
drivers/vulkan/rendering_device_vulkan.h

@@ -1138,6 +1138,8 @@ public:
 
 
 	virtual RenderingDevice *create_local_device();
 	virtual RenderingDevice *create_local_device();
 
 
+	virtual uint64_t get_memory_usage() const;
+
 	RenderingDeviceVulkan();
 	RenderingDeviceVulkan();
 	~RenderingDeviceVulkan();
 	~RenderingDeviceVulkan();
 };
 };

+ 9 - 0
drivers/vulkan/vulkan_context.cpp

@@ -1567,6 +1567,15 @@ void VulkanContext::local_device_push_command_buffers(RID p_local_device, const
 	submit_info.pSignalSemaphores = nullptr;
 	submit_info.pSignalSemaphores = nullptr;
 
 
 	VkResult err = vkQueueSubmit(ld->queue, 1, &submit_info, VK_NULL_HANDLE);
 	VkResult err = vkQueueSubmit(ld->queue, 1, &submit_info, VK_NULL_HANDLE);
+	if (err == VK_ERROR_OUT_OF_HOST_MEMORY) {
+		print_line("out of host memory");
+	}
+	if (err == VK_ERROR_OUT_OF_DEVICE_MEMORY) {
+		print_line("out of device memory");
+	}
+	if (err == VK_ERROR_DEVICE_LOST) {
+		print_line("device lost");
+	}
 	ERR_FAIL_COND(err);
 	ERR_FAIL_COND(err);
 
 
 	ld->waiting = true;
 	ld->waiting = true;

+ 12 - 4
editor/editor_node.cpp

@@ -158,6 +158,7 @@
 #include "editor/plugins/style_box_editor_plugin.h"
 #include "editor/plugins/style_box_editor_plugin.h"
 #include "editor/plugins/text_editor.h"
 #include "editor/plugins/text_editor.h"
 #include "editor/plugins/texture_editor_plugin.h"
 #include "editor/plugins/texture_editor_plugin.h"
+#include "editor/plugins/texture_layered_editor_plugin.h"
 #include "editor/plugins/texture_region_editor_plugin.h"
 #include "editor/plugins/texture_region_editor_plugin.h"
 #include "editor/plugins/theme_editor_plugin.h"
 #include "editor/plugins/theme_editor_plugin.h"
 #include "editor/plugins/tile_map_editor_plugin.h"
 #include "editor/plugins/tile_map_editor_plugin.h"
@@ -381,6 +382,8 @@ void EditorNode::_notification(int p_what) {
 				RS::get_singleton()->shadows_quality_set(shadows_quality);
 				RS::get_singleton()->shadows_quality_set(shadows_quality);
 				RS::ShadowQuality directional_shadow_quality = RS::ShadowQuality(int(GLOBAL_GET("rendering/quality/directional_shadow/soft_shadow_quality")));
 				RS::ShadowQuality directional_shadow_quality = RS::ShadowQuality(int(GLOBAL_GET("rendering/quality/directional_shadow/soft_shadow_quality")));
 				RS::get_singleton()->directional_shadow_quality_set(directional_shadow_quality);
 				RS::get_singleton()->directional_shadow_quality_set(directional_shadow_quality);
+				float probe_update_speed = GLOBAL_GET("rendering/lightmapper/probe_capture_update_speed");
+				RS::get_singleton()->lightmap_set_probe_capture_update_speed(probe_update_speed);
 			}
 			}
 
 
 			ResourceImporterTexture::get_singleton()->update_imports();
 			ResourceImporterTexture::get_singleton()->update_imports();
@@ -713,7 +716,6 @@ void EditorNode::_sources_changed(bool p_exist) {
 
 
 		// Reload the global shader variables, but this time
 		// Reload the global shader variables, but this time
 		// loading texures, as they are now properly imported.
 		// loading texures, as they are now properly imported.
-		print_line("done scanning, reload textures");
 		RenderingServer::get_singleton()->global_variables_load_settings(true);
 		RenderingServer::get_singleton()->global_variables_load_settings(true);
 
 
 		// Start preview thread now that it's safe.
 		// Start preview thread now that it's safe.
@@ -5678,7 +5680,7 @@ EditorNode::EditorNode() {
 		import_texture.instance();
 		import_texture.instance();
 		ResourceFormatImporter::get_singleton()->add_importer(import_texture);
 		ResourceFormatImporter::get_singleton()->add_importer(import_texture);
 
 
-		/*		Ref<ResourceImporterLayeredTexture> import_cubemap;
+		Ref<ResourceImporterLayeredTexture> import_cubemap;
 		import_cubemap.instance();
 		import_cubemap.instance();
 		import_cubemap->set_mode(ResourceImporterLayeredTexture::MODE_CUBEMAP);
 		import_cubemap->set_mode(ResourceImporterLayeredTexture::MODE_CUBEMAP);
 		ResourceFormatImporter::get_singleton()->add_importer(import_cubemap);
 		ResourceFormatImporter::get_singleton()->add_importer(import_cubemap);
@@ -5692,7 +5694,12 @@ EditorNode::EditorNode() {
 		import_cubemap_array.instance();
 		import_cubemap_array.instance();
 		import_cubemap_array->set_mode(ResourceImporterLayeredTexture::MODE_CUBEMAP_ARRAY);
 		import_cubemap_array->set_mode(ResourceImporterLayeredTexture::MODE_CUBEMAP_ARRAY);
 		ResourceFormatImporter::get_singleton()->add_importer(import_cubemap_array);
 		ResourceFormatImporter::get_singleton()->add_importer(import_cubemap_array);
-*/
+
+		/*Ref<ResourceImporterLayeredTexture> import_3d;
+		import_3d.instance();
+		import_3d->set_mode(ResourceImporterLayeredTexture::MODE_3D);
+		ResourceFormatImporter::get_singleton()->add_importer(import_3d);*/
+
 		Ref<ResourceImporterImage> import_image;
 		Ref<ResourceImporterImage> import_image;
 		import_image.instance();
 		import_image.instance();
 		ResourceFormatImporter::get_singleton()->add_importer(import_image);
 		ResourceFormatImporter::get_singleton()->add_importer(import_image);
@@ -6663,7 +6670,7 @@ EditorNode::EditorNode() {
 	add_editor_plugin(memnew(SpriteFramesEditorPlugin(this)));
 	add_editor_plugin(memnew(SpriteFramesEditorPlugin(this)));
 	add_editor_plugin(memnew(TextureRegionEditorPlugin(this)));
 	add_editor_plugin(memnew(TextureRegionEditorPlugin(this)));
 	add_editor_plugin(memnew(GIProbeEditorPlugin(this)));
 	add_editor_plugin(memnew(GIProbeEditorPlugin(this)));
-	//add_editor_plugin(memnew(BakedLightmapEditorPlugin(this)));
+	add_editor_plugin(memnew(BakedLightmapEditorPlugin(this)));
 	add_editor_plugin(memnew(Path2DEditorPlugin(this)));
 	add_editor_plugin(memnew(Path2DEditorPlugin(this)));
 	add_editor_plugin(memnew(Path3DEditorPlugin(this)));
 	add_editor_plugin(memnew(Path3DEditorPlugin(this)));
 	add_editor_plugin(memnew(Line2DEditorPlugin(this)));
 	add_editor_plugin(memnew(Line2DEditorPlugin(this)));
@@ -6674,6 +6681,7 @@ EditorNode::EditorNode() {
 	add_editor_plugin(memnew(CollisionShape2DEditorPlugin(this)));
 	add_editor_plugin(memnew(CollisionShape2DEditorPlugin(this)));
 	add_editor_plugin(memnew(CurveEditorPlugin(this)));
 	add_editor_plugin(memnew(CurveEditorPlugin(this)));
 	add_editor_plugin(memnew(TextureEditorPlugin(this)));
 	add_editor_plugin(memnew(TextureEditorPlugin(this)));
+	add_editor_plugin(memnew(TextureLayeredEditorPlugin(this)));
 	add_editor_plugin(memnew(AudioStreamEditorPlugin(this)));
 	add_editor_plugin(memnew(AudioStreamEditorPlugin(this)));
 	add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor)));
 	add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor)));
 	add_editor_plugin(memnew(Skeleton3DEditorPlugin(this)));
 	add_editor_plugin(memnew(Skeleton3DEditorPlugin(this)));

+ 171 - 150
editor/import/resource_importer_layered_texture.cpp

@@ -36,9 +36,9 @@
 #include "core/io/image_loader.h"
 #include "core/io/image_loader.h"
 #include "editor/editor_file_system.h"
 #include "editor/editor_file_system.h"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
+#include "resource_importer_texture.h"
 #include "scene/resources/texture.h"
 #include "scene/resources/texture.h"
 
 
-#if 0
 String ResourceImporterLayeredTexture::get_importer_name() const {
 String ResourceImporterLayeredTexture::get_importer_name() const {
 
 
 	switch (mode) {
 	switch (mode) {
@@ -51,6 +51,9 @@ String ResourceImporterLayeredTexture::get_importer_name() const {
 		case MODE_CUBEMAP_ARRAY: {
 		case MODE_CUBEMAP_ARRAY: {
 			return "cubemap_array_texture";
 			return "cubemap_array_texture";
 		} break;
 		} break;
+		case MODE_3D: {
+			return "cubemap_3d_texture";
+		} break;
 	}
 	}
 
 
 	ERR_FAIL_V("");
 	ERR_FAIL_V("");
@@ -68,6 +71,9 @@ String ResourceImporterLayeredTexture::get_visible_name() const {
 		case MODE_CUBEMAP_ARRAY: {
 		case MODE_CUBEMAP_ARRAY: {
 			return "CubemapArray";
 			return "CubemapArray";
 		} break;
 		} break;
+		case MODE_3D: {
+			return "3D";
+		} break;
 	}
 	}
 
 
 	ERR_FAIL_V("");
 	ERR_FAIL_V("");
@@ -79,13 +85,16 @@ void ResourceImporterLayeredTexture::get_recognized_extensions(List<String> *p_e
 String ResourceImporterLayeredTexture::get_save_extension() const {
 String ResourceImporterLayeredTexture::get_save_extension() const {
 	switch (mode) {
 	switch (mode) {
 		case MODE_CUBEMAP: {
 		case MODE_CUBEMAP: {
-			return "cube";
+			return "scube";
 		} break;
 		} break;
 		case MODE_2D_ARRAY: {
 		case MODE_2D_ARRAY: {
-			return "tex2darr";
+			return "stexarray";
 		} break;
 		} break;
 		case MODE_CUBEMAP_ARRAY: {
 		case MODE_CUBEMAP_ARRAY: {
-			return "cubearr";
+			return "scubearray";
+		} break;
+		case MODE_3D: {
+			return "stex3d";
 		} break;
 		} break;
 	}
 	}
 
 
@@ -96,13 +105,16 @@ String ResourceImporterLayeredTexture::get_resource_type() const {
 
 
 	switch (mode) {
 	switch (mode) {
 		case MODE_CUBEMAP: {
 		case MODE_CUBEMAP: {
-			return "Cubemap";
+			return "StreamCubemap";
 		} break;
 		} break;
 		case MODE_2D_ARRAY: {
 		case MODE_2D_ARRAY: {
-			return "Texture2DArray";
+			return "StreamTexture2DArray";
 		} break;
 		} break;
 		case MODE_CUBEMAP_ARRAY: {
 		case MODE_CUBEMAP_ARRAY: {
-			return "CubemapArray";
+			return "StreamCubemapArray";
+		} break;
+		case MODE_3D: {
+			return "StreamTexture3D";
 		} break;
 		} break;
 	}
 	}
 	ERR_FAIL_V(String());
 	ERR_FAIL_V(String());
@@ -110,6 +122,9 @@ String ResourceImporterLayeredTexture::get_resource_type() const {
 
 
 bool ResourceImporterLayeredTexture::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
 bool ResourceImporterLayeredTexture::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
 
 
+	if (p_option == "compress/lossy_quality" && p_options.has("compress/mode")) {
+		return int(p_options["compress/mode"]) == COMPRESS_LOSSY;
+	}
 	return true;
 	return true;
 }
 }
 
 
@@ -123,138 +138,109 @@ String ResourceImporterLayeredTexture::get_preset_name(int p_idx) const {
 
 
 void ResourceImporterLayeredTexture::get_import_options(List<ImportOption> *r_options, int p_preset) const {
 void ResourceImporterLayeredTexture::get_import_options(List<ImportOption> *r_options, int p_preset) const {
 
 
-	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless,Video RAM,Uncompressed", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1));
-	r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress/no_bptc_if_rgb"), false));
+	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless,Lossy,Video RAM,Uncompressed,Basis Universal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1));
+	r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "compress/lossy_quality", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.7));
+	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/hdr_compression", PROPERTY_HINT_ENUM, "Disabled,Opaque Only,Always"), 1));
+	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/bptc_ldr", PROPERTY_HINT_ENUM, "Disabled,Enabled,RGBA Only"), 0));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/channel_pack", PROPERTY_HINT_ENUM, "sRGB Friendly,Optimized"), 0));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/channel_pack", PROPERTY_HINT_ENUM, "sRGB Friendly,Optimized"), 0));
-	r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "flags/mipmaps"), true));
-	if (mode == MODE_2D_ARRAY) {
+	r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "mipmaps/generate"), true));
+	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "mipmaps/limit", PROPERTY_HINT_RANGE, "-1,256"), -1));
+
+	if (mode == MODE_2D_ARRAY || mode == MODE_3D) {
 		r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/horizontal", PROPERTY_HINT_RANGE, "1,256,1"), 8));
 		r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/horizontal", PROPERTY_HINT_RANGE, "1,256,1"), 8));
-	}
-	if (mode == MODE_2D_ARRAY || mode == MODE_CUBEMAP_ARRAY) {
 		r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/vertical", PROPERTY_HINT_RANGE, "1,256,1"), 8));
 		r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/vertical", PROPERTY_HINT_RANGE, "1,256,1"), 8));
 	}
 	}
-}
-
-void ResourceImporterLayeredTexture::_save_tex(const Vector<Ref<Image> > &p_images, const String &p_to_path, int p_compress_mode, Image::CompressMode p_vram_compression, bool p_mipmaps) {
-
-	FileAccess *f = FileAccess::open(p_to_path, FileAccess::WRITE);
-	f->store_8('G');
-	f->store_8('D');
-	switch (mode) {
-		case MODE_2D_ARRAY: f->store_8('A'); break;
-		case MODE_CUBEMAP: f->store_8('C'); break;
-		case MODE_CUBEMAP_ARRAY: f->store_8('X'); break;
-	}
-
-	f->store_8('T'); //godot streamable texture
-
-	f->store_32(p_images[0]->get_width());
-	f->store_32(p_images[0]->get_height());
-	f->store_32(p_images.size()); //depth
-	uint32_t flags = 0;
-	if (p_mipmaps) {
-		flags |= TEXTURE_FLAGS_MIPMAPS;
-	}
-	f->store_32(flags);
-	if (p_compress_mode != COMPRESS_VIDEO_RAM) {
-		//vram needs to do a first compression to tell what the format is, for the rest its ok
-		f->store_32(p_images[0]->get_format());
-		f->store_32(p_compress_mode); // 0 - lossless (PNG), 1 - vram, 2 - uncompressed
+	if (mode == MODE_CUBEMAP || mode == MODE_CUBEMAP_ARRAY) {
+		r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/arrangement", PROPERTY_HINT_ENUM, "1x6,2x3,3x2,6x1"), 1));
+		if (mode == MODE_CUBEMAP_ARRAY) {
+			r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/layout", PROPERTY_HINT_ENUM, "Horizontal,Vertical"), 1));
+			r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/amount", PROPERTY_HINT_RANGE, "1,1024,1,or_greater"), 1));
+		}
 	}
 	}
+}
 
 
-	if ((p_compress_mode == COMPRESS_LOSSLESS) && p_images[0]->get_format() > Image::FORMAT_RGBA8) {
-		p_compress_mode = COMPRESS_UNCOMPRESSED; //these can't go as lossy
-	}
+void ResourceImporterLayeredTexture::_save_tex(Vector<Ref<Image>> p_images, const String &p_to_path, int p_compress_mode, float p_lossy, Image::CompressMode p_vram_compression, Image::CompressSource p_csource, Image::UsedChannels used_channels, bool p_mipmaps, bool p_force_po2) {
 
 
 	for (int i = 0; i < p_images.size(); i++) {
 	for (int i = 0; i < p_images.size(); i++) {
 
 
-		switch (p_compress_mode) {
-			case COMPRESS_LOSSLESS: {
-
-				Ref<Image> image = p_images[i]->duplicate();
-				if (p_mipmaps) {
-					image->generate_mipmaps();
-				} else {
-					image->clear_mipmaps();
-				}
-
-				int mmc = image->get_mipmap_count() + 1;
-				f->store_32(mmc);
-
-				for (int j = 0; j < mmc; j++) {
-
-					if (j > 0) {
-						image->shrink_x2();
-					}
-
-					Vector<uint8_t> data = Image::lossless_packer(image);
-					int data_len = data.size();
-					f->store_32(data_len);
-
-					const uint8_t* r = data.ptr();
-					f->store_buffer(r.ptr(), data_len);
-				}
-
-			} break;
-			case COMPRESS_VIDEO_RAM: {
-
-				Ref<Image> image = p_images[i]->duplicate();
-				image->generate_mipmaps(false);
-
-				Image::CompressSource csource = Image::COMPRESS_SOURCE_LAYERED;
-				image->compress(p_vram_compression, csource, 0.7);
-
-				if (i == 0) {
-					//hack so we can properly tell the format
-					f->store_32(image->get_format());
-					f->store_32(p_compress_mode); // 0 - lossless (PNG), 1 - vram, 2 - uncompressed
-				}
-
-				Vector<uint8_t> data = image->get_data();
-				int dl = data.size();
-
-				const uint8_t* r = data.ptr();
-				f->store_buffer(r.ptr(), dl);
-			} break;
-			case COMPRESS_UNCOMPRESSED: {
-
-				Ref<Image> image = p_images[i]->duplicate();
+		if (p_force_po2) {
+			p_images.write[i]->resize_to_po2();
+		}
 
 
-				if (p_mipmaps) {
-					image->generate_mipmaps();
-				} else {
-					image->clear_mipmaps();
-				}
+		if (p_mipmaps) {
+			p_images.write[i]->generate_mipmaps();
+		} else {
+			p_images.write[i]->clear_mipmaps();
+		}
+	}
 
 
-				Vector<uint8_t> data = image->get_data();
-				int dl = data.size();
+	FileAccessRef f = FileAccess::open(p_to_path, FileAccess::WRITE);
+	f->store_8('G');
+	f->store_8('S');
+	f->store_8('T');
+	f->store_8('L');
 
 
-				const uint8_t* r = data.ptr();
+	f->store_32(StreamTextureLayered::FORMAT_VERSION);
+	f->store_32(p_images.size());
+	f->store_32(mode);
+	f->store_32(0); //dataformat
+	f->store_32(0); //mipmap limit
 
 
-				f->store_buffer(r.ptr(), dl);
+	//reserverd
+	f->store_32(0);
+	f->store_32(0);
+	f->store_32(0);
 
 
-			} break;
-		}
+	for (int i = 0; i < p_images.size(); i++) {
+		ResourceImporterTexture::save_to_stex_format(f, p_images[i], ResourceImporterTexture::CompressMode(p_compress_mode), used_channels, p_vram_compression, p_lossy);
 	}
 	}
 
 
-	memdelete(f);
+	f->close();
 }
 }
 
 
 Error ResourceImporterLayeredTexture::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
 Error ResourceImporterLayeredTexture::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
 
 
 	int compress_mode = p_options["compress/mode"];
 	int compress_mode = p_options["compress/mode"];
-	int no_bptc_if_rgb = p_options["compress/no_bptc_if_rgb"];
-	bool mipmaps = p_options["flags/mipmaps"];
+	float lossy = p_options["compress/lossy_quality"];
+	int hdr_compression = p_options["compress/hdr_compression"];
+	int bptc_ldr = p_options["compress/bptc_ldr"];
+	bool mipmaps = p_options["mipmaps/generate"];
+	//bool mipmap_limit = p_options["mipmaps/limit"];
+
 	int channel_pack = p_options["compress/channel_pack"];
 	int channel_pack = p_options["compress/channel_pack"];
 	int hslices = (p_options.has("slices/horizontal")) ? int(p_options["slices/horizontal"]) : 0;
 	int hslices = (p_options.has("slices/horizontal")) ? int(p_options["slices/horizontal"]) : 0;
 	int vslices = (p_options.has("slices/vertical")) ? int(p_options["slices/vertical"]) : 0;
 	int vslices = (p_options.has("slices/vertical")) ? int(p_options["slices/vertical"]) : 0;
+	int arrangement = (p_options.has("slices/arrangement")) ? int(p_options["slices/arrangement"]) : 0;
+	int layout = (p_options.has("slices/layout")) ? int(p_options["slices/layout"]) : 0;
+	int amount = (p_options.has("slices/amount")) ? int(p_options["slices/amount"]) : 0;
+
+	if (mode == MODE_CUBEMAP || mode == MODE_CUBEMAP_ARRAY) {
+		switch (arrangement) {
+			case CUBEMAP_FORMAT_1X6: {
+				hslices = 1;
+				vslices = 6;
+			} break;
+			case CUBEMAP_FORMAT_2X3: {
+				hslices = 2;
+				vslices = 3;
+			} break;
+			case CUBEMAP_FORMAT_3X2: {
+				hslices = 3;
+				vslices = 2;
+			} break;
+			case CUBEMAP_FORMAT_6X1: {
+				hslices = 6;
+				vslices = 1;
+			} break;
+		}
 
 
-	if (mode == MODE_CUBEMAP) {
-		hslices = 3;
-		vslices = 2;
-	} else if (mode == MODE_CUBEMAP_ARRAY) {
-		hslices = 3;
-		vslices *= 2; //put cubemaps vertically
+		if (mode == MODE_CUBEMAP_ARRAY) {
+			if (layout == 0) {
+				hslices *= amount;
+			} else {
+				vslices *= amount;
+			}
+		}
 	}
 	}
 
 
 	Ref<Image> image;
 	Ref<Image> image;
@@ -263,28 +249,40 @@ Error ResourceImporterLayeredTexture::import(const String &p_source_file, const
 	if (err != OK)
 	if (err != OK)
 		return err;
 		return err;
 
 
-	if (compress_mode == COMPRESS_VIDEO_RAM) {
-		mipmaps = true;
+	if (compress_mode == COMPRESS_BASIS_UNIVERSAL && image->get_format() >= Image::FORMAT_RF) {
+		//basis universal does not support float formats, fall back
+		compress_mode = COMPRESS_VRAM_COMPRESSED;
 	}
 	}
 
 
-	Vector<Ref<Image> > slices;
-
-	int slice_w = image->get_width() / hslices;
-	int slice_h = image->get_height() / vslices;
+	if (compress_mode == COMPRESS_VRAM_COMPRESSED) {
+		mipmaps = true;
+	}
 
 
 	//optimize
 	//optimize
-	if (compress_mode == COMPRESS_VIDEO_RAM) {
+	if (compress_mode == COMPRESS_VRAM_COMPRESSED) {
 		//if using video ram, optimize
 		//if using video ram, optimize
 		if (channel_pack == 0) {
 		if (channel_pack == 0) {
 			//remove alpha if not needed, so compression is more efficient
 			//remove alpha if not needed, so compression is more efficient
 			if (image->get_format() == Image::FORMAT_RGBA8 && !image->detect_alpha()) {
 			if (image->get_format() == Image::FORMAT_RGBA8 && !image->detect_alpha()) {
 				image->convert(Image::FORMAT_RGB8);
 				image->convert(Image::FORMAT_RGB8);
 			}
 			}
-		} else {
+		} else if (image->get_format() < Image::FORMAT_RGBA8) {
 			image->optimize_channels();
 			image->optimize_channels();
 		}
 		}
 	}
 	}
 
 
+	Image::CompressSource csource = Image::COMPRESS_SOURCE_GENERIC;
+	if (channel_pack == 0) {
+		csource = Image::COMPRESS_SOURCE_SRGB;
+	}
+
+	Image::UsedChannels used_channels = image->detect_used_channels(csource);
+
+	Vector<Ref<Image>> slices;
+
+	int slice_w = image->get_width() / hslices;
+	int slice_h = image->get_height() / vslices;
+
 	for (int i = 0; i < vslices; i++) {
 	for (int i = 0; i < vslices; i++) {
 		for (int j = 0; j < hslices; j++) {
 		for (int j = 0; j < hslices; j++) {
 			int x = slice_w * j;
 			int x = slice_w * j;
@@ -301,58 +299,82 @@ Error ResourceImporterLayeredTexture::import(const String &p_source_file, const
 	String extension = get_save_extension();
 	String extension = get_save_extension();
 	Array formats_imported;
 	Array formats_imported;
 
 
-	if (compress_mode == COMPRESS_VIDEO_RAM) {
+	if (compress_mode == COMPRESS_VRAM_COMPRESSED) {
 		//must import in all formats, in order of priority (so platform choses the best supported one. IE, etc2 over etc).
 		//must import in all formats, in order of priority (so platform choses the best supported one. IE, etc2 over etc).
 		//Android, GLES 2.x
 		//Android, GLES 2.x
 
 
 		bool ok_on_pc = false;
 		bool ok_on_pc = false;
-		bool encode_bptc = false;
+		bool is_hdr = (image->get_format() >= Image::FORMAT_RF && image->get_format() <= Image::FORMAT_RGBE9995);
+		bool is_ldr = (image->get_format() >= Image::FORMAT_L8 && image->get_format() <= Image::FORMAT_RGB565);
+		bool can_bptc = ProjectSettings::get_singleton()->get("rendering/vram_compression/import_bptc");
+		bool can_s3tc = ProjectSettings::get_singleton()->get("rendering/vram_compression/import_s3tc");
 
 
-		if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_bptc")) {
+		if (can_bptc) {
+			formats_imported.push_back("bptc"); //needs to be aded anyway
+		}
+		bool can_compress_hdr = hdr_compression > 0;
+
+		if (is_hdr && can_compress_hdr) {
+
+			if (used_channels == Image::USED_CHANNELS_LA || used_channels == Image::USED_CHANNELS_RGBA) {
+				//can compress hdr, but hdr with alpha is not compressible
 
 
-			encode_bptc = true;
+				if (hdr_compression == 2) {
+					//but user selected to compress hdr anyway, so force an alpha-less format.
+					if (image->get_format() == Image::FORMAT_RGBAF) {
+						for (int i = 0; i < slices.size(); i++) {
+							slices.write[i]->convert(Image::FORMAT_RGBF);
+						}
 
 
-			if (no_bptc_if_rgb) {
-				Image::UsedChannels channels = image->detect_used_channels();
-				if (channels != Image::USED_CHANNELS_LA && channels != Image::USED_CHANNELS_RGBA) {
-					encode_bptc = false;
+					} else if (image->get_format() == Image::FORMAT_RGBAH) {
+						for (int i = 0; i < slices.size(); i++) {
+							slices.write[i]->convert(Image::FORMAT_RGBH);
+						}
+					}
+				} else {
+					can_compress_hdr = false;
 				}
 				}
 			}
 			}
 
 
-			formats_imported.push_back("bptc");
-		}
+			if (can_compress_hdr) {
 
 
-		if (encode_bptc) {
+				if (!can_bptc) {
 
 
-			_save_tex(slices, p_save_path + ".bptc." + extension, compress_mode, Image::COMPRESS_BPTC, mipmaps);
-			r_platform_variants->push_back("bptc");
-			ok_on_pc = true;
+					//default to rgbe
+					if (image->get_format() != Image::FORMAT_RGBE9995) {
+						for (int i = 0; i < slices.size(); i++) {
+							slices.write[i]->convert(Image::FORMAT_RGBE9995);
+						}
+					}
+				}
+			} else {
+				can_bptc = false;
+			}
 		}
 		}
 
 
-		if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_s3tc")) {
+		if (is_ldr && can_bptc) {
+			if (bptc_ldr == 0 || (bptc_ldr == 1 && !(used_channels == Image::USED_CHANNELS_LA || used_channels == Image::USED_CHANNELS_RGBA))) {
+				can_bptc = false;
+			}
+		}
 
 
-			_save_tex(slices, p_save_path + ".s3tc." + extension, compress_mode, Image::COMPRESS_S3TC, mipmaps);
+		if (can_bptc || can_s3tc) {
+			_save_tex(slices, p_save_path + ".s3tc." + extension, compress_mode, lossy, can_bptc ? Image::COMPRESS_BPTC : Image::COMPRESS_S3TC, csource, used_channels, mipmaps, false);
 			r_platform_variants->push_back("s3tc");
 			r_platform_variants->push_back("s3tc");
-			ok_on_pc = true;
 			formats_imported.push_back("s3tc");
 			formats_imported.push_back("s3tc");
+			ok_on_pc = true;
 		}
 		}
 
 
 		if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc2")) {
 		if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc2")) {
 
 
-			_save_tex(slices, p_save_path + ".etc2." + extension, compress_mode, Image::COMPRESS_ETC2, mipmaps);
+			_save_tex(slices, p_save_path + ".etc2." + extension, compress_mode, lossy, Image::COMPRESS_ETC2, csource, used_channels, mipmaps, true);
 			r_platform_variants->push_back("etc2");
 			r_platform_variants->push_back("etc2");
 			formats_imported.push_back("etc2");
 			formats_imported.push_back("etc2");
 		}
 		}
 
 
-		if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc")) {
-			_save_tex(slices, p_save_path + ".etc." + extension, compress_mode, Image::COMPRESS_ETC, mipmaps);
-			r_platform_variants->push_back("etc");
-			formats_imported.push_back("etc");
-		}
-
 		if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_pvrtc")) {
 		if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_pvrtc")) {
 
 
-			_save_tex(slices, p_save_path + ".pvrtc." + extension, compress_mode, Image::COMPRESS_PVRTC4, mipmaps);
+			_save_tex(slices, p_save_path + ".etc2." + extension, compress_mode, lossy, Image::COMPRESS_ETC2, csource, used_channels, mipmaps, true);
 			r_platform_variants->push_back("pvrtc");
 			r_platform_variants->push_back("pvrtc");
 			formats_imported.push_back("pvrtc");
 			formats_imported.push_back("pvrtc");
 		}
 		}
@@ -362,12 +384,12 @@ Error ResourceImporterLayeredTexture::import(const String &p_source_file, const
 		}
 		}
 	} else {
 	} else {
 		//import normally
 		//import normally
-		_save_tex(slices, p_save_path + "." + extension, compress_mode, Image::COMPRESS_S3TC /*this is ignored */, mipmaps);
+		_save_tex(slices, p_save_path + "." + extension, compress_mode, lossy, Image::COMPRESS_S3TC /* IGNORED */, csource, used_channels, mipmaps, false);
 	}
 	}
 
 
 	if (r_metadata) {
 	if (r_metadata) {
 		Dictionary metadata;
 		Dictionary metadata;
-		metadata["vram_texture"] = compress_mode == COMPRESS_VIDEO_RAM;
+		metadata["vram_texture"] = compress_mode == COMPRESS_VRAM_COMPRESSED;
 		if (formats_imported.size()) {
 		if (formats_imported.size()) {
 			metadata["imported_formats"] = formats_imported;
 			metadata["imported_formats"] = formats_imported;
 		}
 		}
@@ -448,4 +470,3 @@ ResourceImporterLayeredTexture::ResourceImporterLayeredTexture() {
 
 
 ResourceImporterLayeredTexture::~ResourceImporterLayeredTexture() {
 ResourceImporterLayeredTexture::~ResourceImporterLayeredTexture() {
 }
 }
-#endif

+ 21 - 13
editor/import/resource_importer_layered_texture.h

@@ -28,7 +28,6 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /*************************************************************************/
 /*************************************************************************/
 
 
-#if 0
 /*************************************************************************/
 /*************************************************************************/
 /*  resource_importer_layered_texture.h                                  */
 /*  resource_importer_layered_texture.h                                  */
 /*************************************************************************/
 /*************************************************************************/
@@ -65,16 +64,24 @@
 #include "core/image.h"
 #include "core/image.h"
 #include "core/io/resource_importer.h"
 #include "core/io/resource_importer.h"
 
 
-
-class StreamTexture;
+class StreamTexture2D;
 
 
 class ResourceImporterLayeredTexture : public ResourceImporter {
 class ResourceImporterLayeredTexture : public ResourceImporter {
 	GDCLASS(ResourceImporterLayeredTexture, ResourceImporter);
 	GDCLASS(ResourceImporterLayeredTexture, ResourceImporter);
+
 public:
 public:
 	enum Mode {
 	enum Mode {
-		MODE_CUBEMAP,
 		MODE_2D_ARRAY,
 		MODE_2D_ARRAY,
-		MODE_CUBEMAP_ARRAY
+		MODE_CUBEMAP,
+		MODE_CUBEMAP_ARRAY,
+		MODE_3D,
+	};
+
+	enum CubemapFormat {
+		CUBEMAP_FORMAT_1X6,
+		CUBEMAP_FORMAT_2X3,
+		CUBEMAP_FORMAT_3X2,
+		CUBEMAP_FORMAT_6X1,
 	};
 	};
 
 
 	enum TextureFlags {
 	enum TextureFlags {
@@ -86,9 +93,9 @@ private:
 	static const char *compression_formats[];
 	static const char *compression_formats[];
 
 
 protected:
 protected:
-	static void _texture_reimport_srgb(const Ref<StreamTexture> &p_tex);
-	static void _texture_reimport_3d(const Ref<StreamTexture> &p_tex);
-	static void _texture_reimport_normal(const Ref<StreamTexture> &p_tex);
+	static void _texture_reimport_srgb(const Ref<StreamTexture2D> &p_tex);
+	static void _texture_reimport_3d(const Ref<StreamTexture2D> &p_tex);
+	static void _texture_reimport_normal(const Ref<StreamTexture2D> &p_tex);
 
 
 	static ResourceImporterLayeredTexture *singleton;
 	static ResourceImporterLayeredTexture *singleton;
 
 
@@ -102,8 +109,10 @@ public:
 
 
 	enum CompressMode {
 	enum CompressMode {
 		COMPRESS_LOSSLESS,
 		COMPRESS_LOSSLESS,
-		COMPRESS_VIDEO_RAM,
-		COMPRESS_UNCOMPRESSED
+		COMPRESS_LOSSY,
+		COMPRESS_VRAM_COMPRESSED,
+		COMPRESS_VRAM_UNCOMPRESSED,
+		COMPRESS_BASIS_UNIVERSAL
 	};
 	};
 
 
 	virtual int get_preset_count() const;
 	virtual int get_preset_count() const;
@@ -112,7 +121,7 @@ public:
 	virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const;
 	virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const;
 	virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const;
 	virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const;
 
 
-	void _save_tex(const Vector<Ref<Image> > &p_images, const String &p_to_path, int p_compress_mode, Image::CompressMode p_vram_compression, bool p_mipmaps);
+	void _save_tex(Vector<Ref<Image>> p_images, const String &p_to_path, int p_compress_mode, float p_lossy, Image::CompressMode p_vram_compression, Image::CompressSource p_csource, Image::UsedChannels used_channels, bool p_mipmaps, bool p_force_po2);
 
 
 	virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr);
 	virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr);
 
 
@@ -126,6 +135,5 @@ public:
 	ResourceImporterLayeredTexture();
 	ResourceImporterLayeredTexture();
 	~ResourceImporterLayeredTexture();
 	~ResourceImporterLayeredTexture();
 };
 };
-#endif // RESOURCE_IMPORTER_LAYERED_TEXTURE_H
 
 
-#endif
+#endif // RESOURCE_IMPORTER_LAYERED_TEXTURE_H

+ 4 - 3
editor/import/resource_importer_scene.cpp

@@ -354,7 +354,7 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh>
 
 
 		if (p_light_bake_mode != LIGHT_BAKE_DISABLED) {
 		if (p_light_bake_mode != LIGHT_BAKE_DISABLED) {
 
 
-			mi->set_flag(GeometryInstance3D::FLAG_USE_BAKED_LIGHT, true);
+			mi->set_gi_mode(GeometryInstance3D::GI_MODE_BAKED);
 		}
 		}
 	}
 	}
 
 
@@ -955,7 +955,7 @@ void ResourceImporterScene::_find_meshes(Node *p_node, Map<Ref<ArrayMesh>, Trans
 			Transform transform;
 			Transform transform;
 			while (s) {
 			while (s) {
 				transform = transform * s->get_transform();
 				transform = transform * s->get_transform();
-				s = s->get_parent_spatial();
+				s = Object::cast_to<Node3D>(s->get_parent());
 			}
 			}
 
 
 			meshes[mesh] = transform;
 			meshes[mesh] = transform;
@@ -1358,8 +1358,9 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
 		scene->set_script(Variant(root_script));
 		scene->set_script(Variant(root_script));
 	}
 	}
 
 
+	float root_scale = 1.0;
 	if (Object::cast_to<Node3D>(scene)) {
 	if (Object::cast_to<Node3D>(scene)) {
-		float root_scale = p_options["nodes/root_scale"];
+		root_scale = p_options["nodes/root_scale"];
 		Object::cast_to<Node3D>(scene)->scale(Vector3(root_scale, root_scale, root_scale));
 		Object::cast_to<Node3D>(scene)->scale(Vector3(root_scale, root_scale, root_scale));
 	}
 	}
 
 

+ 1 - 1
editor/import/resource_importer_shader_file.cpp

@@ -103,7 +103,7 @@ Error ResourceImporterShaderFile::import(const String &p_source_file, const Stri
 	Ref<RDShaderFile> shader_file;
 	Ref<RDShaderFile> shader_file;
 	shader_file.instance();
 	shader_file.instance();
 	String base_path = p_source_file.get_base_dir();
 	String base_path = p_source_file.get_base_dir();
-	err = shader_file->parse_versions_from_text(file_txt, _include_function, &base_path);
+	err = shader_file->parse_versions_from_text(file_txt, "", _include_function, &base_path);
 
 
 	if (err != OK) {
 	if (err != OK) {
 		if (!ShaderFileEditor::singleton->is_visible_in_tree()) {
 		if (!ShaderFileEditor::singleton->is_visible_in_tree()) {

+ 62 - 51
editor/import/resource_importer_texture.cpp

@@ -36,7 +36,7 @@
 #include "editor/editor_file_system.h"
 #include "editor/editor_file_system.h"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 
 
-void ResourceImporterTexture::_texture_reimport_roughness(const Ref<StreamTexture> &p_tex, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_channel) {
+void ResourceImporterTexture::_texture_reimport_roughness(const Ref<StreamTexture2D> &p_tex, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_channel) {
 
 
 	MutexLock lock(singleton->mutex);
 	MutexLock lock(singleton->mutex);
 
 
@@ -51,7 +51,7 @@ void ResourceImporterTexture::_texture_reimport_roughness(const Ref<StreamTextur
 	singleton->make_flags[path].normal_path_for_roughness = p_normal_path;
 	singleton->make_flags[path].normal_path_for_roughness = p_normal_path;
 }
 }
 
 
-void ResourceImporterTexture::_texture_reimport_3d(const Ref<StreamTexture> &p_tex) {
+void ResourceImporterTexture::_texture_reimport_3d(const Ref<StreamTexture2D> &p_tex) {
 
 
 	MutexLock lock(singleton->mutex);
 	MutexLock lock(singleton->mutex);
 
 
@@ -64,7 +64,7 @@ void ResourceImporterTexture::_texture_reimport_3d(const Ref<StreamTexture> &p_t
 	singleton->make_flags[path].flags |= MAKE_3D_FLAG;
 	singleton->make_flags[path].flags |= MAKE_3D_FLAG;
 }
 }
 
 
-void ResourceImporterTexture::_texture_reimport_normal(const Ref<StreamTexture> &p_tex) {
+void ResourceImporterTexture::_texture_reimport_normal(const Ref<StreamTexture2D> &p_tex) {
 
 
 	MutexLock lock(singleton->mutex);
 	MutexLock lock(singleton->mutex);
 
 
@@ -157,7 +157,7 @@ String ResourceImporterTexture::get_save_extension() const {
 
 
 String ResourceImporterTexture::get_resource_type() const {
 String ResourceImporterTexture::get_resource_type() const {
 
 
-	return "StreamTexture";
+	return "StreamTexture2D";
 }
 }
 
 
 bool ResourceImporterTexture::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
 bool ResourceImporterTexture::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
@@ -207,8 +207,8 @@ void ResourceImporterTexture::get_import_options(List<ImportOption> *r_options,
 
 
 	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless,Lossy,VRAM Compressed,VRAM Uncompressed,Basis Universal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), p_preset == PRESET_3D ? 2 : 0));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless,Lossy,VRAM Compressed,VRAM Uncompressed,Basis Universal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), p_preset == PRESET_3D ? 2 : 0));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "compress/lossy_quality", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.7));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "compress/lossy_quality", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.7));
-	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/hdr_mode", PROPERTY_HINT_ENUM, "Enabled,Force RGBE"), 0));
-	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/bptc_ldr", PROPERTY_HINT_ENUM, "Enabled,RGBA Only"), 0));
+	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/hdr_compression", PROPERTY_HINT_ENUM, "Disabled,Opaque Only,Always"), 1));
+	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/bptc_ldr", PROPERTY_HINT_ENUM, "Disabled,Enabled,RGBA Only"), 0));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/normal_map", PROPERTY_HINT_ENUM, "Detect,Enable,Disabled"), 0));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/normal_map", PROPERTY_HINT_ENUM, "Detect,Enable,Disabled"), 0));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/channel_pack", PROPERTY_HINT_ENUM, "sRGB Friendly,Optimized"), 0));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/channel_pack", PROPERTY_HINT_ENUM, "sRGB Friendly,Optimized"), 0));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/streamed"), false));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/streamed"), false));
@@ -225,12 +225,12 @@ void ResourceImporterTexture::get_import_options(List<ImportOption> *r_options,
 	r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "svg/scale", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 1.0));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "svg/scale", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 1.0));
 }
 }
 
 
-void ResourceImporterTexture::save_to_stex_format(FileAccess *f, const Ref<Image> &p_image, CompressMode p_compress_mode, Image::UsedChannels p_channels, Image::CompressMode p_compress_format, float p_lossy_quality, bool p_force_rgbe) {
+void ResourceImporterTexture::save_to_stex_format(FileAccess *f, const Ref<Image> &p_image, CompressMode p_compress_mode, Image::UsedChannels p_channels, Image::CompressMode p_compress_format, float p_lossy_quality) {
 
 
 	switch (p_compress_mode) {
 	switch (p_compress_mode) {
 		case COMPRESS_LOSSLESS: {
 		case COMPRESS_LOSSLESS: {
 
 
-			f->store_32(StreamTexture::DATA_FORMAT_LOSSLESS);
+			f->store_32(StreamTexture2D::DATA_FORMAT_LOSSLESS);
 			f->store_16(p_image->get_width());
 			f->store_16(p_image->get_width());
 			f->store_16(p_image->get_height());
 			f->store_16(p_image->get_height());
 			f->store_32(p_image->get_mipmap_count());
 			f->store_32(p_image->get_mipmap_count());
@@ -249,7 +249,7 @@ void ResourceImporterTexture::save_to_stex_format(FileAccess *f, const Ref<Image
 		} break;
 		} break;
 		case COMPRESS_LOSSY: {
 		case COMPRESS_LOSSY: {
 
 
-			f->store_32(StreamTexture::DATA_FORMAT_LOSSY);
+			f->store_32(StreamTexture2D::DATA_FORMAT_LOSSY);
 			f->store_16(p_image->get_width());
 			f->store_16(p_image->get_width());
 			f->store_16(p_image->get_height());
 			f->store_16(p_image->get_height());
 			f->store_32(p_image->get_mipmap_count());
 			f->store_32(p_image->get_mipmap_count());
@@ -269,13 +269,9 @@ void ResourceImporterTexture::save_to_stex_format(FileAccess *f, const Ref<Image
 
 
 			Ref<Image> image = p_image->duplicate();
 			Ref<Image> image = p_image->duplicate();
 
 
-			if (p_force_rgbe && image->get_format() >= Image::FORMAT_RF && image->get_format() < Image::FORMAT_RGBE9995) {
-				image->convert(Image::FORMAT_RGBE9995);
-			} else {
-				image->compress_from_channels(p_compress_format, p_channels, p_lossy_quality);
-			}
+			image->compress_from_channels(p_compress_format, p_channels, p_lossy_quality);
 
 
-			f->store_32(StreamTexture::DATA_FORMAT_IMAGE);
+			f->store_32(StreamTexture2D::DATA_FORMAT_IMAGE);
 			f->store_16(image->get_width());
 			f->store_16(image->get_width());
 			f->store_16(image->get_height());
 			f->store_16(image->get_height());
 			f->store_32(image->get_mipmap_count());
 			f->store_32(image->get_mipmap_count());
@@ -288,7 +284,7 @@ void ResourceImporterTexture::save_to_stex_format(FileAccess *f, const Ref<Image
 		} break;
 		} break;
 		case COMPRESS_VRAM_UNCOMPRESSED: {
 		case COMPRESS_VRAM_UNCOMPRESSED: {
 
 
-			f->store_32(StreamTexture::DATA_FORMAT_IMAGE);
+			f->store_32(StreamTexture2D::DATA_FORMAT_IMAGE);
 			f->store_16(p_image->get_width());
 			f->store_16(p_image->get_width());
 			f->store_16(p_image->get_height());
 			f->store_16(p_image->get_height());
 			f->store_32(p_image->get_mipmap_count());
 			f->store_32(p_image->get_mipmap_count());
@@ -303,7 +299,7 @@ void ResourceImporterTexture::save_to_stex_format(FileAccess *f, const Ref<Image
 		} break;
 		} break;
 		case COMPRESS_BASIS_UNIVERSAL: {
 		case COMPRESS_BASIS_UNIVERSAL: {
 
 
-			f->store_32(StreamTexture::DATA_FORMAT_BASIS_UNIVERSAL);
+			f->store_32(StreamTexture2D::DATA_FORMAT_BASIS_UNIVERSAL);
 			f->store_16(p_image->get_width());
 			f->store_16(p_image->get_width());
 			f->store_16(p_image->get_height());
 			f->store_16(p_image->get_height());
 			f->store_32(p_image->get_mipmap_count());
 			f->store_32(p_image->get_mipmap_count());
@@ -322,7 +318,7 @@ void ResourceImporterTexture::save_to_stex_format(FileAccess *f, const Ref<Image
 	}
 	}
 }
 }
 
 
-void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String &p_to_path, CompressMode p_compress_mode, float p_lossy_quality, Image::CompressMode p_vram_compression, bool p_mipmaps, bool p_streamable, bool p_detect_3d, bool p_detect_roughness, bool p_force_rgbe, bool p_detect_normal, bool p_force_normal, bool p_srgb_friendly, bool p_force_po2_for_compressed, uint32_t p_limit_mipmap, const Ref<Image> &p_normal, Image::RoughnessChannel p_roughness_channel) {
+void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String &p_to_path, CompressMode p_compress_mode, float p_lossy_quality, Image::CompressMode p_vram_compression, bool p_mipmaps, bool p_streamable, bool p_detect_3d, bool p_detect_roughness, bool p_detect_normal, bool p_force_normal, bool p_srgb_friendly, bool p_force_po2_for_compressed, uint32_t p_limit_mipmap, const Ref<Image> &p_normal, Image::RoughnessChannel p_roughness_channel) {
 
 
 	FileAccess *f = FileAccess::open(p_to_path, FileAccess::WRITE);
 	FileAccess *f = FileAccess::open(p_to_path, FileAccess::WRITE);
 	f->store_8('G');
 	f->store_8('G');
@@ -331,22 +327,22 @@ void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String
 	f->store_8('2'); //godot streamable texture 2D
 	f->store_8('2'); //godot streamable texture 2D
 
 
 	//format version
 	//format version
-	f->store_32(StreamTexture::FORMAT_VERSION);
+	f->store_32(StreamTexture2D::FORMAT_VERSION);
 	//texture may be resized later, so original size must be saved first
 	//texture may be resized later, so original size must be saved first
 	f->store_32(p_image->get_width());
 	f->store_32(p_image->get_width());
 	f->store_32(p_image->get_height());
 	f->store_32(p_image->get_height());
 
 
 	uint32_t flags = 0;
 	uint32_t flags = 0;
 	if (p_streamable)
 	if (p_streamable)
-		flags |= StreamTexture::FORMAT_BIT_STREAM;
+		flags |= StreamTexture2D::FORMAT_BIT_STREAM;
 	if (p_mipmaps)
 	if (p_mipmaps)
-		flags |= StreamTexture::FORMAT_BIT_HAS_MIPMAPS; //mipmaps bit
+		flags |= StreamTexture2D::FORMAT_BIT_HAS_MIPMAPS; //mipmaps bit
 	if (p_detect_3d)
 	if (p_detect_3d)
-		flags |= StreamTexture::FORMAT_BIT_DETECT_3D;
+		flags |= StreamTexture2D::FORMAT_BIT_DETECT_3D;
 	if (p_detect_roughness)
 	if (p_detect_roughness)
-		flags |= StreamTexture::FORMAT_BIT_DETECT_ROUGNESS;
+		flags |= StreamTexture2D::FORMAT_BIT_DETECT_ROUGNESS;
 	if (p_detect_normal)
 	if (p_detect_normal)
-		flags |= StreamTexture::FORMAT_BIT_DETECT_NORMAL;
+		flags |= StreamTexture2D::FORMAT_BIT_DETECT_NORMAL;
 
 
 	f->store_32(flags);
 	f->store_32(flags);
 	f->store_32(p_limit_mipmap);
 	f->store_32(p_limit_mipmap);
@@ -385,10 +381,6 @@ void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String
 		image->generate_mipmap_roughness(p_roughness_channel, p_normal);
 		image->generate_mipmap_roughness(p_roughness_channel, p_normal);
 	}
 	}
 
 
-	if (p_force_rgbe && image->get_format() >= Image::FORMAT_RF && image->get_format() < Image::FORMAT_RGBE9995) {
-		image->convert(Image::FORMAT_RGBE9995);
-	}
-
 	Image::CompressSource csource = Image::COMPRESS_SOURCE_GENERIC;
 	Image::CompressSource csource = Image::COMPRESS_SOURCE_GENERIC;
 	if (p_force_normal) {
 	if (p_force_normal) {
 		csource = Image::COMPRESS_SOURCE_NORMAL;
 		csource = Image::COMPRESS_SOURCE_NORMAL;
@@ -398,7 +390,7 @@ void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String
 
 
 	Image::UsedChannels used_channels = image->detect_used_channels(csource);
 	Image::UsedChannels used_channels = image->detect_used_channels(csource);
 
 
-	save_to_stex_format(f, image, p_compress_mode, used_channels, p_vram_compression, p_lossy_quality, p_force_rgbe);
+	save_to_stex_format(f, image, p_compress_mode, used_channels, p_vram_compression, p_lossy_quality);
 
 
 	memdelete(f);
 	memdelete(f);
 }
 }
@@ -418,7 +410,7 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
 	bool hdr_as_srgb = p_options["process/HDR_as_SRGB"];
 	bool hdr_as_srgb = p_options["process/HDR_as_SRGB"];
 	int normal = p_options["compress/normal_map"];
 	int normal = p_options["compress/normal_map"];
 	float scale = p_options["svg/scale"];
 	float scale = p_options["svg/scale"];
-	bool force_rgbe = int(p_options["compress/hdr_mode"]) == 1;
+	int hdr_compression = p_options["compress/hdr_compression"];
 	int bptc_ldr = p_options["compress/bptc_ldr"];
 	int bptc_ldr = p_options["compress/bptc_ldr"];
 	int roughness = p_options["roughness/mode"];
 	int roughness = p_options["roughness/mode"];
 	String normal_map = p_options["roughness/src_normal"];
 	String normal_map = p_options["roughness/src_normal"];
@@ -501,30 +493,49 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
 		bool can_s3tc = ProjectSettings::get_singleton()->get("rendering/vram_compression/import_s3tc");
 		bool can_s3tc = ProjectSettings::get_singleton()->get("rendering/vram_compression/import_s3tc");
 
 
 		if (can_bptc) {
 		if (can_bptc) {
-			Image::UsedChannels channels = image->detect_used_channels();
-			if (is_hdr) {
+			//add to the list anyway
+			formats_imported.push_back("bptc");
+		}
 
 
-				if (channels == Image::USED_CHANNELS_LA || channels == Image::USED_CHANNELS_RGBA) {
-					can_bptc = false;
+		bool can_compress_hdr = hdr_compression > 0;
+		bool has_alpha = image->detect_alpha() != Image::ALPHA_NONE;
+
+		if (is_hdr && can_compress_hdr) {
+
+			if (has_alpha) {
+				//can compress hdr, but hdr with alpha is not compressible
+				if (hdr_compression == 2) {
+					//but user selected to compress hdr anyway, so force an alpha-less format.
+					if (image->get_format() == Image::FORMAT_RGBAF) {
+						image->convert(Image::FORMAT_RGBF);
+					} else if (image->get_format() == Image::FORMAT_RGBAH) {
+						image->convert(Image::FORMAT_RGBH);
+					}
+				} else {
+					can_compress_hdr = false;
 				}
 				}
-			} else if (is_ldr) {
+			}
 
 
-				//handle "RGBA Only" setting
-				if (bptc_ldr == 1 && channels != Image::USED_CHANNELS_LA && channels != Image::USED_CHANNELS_RGBA) {
-					can_bptc = false;
+			if (can_compress_hdr) {
+				if (!can_bptc) {
+					//fallback to RGBE99995
+					if (image->get_format() != Image::FORMAT_RGBE9995) {
+						image->convert(Image::FORMAT_RGBE9995);
+					}
 				}
 				}
+			} else {
+				can_bptc = false;
 			}
 			}
-
-			formats_imported.push_back("bptc");
 		}
 		}
 
 
-		if (!can_bptc && is_hdr && !force_rgbe) {
-			//convert to ldr if this can't be stored hdr
-			image->convert(Image::FORMAT_RGBA8);
+		if (is_ldr && can_bptc) {
+			if (bptc_ldr == 0 || (bptc_ldr == 1 && !has_alpha)) {
+				can_bptc = false;
+			}
 		}
 		}
 
 
 		if (can_bptc || can_s3tc) {
 		if (can_bptc || can_s3tc) {
-			_save_stex(image, p_save_path + ".s3tc.stex", compress_mode, lossy, can_bptc ? Image::COMPRESS_BPTC : Image::COMPRESS_S3TC, mipmaps, stream, detect_3d, detect_roughness, force_rgbe, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel);
+			_save_stex(image, p_save_path + ".s3tc.stex", compress_mode, lossy, can_bptc ? Image::COMPRESS_BPTC : Image::COMPRESS_S3TC, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel);
 			r_platform_variants->push_back("s3tc");
 			r_platform_variants->push_back("s3tc");
 			formats_imported.push_back("s3tc");
 			formats_imported.push_back("s3tc");
 			ok_on_pc = true;
 			ok_on_pc = true;
@@ -532,20 +543,20 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
 
 
 		if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc2")) {
 		if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc2")) {
 
 
-			_save_stex(image, p_save_path + ".etc2.stex", compress_mode, lossy, Image::COMPRESS_ETC2, mipmaps, stream, detect_3d, detect_roughness, force_rgbe, detect_normal, force_normal, srgb_friendly_pack, true, mipmap_limit, normal_image, roughness_channel);
+			_save_stex(image, p_save_path + ".etc2.stex", compress_mode, lossy, Image::COMPRESS_ETC2, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, true, mipmap_limit, normal_image, roughness_channel);
 			r_platform_variants->push_back("etc2");
 			r_platform_variants->push_back("etc2");
 			formats_imported.push_back("etc2");
 			formats_imported.push_back("etc2");
 		}
 		}
 
 
 		if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc")) {
 		if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc")) {
-			_save_stex(image, p_save_path + ".etc.stex", compress_mode, lossy, Image::COMPRESS_ETC, mipmaps, stream, detect_3d, detect_roughness, force_rgbe, detect_normal, force_normal, srgb_friendly_pack, true, mipmap_limit, normal_image, roughness_channel);
+			_save_stex(image, p_save_path + ".etc.stex", compress_mode, lossy, Image::COMPRESS_ETC, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, true, mipmap_limit, normal_image, roughness_channel);
 			r_platform_variants->push_back("etc");
 			r_platform_variants->push_back("etc");
 			formats_imported.push_back("etc");
 			formats_imported.push_back("etc");
 		}
 		}
 
 
 		if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_pvrtc")) {
 		if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_pvrtc")) {
 
 
-			_save_stex(image, p_save_path + ".pvrtc.stex", compress_mode, lossy, Image::COMPRESS_PVRTC4, mipmaps, stream, detect_3d, detect_roughness, force_rgbe, detect_normal, force_normal, srgb_friendly_pack, true, mipmap_limit, normal_image, roughness_channel);
+			_save_stex(image, p_save_path + ".pvrtc.stex", compress_mode, lossy, Image::COMPRESS_PVRTC4, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, true, mipmap_limit, normal_image, roughness_channel);
 			r_platform_variants->push_back("pvrtc");
 			r_platform_variants->push_back("pvrtc");
 			formats_imported.push_back("pvrtc");
 			formats_imported.push_back("pvrtc");
 		}
 		}
@@ -555,7 +566,7 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
 		}
 		}
 	} else {
 	} else {
 		//import normally
 		//import normally
-		_save_stex(image, p_save_path + ".stex", compress_mode, lossy, Image::COMPRESS_S3TC /*this is ignored */, mipmaps, stream, detect_3d, detect_roughness, force_rgbe, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel);
+		_save_stex(image, p_save_path + ".stex", compress_mode, lossy, Image::COMPRESS_S3TC /*this is ignored */, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel);
 	}
 	}
 
 
 	if (r_metadata) {
 	if (r_metadata) {
@@ -635,9 +646,9 @@ ResourceImporterTexture *ResourceImporterTexture::singleton = nullptr;
 ResourceImporterTexture::ResourceImporterTexture() {
 ResourceImporterTexture::ResourceImporterTexture() {
 
 
 	singleton = this;
 	singleton = this;
-	StreamTexture::request_3d_callback = _texture_reimport_3d;
-	StreamTexture::request_roughness_callback = _texture_reimport_roughness;
-	StreamTexture::request_normal_callback = _texture_reimport_normal;
+	StreamTexture2D::request_3d_callback = _texture_reimport_3d;
+	StreamTexture2D::request_roughness_callback = _texture_reimport_roughness;
+	StreamTexture2D::request_normal_callback = _texture_reimport_normal;
 }
 }
 
 
 ResourceImporterTexture::~ResourceImporterTexture() {
 ResourceImporterTexture::~ResourceImporterTexture() {

+ 6 - 6
editor/import/resource_importer_texture.h

@@ -37,7 +37,7 @@
 #include "scene/resources/texture.h"
 #include "scene/resources/texture.h"
 #include "servers/rendering_server.h"
 #include "servers/rendering_server.h"
 
 
-class StreamTexture;
+class StreamTexture2D;
 
 
 class ResourceImporterTexture : public ResourceImporter {
 class ResourceImporterTexture : public ResourceImporter {
 	GDCLASS(ResourceImporterTexture, ResourceImporter);
 	GDCLASS(ResourceImporterTexture, ResourceImporter);
@@ -72,17 +72,17 @@ protected:
 
 
 	Map<StringName, MakeInfo> make_flags;
 	Map<StringName, MakeInfo> make_flags;
 
 
-	static void _texture_reimport_roughness(const Ref<StreamTexture> &p_tex, const String &p_normal_path, RenderingServer::TextureDetectRoughnessChannel p_channel);
-	static void _texture_reimport_3d(const Ref<StreamTexture> &p_tex);
-	static void _texture_reimport_normal(const Ref<StreamTexture> &p_tex);
+	static void _texture_reimport_roughness(const Ref<StreamTexture2D> &p_tex, const String &p_normal_path, RenderingServer::TextureDetectRoughnessChannel p_channel);
+	static void _texture_reimport_3d(const Ref<StreamTexture2D> &p_tex);
+	static void _texture_reimport_normal(const Ref<StreamTexture2D> &p_tex);
 
 
 	static ResourceImporterTexture *singleton;
 	static ResourceImporterTexture *singleton;
 	static const char *compression_formats[];
 	static const char *compression_formats[];
 
 
-	void _save_stex(const Ref<Image> &p_image, const String &p_to_path, CompressMode p_compress_mode, float p_lossy_quality, Image::CompressMode p_vram_compression, bool p_mipmaps, bool p_streamable, bool p_detect_3d, bool p_detect_srgb, bool p_force_rgbe, bool p_detect_normal, bool p_force_normal, bool p_srgb_friendly, bool p_force_po2_for_compressed, uint32_t p_limit_mipmap, const Ref<Image> &p_normal, Image::RoughnessChannel p_roughness_channel);
+	void _save_stex(const Ref<Image> &p_image, const String &p_to_path, CompressMode p_compress_mode, float p_lossy_quality, Image::CompressMode p_vram_compression, bool p_mipmaps, bool p_streamable, bool p_detect_3d, bool p_detect_srgb, bool p_detect_normal, bool p_force_normal, bool p_srgb_friendly, bool p_force_po2_for_compressed, uint32_t p_limit_mipmap, const Ref<Image> &p_normal, Image::RoughnessChannel p_roughness_channel);
 
 
 public:
 public:
-	void save_to_stex_format(FileAccess *f, const Ref<Image> &p_image, CompressMode p_compress_mode, Image::UsedChannels p_channels, Image::CompressMode p_compress_format, float p_lossy_quality, bool p_force_rgbe);
+	static void save_to_stex_format(FileAccess *f, const Ref<Image> &p_image, CompressMode p_compress_mode, Image::UsedChannels p_channels, Image::CompressMode p_compress_format, float p_lossy_quality);
 
 
 	static ResourceImporterTexture *get_singleton() { return singleton; }
 	static ResourceImporterTexture *get_singleton() { return singleton; }
 	virtual String get_importer_name() const;
 	virtual String get_importer_name() const;

+ 238 - 77
editor/node_3d_editor_gizmos.cpp

@@ -41,6 +41,7 @@
 #include "scene/3d/gi_probe.h"
 #include "scene/3d/gi_probe.h"
 #include "scene/3d/gpu_particles_3d.h"
 #include "scene/3d/gpu_particles_3d.h"
 #include "scene/3d/light_3d.h"
 #include "scene/3d/light_3d.h"
+#include "scene/3d/lightmap_probe.h"
 #include "scene/3d/listener_3d.h"
 #include "scene/3d/listener_3d.h"
 #include "scene/3d/mesh_instance_3d.h"
 #include "scene/3d/mesh_instance_3d.h"
 #include "scene/3d/navigation_region_3d.h"
 #include "scene/3d/navigation_region_3d.h"
@@ -3069,136 +3070,296 @@ void GIProbeGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
 }
 }
 
 
 ////
 ////
-#if 0
-BakedIndirectLightGizmoPlugin::BakedIndirectLightGizmoPlugin() {
-	Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/baked_indirect_light", Color(0.5, 0.6, 1));
 
 
-	create_material("baked_indirect_light_material", gizmo_color);
+BakedLightmapGizmoPlugin::BakedLightmapGizmoPlugin() {
+	Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/lightmap_lines", Color(0.5, 0.6, 1));
 
 
 	gizmo_color.a = 0.1;
 	gizmo_color.a = 0.1;
-	create_material("baked_indirect_light_internal_material", gizmo_color);
+	create_material("lightmap_lines", gizmo_color);
 
 
-	create_icon_material("baked_indirect_light_icon", Node3DEditor::get_singleton()->get_icon("GizmoBakedLightmap", "EditorIcons"));
-	create_handle_material("handles");
-}
+	Ref<StandardMaterial3D> mat = memnew(StandardMaterial3D);
+	mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+	mat->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
+	mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+	mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, false);
 
 
-String BakedIndirectLightGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const {
+	add_material("lightmap_probe_material", mat);
 
 
-	switch (p_idx) {
-		case 0: return "Extents X";
-		case 1: return "Extents Y";
-		case 2: return "Extents Z";
-	}
+	create_icon_material("baked_indirect_light_icon", Node3DEditor::get_singleton()->get_theme_icon("GizmoBakedLightmap", "EditorIcons"));
+}
+
+String BakedLightmapGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const {
 
 
 	return "";
 	return "";
 }
 }
-Variant BakedIndirectLightGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const {
+Variant BakedLightmapGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const {
 
 
-	BakedLightmap *baker = Object::cast_to<BakedLightmap>(p_gizmo->get_spatial_node());
-	return baker->get_extents();
+	return Variant();
+}
+void BakedLightmapGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) {
 }
 }
-void BakedIndirectLightGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point) {
 
 
+void BakedLightmapGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) {
+}
+
+bool BakedLightmapGizmoPlugin::has_gizmo(Node3D *p_spatial) {
+	return Object::cast_to<BakedLightmap>(p_spatial) != nullptr;
+}
+
+String BakedLightmapGizmoPlugin::get_name() const {
+	return "BakedLightmap";
+}
+
+int BakedLightmapGizmoPlugin::get_priority() const {
+	return -1;
+}
+
+void BakedLightmapGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
+
+	Ref<Material> icon = get_material("baked_indirect_light_icon", p_gizmo);
 	BakedLightmap *baker = Object::cast_to<BakedLightmap>(p_gizmo->get_spatial_node());
 	BakedLightmap *baker = Object::cast_to<BakedLightmap>(p_gizmo->get_spatial_node());
+	Ref<BakedLightmapData> data = baker->get_light_data();
 
 
-	Transform gt = baker->get_global_transform();
-	Transform gi = gt.affine_inverse();
+	p_gizmo->add_unscaled_billboard(icon, 0.05);
 
 
-	Vector3 extents = baker->get_extents();
+	if (data.is_null()) {
+		return;
+	}
 
 
-	Vector3 ray_from = p_camera->project_ray_origin(p_point);
-	Vector3 ray_dir = p_camera->project_ray_normal(p_point);
+	Ref<Material> material_lines = get_material("lightmap_lines", p_gizmo);
+	Ref<Material> material_probes = get_material("lightmap_probe_material", p_gizmo);
 
 
-	Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) };
+	p_gizmo->clear();
 
 
-	Vector3 axis;
-	axis[p_idx] = 1.0;
+	Vector<Vector3> lines;
+	Set<Vector2i> lines_found;
 
 
-	Vector3 ra, rb;
-	Geometry::get_closest_points_between_segments(Vector3(), axis * 16384, sg[0], sg[1], ra, rb);
-	float d = ra[p_idx];
-	if (Node3DEditor::get_singleton()->is_snap_enabled()) {
-		d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap());
+	Vector<Vector3> points = data->get_capture_points();
+	if (points.size() == 0) {
+		return;
+	}
+	Vector<Color> sh = data->get_capture_sh();
+	if (sh.size() != points.size() * 9) {
+		return;
 	}
 	}
 
 
-	if (d < 0.001)
-		d = 0.001;
+	Vector<int> tetrahedrons = data->get_capture_tetrahedra();
 
 
-	extents[p_idx] = d;
-	baker->set_extents(extents);
-}
+	for (int i = 0; i < tetrahedrons.size(); i += 4) {
 
 
-void BakedIndirectLightGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) {
+		for (int j = 0; j < 4; j++) {
+			for (int k = j + 1; k < 4; k++) {
 
 
-	BakedLightmap *baker = Object::cast_to<BakedLightmap>(p_gizmo->get_spatial_node());
+				Vector2i pair;
+				pair.x = tetrahedrons[i + j];
+				pair.y = tetrahedrons[i + k];
 
 
-	Vector3 restore = p_restore;
+				if (pair.y < pair.x) {
+					SWAP(pair.x, pair.y);
+				}
+				if (lines_found.has(pair)) {
+					continue;
+				}
+				lines_found.insert(pair);
+				lines.push_back(points[pair.x]);
+				lines.push_back(points[pair.y]);
+			}
+		}
+	}
 
 
-	if (p_cancel) {
-		baker->set_extents(restore);
-		return;
+	p_gizmo->add_lines(lines, material_lines);
+
+	int stack_count = 8;
+	int sector_count = 16;
+
+	float sector_step = 2 * Math_PI / sector_count;
+	float stack_step = Math_PI / stack_count;
+
+	Vector<Vector3> vertices;
+	Vector<Color> colors;
+	Vector<int> indices;
+	float radius = 0.3;
+
+	for (int p = 0; p < points.size(); p++) {
+
+		int vertex_base = vertices.size();
+		Vector3 sh_col[9];
+		for (int i = 0; i < 9; i++) {
+			sh_col[i].x = sh[p * 9 + i].r;
+			sh_col[i].y = sh[p * 9 + i].g;
+			sh_col[i].z = sh[p * 9 + i].b;
+		}
+
+		for (int i = 0; i <= stack_count; ++i) {
+			float stack_angle = Math_PI / 2 - i * stack_step; // starting from pi/2 to -pi/2
+			float xy = radius * Math::cos(stack_angle); // r * cos(u)
+			float z = radius * Math::sin(stack_angle); // r * sin(u)
+
+			// add (sector_count+1) vertices per stack
+			// the first and last vertices have same position and normal, but different tex coords
+			for (int j = 0; j <= sector_count; ++j) {
+				float sector_angle = j * sector_step; // starting from 0 to 2pi
+
+				// vertex position (x, y, z)
+				float x = xy * Math::cos(sector_angle); // r * cos(u) * cos(v)
+				float y = xy * Math::sin(sector_angle); // r * cos(u) * sin(v)
+
+				Vector3 n = Vector3(x, z, y);
+				vertices.push_back(points[p] + n);
+				n.normalize();
+
+				const float c1 = 0.429043;
+				const float c2 = 0.511664;
+				const float c3 = 0.743125;
+				const float c4 = 0.886227;
+				const float c5 = 0.247708;
+				Vector3 light = (c1 * sh_col[8] * (n.x * n.x - n.y * n.y) +
+								 c3 * sh_col[6] * n.z * n.z +
+								 c4 * sh_col[0] -
+								 c5 * sh_col[6] +
+								 2.0 * c1 * sh_col[4] * n.x * n.y +
+								 2.0 * c1 * sh_col[7] * n.x * n.z +
+								 2.0 * c1 * sh_col[5] * n.y * n.z +
+								 2.0 * c2 * sh_col[3] * n.x +
+								 2.0 * c2 * sh_col[1] * n.y +
+								 2.0 * c2 * sh_col[2] * n.z);
+
+				colors.push_back(Color(light.x, light.y, light.z, 1));
+			}
+		}
+
+		for (int i = 0; i < stack_count; ++i) {
+			int k1 = i * (sector_count + 1); // beginning of current stack
+			int k2 = k1 + sector_count + 1; // beginning of next stack
+
+			for (int j = 0; j < sector_count; ++j, ++k1, ++k2) {
+				// 2 triangles per sector excluding first and last stacks
+				// k1 => k2 => k1+1
+				if (i != 0) {
+					indices.push_back(vertex_base + k1);
+					indices.push_back(vertex_base + k2);
+					indices.push_back(vertex_base + k1 + 1);
+				}
+
+				// k1+1 => k2 => k2+1
+				if (i != (stack_count - 1)) {
+					indices.push_back(vertex_base + k1 + 1);
+					indices.push_back(vertex_base + k2);
+					indices.push_back(vertex_base + k2 + 1);
+				}
+			}
+		}
 	}
 	}
 
 
-	UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
-	ur->create_action(TTR("Change Probe Extents"));
-	ur->add_do_method(baker, "set_extents", baker->get_extents());
-	ur->add_undo_method(baker, "set_extents", restore);
-	ur->commit_action();
+	Array array;
+	array.resize(RS::ARRAY_MAX);
+	array[RS::ARRAY_VERTEX] = vertices;
+	array[RS::ARRAY_INDEX] = indices;
+	array[RS::ARRAY_COLOR] = colors;
+
+	Ref<ArrayMesh> mesh;
+	mesh.instance();
+	mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, array, Array(), Dictionary(), 0); //no compression
+	mesh->surface_set_material(0, material_probes);
+
+	p_gizmo->add_mesh(mesh);
 }
 }
+/////////
 
 
-bool BakedIndirectLightGizmoPlugin::has_gizmo(Spatial *p_spatial) {
-	return Object::cast_to<BakedLightmap>(p_spatial) != nullptr;
+LightmapProbeGizmoPlugin::LightmapProbeGizmoPlugin() {
+	Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/lightprobe_lines", Color(0.5, 0.6, 1));
+
+	gizmo_color.a = 0.3;
+	create_material("lightprobe_lines", gizmo_color);
 }
 }
 
 
-String BakedIndirectLightGizmoPlugin::get_name() const {
-	return "BakedLightmap";
+String LightmapProbeGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const {
+
+	return "";
 }
 }
+Variant LightmapProbeGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const {
 
 
-int BakedIndirectLightGizmoPlugin::get_priority() const {
-	return -1;
+	return Variant();
+}
+void LightmapProbeGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) {
 }
 }
 
 
-void BakedIndirectLightGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
+void LightmapProbeGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) {
+}
 
 
-	BakedLightmap *baker = Object::cast_to<BakedLightmap>(p_gizmo->get_spatial_node());
+bool LightmapProbeGizmoPlugin::has_gizmo(Node3D *p_spatial) {
+	return Object::cast_to<LightmapProbe>(p_spatial) != nullptr;
+}
 
 
-	Ref<Material> material = get_material("baked_indirect_light_material", p_gizmo);
-	Ref<Material> icon = get_material("baked_indirect_light_icon", p_gizmo);
-	Ref<Material> material_internal = get_material("baked_indirect_light_internal_material", p_gizmo);
+String LightmapProbeGizmoPlugin::get_name() const {
+	return "LightmapProbe";
+}
+
+int LightmapProbeGizmoPlugin::get_priority() const {
+	return -1;
+}
+
+void LightmapProbeGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
+
+	Ref<Material> material_lines = get_material("lightprobe_lines", p_gizmo);
 
 
 	p_gizmo->clear();
 	p_gizmo->clear();
 
 
 	Vector<Vector3> lines;
 	Vector<Vector3> lines;
-	Vector3 extents = baker->get_extents();
 
 
-	AABB aabb = AABB(-extents, extents * 2);
+	int stack_count = 8;
+	int sector_count = 16;
 
 
-	for (int i = 0; i < 12; i++) {
-		Vector3 a, b;
-		aabb.get_edge(i, a, b);
-		lines.push_back(a);
-		lines.push_back(b);
-	}
+	float sector_step = 2 * Math_PI / sector_count;
+	float stack_step = Math_PI / stack_count;
 
 
-	p_gizmo->add_lines(lines, material);
+	Vector<Vector3> vertices;
+	float radius = 0.2;
 
 
-	Vector<Vector3> handles;
+	for (int i = 0; i <= stack_count; ++i) {
+		float stack_angle = Math_PI / 2 - i * stack_step; // starting from pi/2 to -pi/2
+		float xy = radius * Math::cos(stack_angle); // r * cos(u)
+		float z = radius * Math::sin(stack_angle); // r * sin(u)
 
 
-	for (int i = 0; i < 3; i++) {
+		// add (sector_count+1) vertices per stack
+		// the first and last vertices have same position and normal, but different tex coords
+		for (int j = 0; j <= sector_count; ++j) {
+			float sector_angle = j * sector_step; // starting from 0 to 2pi
 
 
-		Vector3 ax;
-		ax[i] = aabb.position[i] + aabb.size[i];
-		handles.push_back(ax);
+			// vertex position (x, y, z)
+			float x = xy * Math::cos(sector_angle); // r * cos(u) * cos(v)
+			float y = xy * Math::sin(sector_angle); // r * cos(u) * sin(v)
+
+			Vector3 n = Vector3(x, z, y);
+			vertices.push_back(n);
+		}
 	}
 	}
 
 
-	if (p_gizmo->is_selected()) {
-		p_gizmo->add_solid_box(material_internal, aabb.get_size());
+	for (int i = 0; i < stack_count; ++i) {
+		int k1 = i * (sector_count + 1); // beginning of current stack
+		int k2 = k1 + sector_count + 1; // beginning of next stack
+
+		for (int j = 0; j < sector_count; ++j, ++k1, ++k2) {
+			// 2 triangles per sector excluding first and last stacks
+			// k1 => k2 => k1+1
+			if (i != 0) {
+				lines.push_back(vertices[k1]);
+				lines.push_back(vertices[k2]);
+				lines.push_back(vertices[k1]);
+				lines.push_back(vertices[k1 + 1]);
+			}
+
+			if (i != (stack_count - 1)) {
+				lines.push_back(vertices[k1 + 1]);
+				lines.push_back(vertices[k2]);
+				lines.push_back(vertices[k2]);
+				lines.push_back(vertices[k2 + 1]);
+			}
+		}
 	}
 	}
 
 
-	p_gizmo->add_unscaled_billboard(icon, 0.05);
-	p_gizmo->add_handles(handles, get_material("handles"));
+	p_gizmo->add_lines(lines, material_lines);
 }
 }
-#endif
 ////
 ////
 
 
 CollisionShape3DGizmoPlugin::CollisionShape3DGizmoPlugin() {
 CollisionShape3DGizmoPlugin::CollisionShape3DGizmoPlugin() {

+ 24 - 7
editor/node_3d_editor_gizmos.h

@@ -321,25 +321,42 @@ public:
 	GIProbeGizmoPlugin();
 	GIProbeGizmoPlugin();
 };
 };
 
 
-#if 0
-class BakedIndirectLightGizmoPlugin : public EditorNode3DGizmoPlugin {
+class BakedLightmapGizmoPlugin : public EditorNode3DGizmoPlugin {
 
 
-	GDCLASS(BakedIndirectLightGizmoPlugin, EditorNode3DGizmoPlugin);
+	GDCLASS(BakedLightmapGizmoPlugin, EditorNode3DGizmoPlugin);
 
 
 public:
 public:
-	bool has_gizmo(Spatial *p_spatial);
+	bool has_gizmo(Node3D *p_spatial);
+	String get_name() const;
+	int get_priority() const;
+	void redraw(EditorNode3DGizmo *p_gizmo);
+
+	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const;
+	Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const;
+	void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point);
+	void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false);
+
+	BakedLightmapGizmoPlugin();
+};
+
+class LightmapProbeGizmoPlugin : public EditorNode3DGizmoPlugin {
+
+	GDCLASS(LightmapProbeGizmoPlugin, EditorNode3DGizmoPlugin);
+
+public:
+	bool has_gizmo(Node3D *p_spatial);
 	String get_name() const;
 	String get_name() const;
 	int get_priority() const;
 	int get_priority() const;
 	void redraw(EditorNode3DGizmo *p_gizmo);
 	void redraw(EditorNode3DGizmo *p_gizmo);
 
 
 	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const;
 	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const;
 	Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const;
 	Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const;
-	void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point);
+	void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point);
 	void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false);
 	void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false);
 
 
-	BakedIndirectLightGizmoPlugin();
+	LightmapProbeGizmoPlugin();
 };
 };
-#endif
+
 class CollisionShape3DGizmoPlugin : public EditorNode3DGizmoPlugin {
 class CollisionShape3DGizmoPlugin : public EditorNode3DGizmoPlugin {
 
 
 	GDCLASS(CollisionShape3DGizmoPlugin, EditorNode3DGizmoPlugin);
 	GDCLASS(CollisionShape3DGizmoPlugin, EditorNode3DGizmoPlugin);

+ 43 - 26
editor/plugins/baked_lightmap_editor_plugin.cpp

@@ -28,23 +28,36 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /*************************************************************************/
 /*************************************************************************/
 
 
-#if 0
 #include "baked_lightmap_editor_plugin.h"
 #include "baked_lightmap_editor_plugin.h"
 
 
-void BakedLightmapEditorPlugin::_bake() {
+void BakedLightmapEditorPlugin::_bake_select_file(const String &p_file) {
 
 
 	if (lightmap) {
 	if (lightmap) {
 		BakedLightmap::BakeError err;
 		BakedLightmap::BakeError err;
 		if (get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root() == lightmap) {
 		if (get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root() == lightmap) {
-			err = lightmap->bake(lightmap);
+			err = lightmap->bake(lightmap, p_file, bake_func_step);
 		} else {
 		} else {
-			err = lightmap->bake(lightmap->get_parent());
+			err = lightmap->bake(lightmap->get_parent(), p_file, bake_func_step);
 		}
 		}
 
 
+		bake_func_end();
+
 		switch (err) {
 		switch (err) {
-			case BakedLightmap::BAKE_ERROR_NO_SAVE_PATH:
-				EditorNode::get_singleton()->show_warning(TTR("Can't determine a save path for lightmap images.\nSave your scene (for images to be saved in the same dir), or pick a save path from the BakedLightmap properties."));
-				break;
+			case BakedLightmap::BAKE_ERROR_NO_SAVE_PATH: {
+				String scene_path = lightmap->get_filename();
+				if (scene_path == String()) {
+					scene_path = lightmap->get_owner()->get_filename();
+				}
+				if (scene_path == String()) {
+					EditorNode::get_singleton()->show_warning(TTR("Can't determine a save path for lightmap images.\nSave your scene and try again."));
+					break;
+				}
+				scene_path = scene_path.get_basename() + ".lmbake";
+
+				file_dialog->set_current_path(scene_path);
+				file_dialog->popup_centered_ratio();
+
+			} break;
 			case BakedLightmap::BAKE_ERROR_NO_MESHES:
 			case BakedLightmap::BAKE_ERROR_NO_MESHES:
 				EditorNode::get_singleton()->show_warning(TTR("No meshes to bake. Make sure they contain an UV2 channel and that the 'Bake Light' flag is on."));
 				EditorNode::get_singleton()->show_warning(TTR("No meshes to bake. Make sure they contain an UV2 channel and that the 'Bake Light' flag is on."));
 				break;
 				break;
@@ -57,6 +70,11 @@ void BakedLightmapEditorPlugin::_bake() {
 	}
 	}
 }
 }
 
 
+void BakedLightmapEditorPlugin::_bake() {
+
+	_bake_select_file("");
+}
+
 void BakedLightmapEditorPlugin::edit(Object *p_object) {
 void BakedLightmapEditorPlugin::edit(Object *p_object) {
 
 
 	BakedLightmap *s = Object::cast_to<BakedLightmap>(p_object);
 	BakedLightmap *s = Object::cast_to<BakedLightmap>(p_object);
@@ -83,23 +101,20 @@ void BakedLightmapEditorPlugin::make_visible(bool p_visible) {
 
 
 EditorProgress *BakedLightmapEditorPlugin::tmp_progress = nullptr;
 EditorProgress *BakedLightmapEditorPlugin::tmp_progress = nullptr;
 
 
-void BakedLightmapEditorPlugin::bake_func_begin(int p_steps) {
+bool BakedLightmapEditorPlugin::bake_func_step(float p_progress, const String &p_description, void *, bool p_refresh) {
 
 
-	ERR_FAIL_COND(tmp_progress != nullptr);
-
-	tmp_progress = memnew(EditorProgress("bake_lightmaps", TTR("Bake Lightmaps"), p_steps, true));
-}
-
-bool BakedLightmapEditorPlugin::bake_func_step(int p_step, const String &p_description) {
-
-	ERR_FAIL_COND_V(tmp_progress == nullptr, false);
-	return tmp_progress->step(p_description, p_step, false);
+	if (!tmp_progress) {
+		tmp_progress = memnew(EditorProgress("bake_lightmaps", TTR("Bake Lightmaps"), 1000, false));
+		ERR_FAIL_COND_V(tmp_progress == nullptr, false);
+	}
+	return tmp_progress->step(p_description, p_progress * 1000, p_refresh);
 }
 }
 
 
 void BakedLightmapEditorPlugin::bake_func_end() {
 void BakedLightmapEditorPlugin::bake_func_end() {
-	ERR_FAIL_COND(tmp_progress == nullptr);
-	memdelete(tmp_progress);
-	tmp_progress = nullptr;
+	if (tmp_progress != nullptr) {
+		memdelete(tmp_progress);
+		tmp_progress = nullptr;
+	}
 }
 }
 
 
 void BakedLightmapEditorPlugin::_bind_methods() {
 void BakedLightmapEditorPlugin::_bind_methods() {
@@ -111,18 +126,20 @@ BakedLightmapEditorPlugin::BakedLightmapEditorPlugin(EditorNode *p_node) {
 
 
 	editor = p_node;
 	editor = p_node;
 	bake = memnew(ToolButton);
 	bake = memnew(ToolButton);
-	bake->set_icon(editor->get_gui_base()->get_icon("Bake", "EditorIcons"));
+	bake->set_icon(editor->get_gui_base()->get_theme_icon("Bake", "EditorIcons"));
 	bake->set_text(TTR("Bake Lightmaps"));
 	bake->set_text(TTR("Bake Lightmaps"));
 	bake->hide();
 	bake->hide();
-	bake->connect("pressed", this, "_bake");
+	bake->connect("pressed", Callable(this, "_bake"));
 	add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, bake);
 	add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, bake);
 	lightmap = nullptr;
 	lightmap = nullptr;
 
 
-	BakedLightmap::bake_begin_function = bake_func_begin;
-	BakedLightmap::bake_step_function = bake_func_step;
-	BakedLightmap::bake_end_function = bake_func_end;
+	file_dialog = memnew(EditorFileDialog);
+	file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
+	file_dialog->add_filter("*.lmbake ; LightMap Bake");
+	file_dialog->set_title(TTR("Select lightmap bake file:"));
+	file_dialog->connect("file_selected", callable_mp(this, &BakedLightmapEditorPlugin::_bake_select_file));
+	bake->add_child(file_dialog);
 }
 }
 
 
 BakedLightmapEditorPlugin::~BakedLightmapEditorPlugin() {
 BakedLightmapEditorPlugin::~BakedLightmapEditorPlugin() {
 }
 }
-#endif

+ 3 - 4
editor/plugins/baked_lightmap_editor_plugin.h

@@ -28,7 +28,6 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /*************************************************************************/
 /*************************************************************************/
 
 
-#if 0
 #ifndef BAKED_LIGHTMAP_EDITOR_PLUGIN_H
 #ifndef BAKED_LIGHTMAP_EDITOR_PLUGIN_H
 #define BAKED_LIGHTMAP_EDITOR_PLUGIN_H
 #define BAKED_LIGHTMAP_EDITOR_PLUGIN_H
 
 
@@ -46,11 +45,12 @@ class BakedLightmapEditorPlugin : public EditorPlugin {
 	ToolButton *bake;
 	ToolButton *bake;
 	EditorNode *editor;
 	EditorNode *editor;
 
 
+	EditorFileDialog *file_dialog;
 	static EditorProgress *tmp_progress;
 	static EditorProgress *tmp_progress;
-	static void bake_func_begin(int p_steps);
-	static bool bake_func_step(int p_step, const String &p_description);
+	static bool bake_func_step(float p_progress, const String &p_description, void *, bool p_refresh);
 	static void bake_func_end();
 	static void bake_func_end();
 
 
+	void _bake_select_file(const String &p_file);
 	void _bake();
 	void _bake();
 
 
 protected:
 protected:
@@ -67,5 +67,4 @@ public:
 	~BakedLightmapEditorPlugin();
 	~BakedLightmapEditorPlugin();
 };
 };
 
 
-#endif // BAKED_LIGHTMAP_EDITOR_PLUGIN_H
 #endif
 #endif

+ 1 - 1
editor/plugins/canvas_item_editor_plugin.cpp

@@ -6161,7 +6161,7 @@ bool CanvasItemEditorViewport::can_drop_data(const Point2 &p_point, const Varian
 						   type == "ViewportTexture" ||
 						   type == "ViewportTexture" ||
 						   type == "CurveTexture" ||
 						   type == "CurveTexture" ||
 						   type == "GradientTexture" ||
 						   type == "GradientTexture" ||
-						   type == "StreamTexture" ||
+						   type == "StreamTexture2D" ||
 						   type == "AtlasTexture" ||
 						   type == "AtlasTexture" ||
 						   type == "LargeTexture") {
 						   type == "LargeTexture") {
 					Ref<Texture2D> texture = Ref<Texture2D>(Object::cast_to<Texture2D>(*res));
 					Ref<Texture2D> texture = Ref<Texture2D>(Object::cast_to<Texture2D>(*res));

+ 2 - 1
editor/plugins/node_3d_editor_plugin.cpp

@@ -6008,7 +6008,8 @@ void Node3DEditor::_register_all_gizmos() {
 	add_gizmo_plugin(Ref<ReflectionProbeGizmoPlugin>(memnew(ReflectionProbeGizmoPlugin)));
 	add_gizmo_plugin(Ref<ReflectionProbeGizmoPlugin>(memnew(ReflectionProbeGizmoPlugin)));
 	add_gizmo_plugin(Ref<DecalGizmoPlugin>(memnew(DecalGizmoPlugin)));
 	add_gizmo_plugin(Ref<DecalGizmoPlugin>(memnew(DecalGizmoPlugin)));
 	add_gizmo_plugin(Ref<GIProbeGizmoPlugin>(memnew(GIProbeGizmoPlugin)));
 	add_gizmo_plugin(Ref<GIProbeGizmoPlugin>(memnew(GIProbeGizmoPlugin)));
-	//	add_gizmo_plugin(Ref<BakedIndirectLightGizmoPlugin>(memnew(BakedIndirectLightGizmoPlugin)));
+	add_gizmo_plugin(Ref<BakedLightmapGizmoPlugin>(memnew(BakedLightmapGizmoPlugin)));
+	add_gizmo_plugin(Ref<LightmapProbeGizmoPlugin>(memnew(LightmapProbeGizmoPlugin)));
 	add_gizmo_plugin(Ref<CollisionShape3DGizmoPlugin>(memnew(CollisionShape3DGizmoPlugin)));
 	add_gizmo_plugin(Ref<CollisionShape3DGizmoPlugin>(memnew(CollisionShape3DGizmoPlugin)));
 	add_gizmo_plugin(Ref<CollisionPolygon3DGizmoPlugin>(memnew(CollisionPolygon3DGizmoPlugin)));
 	add_gizmo_plugin(Ref<CollisionPolygon3DGizmoPlugin>(memnew(CollisionPolygon3DGizmoPlugin)));
 	add_gizmo_plugin(Ref<NavigationRegion3DGizmoPlugin>(memnew(NavigationRegion3DGizmoPlugin)));
 	add_gizmo_plugin(Ref<NavigationRegion3DGizmoPlugin>(memnew(NavigationRegion3DGizmoPlugin)));

+ 3 - 3
editor/plugins/texture_editor_plugin.cpp

@@ -84,8 +84,8 @@ void TextureEditor::_notification(int p_what) {
 		String format;
 		String format;
 		if (Object::cast_to<ImageTexture>(*texture)) {
 		if (Object::cast_to<ImageTexture>(*texture)) {
 			format = Image::get_format_name(Object::cast_to<ImageTexture>(*texture)->get_format());
 			format = Image::get_format_name(Object::cast_to<ImageTexture>(*texture)->get_format());
-		} else if (Object::cast_to<StreamTexture>(*texture)) {
-			format = Image::get_format_name(Object::cast_to<StreamTexture>(*texture)->get_format());
+		} else if (Object::cast_to<StreamTexture2D>(*texture)) {
+			format = Image::get_format_name(Object::cast_to<StreamTexture2D>(*texture)->get_format());
 		} else {
 		} else {
 			format = texture->get_class();
 			format = texture->get_class();
 		}
 		}
@@ -144,7 +144,7 @@ TextureEditor::~TextureEditor() {
 //
 //
 bool EditorInspectorPluginTexture::can_handle(Object *p_object) {
 bool EditorInspectorPluginTexture::can_handle(Object *p_object) {
 
 
-	return Object::cast_to<ImageTexture>(p_object) != nullptr || Object::cast_to<AtlasTexture>(p_object) != nullptr || Object::cast_to<StreamTexture>(p_object) != nullptr || Object::cast_to<LargeTexture>(p_object) != nullptr || Object::cast_to<AnimatedTexture>(p_object) != nullptr;
+	return Object::cast_to<ImageTexture>(p_object) != nullptr || Object::cast_to<AtlasTexture>(p_object) != nullptr || Object::cast_to<StreamTexture2D>(p_object) != nullptr || Object::cast_to<LargeTexture>(p_object) != nullptr || Object::cast_to<AnimatedTexture>(p_object) != nullptr;
 }
 }
 
 
 void EditorInspectorPluginTexture::parse_begin(Object *p_object) {
 void EditorInspectorPluginTexture::parse_begin(Object *p_object) {

+ 286 - 0
editor/plugins/texture_layered_editor_plugin.cpp

@@ -0,0 +1,286 @@
+/*************************************************************************/
+/*  texture_editor_plugin.cpp                                            */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "texture_layered_editor_plugin.h"
+
+#include "core/io/resource_loader.h"
+#include "core/project_settings.h"
+#include "editor/editor_settings.h"
+
+void TextureLayeredEditor::_gui_input(Ref<InputEvent> p_event) {
+	Ref<InputEventMouseMotion> mm = p_event;
+	if (mm.is_valid() && mm->get_button_mask() & BUTTON_MASK_LEFT) {
+		y_rot += -mm->get_relative().x * 0.01;
+		x_rot += mm->get_relative().y * 0.01;
+		_update_material();
+	}
+}
+
+void TextureLayeredEditor::_texture_rect_draw() {
+	texture_rect->draw_rect(Rect2(Point2(), texture_rect->get_size()), Color(1, 1, 1, 1));
+}
+
+void TextureLayeredEditor::_notification(int p_what) {
+
+	if (p_what == NOTIFICATION_READY) {
+
+		//get_scene()->connect("node_removed",this,"_node_removed");
+	}
+	if (p_what == NOTIFICATION_RESIZED) {
+		_texture_rect_update_area();
+	}
+
+	if (p_what == NOTIFICATION_DRAW) {
+
+		Ref<Texture2D> checkerboard = get_theme_icon("Checkerboard", "EditorIcons");
+		Size2 size = get_size();
+
+		draw_texture_rect(checkerboard, Rect2(Point2(), size), true);
+	}
+}
+
+void TextureLayeredEditor::_changed_callback(Object *p_changed, const char *p_prop) {
+
+	if (!is_visible())
+		return;
+	update();
+}
+
+void TextureLayeredEditor::_update_material() {
+
+	materials[0]->set_shader_param("layer", layer->get_value());
+	materials[2]->set_shader_param("layer", layer->get_value());
+	materials[texture->get_layered_type()]->set_shader_param("tex", texture->get_rid());
+
+	Vector3 v(1, 1, 1);
+	v.normalize();
+
+	Basis b;
+	b.rotate(Vector3(1, 0, 0), x_rot);
+	b.rotate(Vector3(0, 1, 0), y_rot);
+
+	materials[1]->set_shader_param("normal", v);
+	materials[1]->set_shader_param("rot", b);
+	materials[2]->set_shader_param("normal", v);
+	materials[2]->set_shader_param("rot", b);
+
+	String format = Image::get_format_name(texture->get_format());
+
+	String text;
+	if (texture->get_layered_type() == TextureLayered::LAYERED_TYPE_2D_ARRAY) {
+		text = itos(texture->get_width()) + "x" + itos(texture->get_height()) + " (x " + itos(texture->get_layers()) + ")" + format;
+	} else if (texture->get_layered_type() == TextureLayered::LAYERED_TYPE_CUBEMAP) {
+		text = itos(texture->get_width()) + "x" + itos(texture->get_height()) + " " + format;
+	} else if (texture->get_layered_type() == TextureLayered::LAYERED_TYPE_CUBEMAP_ARRAY) {
+		text = itos(texture->get_width()) + "x" + itos(texture->get_height()) + " (x " + itos(texture->get_layers() / 6) + ")" + format;
+	}
+
+	info->set_text(text);
+}
+
+void TextureLayeredEditor::_make_shaders() {
+	String shader_2d_array = ""
+							 "shader_type canvas_item;\n"
+							 "uniform sampler2DArray tex;\n"
+							 "uniform float layer;\n"
+							 "void fragment() {\n"
+							 "  COLOR = textureLod(tex,vec3(UV,layer),0.0);\n"
+							 "}";
+
+	shaders[0].instance();
+	shaders[0]->set_code(shader_2d_array);
+
+	String shader_cube = ""
+						 "shader_type canvas_item;\n"
+						 "uniform samplerCube tex;\n"
+						 "uniform vec3 normal;\n"
+						 "uniform mat3 rot;\n"
+						 "void fragment() {\n"
+						 "  vec3 n = rot * normalize(vec3(normal.xy*(UV * 2.0 - 1.0),normal.z));\n"
+						 "  COLOR = textureLod(tex,n,0.0);\n"
+						 "}";
+
+	shaders[1].instance();
+	shaders[1]->set_code(shader_cube);
+
+	String shader_cube_array = ""
+							   "shader_type canvas_item;\n"
+							   "uniform samplerCubeArray tex;\n"
+							   "uniform vec3 normal;\n"
+							   "uniform mat3 rot;\n"
+							   "uniform float layer;\n"
+							   "void fragment() {\n"
+							   "  vec3 n = rot * normalize(vec3(normal.xy*(UV * 2.0 - 1.0),normal.z));\n"
+							   "  COLOR = textureLod(tex,vec4(n,layer),0.0);\n"
+							   "}";
+
+	shaders[2].instance();
+	shaders[2]->set_code(shader_cube_array);
+
+	for (int i = 0; i < 3; i++) {
+		materials[i].instance();
+		materials[i]->set_shader(shaders[i]);
+	}
+}
+
+void TextureLayeredEditor::_texture_rect_update_area() {
+
+	Size2 size = get_size();
+	int tex_width = texture->get_width() * size.height / texture->get_height();
+	int tex_height = size.height;
+
+	if (tex_width > size.width) {
+		tex_width = size.width;
+		tex_height = texture->get_height() * tex_width / texture->get_width();
+	}
+
+	// Prevent the texture from being unpreviewable after the rescale, so that we can still see something
+	if (tex_height <= 0)
+		tex_height = 1;
+	if (tex_width <= 0)
+		tex_width = 1;
+
+	int ofs_x = (size.width - tex_width) / 2;
+	int ofs_y = (size.height - tex_height) / 2;
+
+	texture_rect->set_position(Vector2(ofs_x, ofs_y));
+	texture_rect->set_size(Vector2(tex_width, tex_height));
+}
+
+void TextureLayeredEditor::edit(Ref<TextureLayered> p_texture) {
+
+	if (!texture.is_null())
+		texture->remove_change_receptor(this);
+
+	texture = p_texture;
+
+	if (!texture.is_null()) {
+
+		if (shaders[0].is_null()) {
+			_make_shaders();
+		}
+
+		texture->add_change_receptor(this);
+		update();
+		texture_rect->set_material(materials[texture->get_layered_type()]);
+		setting = true;
+		if (texture->get_layered_type() == TextureLayered::LAYERED_TYPE_2D_ARRAY) {
+			layer->set_max(texture->get_layers() - 1);
+			layer->set_value(0);
+			layer->show();
+		} else if (texture->get_layered_type() == TextureLayered::LAYERED_TYPE_CUBEMAP_ARRAY) {
+			layer->set_max(texture->get_layers() / 6 - 1);
+			layer->set_value(0);
+			layer->show();
+		} else {
+			layer->hide();
+		}
+		x_rot = 0;
+		y_rot = 0;
+		_update_material();
+		setting = false;
+		_texture_rect_update_area();
+	} else {
+		hide();
+	}
+}
+
+void TextureLayeredEditor::_bind_methods() {
+
+	ClassDB::bind_method(D_METHOD("_gui_input"), &TextureLayeredEditor::_gui_input);
+	ClassDB::bind_method(D_METHOD("_layer_changed"), &TextureLayeredEditor::_layer_changed);
+}
+
+TextureLayeredEditor::TextureLayeredEditor() {
+
+	set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED);
+	set_custom_minimum_size(Size2(1, 150));
+	texture_rect = memnew(Control);
+	texture_rect->connect("draw", callable_mp(this, &TextureLayeredEditor::_texture_rect_draw));
+	texture_rect->set_mouse_filter(MOUSE_FILTER_IGNORE);
+	add_child(texture_rect);
+
+	layer = memnew(SpinBox);
+	layer->set_step(1);
+	layer->set_max(100);
+	add_child(layer);
+	layer->set_anchor(MARGIN_RIGHT, 1);
+	layer->set_anchor(MARGIN_LEFT, 1);
+	layer->set_h_grow_direction(GROW_DIRECTION_BEGIN);
+	layer->set_modulate(Color(1, 1, 1, 0.8));
+	info = memnew(Label);
+	add_child(info);
+	info->set_anchor(MARGIN_RIGHT, 1);
+	info->set_anchor(MARGIN_LEFT, 1);
+	info->set_anchor(MARGIN_BOTTOM, 1);
+	info->set_anchor(MARGIN_TOP, 1);
+	info->set_h_grow_direction(GROW_DIRECTION_BEGIN);
+	info->set_v_grow_direction(GROW_DIRECTION_BEGIN);
+	info->add_theme_color_override("font_color", Color(1, 1, 1, 1));
+	info->add_theme_color_override("font_color_shadow", Color(0, 0, 0, 0.5));
+	info->add_theme_color_override("font_color_shadow", Color(0, 0, 0, 0.5));
+	info->add_theme_constant_override("shadow_as_outline", 1);
+	info->add_theme_constant_override("shadow_offset_x", 2);
+	info->add_theme_constant_override("shadow_offset_y", 2);
+
+	setting = false;
+	layer->connect("value_changed", Callable(this, "_layer_changed"));
+}
+
+TextureLayeredEditor::~TextureLayeredEditor() {
+	if (!texture.is_null()) {
+		texture->remove_change_receptor(this);
+	}
+}
+//
+bool EditorInspectorPluginLayeredTexture::can_handle(Object *p_object) {
+
+	return Object::cast_to<TextureLayered>(p_object) != nullptr;
+}
+
+void EditorInspectorPluginLayeredTexture::parse_begin(Object *p_object) {
+
+	TextureLayered *texture = Object::cast_to<TextureLayered>(p_object);
+	if (!texture) {
+		return;
+	}
+	Ref<TextureLayered> m(texture);
+
+	TextureLayeredEditor *editor = memnew(TextureLayeredEditor);
+	editor->edit(m);
+	add_custom_control(editor);
+}
+
+TextureLayeredEditorPlugin::TextureLayeredEditorPlugin(EditorNode *p_node) {
+
+	Ref<EditorInspectorPluginLayeredTexture> plugin;
+	plugin.instance();
+	add_inspector_plugin(plugin);
+}

+ 95 - 0
editor/plugins/texture_layered_editor_plugin.h

@@ -0,0 +1,95 @@
+/*************************************************************************/
+/*  texture_editor_plugin.h                                              */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef TEXTURE_LAYERED_EDITOR_PLUGIN_H
+#define TEXTURE_LAYERED_EDITOR_PLUGIN_H
+
+#include "editor/editor_node.h"
+#include "editor/editor_plugin.h"
+#include "scene/resources/shader.h"
+#include "scene/resources/texture.h"
+class TextureLayeredEditor : public Control {
+
+	GDCLASS(TextureLayeredEditor, Control);
+
+	SpinBox *layer;
+	Label *info;
+	Ref<TextureLayered> texture;
+
+	Ref<Shader> shaders[3];
+	Ref<ShaderMaterial> materials[3];
+
+	float x_rot = 0;
+	float y_rot = 0;
+	Control *texture_rect;
+
+	void _make_shaders();
+
+	void _update_material();
+	bool setting;
+	void _layer_changed(double) {
+		if (!setting)
+			_update_material();
+	}
+
+	void _texture_rect_update_area();
+	void _texture_rect_draw();
+
+protected:
+	void _notification(int p_what);
+	void _gui_input(Ref<InputEvent> p_event);
+	void _changed_callback(Object *p_changed, const char *p_prop);
+	static void _bind_methods();
+
+public:
+	void edit(Ref<TextureLayered> p_texture);
+	TextureLayeredEditor();
+	~TextureLayeredEditor();
+};
+
+class EditorInspectorPluginLayeredTexture : public EditorInspectorPlugin {
+	GDCLASS(EditorInspectorPluginLayeredTexture, EditorInspectorPlugin);
+
+public:
+	virtual bool can_handle(Object *p_object);
+	virtual void parse_begin(Object *p_object);
+};
+
+class TextureLayeredEditorPlugin : public EditorPlugin {
+
+	GDCLASS(TextureLayeredEditorPlugin, EditorPlugin);
+
+public:
+	virtual String get_name() const { return "TextureLayered"; }
+
+	TextureLayeredEditorPlugin(EditorNode *p_node);
+};
+
+#endif // TEXTURE_EDITOR_PLUGIN_H

+ 64 - 0
gles_builders.py

@@ -740,5 +740,69 @@ def build_rd_headers(target, source, env):
         build_rd_header(str(x))
         build_rd_header(str(x))
 
 
 
 
+class RAWHeaderStruct:
+    def __init__(self):
+        self.code = ""
+
+
+def include_file_in_raw_header(filename, header_data, depth):
+    fs = open(filename, "r")
+    line = fs.readline()
+    text = ""
+
+    while line:
+
+        while line.find("#include ") != -1:
+            includeline = line.replace("#include ", "").strip()[1:-1]
+
+            import os.path
+
+            included_file = os.path.relpath(os.path.dirname(filename) + "/" + includeline)
+            include_file_in_raw_header(included_file, header_data, depth + 1)
+
+            line = fs.readline()
+
+        header_data.code += line
+        line = fs.readline()
+
+    fs.close()
+
+
+def build_raw_header(filename):
+    header_data = RAWHeaderStruct()
+    include_file_in_raw_header(filename, header_data, 0)
+
+    out_file = filename + ".gen.h"
+    fd = open(out_file, "w")
+
+    enum_constants = []
+
+    fd.write("/* WARNING, THIS FILE WAS GENERATED, DO NOT EDIT */\n")
+
+    out_file_base = out_file.replace(".glsl.gen.h", "_shader_glsl")
+    out_file_base = out_file_base[out_file_base.rfind("/") + 1 :]
+    out_file_base = out_file_base[out_file_base.rfind("\\") + 1 :]
+    out_file_ifdef = out_file_base.replace(".", "_").upper()
+    fd.write("#ifndef " + out_file_ifdef + "_RAW_H\n")
+    fd.write("#define " + out_file_ifdef + "_RAW_H\n")
+    fd.write("\n")
+    fd.write("static const char " + out_file_base + "[] = {\n")
+    for c in header_data.code:
+        fd.write(str(ord(c)) + ",")
+    fd.write("\t\t0};\n\n")
+    fd.write("#endif\n")
+    fd.close()
+
+
+def build_rd_headers(target, source, env):
+    for x in source:
+        build_rd_header(str(x))
+
+
+def build_raw_headers(target, source, env):
+    for x in source:
+        build_raw_header(str(x))
+
+
 if __name__ == "__main__":
 if __name__ == "__main__":
     subprocess_main(globals())
     subprocess_main(globals())

+ 5 - 3
main/main.cpp

@@ -2063,9 +2063,11 @@ bool Main::start() {
 		}
 		}
 
 
 		if (project_manager || editor) {
 		if (project_manager || editor) {
-			// Hide console window if requested (Windows-only).
-			bool hide_console = EditorSettings::get_singleton()->get_setting("interface/editor/hide_console_window");
-			DisplayServer::get_singleton()->console_set_visible(!hide_console);
+			if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CONSOLE_WINDOW)) {
+				// Hide console window if requested (Windows-only).
+				bool hide_console = EditorSettings::get_singleton()->get_setting("interface/editor/hide_console_window");
+				DisplayServer::get_singleton()->console_set_visible(!hide_console);
+			}
 
 
 			// Load SSL Certificates from Editor Settings (or builtin)
 			// Load SSL Certificates from Editor Settings (or builtin)
 			Crypto::load_default_certificates(EditorSettings::get_singleton()->get_setting("network/ssl/editor_ssl_certificates").operator String());
 			Crypto::load_default_certificates(EditorSettings::get_singleton()->get_setting("network/ssl/editor_ssl_certificates").operator String());

+ 51 - 2
main/tests/test_math.cpp

@@ -32,8 +32,10 @@
 
 
 #include "core/math/basis.h"
 #include "core/math/basis.h"
 #include "core/math/camera_matrix.h"
 #include "core/math/camera_matrix.h"
+#include "core/math/delaunay_3d.h"
 #include "core/math/math_funcs.h"
 #include "core/math/math_funcs.h"
 #include "core/math/transform.h"
 #include "core/math/transform.h"
+#include "core/method_ptrcall.h"
 #include "core/os/file_access.h"
 #include "core/os/file_access.h"
 #include "core/os/keyboard.h"
 #include "core/os/keyboard.h"
 #include "core/os/os.h"
 #include "core/os/os.h"
@@ -45,8 +47,6 @@
 #include "scene/resources/texture.h"
 #include "scene/resources/texture.h"
 #include "servers/rendering/shader_language.h"
 #include "servers/rendering/shader_language.h"
 
 
-#include "core/method_ptrcall.h"
-
 namespace TestMath {
 namespace TestMath {
 
 
 class GetClassAndNamespace {
 class GetClassAndNamespace {
@@ -414,6 +414,55 @@ uint32_t ihash3(uint32_t a) {
 
 
 MainLoop *test() {
 MainLoop *test() {
 
 
+	{
+		Vector<Vector3> points;
+		points.push_back(Vector3(0, 0, 0));
+		points.push_back(Vector3(0, 0, 1));
+		points.push_back(Vector3(0, 1, 0));
+		points.push_back(Vector3(0, 1, 1));
+		points.push_back(Vector3(1, 1, 0));
+		points.push_back(Vector3(1, 0, 0));
+		points.push_back(Vector3(1, 0, 1));
+		points.push_back(Vector3(1, 1, 1));
+
+		for (int i = 0; i < 800; i++) {
+			points.push_back(Vector3(Math::randf() * 2.0 - 1.0, Math::randf() * 2.0 - 1.0, Math::randf() * 2.0 - 1.0) * Vector3(25, 30, 33));
+		}
+
+		Vector<Delaunay3D::OutputSimplex> os = Delaunay3D::tetrahedralize(points);
+		print_line("simplices in the end: " + itos(os.size()));
+		for (int i = 0; i < os.size(); i++) {
+			print_line("Simplex " + itos(i) + ": ");
+			print_line(points[os[i].points[0]]);
+			print_line(points[os[i].points[1]]);
+			print_line(points[os[i].points[2]]);
+			print_line(points[os[i].points[3]]);
+		}
+
+		{
+			FileAccessRef f = FileAccess::open("res://bsp.obj", FileAccess::WRITE);
+			for (int i = 0; i < os.size(); i++) {
+				f->store_line("o Simplex" + itos(i));
+				for (int j = 0; j < 4; j++) {
+					f->store_line(vformat("v %f %f %f", points[os[i].points[j]].x, points[os[i].points[j]].y, points[os[i].points[j]].z));
+				}
+				static const int face_order[4][3] = {
+					{ 1, 2, 3 },
+					{ 1, 3, 4 },
+					{ 1, 2, 4 },
+					{ 2, 3, 4 }
+				};
+
+				for (int j = 0; j < 4; j++) {
+					f->store_line(vformat("f %d %d %d", 4 * i + face_order[j][0], 4 * i + face_order[j][1], 4 * i + face_order[j][2]));
+				}
+			}
+			f->close();
+		}
+
+		return nullptr;
+	}
+
 	{
 	{
 		float r = 1;
 		float r = 1;
 		float g = 0.5;
 		float g = 0.5;

+ 119 - 0
modules/denoise/SCsub

@@ -0,0 +1,119 @@
+#!/usr/bin/env python
+
+import resource_to_cpp
+from platform_methods import run_in_subprocess
+
+Import("env")
+Import("env_modules")
+
+env_oidn = env_modules.Clone()
+
+# Thirdparty source files
+thirdparty_dir = "#thirdparty/oidn/"
+thirdparty_sources = [
+    "core/api.cpp",
+    "core/device.cpp",
+    "core/filter.cpp",
+    "core/network.cpp",
+    "core/autoencoder.cpp",
+    "core/transfer_function.cpp",
+    "weights/rtlightmap_hdr.cpp",
+    "mkl-dnn/src/common/batch_normalization.cpp",
+    "mkl-dnn/src/common/concat.cpp",
+    "mkl-dnn/src/common/convolution.cpp",
+    "mkl-dnn/src/common/convolution_pd.cpp",
+    "mkl-dnn/src/common/deconvolution.cpp",
+    "mkl-dnn/src/common/eltwise.cpp",
+    "mkl-dnn/src/common/engine.cpp",
+    "mkl-dnn/src/common/inner_product.cpp",
+    "mkl-dnn/src/common/inner_product_pd.cpp",
+    "mkl-dnn/src/common/lrn.cpp",
+    "mkl-dnn/src/common/memory.cpp",
+    "mkl-dnn/src/common/memory_desc_wrapper.cpp",
+    "mkl-dnn/src/common/mkldnn_debug.cpp",
+    "mkl-dnn/src/common/mkldnn_debug_autogenerated.cpp",
+    "mkl-dnn/src/common/pooling.cpp",
+    "mkl-dnn/src/common/primitive.cpp",
+    "mkl-dnn/src/common/primitive_attr.cpp",
+    "mkl-dnn/src/common/primitive_desc.cpp",
+    "mkl-dnn/src/common/primitive_exec_types.cpp",
+    "mkl-dnn/src/common/primitive_iterator.cpp",
+    "mkl-dnn/src/common/query.cpp",
+    "mkl-dnn/src/common/reorder.cpp",
+    "mkl-dnn/src/common/rnn.cpp",
+    "mkl-dnn/src/common/scratchpad.cpp",
+    "mkl-dnn/src/common/shuffle.cpp",
+    "mkl-dnn/src/common/softmax.cpp",
+    "mkl-dnn/src/common/stream.cpp",
+    "mkl-dnn/src/common/sum.cpp",
+    "mkl-dnn/src/common/utils.cpp",
+    "mkl-dnn/src/common/verbose.cpp",
+    "mkl-dnn/src/cpu/cpu_barrier.cpp",
+    "mkl-dnn/src/cpu/cpu_concat.cpp",
+    "mkl-dnn/src/cpu/cpu_engine.cpp",
+    "mkl-dnn/src/cpu/cpu_memory.cpp",
+    "mkl-dnn/src/cpu/cpu_reducer.cpp",
+    "mkl-dnn/src/cpu/cpu_reorder.cpp",
+    "mkl-dnn/src/cpu/cpu_sum.cpp",
+    "mkl-dnn/src/cpu/jit_avx2_conv_kernel_f32.cpp",
+    "mkl-dnn/src/cpu/jit_avx2_convolution.cpp",
+    "mkl-dnn/src/cpu/jit_avx512_common_conv_kernel.cpp",
+    "mkl-dnn/src/cpu/jit_avx512_common_conv_winograd_kernel_f32.cpp",
+    "mkl-dnn/src/cpu/jit_avx512_common_convolution.cpp",
+    "mkl-dnn/src/cpu/jit_avx512_common_convolution_winograd.cpp",
+    "mkl-dnn/src/cpu/jit_avx512_core_fp32_wino_conv_2x3.cpp",
+    "mkl-dnn/src/cpu/jit_avx512_core_fp32_wino_conv_4x3.cpp",
+    "mkl-dnn/src/cpu/jit_avx512_core_fp32_wino_conv_4x3_kernel.cpp",
+    "mkl-dnn/src/cpu/jit_sse42_conv_kernel_f32.cpp",
+    "mkl-dnn/src/cpu/jit_sse42_convolution.cpp",
+    "mkl-dnn/src/cpu/jit_transpose_src_utils.cpp",
+    "mkl-dnn/src/cpu/jit_uni_eltwise.cpp",
+    "mkl-dnn/src/cpu/jit_uni_pool_kernel_f32.cpp",
+    "mkl-dnn/src/cpu/jit_uni_pooling.cpp",
+    "mkl-dnn/src/cpu/jit_uni_reorder.cpp",
+    "mkl-dnn/src/cpu/jit_uni_reorder_utils.cpp",
+    "mkl-dnn/src/cpu/jit_utils/jit_utils.cpp",
+    "mkl-dnn/src/cpu/jit_utils/jitprofiling/jitprofiling.c",
+    "common/platform.cpp",
+    "common/thread.cpp",
+    "common/tensor.cpp",
+]
+thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+thirdparty_include_dirs = [
+    "",
+    "include",
+    "mkl-dnn/include",
+    "mkl-dnn/src",
+    "mkl-dnn/src/common",
+    "mkl-dnn/src/cpu/xbyak",
+    "mkl-dnn/src/cpu",
+]
+thirdparty_include_dirs = [thirdparty_dir + file for file in thirdparty_include_dirs]
+
+
+env_oidn.Prepend(CPPPATH=thirdparty_include_dirs)
+env_oidn.Append(
+    CPPDEFINES=[
+        "MKLDNN_THR=MKLDNN_THR_SEQ",
+        "OIDN_STATIC_LIB",
+        "__STDC_CONSTANT_MACROS",
+        "__STDC_LIMIT_MACROS",
+        "DISABLE_VERBOSE",
+        "MKLDNN_ENABLE_CONCURRENT_EXEC",
+        "NDEBUG",
+    ]
+)
+
+env_thirdparty = env_oidn.Clone()
+env_thirdparty.disable_warnings()
+env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+
+weights_in_path = thirdparty_dir + "weights/rtlightmap_hdr.tza"
+weights_out_path = thirdparty_dir + "weights/rtlightmap_hdr.cpp"
+
+env_thirdparty.Depends(weights_out_path, weights_in_path)
+env_thirdparty.CommandNoCache(weights_out_path, weights_in_path, resource_to_cpp.tza_to_cpp)
+
+env_oidn.add_source_files(env.modules_sources, "denoise_wrapper.cpp")
+env_modules.add_source_files(env.modules_sources, ["register_types.cpp", "lightmap_denoiser.cpp"])

+ 6 - 0
modules/denoise/config.py

@@ -0,0 +1,6 @@
+def can_build(env, platform):
+    return env["tools"]
+
+
+def configure(env):
+    pass

+ 34 - 0
modules/denoise/denoise_wrapper.cpp

@@ -0,0 +1,34 @@
+#include "denoise_wrapper.h"
+#include "thirdparty/oidn/include/OpenImageDenoise/oidn.h"
+#include <stdio.h>
+
+void *oidn_denoiser_init() {
+	OIDNDeviceImpl *device = oidnNewDevice(OIDN_DEVICE_TYPE_CPU);
+	oidnCommitDevice(device);
+	return device;
+}
+
+bool oidn_denoise(void *deviceptr, float *p_floats, int p_width, int p_height) {
+	OIDNDeviceImpl *device = (OIDNDeviceImpl *)deviceptr;
+	OIDNFilter filter = oidnNewFilter(device, "RTLightmap");
+	oidnSetSharedFilterImage(filter, "color", (void *)p_floats, OIDN_FORMAT_FLOAT3, p_width, p_height, 0, 0, 0);
+	oidnSetSharedFilterImage(filter, "output", (void *)p_floats, OIDN_FORMAT_FLOAT3, p_width, p_height, 0, 0, 0);
+	oidnSetFilter1b(filter, "hdr", true);
+	//oidnSetFilter1f(filter, "hdrScale", 1.0f);
+	oidnCommitFilter(filter);
+	oidnExecuteFilter(filter);
+
+	const char *msg;
+	bool success = true;
+	if (oidnGetDeviceError(device, &msg) != OIDN_ERROR_NONE) {
+		printf("LightmapDenoiser: %s\n", msg);
+		success = false;
+	}
+
+	oidnReleaseFilter(filter);
+	return success;
+}
+
+void oidn_denoiser_finish(void *device) {
+	oidnReleaseDevice((OIDNDeviceImpl *)device);
+}

+ 8 - 0
modules/denoise/denoise_wrapper.h

@@ -0,0 +1,8 @@
+#ifndef DENOISE_WRAPPER_H
+#define DENOISE_WRAPPER_H
+
+void *oidn_denoiser_init();
+bool oidn_denoise(void *device, float *p_floats, int p_width, int p_height);
+void oidn_denoiser_finish(void *device);
+
+#endif // DENOISE_WRAPPER_H

+ 63 - 0
modules/denoise/lightmap_denoiser.cpp

@@ -0,0 +1,63 @@
+/*************************************************************************/
+/*  lightmap_denoiser.cpp                                                */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "lightmap_denoiser.h"
+#include "denoise_wrapper.h"
+
+LightmapDenoiser *LightmapDenoiserOIDN::create_oidn_denoiser() {
+	return memnew(LightmapDenoiserOIDN);
+}
+
+void LightmapDenoiserOIDN::make_default_denoiser() {
+	create_function = create_oidn_denoiser;
+}
+
+Ref<Image> LightmapDenoiserOIDN::denoise_image(const Ref<Image> &p_image) {
+
+	Ref<Image> img = p_image->duplicate();
+
+	img->convert(Image::FORMAT_RGBF);
+
+	Vector<uint8_t> data = img->get_data();
+	if (!oidn_denoise(device, (float *)data.ptrw(), img->get_width(), img->get_height())) {
+		return p_image;
+	}
+
+	img->create(img->get_width(), img->get_height(), false, img->get_format(), data);
+	return img;
+}
+
+LightmapDenoiserOIDN::LightmapDenoiserOIDN() {
+	device = oidn_denoiser_init();
+}
+
+LightmapDenoiserOIDN::~LightmapDenoiserOIDN() {
+	oidn_denoiser_finish(device);
+}

+ 57 - 0
modules/denoise/lightmap_denoiser.h

@@ -0,0 +1,57 @@
+/*************************************************************************/
+/*  lightmap_denoiser.h                                                  */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef LIGHTMAP_DENOISER_H
+#define LIGHTMAP_DENOISER_H
+
+#include "core/object.h"
+#include "scene/3d/lightmapper.h"
+
+struct OIDNDeviceImpl;
+
+class LightmapDenoiserOIDN : public LightmapDenoiser {
+
+	GDCLASS(LightmapDenoiserOIDN, LightmapDenoiser);
+
+protected:
+	void *device = nullptr;
+
+public:
+	static LightmapDenoiser *create_oidn_denoiser();
+
+	Ref<Image> denoise_image(const Ref<Image> &p_image);
+
+	static void make_default_denoiser();
+
+	LightmapDenoiserOIDN();
+	~LightmapDenoiserOIDN();
+};
+
+#endif // LIGHTMAP_DENOISER_H

+ 41 - 0
modules/denoise/register_types.cpp

@@ -0,0 +1,41 @@
+/*************************************************************************/
+/*  register_types.cpp                                                   */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "register_types.h"
+#include "core/engine.h"
+#include "lightmap_denoiser.h"
+
+void register_denoise_types() {
+
+	LightmapDenoiserOIDN::make_default_denoiser();
+}
+
+void unregister_denoise_types() {
+}

+ 32 - 0
modules/denoise/register_types.h

@@ -0,0 +1,32 @@
+/*************************************************************************/
+/*  register_types.h                                                     */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+void register_denoise_types();
+void unregister_denoise_types();

+ 70 - 0
modules/denoise/resource_to_cpp.py

@@ -0,0 +1,70 @@
+#!/usr/bin/env python
+
+## ======================================================================== ##
+## Copyright 2009-2019 Intel Corporation                                    ##
+##                                                                          ##
+## Licensed under the Apache License, Version 2.0 (the "License");          ##
+## you may not use this file except in compliance with the License.         ##
+## You may obtain a copy of the License at                                  ##
+##                                                                          ##
+##     http://www.apache.org/licenses/LICENSE-2.0                           ##
+##                                                                          ##
+## Unless required by applicable law or agreed to in writing, software      ##
+## distributed under the License is distributed on an "AS IS" BASIS,        ##
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ##
+## See the License for the specific language governing permissions and      ##
+## limitations under the License.                                           ##
+## ======================================================================== ##
+
+import os
+import sys
+import argparse
+from array import array
+
+# Generates a C++ file from the specified binary resource file
+def generate(in_path, out_path):
+
+    namespace = "oidn::weights"
+    scopes = namespace.split("::")
+
+    file_name = os.path.basename(in_path)
+    var_name = os.path.splitext(file_name)[0]
+
+    with open(in_path, "rb") as in_file, open(out_path, "w") as out_file:
+        # Header
+        out_file.write("// Generated from: %s\n" % file_name)
+        out_file.write("#include <cstddef>\n\n")
+
+        # Open the namespaces
+        for s in scopes:
+            out_file.write("namespace %s {\n" % s)
+        if scopes:
+            out_file.write("\n")
+
+        # Read the file
+        in_data = array("B", in_file.read())
+
+        # Write the size
+        out_file.write("//const size_t %s_size = %d;\n\n" % (var_name, len(in_data)))
+
+        # Write the data
+        out_file.write("unsigned char %s[] = {" % var_name)
+        for i in range(len(in_data)):
+            c = in_data[i]
+            if i > 0:
+                out_file.write(",")
+            if (i + 1) % 20 == 1:
+                out_file.write("\n")
+            out_file.write("%d" % c)
+        out_file.write("\n};\n")
+
+        # Close the namespaces
+        if scopes:
+            out_file.write("\n")
+        for scope in reversed(scopes):
+            out_file.write("} // namespace %s\n" % scope)
+
+
+def tza_to_cpp(target, source, env):
+    for x in zip(source, target):
+        generate(str(x[0]), str(x[1]))

+ 12 - 0
modules/lightmapper_rd/SCsub

@@ -0,0 +1,12 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_lightmapper_rd = env_modules.Clone()
+env_lightmapper_rd.GLSL_HEADER("lm_raster.glsl")
+env_lightmapper_rd.GLSL_HEADER("lm_compute.glsl")
+env_lightmapper_rd.GLSL_HEADER("lm_blendseams.glsl")
+
+# Godot source files
+env_lightmapper_rd.add_source_files(env.modules_sources, "*.cpp")

+ 6 - 0
modules/lightmapper_rd/config.py

@@ -0,0 +1,6 @@
+def can_build(env, platform):
+    return True
+
+
+def configure(env):
+    pass

+ 1754 - 0
modules/lightmapper_rd/lightmapper_rd.cpp

@@ -0,0 +1,1754 @@
+#include "lightmapper_rd.h"
+#include "core/math/geometry.h"
+#include "core/project_settings.h"
+#include "lm_blendseams.glsl.gen.h"
+#include "lm_compute.glsl.gen.h"
+#include "lm_raster.glsl.gen.h"
+#include "servers/rendering/rendering_device_binds.h"
+
+//uncomment this if you want to see textures from all the process saved
+//#define DEBUG_TEXTURES
+
+void LightmapperRD::add_mesh(const MeshData &p_mesh) {
+	ERR_FAIL_COND(p_mesh.albedo_on_uv2.is_null() || p_mesh.albedo_on_uv2->empty());
+	ERR_FAIL_COND(p_mesh.emission_on_uv2.is_null() || p_mesh.emission_on_uv2->empty());
+	ERR_FAIL_COND(p_mesh.albedo_on_uv2->get_width() != p_mesh.emission_on_uv2->get_width());
+	ERR_FAIL_COND(p_mesh.albedo_on_uv2->get_height() != p_mesh.emission_on_uv2->get_height());
+	ERR_FAIL_COND(p_mesh.points.size() == 0);
+	MeshInstance mi;
+	mi.data = p_mesh;
+	mesh_instances.push_back(mi);
+}
+
+void LightmapperRD::add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_angular_distance) {
+	Light l;
+	l.type = LIGHT_TYPE_DIRECTIONAL;
+	l.direction[0] = p_direction.x;
+	l.direction[1] = p_direction.y;
+	l.direction[2] = p_direction.z;
+	l.color[0] = p_color.r;
+	l.color[1] = p_color.g;
+	l.color[2] = p_color.b;
+	l.energy = p_energy;
+	l.static_bake = p_static;
+	l.size = p_angular_distance;
+	lights.push_back(l);
+}
+void LightmapperRD::add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size) {
+	Light l;
+	l.type = LIGHT_TYPE_OMNI;
+	l.position[0] = p_position.x;
+	l.position[1] = p_position.y;
+	l.position[2] = p_position.z;
+	l.range = p_range;
+	l.attenuation = p_attenuation;
+	l.color[0] = p_color.r;
+	l.color[1] = p_color.g;
+	l.color[2] = p_color.b;
+	l.energy = p_energy;
+	l.static_bake = p_static;
+	l.size = p_size;
+	lights.push_back(l);
+}
+void LightmapperRD::add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size) {
+
+	Light l;
+	l.type = LIGHT_TYPE_SPOT;
+	l.position[0] = p_position.x;
+	l.position[1] = p_position.y;
+	l.position[2] = p_position.z;
+	l.direction[0] = p_direction.x;
+	l.direction[1] = p_direction.y;
+	l.direction[2] = p_direction.z;
+	l.range = p_range;
+	l.attenuation = p_attenuation;
+	l.spot_angle = Math::deg2rad(p_spot_angle);
+	l.spot_attenuation = p_spot_attenuation;
+	l.color[0] = p_color.r;
+	l.color[1] = p_color.g;
+	l.color[2] = p_color.b;
+	l.energy = p_energy;
+	l.static_bake = p_static;
+	l.size = p_size;
+	lights.push_back(l);
+}
+
+void LightmapperRD::add_probe(const Vector3 &p_position) {
+	Probe probe;
+	probe.position[0] = p_position.x;
+	probe.position[1] = p_position.y;
+	probe.position[2] = p_position.z;
+	probe.position[3] = 0;
+	probe_positions.push_back(probe);
+}
+
+void LightmapperRD::_plot_triangle_into_triangle_index_list(int p_size, const Vector3i &p_ofs, const AABB &p_bounds, const Vector3 p_points[3], uint32_t p_triangle_index, LocalVector<TriangleSort> &triangles, uint32_t p_grid_size) {
+
+	int half_size = p_size / 2;
+
+	for (int i = 0; i < 8; i++) {
+
+		AABB aabb = p_bounds;
+		aabb.size *= 0.5;
+		Vector3i n = p_ofs;
+
+		if (i & 1) {
+			aabb.position.x += aabb.size.x;
+			n.x += half_size;
+		}
+		if (i & 2) {
+			aabb.position.y += aabb.size.y;
+			n.y += half_size;
+		}
+		if (i & 4) {
+			aabb.position.z += aabb.size.z;
+			n.z += half_size;
+		}
+
+		{
+			Vector3 qsize = aabb.size * 0.5; //quarter size, for fast aabb test
+
+			if (!Geometry::triangle_box_overlap(aabb.position + qsize, qsize, p_points)) {
+				//does not fit in child, go on
+				continue;
+			}
+		}
+
+		if (half_size == 1) {
+			//got to the end
+			TriangleSort ts;
+			ts.cell_index = n.x + (n.y * p_grid_size) + (n.z * p_grid_size * p_grid_size);
+			ts.triangle_index = p_triangle_index;
+			triangles.push_back(ts);
+		} else {
+			_plot_triangle_into_triangle_index_list(half_size, n, aabb, p_points, p_triangle_index, triangles, p_grid_size);
+		}
+	}
+}
+
+Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_size, Vector<Ref<Image>> &albedo_images, Vector<Ref<Image>> &emission_images, AABB &bounds, Size2i &atlas_size, int &atlas_slices, BakeStepFunc p_step_function, void *p_bake_userdata) {
+
+	Vector<Size2i> sizes;
+
+	for (int m_i = 0; m_i < mesh_instances.size(); m_i++) {
+
+		MeshInstance &mi = mesh_instances.write[m_i];
+		Size2i s = Size2i(mi.data.albedo_on_uv2->get_width(), mi.data.albedo_on_uv2->get_height());
+		sizes.push_back(s);
+		atlas_size.width = MAX(atlas_size.width, s.width);
+		atlas_size.height = MAX(atlas_size.height, s.height);
+	}
+
+	int max = nearest_power_of_2_templated(atlas_size.width);
+	max = MAX(max, nearest_power_of_2_templated(atlas_size.height));
+
+	if (max > p_max_texture_size) {
+		return BAKE_ERROR_LIGHTMAP_TOO_SMALL;
+	}
+
+	if (p_step_function) {
+		p_step_function(0.1, TTR("Determining optimal atlas size"), p_bake_userdata, true);
+	}
+
+	atlas_size = Size2i(max, max);
+
+	Size2i best_atlas_size;
+	int best_atlas_slices = 0;
+	int best_atlas_memory = 0x7FFFFFFF;
+	Vector<Vector3i> best_atlas_offsets;
+
+	//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<Vector2i> source_sizes = sizes;
+		Vector<int> source_indices;
+		source_indices.resize(source_sizes.size());
+		for (int i = 0; i < source_indices.size(); i++) {
+			source_indices.write[i] = i;
+		}
+		Vector<Vector3i> atlas_offsets;
+		atlas_offsets.resize(source_sizes.size());
+
+		int slices = 0;
+
+		while (source_sizes.size() > 0) {
+
+			Vector<Vector3i> offsets = Geometry::partial_pack_rects(source_sizes, atlas_size);
+			Vector<int> new_indices;
+			Vector<Vector2i> new_sources;
+			for (int i = 0; i < offsets.size(); i++) {
+				Vector3i ofs = offsets[i];
+				int sidx = source_indices[i];
+				if (ofs.z > 0) {
+					//valid
+					ofs.z = slices;
+					atlas_offsets.write[sidx] = ofs;
+				} else {
+					new_indices.push_back(sidx);
+					new_sources.push_back(source_sizes[i]);
+				}
+			}
+
+			source_sizes = new_sources;
+			source_indices = new_indices;
+			slices++;
+		}
+
+		int mem_used = atlas_size.x * atlas_size.y * slices;
+		if (mem_used < best_atlas_memory) {
+			best_atlas_size = atlas_size;
+			best_atlas_offsets = atlas_offsets;
+			best_atlas_slices = slices;
+			best_atlas_memory = mem_used;
+		}
+
+		if (atlas_size.width == atlas_size.height) {
+			atlas_size.width *= 2;
+		} else {
+			atlas_size.height *= 2;
+		}
+	}
+	atlas_size = best_atlas_size;
+	atlas_slices = best_atlas_slices;
+
+	// apply the offsets and slice to all images, and also blit albedo and emission
+	albedo_images.resize(atlas_slices);
+	emission_images.resize(atlas_slices);
+
+	if (p_step_function) {
+		p_step_function(0.2, TTR("Blitting albedo and emission"), p_bake_userdata, true);
+	}
+
+	for (int i = 0; i < atlas_slices; i++) {
+		Ref<Image> albedo;
+		albedo.instance();
+		albedo->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBA8);
+		albedo->set_as_black();
+		albedo_images.write[i] = albedo;
+
+		Ref<Image> emission;
+		emission.instance();
+		emission->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH);
+		emission->set_as_black();
+		emission_images.write[i] = emission;
+	}
+
+	//assign uv positions
+
+	for (int m_i = 0; m_i < mesh_instances.size(); m_i++) {
+
+		MeshInstance &mi = mesh_instances.write[m_i];
+		mi.offset.x = best_atlas_offsets[m_i].x;
+		mi.offset.y = best_atlas_offsets[m_i].y;
+		mi.slice = best_atlas_offsets[m_i].z;
+		albedo_images.write[mi.slice]->blit_rect(mi.data.albedo_on_uv2, Rect2(Vector2(), Size2i(mi.data.albedo_on_uv2->get_width(), mi.data.albedo_on_uv2->get_height())), mi.offset);
+		emission_images.write[mi.slice]->blit_rect(mi.data.emission_on_uv2, Rect2(Vector2(), Size2i(mi.data.emission_on_uv2->get_width(), mi.data.emission_on_uv2->get_height())), mi.offset);
+	}
+
+	return BAKE_OK;
+}
+
+void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_size, Vector<Probe> &probe_positions, GenerateProbes p_generate_probes, Vector<int> &slice_triangle_count, Vector<int> &slice_seam_count, RID &vertex_buffer, RID &triangle_buffer, RID &box_buffer, RID &lights_buffer, RID &triangle_cell_indices_buffer, RID &probe_positions_buffer, RID &grid_texture, RID &grid_texture_sdf, RID &seams_buffer, BakeStepFunc p_step_function, void *p_bake_userdata) {
+
+	HashMap<Vertex, uint32_t, VertexHash> vertex_map;
+
+	//fill triangles array and vertex array
+	LocalVector<Triangle> triangles;
+	LocalVector<Vertex> vertex_array;
+	LocalVector<Box> box_array;
+	LocalVector<Seam> seams;
+
+	slice_triangle_count.resize(atlas_slices);
+	slice_seam_count.resize(atlas_slices);
+
+	for (int i = 0; i < atlas_slices; i++) {
+		slice_triangle_count.write[i] = 0;
+		slice_seam_count.write[i] = 0;
+	}
+
+	bounds = AABB();
+
+	for (int m_i = 0; m_i < mesh_instances.size(); m_i++) {
+
+		if (p_step_function) {
+			float p = float(m_i + 1) / mesh_instances.size() * 0.1;
+			p_step_function(0.3 + p, vformat(TTR("Plotting mesh into acceleration structure %d/%d"), m_i + 1, mesh_instances.size()), p_bake_userdata, false);
+		}
+
+		HashMap<Edge, EdgeUV2, EdgeHash> edges;
+
+		MeshInstance &mi = mesh_instances.write[m_i];
+
+		Vector2 uv_scale = Vector2(mi.data.albedo_on_uv2->get_width(), mi.data.albedo_on_uv2->get_height()) / Vector2(atlas_size);
+		Vector2 uv_offset = Vector2(mi.offset) / Vector2(atlas_size);
+		if (m_i == 0) {
+			bounds.position = mi.data.points[0];
+		}
+
+		for (int i = 0; i < mi.data.points.size(); i += 3) {
+
+			Vector3 vtxs[3] = { mi.data.points[i + 0], mi.data.points[i + 1], mi.data.points[i + 2] };
+			Vector2 uvs[3] = { mi.data.uv2[i + 0] * uv_scale + uv_offset, mi.data.uv2[i + 1] * uv_scale + uv_offset, mi.data.uv2[i + 2] * uv_scale + uv_offset };
+			Vector3 normal[3] = { mi.data.normal[i + 0], mi.data.normal[i + 1], mi.data.normal[i + 2] };
+
+			AABB taabb;
+			Triangle t;
+			t.slice = mi.slice;
+			for (int k = 0; k < 3; k++) {
+
+				bounds.expand_to(vtxs[k]);
+
+				Vertex v;
+				v.position[0] = vtxs[k].x;
+				v.position[1] = vtxs[k].y;
+				v.position[2] = vtxs[k].z;
+				v.uv[0] = uvs[k].x;
+				v.uv[1] = uvs[k].y;
+				v.normal_xy[0] = normal[k].x;
+				v.normal_xy[1] = normal[k].y;
+				v.normal_z = normal[k].z;
+
+				uint32_t *indexptr = vertex_map.getptr(v);
+
+				if (indexptr) {
+					t.indices[k] = *indexptr;
+				} else {
+					uint32_t new_index = vertex_map.size();
+					t.indices[k] = new_index;
+					vertex_map[v] = new_index;
+					vertex_array.push_back(v);
+				}
+
+				if (k == 0) {
+					taabb.position = vtxs[k];
+				} else {
+					taabb.expand_to(vtxs[k]);
+				}
+			}
+
+			//compute seams that will need to be blended later
+			for (int k = 0; k < 3; k++) {
+				int n = (k + 1) % 3;
+
+				Edge edge(vtxs[k], vtxs[n], normal[k], normal[n]);
+				Vector2i edge_indices(t.indices[k], t.indices[n]);
+				EdgeUV2 uv2(uvs[k], uvs[n], edge_indices);
+
+				if (edge.b == edge.a) {
+					continue; //degenerate, somehow
+				}
+				if (edge.b < edge.a) {
+					SWAP(edge.a, edge.b);
+					SWAP(edge.na, edge.nb);
+					SWAP(uv2.a, uv2.b);
+					SWAP(edge_indices.x, edge_indices.y);
+				}
+
+				EdgeUV2 *euv2 = edges.getptr(edge);
+				if (!euv2) {
+					edges[edge] = uv2;
+				} else {
+					if (*euv2 == uv2) {
+						continue; // seam shared UV space, no need to blend
+					}
+					if (euv2->seam_found) {
+						continue; //bad geometry
+					}
+
+					Seam seam;
+					seam.a = edge_indices;
+					seam.b = euv2->indices;
+					seam.slice = mi.slice;
+					seams.push_back(seam);
+					slice_seam_count.write[mi.slice]++;
+					euv2->seam_found = true;
+				}
+			}
+
+			Box box;
+			box.min_bounds[0] = taabb.position.x;
+			box.min_bounds[1] = taabb.position.y;
+			box.min_bounds[2] = taabb.position.z;
+			box.max_bounds[0] = taabb.position.x + MAX(taabb.size.x, 0.0001);
+			box.max_bounds[1] = taabb.position.y + MAX(taabb.size.y, 0.0001);
+			box.max_bounds[2] = taabb.position.z + MAX(taabb.size.z, 0.0001);
+			box.pad0 = box.pad1 = 0; //make valgrind not complain
+			box_array.push_back(box);
+
+			triangles.push_back(t);
+			slice_triangle_count.write[t.slice]++;
+		}
+	}
+
+	//also consider probe positions for bounds
+	for (int i = 0; i < probe_positions.size(); i++) {
+		Vector3 pp(probe_positions[i].position[0], probe_positions[i].position[1], probe_positions[i].position[2]);
+		bounds.expand_to(pp);
+	}
+	bounds.grow_by(0.1); //grow a bit to avoid numerical error
+
+	triangles.sort(); //sort by slice
+	seams.sort();
+
+	if (p_step_function) {
+		p_step_function(0.4, TTR("Optimizing acceleration structure"), p_bake_userdata, true);
+	}
+
+	//fill list of triangles in grid
+	LocalVector<TriangleSort> triangle_sort;
+	for (uint32_t i = 0; i < triangles.size(); i++) {
+
+		const Triangle &t = triangles[i];
+		Vector3 face[3] = {
+			Vector3(vertex_array[t.indices[0]].position[0], vertex_array[t.indices[0]].position[1], vertex_array[t.indices[0]].position[2]),
+			Vector3(vertex_array[t.indices[1]].position[0], vertex_array[t.indices[1]].position[1], vertex_array[t.indices[1]].position[2]),
+			Vector3(vertex_array[t.indices[2]].position[0], vertex_array[t.indices[2]].position[1], vertex_array[t.indices[2]].position[2])
+		};
+		_plot_triangle_into_triangle_index_list(grid_size, Vector3i(), bounds, face, i, triangle_sort, grid_size);
+	}
+	//sort it
+	triangle_sort.sort();
+
+	Vector<uint32_t> triangle_indices;
+	triangle_indices.resize(triangle_sort.size());
+	Vector<uint32_t> grid_indices;
+	grid_indices.resize(grid_size * grid_size * grid_size * 2);
+	zeromem(grid_indices.ptrw(), grid_indices.size() * sizeof(uint32_t));
+	Vector<bool> solid;
+	solid.resize(grid_size * grid_size * grid_size);
+	zeromem(solid.ptrw(), solid.size() * sizeof(bool));
+
+	{
+		uint32_t *tiw = triangle_indices.ptrw();
+		uint32_t last_cell = 0xFFFFFFFF;
+		uint32_t *giw = grid_indices.ptrw();
+		bool *solidw = solid.ptrw();
+		for (uint32_t i = 0; i < triangle_sort.size(); i++) {
+			uint32_t cell = triangle_sort[i].cell_index;
+			if (cell != last_cell) {
+				//cell changed, update pointer to indices
+				giw[cell * 2 + 1] = i;
+				last_cell = cell;
+				solidw[cell] = true;
+			}
+			tiw[i] = triangle_sort[i].triangle_index;
+			giw[cell * 2]++; //update counter
+			last_cell = cell;
+		}
+	}
+#if 0
+	for (int i = 0; i < grid_size; i++) {
+		for (int j = 0; j < grid_size; j++) {
+			for (int k = 0; k < grid_size; k++) {
+				uint32_t index = i * (grid_size * grid_size) + j * grid_size + k;
+				grid_indices.write[index * 2] = float(i) / grid_size * 255;
+				grid_indices.write[index * 2 + 1] = float(j) / grid_size * 255;
+			}
+		}
+	}
+#endif
+
+#if 0
+	for (int i = 0; i < grid_size; i++) {
+		Vector<uint8_t> grid_usage;
+		grid_usage.resize(grid_size * grid_size);
+		for (int j = 0; j < grid_usage.size(); j++) {
+			uint32_t ofs = i * grid_size * grid_size + j;
+			uint32_t count = grid_indices[ofs * 2];
+			grid_usage.write[j] = count > 0 ? 255 : 0;
+		}
+
+		Ref<Image> img;
+		img.instance();
+		img->create(grid_size, grid_size, false, Image::FORMAT_L8, grid_usage);
+		img->save_png("res://grid_layer_" + itos(1000 + i).substr(1, 3) + ".png");
+	}
+#endif
+	if (p_step_function) {
+		p_step_function(0.45, TTR("Generating Signed Distance Field"), p_bake_userdata, true);
+	}
+
+	//generate SDF for raytracing
+	Vector<uint32_t> euclidean_pos = Geometry::generate_edf(solid, Vector3i(grid_size, grid_size, grid_size), false);
+	Vector<uint32_t> euclidean_neg = Geometry::generate_edf(solid, Vector3i(grid_size, grid_size, grid_size), true);
+	Vector<int8_t> sdf8 = Geometry::generate_sdf8(euclidean_pos, euclidean_neg);
+
+	/*****************************/
+	/*** CREATE GPU STRUCTURES ***/
+	/*****************************/
+
+	lights.sort();
+
+	Vector<Vector2i> seam_buffer_vec;
+	seam_buffer_vec.resize(seams.size() * 2);
+	for (uint32_t i = 0; i < seams.size(); i++) {
+		seam_buffer_vec.write[i * 2 + 0] = seams[i].a;
+		seam_buffer_vec.write[i * 2 + 1] = seams[i].b;
+	}
+
+	{ //buffers
+		Vector<uint8_t> vb = vertex_array.to_byte_array();
+		vertex_buffer = rd->storage_buffer_create(vb.size(), vb);
+
+		Vector<uint8_t> tb = triangles.to_byte_array();
+		triangle_buffer = rd->storage_buffer_create(tb.size(), tb);
+
+		Vector<uint8_t> bb = box_array.to_byte_array();
+		box_buffer = rd->storage_buffer_create(bb.size(), bb);
+
+		Vector<uint8_t> tib = triangle_indices.to_byte_array();
+		triangle_cell_indices_buffer = rd->storage_buffer_create(tib.size(), tib);
+
+		Vector<uint8_t> lb = lights.to_byte_array();
+		if (lb.size() == 0) {
+			lb.resize(sizeof(Light)); //even if no lights, the buffer must exist
+		}
+		lights_buffer = rd->storage_buffer_create(lb.size(), lb);
+
+		Vector<uint8_t> sb = seam_buffer_vec.to_byte_array();
+		if (sb.size() == 0) {
+			sb.resize(sizeof(Vector2i) * 2); //even if no seams, the buffer must exist
+		}
+		seams_buffer = rd->storage_buffer_create(sb.size(), sb);
+
+		Vector<uint8_t> pb = probe_positions.to_byte_array();
+		if (pb.size() == 0) {
+			pb.resize(sizeof(Probe));
+		}
+		probe_positions_buffer = rd->storage_buffer_create(pb.size(), pb);
+	}
+
+	{ //grid
+
+		RD::TextureFormat tf;
+		tf.width = grid_size;
+		tf.height = grid_size;
+		tf.depth = grid_size;
+		tf.type = RD::TEXTURE_TYPE_3D;
+		tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT;
+
+		Vector<Vector<uint8_t>> texdata;
+		texdata.resize(1);
+		//grid and indices
+		tf.format = RD::DATA_FORMAT_R32G32_UINT;
+		texdata.write[0] = grid_indices.to_byte_array();
+		grid_texture = rd->texture_create(tf, RD::TextureView(), texdata);
+		//sdf
+		tf.format = RD::DATA_FORMAT_R8_SNORM;
+		texdata.write[0] = sdf8.to_byte_array();
+		grid_texture_sdf = rd->texture_create(tf, RD::TextureView(), texdata);
+	}
+}
+
+void LightmapperRD::_raster_geometry(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, int grid_size, AABB bounds, float p_bias, Vector<int> slice_triangle_count, RID position_tex, RID unocclude_tex, RID normal_tex, RID raster_depth_buffer, RID rasterize_shader, RID raster_base_uniform) {
+
+	Vector<RID> framebuffers;
+
+	for (int i = 0; i < atlas_slices; i++) {
+		RID slice_pos_tex = rd->texture_create_shared_from_slice(RD::TextureView(), position_tex, i, 0);
+		RID slice_unoc_tex = rd->texture_create_shared_from_slice(RD::TextureView(), unocclude_tex, i, 0);
+		RID slice_norm_tex = rd->texture_create_shared_from_slice(RD::TextureView(), normal_tex, i, 0);
+		Vector<RID> fb;
+		fb.push_back(slice_pos_tex);
+		fb.push_back(slice_norm_tex);
+		fb.push_back(slice_unoc_tex);
+		fb.push_back(raster_depth_buffer);
+		framebuffers.push_back(rd->framebuffer_create(fb));
+	}
+
+	RD::PipelineDepthStencilState ds;
+	ds.enable_depth_test = true;
+	ds.enable_depth_write = true;
+	ds.depth_compare_operator = RD::COMPARE_OP_LESS; //so it does render same pixel twice
+
+	RID raster_pipeline = rd->render_pipeline_create(rasterize_shader, rd->framebuffer_get_format(framebuffers[0]), RD::INVALID_FORMAT_ID, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), ds, RD::PipelineColorBlendState::create_disabled(3), 0);
+	RID raster_pipeline_wire;
+	{
+
+		RD::PipelineRasterizationState rw;
+		rw.wireframe = true;
+		raster_pipeline_wire = rd->render_pipeline_create(rasterize_shader, rd->framebuffer_get_format(framebuffers[0]), RD::INVALID_FORMAT_ID, RD::RENDER_PRIMITIVE_TRIANGLES, rw, RD::PipelineMultisampleState(), ds, RD::PipelineColorBlendState::create_disabled(3), 0);
+	}
+
+	uint32_t triangle_offset = 0;
+	Vector<Color> clear_colors;
+	clear_colors.push_back(Color(0, 0, 0, 0));
+	clear_colors.push_back(Color(0, 0, 0, 0));
+	clear_colors.push_back(Color(0, 0, 0, 0));
+
+	for (int i = 0; i < atlas_slices; i++) {
+
+		RasterPushConstant raster_push_constant;
+		raster_push_constant.atlas_size[0] = atlas_size.x;
+		raster_push_constant.atlas_size[1] = atlas_size.y;
+		raster_push_constant.base_triangle = triangle_offset;
+		raster_push_constant.to_cell_offset[0] = bounds.position.x;
+		raster_push_constant.to_cell_offset[1] = bounds.position.y;
+		raster_push_constant.to_cell_offset[2] = bounds.position.z;
+		raster_push_constant.bias = p_bias;
+		raster_push_constant.to_cell_size[0] = (1.0 / bounds.size.x) * float(grid_size);
+		raster_push_constant.to_cell_size[1] = (1.0 / bounds.size.y) * float(grid_size);
+		raster_push_constant.to_cell_size[2] = (1.0 / bounds.size.z) * float(grid_size);
+		raster_push_constant.grid_size[0] = grid_size;
+		raster_push_constant.grid_size[1] = grid_size;
+		raster_push_constant.grid_size[2] = grid_size;
+		raster_push_constant.uv_offset[0] = 0;
+		raster_push_constant.uv_offset[1] = 0;
+
+		RD::DrawListID draw_list = rd->draw_list_begin(framebuffers[i], RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors);
+		//draw opaque
+		rd->draw_list_bind_render_pipeline(draw_list, raster_pipeline);
+		rd->draw_list_bind_uniform_set(draw_list, raster_base_uniform, 0);
+		rd->draw_list_set_push_constant(draw_list, &raster_push_constant, sizeof(RasterPushConstant));
+		rd->draw_list_draw(draw_list, false, 1, slice_triangle_count[i] * 3);
+		//draw wire
+		rd->draw_list_bind_render_pipeline(draw_list, raster_pipeline_wire);
+		rd->draw_list_bind_uniform_set(draw_list, raster_base_uniform, 0);
+		rd->draw_list_set_push_constant(draw_list, &raster_push_constant, sizeof(RasterPushConstant));
+		rd->draw_list_draw(draw_list, false, 1, slice_triangle_count[i] * 3);
+
+		rd->draw_list_end();
+
+		triangle_offset += slice_triangle_count[i];
+	}
+}
+
+LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function, void *p_bake_userdata) {
+
+	if (p_step_function) {
+		p_step_function(0.0, TTR("Begin Bake"), p_bake_userdata, true);
+	}
+	bake_textures.clear();
+	int grid_size = 128;
+
+	/* STEP 1: Fetch material textures and compute the bounds */
+
+	AABB bounds;
+	Size2i atlas_size;
+	int atlas_slices;
+	Vector<Ref<Image>> albedo_images;
+	Vector<Ref<Image>> emission_images;
+
+	BakeError bake_error = _blit_meshes_into_atlas(p_max_texture_size, albedo_images, emission_images, bounds, atlas_size, atlas_slices, p_step_function, p_bake_userdata);
+	if (bake_error != BAKE_OK) {
+		return bake_error;
+	}
+
+#ifdef DEBUG_TEXTURES
+	for (int i = 0; i < atlas_slices; i++) {
+		albedo_images[i]->save_png("res://0_albedo_" + itos(i) + ".png");
+		emission_images[i]->save_png("res://0_emission_" + itos(i) + ".png");
+	}
+#endif
+
+	RenderingDevice *rd = RenderingDevice::get_singleton()->create_local_device();
+
+	RID albedo_array_tex;
+	RID emission_array_tex;
+	RID normal_tex;
+	RID position_tex;
+	RID unocclude_tex;
+	RID light_source_tex;
+	RID light_dest_tex;
+	RID light_accum_tex;
+	RID light_accum_tex2;
+	RID light_primary_dynamic_tex;
+	RID light_environment_tex;
+
+#define FREE_TEXTURES                    \
+	rd->free(albedo_array_tex);          \
+	rd->free(emission_array_tex);        \
+	rd->free(normal_tex);                \
+	rd->free(position_tex);              \
+	rd->free(unocclude_tex);             \
+	rd->free(light_source_tex);          \
+	rd->free(light_accum_tex2);          \
+	rd->free(light_accum_tex);           \
+	rd->free(light_primary_dynamic_tex); \
+	rd->free(light_environment_tex);
+
+	{ // create all textures
+
+		Vector<Vector<uint8_t>> albedo_data;
+		Vector<Vector<uint8_t>> emission_data;
+		for (int i = 0; i < atlas_slices; i++) {
+			albedo_data.push_back(albedo_images[i]->get_data());
+			emission_data.push_back(emission_images[i]->get_data());
+		}
+
+		RD::TextureFormat tf;
+		tf.width = atlas_size.width;
+		tf.height = atlas_size.height;
+		tf.array_layers = atlas_slices;
+		tf.type = RD::TEXTURE_TYPE_2D_ARRAY;
+		tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT;
+		tf.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
+
+		albedo_array_tex = rd->texture_create(tf, RD::TextureView(), albedo_data);
+
+		tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
+
+		emission_array_tex = rd->texture_create(tf, RD::TextureView(), emission_data);
+
+		//this will be rastered to
+		tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
+		normal_tex = rd->texture_create(tf, RD::TextureView());
+		tf.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT;
+		position_tex = rd->texture_create(tf, RD::TextureView());
+		unocclude_tex = rd->texture_create(tf, RD::TextureView());
+
+		tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
+		tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT;
+
+		light_source_tex = rd->texture_create(tf, RD::TextureView());
+		rd->texture_clear(light_source_tex, Color(0, 0, 0, 0), 0, 1, 0, atlas_slices);
+		light_primary_dynamic_tex = rd->texture_create(tf, RD::TextureView());
+		rd->texture_clear(light_primary_dynamic_tex, Color(0, 0, 0, 0), 0, 1, 0, atlas_slices);
+
+		if (p_bake_sh) {
+			tf.array_layers *= 4;
+		}
+		light_accum_tex = rd->texture_create(tf, RD::TextureView());
+		rd->texture_clear(light_accum_tex, Color(0, 0, 0, 0), 0, 1, 0, tf.array_layers);
+		light_dest_tex = rd->texture_create(tf, RD::TextureView());
+		rd->texture_clear(light_dest_tex, Color(0, 0, 0, 0), 0, 1, 0, tf.array_layers);
+		light_accum_tex2 = light_dest_tex;
+
+		//env
+		{
+			Ref<Image> panorama_tex;
+			if (p_environment_panorama.is_valid()) {
+				panorama_tex = p_environment_panorama;
+				panorama_tex->convert(Image::FORMAT_RGBAF);
+			} else {
+				panorama_tex.instance();
+				panorama_tex->create(8, 8, false, Image::FORMAT_RGBAF);
+				for (int i = 0; i < 8; i++) {
+					for (int j = 0; j < 8; j++) {
+						panorama_tex->set_pixel(i, j, Color(0, 0, 0, 1));
+					}
+				}
+			}
+
+			RD::TextureFormat tfp;
+			tfp.width = panorama_tex->get_width();
+			tfp.height = panorama_tex->get_height();
+			tfp.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT;
+			tfp.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT;
+
+			Vector<Vector<uint8_t>> tdata;
+			tdata.push_back(panorama_tex->get_data());
+			light_environment_tex = rd->texture_create(tfp, RD::TextureView(), tdata);
+
+#ifdef DEBUG_TEXTURES
+			panorama_tex->convert(Image::FORMAT_RGB8);
+			panorama_tex->save_png("res://0_panorama.png");
+#endif
+		}
+	}
+
+	/* STEP 2: create the acceleration structure for the GPU*/
+
+	Vector<int> slice_triangle_count;
+	RID vertex_buffer;
+	RID triangle_buffer;
+	RID box_buffer;
+	RID lights_buffer;
+	RID triangle_cell_indices_buffer;
+	RID grid_texture;
+	RID grid_texture_sdf;
+	RID seams_buffer;
+	RID probe_positions_buffer;
+
+	Vector<int> slice_seam_count;
+
+#define FREE_BUFFERS                        \
+	rd->free(vertex_buffer);                \
+	rd->free(triangle_buffer);              \
+	rd->free(box_buffer);                   \
+	rd->free(lights_buffer);                \
+	rd->free(triangle_cell_indices_buffer); \
+	rd->free(grid_texture);                 \
+	rd->free(grid_texture_sdf);             \
+	rd->free(seams_buffer);                 \
+	rd->free(probe_positions_buffer);
+
+	_create_acceleration_structures(rd, atlas_size, atlas_slices, bounds, grid_size, probe_positions, p_generate_probes, slice_triangle_count, slice_seam_count, vertex_buffer, triangle_buffer, box_buffer, lights_buffer, triangle_cell_indices_buffer, probe_positions_buffer, grid_texture, grid_texture_sdf, seams_buffer, p_step_function, p_bake_userdata);
+
+	if (p_step_function) {
+		p_step_function(0.47, TTR("Preparing shaders"), p_bake_userdata, true);
+	}
+
+	//shaders
+	Ref<RDShaderFile> raster_shader;
+	raster_shader.instance();
+	Error err = raster_shader->parse_versions_from_text(lm_raster_shader_glsl);
+	if (err != OK) {
+		raster_shader->print_errors("raster_shader");
+
+		FREE_TEXTURES
+		FREE_BUFFERS
+
+		memdelete(rd);
+	}
+	ERR_FAIL_COND_V(err != OK, BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES);
+
+	RID rasterize_shader = rd->shader_create_from_bytecode(raster_shader->get_bytecode());
+
+	ERR_FAIL_COND_V(rasterize_shader.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); //this is a bug check, though, should not happen
+
+	RID sampler;
+	{
+		RD::SamplerState s;
+		s.mag_filter = RD::SAMPLER_FILTER_LINEAR;
+		s.min_filter = RD::SAMPLER_FILTER_LINEAR;
+		s.max_lod = 0;
+
+		sampler = rd->sampler_create(s);
+	}
+
+	Vector<RD::Uniform> base_uniforms;
+	{
+		{
+
+			RD::Uniform u;
+			u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+			u.binding = 1;
+			u.ids.push_back(vertex_buffer);
+			base_uniforms.push_back(u);
+		}
+		{
+			RD::Uniform u;
+			u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+			u.binding = 2;
+			u.ids.push_back(triangle_buffer);
+			base_uniforms.push_back(u);
+		}
+		{
+			RD::Uniform u;
+			u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+			u.binding = 3;
+			u.ids.push_back(box_buffer);
+			base_uniforms.push_back(u);
+		}
+		{
+			RD::Uniform u;
+			u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+			u.binding = 4;
+			u.ids.push_back(triangle_cell_indices_buffer);
+			base_uniforms.push_back(u);
+		}
+		{
+			RD::Uniform u;
+			u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+			u.binding = 5;
+			u.ids.push_back(lights_buffer);
+			base_uniforms.push_back(u);
+		}
+		{
+			RD::Uniform u;
+			u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+			u.binding = 6;
+			u.ids.push_back(seams_buffer);
+			base_uniforms.push_back(u);
+		}
+		{
+			RD::Uniform u;
+			u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+			u.binding = 7;
+			u.ids.push_back(probe_positions_buffer);
+			base_uniforms.push_back(u);
+		}
+		{
+			RD::Uniform u;
+			u.type = RD::UNIFORM_TYPE_TEXTURE;
+			u.binding = 8;
+			u.ids.push_back(grid_texture);
+			base_uniforms.push_back(u);
+		}
+		{
+			RD::Uniform u;
+			u.type = RD::UNIFORM_TYPE_TEXTURE;
+			u.binding = 9;
+			u.ids.push_back(grid_texture_sdf);
+			base_uniforms.push_back(u);
+		}
+		{
+			RD::Uniform u;
+			u.type = RD::UNIFORM_TYPE_TEXTURE;
+			u.binding = 10;
+			u.ids.push_back(albedo_array_tex);
+			base_uniforms.push_back(u);
+		}
+		{
+			RD::Uniform u;
+			u.type = RD::UNIFORM_TYPE_TEXTURE;
+			u.binding = 11;
+			u.ids.push_back(emission_array_tex);
+			base_uniforms.push_back(u);
+		}
+		{
+			RD::Uniform u;
+			u.type = RD::UNIFORM_TYPE_SAMPLER;
+			u.binding = 12;
+			u.ids.push_back(sampler);
+			base_uniforms.push_back(u);
+		}
+	}
+
+	RID raster_base_uniform = rd->uniform_set_create(base_uniforms, rasterize_shader, 0);
+	RID raster_depth_buffer;
+	{
+		RD::TextureFormat tf;
+		tf.width = atlas_size.width;
+		tf.height = atlas_size.height;
+		tf.depth = 1;
+		tf.type = RD::TEXTURE_TYPE_2D;
+		tf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+		tf.format = RD::DATA_FORMAT_D32_SFLOAT;
+
+		raster_depth_buffer = rd->texture_create(tf, RD::TextureView());
+	}
+
+	rd->submit();
+	rd->sync();
+
+	/* STEP 3: Raster the geometry to UV2 coords in the atlas textures GPU*/
+
+	_raster_geometry(rd, atlas_size, atlas_slices, grid_size, bounds, p_bias, slice_triangle_count, position_tex, unocclude_tex, normal_tex, raster_depth_buffer, rasterize_shader, raster_base_uniform);
+
+#ifdef DEBUG_TEXTURES
+
+	for (int i = 0; i < atlas_slices; i++) {
+		Vector<uint8_t> s = rd->texture_get_data(position_tex, i);
+		Ref<Image> img;
+		img.instance();
+		img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAF, s);
+		img->convert(Image::FORMAT_RGBA8);
+		img->save_png("res://1_position_" + itos(i) + ".png");
+
+		s = rd->texture_get_data(normal_tex, i);
+		img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
+		img->convert(Image::FORMAT_RGBA8);
+		img->save_png("res://1_normal_" + itos(i) + ".png");
+	}
+#endif
+
+#define FREE_RASTER_RESOURCES   \
+	rd->free(rasterize_shader); \
+	rd->free(sampler);          \
+	rd->free(raster_depth_buffer);
+
+	/* Plot direct light */
+
+	Ref<RDShaderFile> compute_shader;
+	compute_shader.instance();
+	err = compute_shader->parse_versions_from_text(lm_compute_shader_glsl, p_bake_sh ? "\n#define USE_SH_LIGHTMAPS\n" : "");
+	if (err != OK) {
+
+		FREE_TEXTURES
+		FREE_BUFFERS
+		FREE_RASTER_RESOURCES
+		memdelete(rd);
+		compute_shader->print_errors("compute_shader");
+	}
+	ERR_FAIL_COND_V(err != OK, BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES);
+
+	//unoccluder
+	RID compute_shader_unocclude = rd->shader_create_from_bytecode(compute_shader->get_bytecode("unocclude"));
+	ERR_FAIL_COND_V(compute_shader_unocclude.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); // internal check, should not happen
+	RID compute_shader_unocclude_pipeline = rd->compute_pipeline_create(compute_shader_unocclude);
+
+	//direct light
+	RID compute_shader_primary = rd->shader_create_from_bytecode(compute_shader->get_bytecode("primary"));
+	ERR_FAIL_COND_V(compute_shader_primary.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); // internal check, should not happen
+	RID compute_shader_primary_pipeline = rd->compute_pipeline_create(compute_shader_primary);
+
+	//indirect light
+	RID compute_shader_secondary = rd->shader_create_from_bytecode(compute_shader->get_bytecode("secondary"));
+	ERR_FAIL_COND_V(compute_shader_secondary.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); //internal check, should not happen
+	RID compute_shader_secondary_pipeline = rd->compute_pipeline_create(compute_shader_secondary);
+
+	//dilate
+	RID compute_shader_dilate = rd->shader_create_from_bytecode(compute_shader->get_bytecode("dilate"));
+	ERR_FAIL_COND_V(compute_shader_dilate.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); //internal check, should not happen
+	RID compute_shader_dilate_pipeline = rd->compute_pipeline_create(compute_shader_dilate);
+
+	//dilate
+	RID compute_shader_light_probes = rd->shader_create_from_bytecode(compute_shader->get_bytecode("light_probes"));
+	ERR_FAIL_COND_V(compute_shader_light_probes.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); //internal check, should not happen
+	RID compute_shader_light_probes_pipeline = rd->compute_pipeline_create(compute_shader_light_probes);
+
+	RID compute_base_uniform_set = rd->uniform_set_create(base_uniforms, compute_shader_primary, 0);
+
+#define FREE_COMPUTE_RESOURCES          \
+	rd->free(compute_shader_unocclude); \
+	rd->free(compute_shader_primary);   \
+	rd->free(compute_shader_secondary); \
+	rd->free(compute_shader_dilate);    \
+	rd->free(compute_shader_light_probes);
+
+	PushConstant push_constant;
+	{
+		//set defaults
+		push_constant.atlas_size[0] = atlas_size.width;
+		push_constant.atlas_size[1] = atlas_size.height;
+		push_constant.world_size[0] = bounds.size.x;
+		push_constant.world_size[1] = bounds.size.y;
+		push_constant.world_size[2] = bounds.size.z;
+		push_constant.to_cell_offset[0] = bounds.position.x;
+		push_constant.to_cell_offset[1] = bounds.position.y;
+		push_constant.to_cell_offset[2] = bounds.position.z;
+		push_constant.bias = p_bias;
+		push_constant.to_cell_size[0] = (1.0 / bounds.size.x) * float(grid_size);
+		push_constant.to_cell_size[1] = (1.0 / bounds.size.y) * float(grid_size);
+		push_constant.to_cell_size[2] = (1.0 / bounds.size.z) * float(grid_size);
+		push_constant.light_count = lights.size();
+		push_constant.grid_size = grid_size;
+		push_constant.atlas_slice = 0;
+		push_constant.region_ofs[0] = 0;
+		push_constant.region_ofs[1] = 0;
+		push_constant.environment_xform[0] = p_environment_transform.elements[0][0];
+		push_constant.environment_xform[1] = p_environment_transform.elements[1][0];
+		push_constant.environment_xform[2] = p_environment_transform.elements[2][0];
+		push_constant.environment_xform[3] = 0;
+		push_constant.environment_xform[4] = p_environment_transform.elements[0][1];
+		push_constant.environment_xform[5] = p_environment_transform.elements[1][1];
+		push_constant.environment_xform[6] = p_environment_transform.elements[2][1];
+		push_constant.environment_xform[7] = 0;
+		push_constant.environment_xform[8] = p_environment_transform.elements[0][2];
+		push_constant.environment_xform[9] = p_environment_transform.elements[1][2];
+		push_constant.environment_xform[10] = p_environment_transform.elements[2][2];
+		push_constant.environment_xform[11] = 0;
+	}
+
+	Vector3i group_size((atlas_size.x - 1) / 8 + 1, (atlas_size.y - 1) / 8 + 1, 1);
+	rd->submit();
+	rd->sync();
+
+	if (p_step_function) {
+		p_step_function(0.49, TTR("Un-occluding geometry"), p_bake_userdata, true);
+	}
+
+	/* UNOCCLUDE */
+	{
+
+		Vector<RD::Uniform> uniforms;
+		{
+			{
+
+				RD::Uniform u;
+				u.type = RD::UNIFORM_TYPE_IMAGE;
+				u.binding = 0;
+				u.ids.push_back(position_tex);
+				uniforms.push_back(u);
+			}
+			{
+				RD::Uniform u;
+				u.type = RD::UNIFORM_TYPE_IMAGE;
+				u.binding = 1;
+				u.ids.push_back(unocclude_tex); //will be unused
+				uniforms.push_back(u);
+			}
+		}
+
+		RID unocclude_uniform_set = rd->uniform_set_create(uniforms, compute_shader_unocclude, 1);
+
+		RD::ComputeListID compute_list = rd->compute_list_begin();
+		rd->compute_list_bind_compute_pipeline(compute_list, compute_shader_unocclude_pipeline);
+		rd->compute_list_bind_uniform_set(compute_list, compute_base_uniform_set, 0);
+		rd->compute_list_bind_uniform_set(compute_list, unocclude_uniform_set, 1);
+
+		for (int i = 0; i < atlas_slices; i++) {
+			push_constant.atlas_slice = i;
+			rd->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant));
+			rd->compute_list_dispatch(compute_list, group_size.x, group_size.y, group_size.z);
+			//no barrier, let them run all together
+		}
+		rd->compute_list_end(); //done
+	}
+
+	if (p_step_function) {
+		p_step_function(0.5, TTR("Plot direct lighting"), p_bake_userdata, true);
+	}
+
+	/* PRIMARY (direct) LIGHT PASS */
+	{
+
+		Vector<RD::Uniform> uniforms;
+		{
+			{
+
+				RD::Uniform u;
+				u.type = RD::UNIFORM_TYPE_IMAGE;
+				u.binding = 0;
+				u.ids.push_back(light_source_tex);
+				uniforms.push_back(u);
+			}
+			{
+				RD::Uniform u;
+				u.type = RD::UNIFORM_TYPE_TEXTURE;
+				u.binding = 1;
+				u.ids.push_back(light_dest_tex); //will be unused
+				uniforms.push_back(u);
+			}
+			{
+				RD::Uniform u;
+				u.type = RD::UNIFORM_TYPE_TEXTURE;
+				u.binding = 2;
+				u.ids.push_back(position_tex);
+				uniforms.push_back(u);
+			}
+			{
+				RD::Uniform u;
+				u.type = RD::UNIFORM_TYPE_TEXTURE;
+				u.binding = 3;
+				u.ids.push_back(normal_tex);
+				uniforms.push_back(u);
+			}
+			{
+				RD::Uniform u;
+				u.type = RD::UNIFORM_TYPE_IMAGE;
+				u.binding = 4;
+				u.ids.push_back(light_accum_tex);
+				uniforms.push_back(u);
+			}
+			{
+				RD::Uniform u;
+				u.type = RD::UNIFORM_TYPE_IMAGE;
+				u.binding = 5;
+				u.ids.push_back(light_primary_dynamic_tex);
+				uniforms.push_back(u);
+			}
+		}
+
+		RID light_uniform_set = rd->uniform_set_create(uniforms, compute_shader_primary, 1);
+
+		RD::ComputeListID compute_list = rd->compute_list_begin();
+		rd->compute_list_bind_compute_pipeline(compute_list, compute_shader_primary_pipeline);
+		rd->compute_list_bind_uniform_set(compute_list, compute_base_uniform_set, 0);
+		rd->compute_list_bind_uniform_set(compute_list, light_uniform_set, 1);
+
+		for (int i = 0; i < atlas_slices; i++) {
+			push_constant.atlas_slice = i;
+			rd->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant));
+			rd->compute_list_dispatch(compute_list, group_size.x, group_size.y, group_size.z);
+			//no barrier, let them run all together
+		}
+		rd->compute_list_end(); //done
+	}
+
+#ifdef DEBUG_TEXTURES
+
+	for (int i = 0; i < atlas_slices; i++) {
+		Vector<uint8_t> s = rd->texture_get_data(light_source_tex, i);
+		Ref<Image> img;
+		img.instance();
+		img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
+		img->convert(Image::FORMAT_RGBA8);
+		img->save_png("res://2_light_primary_" + itos(i) + ".png");
+	}
+#endif
+
+	/* SECONDARY (indirect) LIGHT PASS(ES) */
+	if (p_step_function) {
+		p_step_function(0.6, TTR("Integrate indirect lighting"), p_bake_userdata, true);
+	}
+
+	if (p_bounces > 0) {
+
+		Vector<RD::Uniform> uniforms;
+		{
+			{
+
+				RD::Uniform u;
+				u.type = RD::UNIFORM_TYPE_IMAGE;
+				u.binding = 0;
+				u.ids.push_back(light_dest_tex);
+				uniforms.push_back(u);
+			}
+			{
+				RD::Uniform u;
+				u.type = RD::UNIFORM_TYPE_TEXTURE;
+				u.binding = 1;
+				u.ids.push_back(light_source_tex);
+				uniforms.push_back(u);
+			}
+			{
+				RD::Uniform u;
+				u.type = RD::UNIFORM_TYPE_TEXTURE;
+				u.binding = 2;
+				u.ids.push_back(position_tex);
+				uniforms.push_back(u);
+			}
+			{
+				RD::Uniform u;
+				u.type = RD::UNIFORM_TYPE_TEXTURE;
+				u.binding = 3;
+				u.ids.push_back(normal_tex);
+				uniforms.push_back(u);
+			}
+			{
+				RD::Uniform u;
+				u.type = RD::UNIFORM_TYPE_IMAGE;
+				u.binding = 4;
+				u.ids.push_back(light_accum_tex);
+				uniforms.push_back(u);
+			}
+			{
+				RD::Uniform u;
+				u.type = RD::UNIFORM_TYPE_IMAGE;
+				u.binding = 5;
+				u.ids.push_back(unocclude_tex); //reuse unocclude tex
+				uniforms.push_back(u);
+			}
+			{
+				RD::Uniform u;
+				u.type = RD::UNIFORM_TYPE_TEXTURE;
+				u.binding = 6;
+				u.ids.push_back(light_environment_tex); //reuse unocclude tex
+				uniforms.push_back(u);
+			}
+		}
+
+		RID secondary_uniform_set[2];
+		secondary_uniform_set[0] = rd->uniform_set_create(uniforms, compute_shader_secondary, 1);
+		uniforms.write[0].ids.write[0] = light_source_tex;
+		uniforms.write[1].ids.write[0] = light_dest_tex;
+		secondary_uniform_set[1] = rd->uniform_set_create(uniforms, compute_shader_secondary, 1);
+
+		switch (p_quality) {
+			case BAKE_QUALITY_LOW: {
+				push_constant.ray_count = GLOBAL_GET("rendering/gpu_lightmapper/quality/low_quality_ray_count");
+			} break;
+			case BAKE_QUALITY_MEDIUM: {
+				push_constant.ray_count = GLOBAL_GET("rendering/gpu_lightmapper/quality/medium_quality_ray_count");
+			} break;
+			case BAKE_QUALITY_HIGH: {
+				push_constant.ray_count = GLOBAL_GET("rendering/gpu_lightmapper/quality/high_quality_ray_count");
+			} break;
+			case BAKE_QUALITY_ULTRA: {
+				push_constant.ray_count = GLOBAL_GET("rendering/gpu_lightmapper/quality/ultra_quality_ray_count");
+			} break;
+		}
+
+		push_constant.ray_count = CLAMP(push_constant.ray_count, 16, 8192);
+
+		int max_region_size = nearest_power_of_2_templated(int(GLOBAL_GET("rendering/gpu_lightmapper/performance/region_size")));
+		int max_rays = GLOBAL_GET("rendering/gpu_lightmapper/performance/max_rays_per_pass");
+
+		int x_regions = (atlas_size.width - 1) / max_region_size + 1;
+		int y_regions = (atlas_size.height - 1) / max_region_size + 1;
+		int ray_iterations = (push_constant.ray_count - 1) / max_rays + 1;
+
+		rd->submit();
+		rd->sync();
+
+		for (int b = 0; b < p_bounces; b++) {
+			int count = 0;
+			if (b > 0) {
+				SWAP(light_source_tex, light_dest_tex);
+				SWAP(secondary_uniform_set[0], secondary_uniform_set[1]);
+			}
+
+			for (int s = 0; s < atlas_slices; s++) {
+				push_constant.atlas_slice = s;
+
+				for (int i = 0; i < x_regions; i++) {
+					for (int j = 0; j < y_regions; j++) {
+
+						int x = i * max_region_size;
+						int y = j * max_region_size;
+						int w = MIN((i + 1) * max_region_size, atlas_size.width) - x;
+						int h = MIN((j + 1) * max_region_size, atlas_size.height) - y;
+
+						push_constant.region_ofs[0] = x;
+						push_constant.region_ofs[1] = y;
+
+						group_size = Vector3i((w - 1) / 8 + 1, (h - 1) / 8 + 1, 1);
+
+						for (int k = 0; k < ray_iterations; k++) {
+
+							RD::ComputeListID compute_list = rd->compute_list_begin();
+							rd->compute_list_bind_compute_pipeline(compute_list, compute_shader_secondary_pipeline);
+							rd->compute_list_bind_uniform_set(compute_list, compute_base_uniform_set, 0);
+							rd->compute_list_bind_uniform_set(compute_list, secondary_uniform_set[0], 1);
+
+							push_constant.ray_from = k * max_rays;
+							push_constant.ray_to = MIN((k + 1) * max_rays, int32_t(push_constant.ray_count));
+							rd->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant));
+							rd->compute_list_dispatch(compute_list, group_size.x, group_size.y, group_size.z);
+
+							rd->compute_list_end(); //done
+							rd->submit();
+							rd->sync();
+
+							count++;
+							if (p_step_function) {
+								int total = (atlas_slices * x_regions * y_regions * ray_iterations);
+								int percent = count * 100 / total;
+								float p = float(count) / total * 0.1;
+								p_step_function(0.6 + p, vformat(TTR("Bounce %d/%d: Integrate indirect lighting %d%%"), b + 1, p_bounces, percent), p_bake_userdata, false);
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+
+	/* LIGHPROBES */
+
+	RID light_probe_buffer;
+
+	if (probe_positions.size()) {
+
+		light_probe_buffer = rd->storage_buffer_create(sizeof(float) * 4 * 9 * probe_positions.size());
+
+		if (p_step_function) {
+			p_step_function(0.7, TTR("Baking lightprobes"), p_bake_userdata, true);
+		}
+
+		Vector<RD::Uniform> uniforms;
+		{
+
+			{
+
+				RD::Uniform u;
+				u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+				u.binding = 0;
+				u.ids.push_back(light_probe_buffer);
+				uniforms.push_back(u);
+			}
+			{
+				RD::Uniform u;
+				u.type = RD::UNIFORM_TYPE_TEXTURE;
+				u.binding = 1;
+				u.ids.push_back(light_dest_tex);
+				uniforms.push_back(u);
+			}
+			{
+				RD::Uniform u;
+				u.type = RD::UNIFORM_TYPE_TEXTURE;
+				u.binding = 2;
+				u.ids.push_back(light_primary_dynamic_tex);
+				uniforms.push_back(u);
+			}
+			{
+				RD::Uniform u;
+				u.type = RD::UNIFORM_TYPE_TEXTURE;
+				u.binding = 3;
+				u.ids.push_back(light_environment_tex);
+				uniforms.push_back(u);
+			}
+		}
+		RID light_probe_uniform_set = rd->uniform_set_create(uniforms, compute_shader_light_probes, 1);
+
+		switch (p_quality) {
+			case BAKE_QUALITY_LOW: {
+				push_constant.ray_count = GLOBAL_GET("rendering/gpu_lightmapper/quality/low_quality_probe_ray_count");
+			} break;
+			case BAKE_QUALITY_MEDIUM: {
+				push_constant.ray_count = GLOBAL_GET("rendering/gpu_lightmapper/quality/medium_quality_probe_ray_count");
+			} break;
+			case BAKE_QUALITY_HIGH: {
+				push_constant.ray_count = GLOBAL_GET("rendering/gpu_lightmapper/quality/high_quality_probe_ray_count");
+			} break;
+			case BAKE_QUALITY_ULTRA: {
+				push_constant.ray_count = GLOBAL_GET("rendering/gpu_lightmapper/quality/ultra_quality_probe_ray_count");
+			} break;
+		}
+
+		push_constant.atlas_size[0] = probe_positions.size();
+		push_constant.ray_count = CLAMP(push_constant.ray_count, 16, 8192);
+
+		int max_rays = GLOBAL_GET("rendering/gpu_lightmapper/performance/max_rays_per_probe_pass");
+		int ray_iterations = (push_constant.ray_count - 1) / max_rays + 1;
+
+		for (int i = 0; i < ray_iterations; i++) {
+
+			RD::ComputeListID compute_list = rd->compute_list_begin();
+			rd->compute_list_bind_compute_pipeline(compute_list, compute_shader_light_probes_pipeline);
+			rd->compute_list_bind_uniform_set(compute_list, compute_base_uniform_set, 0);
+			rd->compute_list_bind_uniform_set(compute_list, light_probe_uniform_set, 1);
+
+			push_constant.ray_from = i * max_rays;
+			push_constant.ray_to = MIN((i + 1) * max_rays, int32_t(push_constant.ray_count));
+			rd->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant));
+			rd->compute_list_dispatch(compute_list, (probe_positions.size() - 1) / 64 + 1, 1, 1);
+
+			rd->compute_list_end(); //done
+			rd->submit();
+			rd->sync();
+
+			if (p_step_function) {
+				int percent = i * 100 / ray_iterations;
+				float p = float(i) / ray_iterations * 0.1;
+				p_step_function(0.7 + p, vformat(TTR("Integrating light probes %d%%"), percent), p_bake_userdata, false);
+			}
+		}
+
+		push_constant.atlas_size[0] = atlas_size.x; //restore
+	}
+
+#if 0
+	for (int i = 0; i < probe_positions.size(); i++) {
+		Ref<Image> img;
+		img.instance();
+		img->create(6, 4, false, Image::FORMAT_RGB8);
+		for (int j = 0; j < 6; j++) {
+			Vector<uint8_t> s = rd->texture_get_data(lightprobe_tex, i * 6 + j);
+			Ref<Image> img2;
+			img2.instance();
+			img2->create(2, 2, false, Image::FORMAT_RGBAF, s);
+			img2->convert(Image::FORMAT_RGB8);
+			img->blit_rect(img2, Rect2(0, 0, 2, 2), Point2((j % 3) * 2, (j / 3) * 2));
+		}
+		img->save_png("res://3_light_probe_" + itos(i) + ".png");
+	}
+#endif
+
+	/* DENOISE */
+
+	if (p_use_denoiser) {
+		if (p_step_function) {
+			p_step_function(0.8, TTR("Denoising"), p_bake_userdata, true);
+		}
+
+		Ref<LightmapDenoiser> denoiser = LightmapDenoiser::create();
+		if (denoiser.is_valid()) {
+			for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) {
+				Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i);
+				Ref<Image> img;
+				img.instance();
+				img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
+
+				Ref<Image> denoised = denoiser->denoise_image(img);
+				if (denoised != img) {
+					denoised->convert(Image::FORMAT_RGBAH);
+					Vector<uint8_t> ds = denoised->get_data();
+					denoised.unref(); //avoid copy on write
+					{ //restore alpha
+						uint32_t count = s.size() / 2; //uint16s
+						const uint16_t *src = (const uint16_t *)s.ptr();
+						uint16_t *dst = (uint16_t *)ds.ptrw();
+						for (uint32_t j = 0; j < count; j += 4) {
+							dst[j + 3] = src[j + 3];
+						}
+					}
+					rd->texture_update(light_accum_tex, i, ds, true);
+				}
+			}
+		}
+	}
+
+#ifdef DEBUG_TEXTURES
+
+	for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) {
+		Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i);
+		Ref<Image> img;
+		img.instance();
+		img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
+		img->convert(Image::FORMAT_RGBA8);
+		img->save_png("res://4_light_secondary_" + itos(i) + ".png");
+	}
+#endif
+
+	/* DILATE LIGHTMAP */
+	{
+
+		SWAP(light_accum_tex, light_accum_tex2);
+
+		Vector<RD::Uniform> uniforms;
+		{
+			{
+
+				RD::Uniform u;
+				u.type = RD::UNIFORM_TYPE_IMAGE;
+				u.binding = 0;
+				u.ids.push_back(light_accum_tex);
+				uniforms.push_back(u);
+			}
+			{
+				RD::Uniform u;
+				u.type = RD::UNIFORM_TYPE_TEXTURE;
+				u.binding = 1;
+				u.ids.push_back(light_accum_tex2);
+				uniforms.push_back(u);
+			}
+		}
+
+		RID dilate_uniform_set = rd->uniform_set_create(uniforms, compute_shader_dilate, 1);
+
+		RD::ComputeListID compute_list = rd->compute_list_begin();
+		rd->compute_list_bind_compute_pipeline(compute_list, compute_shader_dilate_pipeline);
+		rd->compute_list_bind_uniform_set(compute_list, compute_base_uniform_set, 0);
+		rd->compute_list_bind_uniform_set(compute_list, dilate_uniform_set, 1);
+		push_constant.region_ofs[0] = 0;
+		push_constant.region_ofs[1] = 0;
+		group_size = Vector3i((atlas_size.x - 1) / 8 + 1, (atlas_size.y - 1) / 8 + 1, 1); //restore group size
+
+		for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) {
+			push_constant.atlas_slice = i;
+			rd->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant));
+			rd->compute_list_dispatch(compute_list, group_size.x, group_size.y, group_size.z);
+			//no barrier, let them run all together
+		}
+		rd->compute_list_end();
+	}
+
+#ifdef DEBUG_TEXTURES
+
+	for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) {
+		Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i);
+		Ref<Image> img;
+		img.instance();
+		img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
+		img->convert(Image::FORMAT_RGBA8);
+		img->save_png("res://5_dilated_" + itos(i) + ".png");
+	}
+#endif
+
+	/* BLEND SEAMS */
+	//shaders
+	Ref<RDShaderFile> blendseams_shader;
+	blendseams_shader.instance();
+	err = blendseams_shader->parse_versions_from_text(lm_blendseams_shader_glsl);
+	if (err != OK) {
+		FREE_TEXTURES
+		FREE_BUFFERS
+		FREE_RASTER_RESOURCES
+		FREE_COMPUTE_RESOURCES
+		memdelete(rd);
+		blendseams_shader->print_errors("blendseams_shader");
+	}
+	ERR_FAIL_COND_V(err != OK, BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES);
+
+	RID blendseams_line_raster_shader = rd->shader_create_from_bytecode(blendseams_shader->get_bytecode("lines"));
+
+	ERR_FAIL_COND_V(blendseams_line_raster_shader.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES);
+
+	RID blendseams_triangle_raster_shader = rd->shader_create_from_bytecode(blendseams_shader->get_bytecode("triangles"));
+
+	ERR_FAIL_COND_V(blendseams_triangle_raster_shader.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES);
+
+#define FREE_BLENDSEAMS_RESOURCES            \
+	rd->free(blendseams_line_raster_shader); \
+	rd->free(blendseams_triangle_raster_shader);
+
+	{
+
+		//pre copy
+		for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) {
+			rd->texture_copy(light_accum_tex, light_accum_tex2, Vector3(), Vector3(), Vector3(atlas_size.width, atlas_size.height, 1), 0, 0, i, i, true);
+		}
+
+		Vector<RID> framebuffers;
+		for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) {
+			RID slice_tex = rd->texture_create_shared_from_slice(RD::TextureView(), light_accum_tex, i, 0);
+			Vector<RID> fb;
+			fb.push_back(slice_tex);
+			fb.push_back(raster_depth_buffer);
+			framebuffers.push_back(rd->framebuffer_create(fb));
+		}
+
+		Vector<RD::Uniform> uniforms;
+		{
+			{
+
+				RD::Uniform u;
+				u.type = RD::UNIFORM_TYPE_TEXTURE;
+				u.binding = 0;
+				u.ids.push_back(light_accum_tex2);
+				uniforms.push_back(u);
+			}
+		}
+
+		RID blendseams_raster_uniform = rd->uniform_set_create(uniforms, blendseams_line_raster_shader, 1);
+
+		bool debug = false;
+		RD::PipelineColorBlendState bs = RD::PipelineColorBlendState::create_blend(1);
+		bs.attachments.write[0].src_alpha_blend_factor = RD::BLEND_FACTOR_ZERO;
+		bs.attachments.write[0].dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
+
+		RD::PipelineDepthStencilState ds;
+		ds.enable_depth_test = true;
+		ds.enable_depth_write = true;
+		ds.depth_compare_operator = RD::COMPARE_OP_LESS; //so it does not render same pixel twice, this avoids wrong blending
+
+		RID blendseams_line_raster_pipeline = rd->render_pipeline_create(blendseams_line_raster_shader, rd->framebuffer_get_format(framebuffers[0]), RD::INVALID_FORMAT_ID, RD::RENDER_PRIMITIVE_LINES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), ds, bs, 0);
+		RID blendseams_triangle_raster_pipeline = rd->render_pipeline_create(blendseams_triangle_raster_shader, rd->framebuffer_get_format(framebuffers[0]), RD::INVALID_FORMAT_ID, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), ds, bs, 0);
+
+		uint32_t seam_offset = 0;
+		uint32_t triangle_offset = 0;
+
+		Vector<Color> clear_colors;
+		clear_colors.push_back(Color(0, 0, 0, 1));
+		for (int i = 0; i < atlas_slices; i++) {
+
+			int subslices = (p_bake_sh ? 4 : 1);
+			for (int k = 0; k < subslices; k++) {
+
+				RasterSeamsPushConstant seams_push_constant;
+				seams_push_constant.slice = uint32_t(i * subslices + k);
+				seams_push_constant.debug = debug;
+
+				RD::DrawListID draw_list = rd->draw_list_begin(framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors);
+
+				rd->draw_list_bind_uniform_set(draw_list, raster_base_uniform, 0);
+				rd->draw_list_bind_uniform_set(draw_list, blendseams_raster_uniform, 1);
+
+				const int uv_offset_count = 9;
+				static const Vector3 uv_offsets[uv_offset_count] = {
+					Vector3(0, 0, 0.5), //using zbuffer, so go inwards-outwards
+					Vector3(0, 1, 0.2),
+					Vector3(0, -1, 0.2),
+					Vector3(1, 0, 0.2),
+					Vector3(-1, 0, 0.2),
+					Vector3(-1, -1, 0.1),
+					Vector3(1, -1, 0.1),
+					Vector3(1, 1, 0.1),
+					Vector3(-1, 1, 0.1),
+				};
+
+				/* step 1 use lines to blend the edges */
+				{
+					seams_push_constant.base_index = seam_offset;
+					rd->draw_list_bind_render_pipeline(draw_list, blendseams_line_raster_pipeline);
+					seams_push_constant.uv_offset[0] = uv_offsets[0].x / float(atlas_size.width);
+					seams_push_constant.uv_offset[1] = uv_offsets[0].y / float(atlas_size.height);
+					seams_push_constant.blend = uv_offsets[0].z;
+
+					rd->draw_list_set_push_constant(draw_list, &seams_push_constant, sizeof(RasterSeamsPushConstant));
+					rd->draw_list_draw(draw_list, false, 1, slice_seam_count[i] * 4);
+				}
+
+				/* step 2 use triangles to mask the interior */
+
+				{
+					seams_push_constant.base_index = triangle_offset;
+					rd->draw_list_bind_render_pipeline(draw_list, blendseams_triangle_raster_pipeline);
+					seams_push_constant.blend = 0; //do not draw them, just fill the z-buffer so its used as a mask
+
+					rd->draw_list_set_push_constant(draw_list, &seams_push_constant, sizeof(RasterSeamsPushConstant));
+					rd->draw_list_draw(draw_list, false, 1, slice_triangle_count[i] * 3);
+				}
+				/* step 3 blend around the triangle */
+
+				rd->draw_list_bind_render_pipeline(draw_list, blendseams_line_raster_pipeline);
+
+				for (int j = 1; j < uv_offset_count; j++) {
+
+					seams_push_constant.base_index = seam_offset;
+					seams_push_constant.uv_offset[0] = uv_offsets[j].x / float(atlas_size.width);
+					seams_push_constant.uv_offset[1] = uv_offsets[j].y / float(atlas_size.height);
+					seams_push_constant.blend = uv_offsets[0].z;
+
+					rd->draw_list_set_push_constant(draw_list, &seams_push_constant, sizeof(RasterSeamsPushConstant));
+					rd->draw_list_draw(draw_list, false, 1, slice_seam_count[i] * 4);
+				}
+				rd->draw_list_end();
+			}
+			seam_offset += slice_seam_count[i];
+			triangle_offset += slice_triangle_count[i];
+		}
+	}
+
+#ifdef DEBUG_TEXTURES
+
+	for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) {
+		Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i);
+		Ref<Image> img;
+		img.instance();
+		img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
+		img->convert(Image::FORMAT_RGBA8);
+		img->save_png("res://5_blendseams" + itos(i) + ".png");
+	}
+#endif
+	if (p_step_function) {
+		p_step_function(0.9, TTR("Retrieving textures"), p_bake_userdata, true);
+	}
+
+	for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) {
+		Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i);
+		Ref<Image> img;
+		img.instance();
+		img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
+		img->convert(Image::FORMAT_RGBH); //remove alpha
+		bake_textures.push_back(img);
+	}
+
+	if (probe_positions.size() > 0) {
+		probe_values.resize(probe_positions.size() * 9);
+		Vector<uint8_t> probe_data = rd->buffer_get_data(light_probe_buffer);
+		copymem(probe_values.ptrw(), probe_data.ptr(), probe_data.size());
+		rd->free(light_probe_buffer);
+
+#ifdef DEBUG_TEXTURES
+		{
+			Ref<Image> img2;
+			img2.instance();
+			img2->create(probe_values.size(), 1, false, Image::FORMAT_RGBAF, probe_data);
+			img2->save_png("res://6_lightprobes.png");
+		}
+#endif
+	}
+
+	FREE_TEXTURES
+	FREE_BUFFERS
+	FREE_RASTER_RESOURCES
+	FREE_COMPUTE_RESOURCES
+	FREE_BLENDSEAMS_RESOURCES
+
+	memdelete(rd);
+
+	return BAKE_OK;
+}
+
+int LightmapperRD::get_bake_texture_count() const {
+	return bake_textures.size();
+}
+Ref<Image> LightmapperRD::get_bake_texture(int p_index) const {
+	ERR_FAIL_INDEX_V(p_index, bake_textures.size(), Ref<Image>());
+	return bake_textures[p_index];
+}
+int LightmapperRD::get_bake_mesh_count() const {
+	return mesh_instances.size();
+}
+Variant LightmapperRD::get_bake_mesh_userdata(int p_index) const {
+	ERR_FAIL_INDEX_V(p_index, mesh_instances.size(), Variant());
+	return mesh_instances[p_index].data.userdata;
+}
+Rect2 LightmapperRD::get_bake_mesh_uv_scale(int p_index) const {
+
+	ERR_FAIL_COND_V(bake_textures.size() == 0, Rect2());
+	Rect2 uv_ofs;
+	Vector2 atlas_size = Vector2(bake_textures[0]->get_width(), bake_textures[0]->get_height());
+	uv_ofs.position = Vector2(mesh_instances[p_index].offset) / atlas_size;
+	uv_ofs.size = Vector2(mesh_instances[p_index].data.albedo_on_uv2->get_width(), mesh_instances[p_index].data.albedo_on_uv2->get_height()) / atlas_size;
+	return uv_ofs;
+}
+int LightmapperRD::get_bake_mesh_texture_slice(int p_index) const {
+	ERR_FAIL_INDEX_V(p_index, mesh_instances.size(), Variant());
+	return mesh_instances[p_index].slice;
+}
+
+int LightmapperRD::get_bake_probe_count() const {
+	return probe_positions.size();
+}
+
+Vector3 LightmapperRD::get_bake_probe_point(int p_probe) const {
+	ERR_FAIL_INDEX_V(p_probe, probe_positions.size(), Variant());
+	return Vector3(probe_positions[p_probe].position[0], probe_positions[p_probe].position[1], probe_positions[p_probe].position[2]);
+}
+
+Vector<Color> LightmapperRD::get_bake_probe_sh(int p_probe) const {
+	ERR_FAIL_INDEX_V(p_probe, probe_positions.size(), Vector<Color>());
+	Vector<Color> ret;
+	ret.resize(9);
+	copymem(ret.ptrw(), &probe_values[p_probe * 9], sizeof(Color) * 9);
+	return ret;
+}
+
+LightmapperRD::LightmapperRD() {
+}

+ 229 - 0
modules/lightmapper_rd/lightmapper_rd.h

@@ -0,0 +1,229 @@
+#ifndef LIGHTMAPPER_RD_H
+#define LIGHTMAPPER_RD_H
+
+#include "core/local_vector.h"
+#include "scene/3d/lightmapper.h"
+#include "scene/resources/mesh.h"
+#include "servers/rendering/rendering_device.h"
+
+class LightmapperRD : public Lightmapper {
+	GDCLASS(LightmapperRD, Lightmapper)
+
+	struct MeshInstance {
+		MeshData data;
+		int slice = 0;
+		Vector2i offset;
+	};
+
+	struct Light {
+		float position[3];
+		uint32_t type = LIGHT_TYPE_DIRECTIONAL;
+		float direction[3];
+		float energy;
+		float color[3];
+		float size;
+		float range;
+		float attenuation;
+		float spot_angle;
+		float spot_attenuation;
+		uint32_t static_bake;
+		uint32_t pad[3];
+
+		bool operator<(const Light &p_light) const {
+			return type < p_light.type;
+		}
+	};
+
+	struct Vertex {
+		float position[3];
+		float normal_z;
+		float uv[2];
+		float normal_xy[2];
+
+		bool operator==(const Vertex &p_vtx) const {
+			return (position[0] == p_vtx.position[0]) &&
+				   (position[1] == p_vtx.position[1]) &&
+				   (position[2] == p_vtx.position[2]) &&
+				   (uv[0] == p_vtx.uv[0]) &&
+				   (uv[1] == p_vtx.uv[1]) &&
+				   (normal_xy[0] == p_vtx.normal_xy[0]) &&
+				   (normal_xy[1] == p_vtx.normal_xy[1]) &&
+				   (normal_z == p_vtx.normal_z);
+		}
+	};
+
+	struct Edge {
+		Vector3 a;
+		Vector3 b;
+		Vector3 na;
+		Vector3 nb;
+		bool operator==(const Edge &p_seam) const {
+			return a == p_seam.a && b == p_seam.b && na == p_seam.na && nb == p_seam.nb;
+		}
+		Edge() {
+		}
+
+		Edge(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_na, const Vector3 &p_nb) {
+			a = p_a;
+			b = p_b;
+			na = p_na;
+			nb = p_nb;
+		}
+	};
+
+	struct Probe {
+		float position[4];
+	};
+
+	Vector<Probe> probe_positions;
+
+	struct EdgeHash {
+		_FORCE_INLINE_ static uint32_t hash(const Edge &p_edge) {
+			uint32_t h = hash_djb2_one_float(p_edge.a.x);
+			h = hash_djb2_one_float(p_edge.a.y, h);
+			h = hash_djb2_one_float(p_edge.a.z, h);
+			h = hash_djb2_one_float(p_edge.b.x, h);
+			h = hash_djb2_one_float(p_edge.b.y, h);
+			h = hash_djb2_one_float(p_edge.b.z, h);
+			return h;
+		}
+	};
+	struct EdgeUV2 {
+		Vector2 a;
+		Vector2 b;
+		Vector2i indices;
+		bool operator==(const EdgeUV2 &p_uv2) const {
+			return a == p_uv2.a && b == p_uv2.b;
+		}
+		bool seam_found = false;
+		EdgeUV2(Vector2 p_a, Vector2 p_b, Vector2i p_indices) {
+			a = p_a;
+			b = p_b;
+			indices = p_indices;
+		}
+		EdgeUV2() {}
+	};
+
+	struct Seam {
+		Vector2i a;
+		Vector2i b;
+		uint32_t slice;
+		bool operator<(const Seam &p_seam) const {
+			return slice < p_seam.slice;
+		}
+	};
+
+	struct VertexHash {
+		_FORCE_INLINE_ static uint32_t hash(const Vertex &p_vtx) {
+			uint32_t h = hash_djb2_one_float(p_vtx.position[0]);
+			h = hash_djb2_one_float(p_vtx.position[1], h);
+			h = hash_djb2_one_float(p_vtx.position[2], h);
+			h = hash_djb2_one_float(p_vtx.uv[0], h);
+			h = hash_djb2_one_float(p_vtx.uv[1], h);
+			h = hash_djb2_one_float(p_vtx.normal_xy[0], h);
+			h = hash_djb2_one_float(p_vtx.normal_xy[1], h);
+			h = hash_djb2_one_float(p_vtx.normal_z, h);
+			return h;
+		}
+	};
+
+	struct Box {
+		float min_bounds[3];
+		float pad0;
+		float max_bounds[3];
+		float pad1;
+	};
+
+	struct Triangle {
+		uint32_t indices[3];
+		uint32_t slice;
+		bool operator<(const Triangle &p_triangle) const {
+			return slice < p_triangle.slice;
+		}
+	};
+
+	Vector<MeshInstance> mesh_instances;
+
+	Vector<Light> lights;
+
+	struct TriangleSort {
+		uint32_t cell_index;
+		uint32_t triangle_index;
+		bool operator<(const TriangleSort &p_triangle_sort) const {
+			return cell_index < p_triangle_sort.cell_index; //sorting by triangle index in this case makes no sense
+		}
+	};
+
+	void _plot_triangle_into_triangle_index_list(int p_size, const Vector3i &p_ofs, const AABB &p_bounds, const Vector3 p_points[], uint32_t p_triangle_index, LocalVector<TriangleSort> &triangles, uint32_t p_grid_size);
+
+	struct RasterPushConstant {
+		float atlas_size[2];
+		float uv_offset[2];
+		float to_cell_size[3];
+		uint32_t base_triangle;
+		float to_cell_offset[3];
+		float bias;
+		int32_t grid_size[3];
+		uint32_t pad2;
+	};
+
+	struct RasterSeamsPushConstant {
+
+		uint32_t base_index;
+		uint32_t slice;
+		float uv_offset[2];
+		uint32_t debug;
+		float blend;
+		uint32_t pad[2];
+	};
+
+	struct PushConstant {
+		int32_t atlas_size[2];
+		uint32_t ray_count;
+		uint32_t ray_to;
+
+		float world_size[3];
+		float bias;
+
+		float to_cell_offset[3];
+		uint32_t ray_from;
+
+		float to_cell_size[3];
+		uint32_t light_count;
+
+		int32_t grid_size;
+		int32_t atlas_slice;
+		int32_t region_ofs[2];
+
+		float environment_xform[12];
+	};
+
+	Vector<Ref<Image>> bake_textures;
+	Vector<Color> probe_values;
+
+	BakeError _blit_meshes_into_atlas(int p_max_texture_size, Vector<Ref<Image>> &albedo_images, Vector<Ref<Image>> &emission_images, AABB &bounds, Size2i &atlas_size, int &atlas_slices, BakeStepFunc p_step_function, void *p_bake_userdata);
+	void _create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_size, Vector<Probe> &probe_positions, GenerateProbes p_generate_probes, Vector<int> &slice_triangle_count, Vector<int> &slice_seam_count, RID &vertex_buffer, RID &triangle_buffer, RID &box_buffer, RID &lights_buffer, RID &triangle_cell_indices_buffer, RID &probe_positions_buffer, RID &grid_texture, RID &grid_texture_sdf, RID &seams_buffer, BakeStepFunc p_step_function, void *p_bake_userdata);
+	void _raster_geometry(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, int grid_size, AABB bounds, float p_bias, Vector<int> slice_triangle_count, RID position_tex, RID unocclude_tex, RID normal_tex, RID raster_depth_buffer, RID rasterize_shader, RID raster_base_uniform);
+
+public:
+	virtual void add_mesh(const MeshData &p_mesh);
+	virtual void add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_angular_distance);
+	virtual void add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size);
+	virtual void add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size);
+	virtual void add_probe(const Vector3 &p_position);
+	virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_bake_userdata = nullptr);
+
+	int get_bake_texture_count() const;
+	Ref<Image> get_bake_texture(int p_index) const;
+	int get_bake_mesh_count() const;
+	Variant get_bake_mesh_userdata(int p_index) const;
+	Rect2 get_bake_mesh_uv_scale(int p_index) const;
+	int get_bake_mesh_texture_slice(int p_index) const;
+	int get_bake_probe_count() const;
+	Vector3 get_bake_probe_point(int p_probe) const;
+	Vector<Color> get_bake_probe_sh(int p_probe) const;
+
+	LightmapperRD();
+};
+
+#endif // LIGHTMAPPER_H

+ 117 - 0
modules/lightmapper_rd/lm_blendseams.glsl

@@ -0,0 +1,117 @@
+/* clang-format off */
+[versions]
+
+lines = "#define MODE_LINES"
+triangles = "#define MODE_TRIANGLES"
+
+[vertex]
+
+#version 450
+
+VERSION_DEFINES
+
+#include "lm_common_inc.glsl"
+
+				/* clang-format on */
+
+				layout(push_constant, binding = 0, std430) uniform Params {
+					uint base_index;
+					uint slice;
+					vec2 uv_offset;
+					bool debug;
+					float blend;
+					uint pad[2];
+				} params;
+
+layout(location = 0) out vec3 uv_interp;
+
+void main() {
+
+#ifdef MODE_TRIANGLES
+
+	uint triangle_idx = params.base_index + gl_VertexIndex / 3;
+	uint triangle_subidx = gl_VertexIndex % 3;
+
+	vec2 uv;
+	if (triangle_subidx == 0) {
+		uv = vertices.data[triangles.data[triangle_idx].indices.x].uv;
+	} else if (triangle_subidx == 1) {
+		uv = vertices.data[triangles.data[triangle_idx].indices.y].uv;
+	} else {
+		uv = vertices.data[triangles.data[triangle_idx].indices.z].uv;
+	}
+
+	uv_interp = vec3(uv, float(params.slice));
+	gl_Position = vec4((uv + params.uv_offset) * 2.0 - 1.0, 0.0001, 1.0);
+
+#endif
+
+#ifdef MODE_LINES
+	uint seam_idx = params.base_index + gl_VertexIndex / 4;
+	uint seam_subidx = gl_VertexIndex % 4;
+
+	uint src_idx;
+	uint dst_idx;
+
+	if (seam_subidx == 0) {
+		src_idx = seams.data[seam_idx].b.x;
+		dst_idx = seams.data[seam_idx].a.x;
+	} else if (seam_subidx == 1) {
+		src_idx = seams.data[seam_idx].b.y;
+		dst_idx = seams.data[seam_idx].a.y;
+	} else if (seam_subidx == 2) {
+		src_idx = seams.data[seam_idx].a.x;
+		dst_idx = seams.data[seam_idx].b.x;
+	} else if (seam_subidx == 3) {
+		src_idx = seams.data[seam_idx].a.y;
+		dst_idx = seams.data[seam_idx].b.y;
+	}
+
+	vec2 src_uv = vertices.data[src_idx].uv;
+	vec2 dst_uv = vertices.data[dst_idx].uv + params.uv_offset;
+
+	uv_interp = vec3(src_uv, float(params.slice));
+	gl_Position = vec4(dst_uv * 2.0 - 1.0, 0.0001, 1.0);
+	;
+#endif
+}
+
+/* clang-format off */
+[fragment]
+
+#version 450
+
+VERSION_DEFINES
+
+#include "lm_common_inc.glsl"
+
+				/* clang-format on */
+
+				layout(push_constant, binding = 0, std430) uniform Params {
+					uint base_index;
+					uint slice;
+					vec2 uv_offset;
+					bool debug;
+					float blend;
+					uint pad[2];
+				} params;
+
+layout(location = 0) in vec3 uv_interp;
+
+layout(location = 0) out vec4 dst_color;
+
+layout(set = 1, binding = 0) uniform texture2DArray src_color_tex;
+
+void main() {
+
+	if (params.debug) {
+#ifdef MODE_TRIANGLES
+		dst_color = vec4(1, 0, 1, 1);
+#else
+		dst_color = vec4(1, 1, 0, 1);
+#endif
+	} else {
+		vec4 src_color = textureLod(sampler2DArray(src_color_tex, linear_sampler), uv_interp, 0.0);
+		dst_color = vec4(src_color.rgb, params.blend); //mix
+	}
+}

+ 92 - 0
modules/lightmapper_rd/lm_common_inc.glsl

@@ -0,0 +1,92 @@
+
+/* SET 0, static data that does not change between any call */
+
+struct Vertex {
+	vec3 position;
+	float normal_z;
+	vec2 uv;
+	vec2 normal_xy;
+};
+
+layout(set = 0, binding = 1, std430) restrict readonly buffer Vertices {
+	Vertex data[];
+}
+vertices;
+
+struct Triangle {
+	uvec3 indices;
+	uint slice;
+};
+
+layout(set = 0, binding = 2, std430) restrict readonly buffer Triangles {
+	Triangle data[];
+}
+triangles;
+
+struct Box {
+	vec3 min_bounds;
+	uint pad0;
+	vec3 max_bounds;
+	uint pad1;
+};
+
+layout(set = 0, binding = 3, std430) restrict readonly buffer Boxes {
+	Box data[];
+}
+boxes;
+
+layout(set = 0, binding = 4, std430) restrict readonly buffer GridIndices {
+	uint data[];
+}
+grid_indices;
+
+#define LIGHT_TYPE_DIRECTIONAL 0
+#define LIGHT_TYPE_OMNI 1
+#define LIGHT_TYPE_SPOT 2
+
+struct Light {
+	vec3 position;
+	uint type;
+
+	vec3 direction;
+	float energy;
+
+	vec3 color;
+	float size;
+
+	float range;
+	float attenuation;
+	float spot_angle;
+	float spot_attenuation;
+
+	bool static_bake;
+	uint pad[3];
+};
+
+layout(set = 0, binding = 5, std430) restrict readonly buffer Lights {
+	Light data[];
+}
+lights;
+
+struct Seam {
+	uvec2 a;
+	uvec2 b;
+};
+
+layout(set = 0, binding = 6, std430) restrict readonly buffer Seams {
+	Seam data[];
+}
+seams;
+
+layout(set = 0, binding = 7, std430) restrict readonly buffer Probes {
+	vec4 data[];
+}
+probe_positions;
+
+layout(set = 0, binding = 8) uniform utexture3D grid;
+layout(set = 0, binding = 9) uniform texture3D grid_sdf;
+
+layout(set = 0, binding = 10) uniform texture2DArray albedo_tex;
+layout(set = 0, binding = 11) uniform texture2DArray emission_tex;
+
+layout(set = 0, binding = 12) uniform sampler linear_sampler;

+ 657 - 0
modules/lightmapper_rd/lm_compute.glsl

@@ -0,0 +1,657 @@
+/* clang-format off */
+[versions]
+
+primary = "#define MODE_DIRECT_LIGHT"
+secondary = "#define MODE_BOUNCE_LIGHT"
+dilate = "#define MODE_DILATE"
+unocclude = "#define MODE_UNOCCLUDE"
+light_probes = "#define MODE_LIGHT_PROBES"
+
+[compute]
+
+#version 450
+
+VERSION_DEFINES
+
+// One 2D local group focusing in one layer at a time, though all
+// in parallel (no barriers) makes more sense than a 3D local group
+// as this can take more advantage of the cache for each group.
+
+#ifdef MODE_LIGHT_PROBES
+
+layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
+
+#else
+
+layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
+
+#endif
+
+#include "lm_common_inc.glsl"
+
+/* clang-format on */
+
+#ifdef MODE_LIGHT_PROBES
+
+layout(set = 1, binding = 0, std430) restrict buffer LightProbeData {
+	vec4 data[];
+}
+light_probes;
+
+layout(set = 1, binding = 1) uniform texture2DArray source_light;
+layout(set = 1, binding = 2) uniform texture2DArray source_direct_light; //also need the direct light, which was omitted
+layout(set = 1, binding = 3) uniform texture2D environment;
+#endif
+
+#ifdef MODE_UNOCCLUDE
+
+layout(rgba32f, set = 1, binding = 0) uniform restrict image2DArray position;
+layout(rgba32f, set = 1, binding = 1) uniform restrict readonly image2DArray unocclude;
+
+#endif
+
+#if defined(MODE_DIRECT_LIGHT) || defined(MODE_BOUNCE_LIGHT)
+
+layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly image2DArray dest_light;
+layout(set = 1, binding = 1) uniform texture2DArray source_light;
+layout(set = 1, binding = 2) uniform texture2DArray source_position;
+layout(set = 1, binding = 3) uniform texture2DArray source_normal;
+layout(rgba16f, set = 1, binding = 4) uniform restrict image2DArray accum_light;
+
+#endif
+
+#ifdef MODE_BOUNCE_LIGHT
+layout(rgba32f, set = 1, binding = 5) uniform restrict image2DArray bounce_accum;
+layout(set = 1, binding = 6) uniform texture2D environment;
+#endif
+#ifdef MODE_DIRECT_LIGHT
+layout(rgba32f, set = 1, binding = 5) uniform restrict writeonly image2DArray primary_dynamic;
+#endif
+
+#ifdef MODE_DILATE
+layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly image2DArray dest_light;
+layout(set = 1, binding = 1) uniform texture2DArray source_light;
+#endif
+
+layout(push_constant, binding = 0, std430) uniform Params {
+	ivec2 atlas_size; // x used for light probe mode total probes
+	uint ray_count;
+	uint ray_to;
+
+	vec3 world_size;
+	float bias;
+
+	vec3 to_cell_offset;
+	uint ray_from;
+
+	vec3 to_cell_size;
+	uint light_count;
+
+	int grid_size;
+	int atlas_slice;
+	ivec2 region_ofs;
+
+	mat3x4 env_transform;
+}
+params;
+
+//check it, but also return distance and barycentric coords (for uv lookup)
+bool ray_hits_triangle(vec3 from, vec3 dir, float max_dist, vec3 p0, vec3 p1, vec3 p2, out float r_distance, out vec3 r_barycentric) {
+
+	const vec3 e0 = p1 - p0;
+	const vec3 e1 = p0 - p2;
+	vec3 triangleNormal = cross(e1, e0);
+
+	const vec3 e2 = (1.0 / dot(triangleNormal, dir)) * (p0 - from);
+	const vec3 i = cross(dir, e2);
+
+	r_barycentric.y = dot(i, e1);
+	r_barycentric.z = dot(i, e0);
+	r_barycentric.x = 1.0 - (r_barycentric.z + r_barycentric.y);
+	r_distance = dot(triangleNormal, e2);
+	return (r_distance > params.bias) && (r_distance < max_dist) && all(greaterThanEqual(r_barycentric, vec3(0.0)));
+}
+
+bool trace_ray(vec3 p_from, vec3 p_to
+#if defined(MODE_BOUNCE_LIGHT) || defined(MODE_LIGHT_PROBES)
+		,
+		out uint r_triangle, out vec3 r_barycentric
+#endif
+#if defined(MODE_UNOCCLUDE)
+		,
+		out float r_distance, out vec3 r_normal
+#endif
+) {
+
+	/* world coords */
+
+	vec3 rel = p_to - p_from;
+	float rel_len = length(rel);
+	vec3 dir = normalize(rel);
+	vec3 inv_dir = 1.0 / dir;
+
+	/* cell coords */
+
+	vec3 from_cell = (p_from - params.to_cell_offset) * params.to_cell_size;
+	vec3 to_cell = (p_to - params.to_cell_offset) * params.to_cell_size;
+
+	//prepare DDA
+	vec3 rel_cell = to_cell - from_cell;
+	ivec3 icell = ivec3(from_cell);
+	ivec3 iendcell = ivec3(to_cell);
+	vec3 dir_cell = normalize(rel_cell);
+	vec3 delta = abs(1.0 / dir_cell); //vec3(length(rel_cell)) / rel_cell);
+	ivec3 step = ivec3(sign(rel_cell));
+	vec3 side = (sign(rel_cell) * (vec3(icell) - from_cell) + (sign(rel_cell) * 0.5) + 0.5) * delta;
+
+	uint iters = 0;
+	while (all(greaterThanEqual(icell, ivec3(0))) && all(lessThan(icell, ivec3(params.grid_size))) && iters < 1000) {
+
+		uvec2 cell_data = texelFetch(usampler3D(grid, linear_sampler), icell, 0).xy;
+		if (cell_data.x > 0) { //triangles here
+
+			bool hit = false;
+#if defined(MODE_UNOCCLUDE)
+			bool hit_backface = false;
+#endif
+			float best_distance = 1e20;
+
+			for (uint i = 0; i < cell_data.x; i++) {
+				uint tidx = grid_indices.data[cell_data.y + i];
+
+				//Ray-Box test
+				vec3 t0 = (boxes.data[tidx].min_bounds - p_from) * inv_dir;
+				vec3 t1 = (boxes.data[tidx].max_bounds - p_from) * inv_dir;
+				vec3 tmin = min(t0, t1), tmax = max(t0, t1);
+
+				if (max(tmin.x, max(tmin.y, tmin.z)) <= min(tmax.x, min(tmax.y, tmax.z))) {
+					continue; //ray box failed
+				}
+
+				//prepare triangle vertices
+				vec3 vtx0 = vertices.data[triangles.data[tidx].indices.x].position;
+				vec3 vtx1 = vertices.data[triangles.data[tidx].indices.y].position;
+				vec3 vtx2 = vertices.data[triangles.data[tidx].indices.z].position;
+#if defined(MODE_UNOCCLUDE)
+				vec3 normal = -normalize(cross((vtx0 - vtx1), (vtx0 - vtx2)));
+
+				bool backface = dot(normal, dir) >= 0.0;
+#endif
+				float distance;
+				vec3 barycentric;
+
+				if (ray_hits_triangle(p_from, dir, rel_len, vtx0, vtx1, vtx2, distance, barycentric)) {
+#ifdef MODE_DIRECT_LIGHT
+					return true; //any hit good
+#endif
+
+#if defined(MODE_UNOCCLUDE)
+					if (!backface) {
+						// the case of meshes having both a front and back face in the same plane is more common than
+						// expected, so if this is a front-face, bias it closer to the ray origin, so it always wins over the back-face
+						distance = max(params.bias, distance - params.bias);
+					}
+
+					hit = true;
+
+					if (distance < best_distance) {
+						hit_backface = backface;
+						best_distance = distance;
+						r_distance = distance;
+						r_normal = normal;
+					}
+
+#endif
+
+#if defined(MODE_BOUNCE_LIGHT) || defined(MODE_LIGHT_PROBES)
+
+					hit = true;
+					if (distance < best_distance) {
+						best_distance = distance;
+						r_triangle = tidx;
+						r_barycentric = barycentric;
+					}
+
+#endif
+				}
+			}
+#if defined(MODE_UNOCCLUDE)
+
+			if (hit) {
+				return hit_backface;
+			}
+#endif
+#if defined(MODE_BOUNCE_LIGHT) || defined(MODE_LIGHT_PROBES)
+			if (hit) {
+				return true;
+			}
+#endif
+		}
+
+		if (icell == iendcell) {
+			break;
+		}
+
+		bvec3 mask = lessThanEqual(side.xyz, min(side.yzx, side.zxy));
+		side += vec3(mask) * delta;
+		icell += ivec3(vec3(mask)) * step;
+
+		iters++;
+	}
+
+	return false;
+}
+
+const float PI = 3.14159265f;
+const float GOLDEN_ANGLE = PI * (3.0 - sqrt(5.0));
+
+vec3 vogel_hemisphere(uint p_index, uint p_count, float p_offset) {
+	float r = sqrt(float(p_index) + 0.5f) / sqrt(float(p_count));
+	float theta = float(p_index) * GOLDEN_ANGLE + p_offset;
+	float y = cos(r * PI * 0.5);
+	float l = sin(r * PI * 0.5);
+	return vec3(l * cos(theta), l * sin(theta), y);
+}
+
+float quick_hash(vec2 pos) {
+	return fract(sin(dot(pos * 19.19, vec2(49.5791, 97.413))) * 49831.189237);
+}
+
+void main() {
+
+#ifdef MODE_LIGHT_PROBES
+	int probe_index = int(gl_GlobalInvocationID.x);
+	if (probe_index >= params.atlas_size.x) { //too large, do nothing
+		return;
+	}
+
+#else
+	ivec2 atlas_pos = ivec2(gl_GlobalInvocationID.xy) + params.region_ofs;
+	if (any(greaterThanEqual(atlas_pos, params.atlas_size))) { //too large, do nothing
+		return;
+	}
+#endif
+
+#ifdef MODE_DIRECT_LIGHT
+
+	vec3 normal = texelFetch(sampler2DArray(source_normal, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0).xyz;
+	if (length(normal) < 0.5) {
+		return; //empty texel, no process
+	}
+	vec3 position = texelFetch(sampler2DArray(source_position, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0).xyz;
+
+	//go through all lights
+	//start by own light (emissive)
+	vec3 static_light = vec3(0.0);
+	vec3 dynamic_light = vec3(0.0);
+
+#ifdef USE_SH_LIGHTMAPS
+	vec4 sh_accum[4] = vec4[](
+			vec4(0.0, 0.0, 0.0, 1.0),
+			vec4(0.0, 0.0, 0.0, 1.0),
+			vec4(0.0, 0.0, 0.0, 1.0),
+			vec4(0.0, 0.0, 0.0, 1.0));
+#endif
+
+	for (uint i = 0; i < params.light_count; i++) {
+
+		vec3 light_pos;
+		float attenuation;
+		if (lights.data[i].type == LIGHT_TYPE_DIRECTIONAL) {
+			vec3 light_vec = lights.data[i].direction;
+			light_pos = position - light_vec * length(params.world_size);
+			attenuation = 1.0;
+		} else {
+			light_pos = lights.data[i].position;
+			float d = distance(position, light_pos);
+			if (d > lights.data[i].range) {
+				continue;
+			}
+
+			d /= lights.data[i].range;
+
+			attenuation = pow(max(1.0 - d, 0.0), lights.data[i].attenuation);
+
+			if (lights.data[i].type == LIGHT_TYPE_SPOT) {
+
+				vec3 rel = normalize(position - light_pos);
+				float angle = acos(dot(rel, lights.data[i].direction));
+				if (angle > lights.data[i].spot_angle) {
+					continue; //invisible, dont try
+				}
+
+				float d = clamp(angle / lights.data[i].spot_angle, 0, 1);
+				attenuation *= pow(1.0 - d, lights.data[i].spot_attenuation);
+			}
+		}
+
+		vec3 light_dir = normalize(light_pos - position);
+		attenuation *= max(0.0, dot(normal, light_dir));
+
+		if (attenuation <= 0.0001) {
+			continue; //no need to do anything
+		}
+
+		if (!trace_ray(position + light_dir * params.bias, light_pos)) {
+			vec3 light = lights.data[i].color * lights.data[i].energy * attenuation;
+			if (lights.data[i].static_bake) {
+				static_light += light;
+#ifdef USE_SH_LIGHTMAPS
+
+				float c[4] = float[](
+						0.282095, //l0
+						0.488603 * light_dir.y, //l1n1
+						0.488603 * light_dir.z, //l1n0
+						0.488603 * light_dir.x //l1p1
+				);
+
+				for (uint j = 0; j < 4; j++) {
+					sh_accum[j].rgb += light * c[j] * (1.0 / 3.0);
+				}
+#endif
+
+			} else {
+				dynamic_light += light;
+			}
+		}
+	}
+
+	vec3 albedo = texelFetch(sampler2DArray(albedo_tex, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0).rgb;
+	vec3 emissive = texelFetch(sampler2DArray(emission_tex, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0).rgb;
+
+	dynamic_light *= albedo; //if it will bounce, must multiply by albedo
+	dynamic_light += emissive;
+
+	//keep for lightprobes
+	imageStore(primary_dynamic, ivec3(atlas_pos, params.atlas_slice), vec4(dynamic_light, 1.0));
+
+	dynamic_light += static_light * albedo; //send for bounces
+	imageStore(dest_light, ivec3(atlas_pos, params.atlas_slice), vec4(dynamic_light, 1.0));
+
+#ifdef USE_SH_LIGHTMAPS
+	//keep for adding at the end
+	imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice * 4 + 0), sh_accum[0]);
+	imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice * 4 + 1), sh_accum[1]);
+	imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice * 4 + 2), sh_accum[2]);
+	imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice * 4 + 3), sh_accum[3]);
+
+#else
+	imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice), vec4(static_light, 1.0));
+#endif
+
+#endif
+
+#ifdef MODE_BOUNCE_LIGHT
+
+	vec3 normal = texelFetch(sampler2DArray(source_normal, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0).xyz;
+	if (length(normal) < 0.5) {
+		return; //empty texel, no process
+	}
+
+	vec3 position = texelFetch(sampler2DArray(source_position, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0).xyz;
+
+	vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);
+	vec3 tangent = normalize(cross(v0, normal));
+	vec3 bitangent = normalize(cross(tangent, normal));
+	mat3 normal_mat = mat3(tangent, bitangent, normal);
+
+#ifdef USE_SH_LIGHTMAPS
+	vec4 sh_accum[4] = vec4[](
+			vec4(0.0, 0.0, 0.0, 1.0),
+			vec4(0.0, 0.0, 0.0, 1.0),
+			vec4(0.0, 0.0, 0.0, 1.0),
+			vec4(0.0, 0.0, 0.0, 1.0));
+#endif
+	vec3 light_average = vec3(0.0);
+	for (uint i = params.ray_from; i < params.ray_to; i++) {
+		vec3 ray_dir = normal_mat * vogel_hemisphere(i, params.ray_count, quick_hash(vec2(atlas_pos)));
+
+		uint tidx;
+		vec3 barycentric;
+
+		vec3 light;
+		if (trace_ray(position + ray_dir * params.bias, position + ray_dir * length(params.world_size), tidx, barycentric)) {
+			//hit a triangle
+			vec2 uv0 = vertices.data[triangles.data[tidx].indices.x].uv;
+			vec2 uv1 = vertices.data[triangles.data[tidx].indices.y].uv;
+			vec2 uv2 = vertices.data[triangles.data[tidx].indices.z].uv;
+			vec3 uvw = vec3(barycentric.x * uv0 + barycentric.y * uv1 + barycentric.z * uv2, float(triangles.data[tidx].slice));
+
+			light = textureLod(sampler2DArray(source_light, linear_sampler), uvw, 0.0).rgb;
+		} else {
+			//did not hit a triangle, reach out for the sky
+			vec3 sky_dir = normalize(mat3(params.env_transform) * ray_dir);
+
+			vec2 st = vec2(
+					atan(sky_dir.x, sky_dir.z),
+					acos(sky_dir.y));
+
+			if (st.x < 0.0)
+				st.x += PI * 2.0;
+
+			st /= vec2(PI * 2.0, PI);
+
+			light = textureLod(sampler2D(environment, linear_sampler), st, 0.0).rgb;
+		}
+
+		light_average += light;
+
+#ifdef USE_SH_LIGHTMAPS
+
+		float c[4] = float[](
+				0.282095, //l0
+				0.488603 * ray_dir.y, //l1n1
+				0.488603 * ray_dir.z, //l1n0
+				0.488603 * ray_dir.x //l1p1
+		);
+
+		for (uint j = 0; j < 4; j++) {
+			sh_accum[j].rgb += light * c[j] * (8.0 / float(params.ray_count));
+		}
+#endif
+	}
+
+	vec3 light_total;
+	if (params.ray_from == 0) {
+		light_total = vec3(0.0);
+	} else {
+		light_total = imageLoad(bounce_accum, ivec3(atlas_pos, params.atlas_slice)).rgb;
+	}
+
+	light_total += light_average;
+
+#ifdef USE_SH_LIGHTMAPS
+
+	for (int i = 0; i < 4; i++) {
+		vec4 accum = imageLoad(accum_light, ivec3(atlas_pos, params.atlas_slice * 4 + i));
+		accum.rgb += sh_accum[i].rgb;
+		imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice * 4 + i), accum);
+	}
+
+#endif
+	if (params.ray_to == params.ray_count) {
+		light_total /= float(params.ray_count);
+		imageStore(dest_light, ivec3(atlas_pos, params.atlas_slice), vec4(light_total, 1.0));
+#ifndef USE_SH_LIGHTMAPS
+		vec4 accum = imageLoad(accum_light, ivec3(atlas_pos, params.atlas_slice));
+		accum.rgb += light_total;
+		imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice), accum);
+#endif
+	} else {
+		imageStore(bounce_accum, ivec3(atlas_pos, params.atlas_slice), vec4(light_total, 1.0));
+	}
+
+#endif
+
+#ifdef MODE_UNOCCLUDE
+
+	//texel_size = 0.5;
+	//compute tangents
+
+	vec4 position_alpha = imageLoad(position, ivec3(atlas_pos, params.atlas_slice));
+	if (position_alpha.a < 0.5) {
+		return;
+	}
+
+	vec3 vertex_pos = position_alpha.xyz;
+	vec4 normal_tsize = imageLoad(unocclude, ivec3(atlas_pos, params.atlas_slice));
+
+	vec3 face_normal = normal_tsize.xyz;
+	float texel_size = normal_tsize.w;
+
+	vec3 v0 = abs(face_normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);
+	vec3 tangent = normalize(cross(v0, face_normal));
+	vec3 bitangent = normalize(cross(tangent, face_normal));
+	vec3 base_pos = vertex_pos + face_normal * params.bias; //raise a bit
+
+	vec3 rays[4] = vec3[](tangent, bitangent, -tangent, -bitangent);
+	float min_d = 1e20;
+	for (int i = 0; i < 4; i++) {
+		vec3 ray_to = base_pos + rays[i] * texel_size;
+		float d;
+		vec3 norm;
+
+		if (trace_ray(base_pos, ray_to, d, norm)) {
+
+			if (d < min_d) {
+				vertex_pos = base_pos + rays[i] * d + norm * params.bias * 10.0; //this bias needs to be greater than the regular bias, because otherwise later, rays will go the other side when pointing back.
+				min_d = d;
+			}
+		}
+	}
+
+	position_alpha.xyz = vertex_pos;
+
+	imageStore(position, ivec3(atlas_pos, params.atlas_slice), position_alpha);
+
+#endif
+
+#ifdef MODE_LIGHT_PROBES
+
+	vec3 position = probe_positions.data[probe_index].xyz;
+
+	vec4 probe_sh_accum[9] = vec4[](
+			vec4(0.0),
+			vec4(0.0),
+			vec4(0.0),
+			vec4(0.0),
+			vec4(0.0),
+			vec4(0.0),
+			vec4(0.0),
+			vec4(0.0),
+			vec4(0.0));
+
+	for (uint i = params.ray_from; i < params.ray_to; i++) {
+		vec3 ray_dir = vogel_hemisphere(i, params.ray_count, quick_hash(vec2(float(probe_index), 0.0)));
+		if (bool(i & 1)) {
+			//throw to both sides, so alternate them
+			ray_dir.z *= -1.0;
+		}
+
+		uint tidx;
+		vec3 barycentric;
+		vec3 light;
+
+		if (trace_ray(position + ray_dir * params.bias, position + ray_dir * length(params.world_size), tidx, barycentric)) {
+			vec2 uv0 = vertices.data[triangles.data[tidx].indices.x].uv;
+			vec2 uv1 = vertices.data[triangles.data[tidx].indices.y].uv;
+			vec2 uv2 = vertices.data[triangles.data[tidx].indices.z].uv;
+			vec3 uvw = vec3(barycentric.x * uv0 + barycentric.y * uv1 + barycentric.z * uv2, float(triangles.data[tidx].slice));
+
+			light = textureLod(sampler2DArray(source_light, linear_sampler), uvw, 0.0).rgb;
+			light += textureLod(sampler2DArray(source_direct_light, linear_sampler), uvw, 0.0).rgb;
+		} else {
+
+			//did not hit a triangle, reach out for the sky
+			vec3 sky_dir = normalize(mat3(params.env_transform) * ray_dir);
+
+			vec2 st = vec2(
+					atan(sky_dir.x, sky_dir.z),
+					acos(sky_dir.y));
+
+			if (st.x < 0.0)
+				st.x += PI * 2.0;
+
+			st /= vec2(PI * 2.0, PI);
+
+			light = textureLod(sampler2D(environment, linear_sampler), st, 0.0).rgb;
+		}
+
+		{
+			float c[9] = float[](
+					0.282095, //l0
+					0.488603 * ray_dir.y, //l1n1
+					0.488603 * ray_dir.z, //l1n0
+					0.488603 * ray_dir.x, //l1p1
+					1.092548 * ray_dir.x * ray_dir.y, //l2n2
+					1.092548 * ray_dir.y * ray_dir.z, //l2n1
+					//0.315392 * (ray_dir.x * ray_dir.x + ray_dir.y * ray_dir.y + 2.0 * ray_dir.z * ray_dir.z), //l20
+					0.315392 * (3.0 * ray_dir.z * ray_dir.z - 1.0), //l20
+					1.092548 * ray_dir.x * ray_dir.z, //l2p1
+					0.546274 * (ray_dir.x * ray_dir.x - ray_dir.y * ray_dir.y) //l2p2
+			);
+
+			for (uint j = 0; j < 9; j++) {
+				probe_sh_accum[j].rgb += light * c[j];
+			}
+		}
+	}
+
+	if (params.ray_from > 0) {
+		for (uint j = 0; j < 9; j++) { //accum from existing
+			probe_sh_accum[j] += light_probes.data[probe_index * 9 + j];
+		}
+	}
+
+	if (params.ray_to == params.ray_count) {
+		for (uint j = 0; j < 9; j++) { //accum from existing
+			probe_sh_accum[j] *= 4.0 / float(params.ray_count);
+		}
+	}
+
+	for (uint j = 0; j < 9; j++) { //accum from existing
+		light_probes.data[probe_index * 9 + j] = probe_sh_accum[j];
+	}
+
+#endif
+
+#ifdef MODE_DILATE
+
+	vec4 c = texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0);
+	//sides first, as they are closer
+	c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-1, 0), params.atlas_slice), 0);
+	c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(0, 1), params.atlas_slice), 0);
+	c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(1, 0), params.atlas_slice), 0);
+	c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(0, -1), params.atlas_slice), 0);
+	//endpoints second
+	c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-1, -1), params.atlas_slice), 0);
+	c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-1, 1), params.atlas_slice), 0);
+	c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(1, -1), params.atlas_slice), 0);
+	c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(1, 1), params.atlas_slice), 0);
+
+	//far sides third
+	c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-2, 0), params.atlas_slice), 0);
+	c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(0, 2), params.atlas_slice), 0);
+	c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(2, 0), params.atlas_slice), 0);
+	c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(0, -2), params.atlas_slice), 0);
+
+	//far-mid endpoints
+	c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-2, -1), params.atlas_slice), 0);
+	c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-2, 1), params.atlas_slice), 0);
+	c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(2, -1), params.atlas_slice), 0);
+	c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(2, 1), params.atlas_slice), 0);
+
+	c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-1, -2), params.atlas_slice), 0);
+	c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-1, 2), params.atlas_slice), 0);
+	c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(1, -2), params.atlas_slice), 0);
+	c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(1, 2), params.atlas_slice), 0);
+	//far endpoints
+	c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-2, -2), params.atlas_slice), 0);
+	c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-2, 2), params.atlas_slice), 0);
+	c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(2, -2), params.atlas_slice), 0);
+	c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(2, 2), params.atlas_slice), 0);
+
+	imageStore(dest_light, ivec3(atlas_pos, params.atlas_slice), c);
+
+#endif
+}

+ 170 - 0
modules/lightmapper_rd/lm_raster.glsl

@@ -0,0 +1,170 @@
+/* clang-format off */
+[vertex]
+
+#version 450
+
+VERSION_DEFINES
+
+#include "lm_common_inc.glsl"
+
+				/* clang-format on */
+
+				layout(location = 0) out vec3 vertex_interp;
+layout(location = 1) out vec3 normal_interp;
+layout(location = 2) out vec2 uv_interp;
+layout(location = 3) out vec3 barycentric;
+layout(location = 4) flat out uvec3 vertex_indices;
+layout(location = 5) flat out vec3 face_normal;
+
+layout(push_constant, binding = 0, std430) uniform Params {
+	vec2 atlas_size;
+	vec2 uv_offset;
+	vec3 to_cell_size;
+	uint base_triangle;
+	vec3 to_cell_offset;
+	float bias;
+	ivec3 grid_size;
+	uint pad2;
+}
+params;
+
+/* clang-format on */
+
+void main() {
+
+	uint triangle_idx = params.base_triangle + gl_VertexIndex / 3;
+	uint triangle_subidx = gl_VertexIndex % 3;
+
+	vertex_indices = triangles.data[triangle_idx].indices;
+
+	uint vertex_idx;
+	if (triangle_subidx == 0) {
+		vertex_idx = vertex_indices.x;
+		barycentric = vec3(1, 0, 0);
+	} else if (triangle_subidx == 1) {
+		vertex_idx = vertex_indices.y;
+		barycentric = vec3(0, 1, 0);
+	} else {
+		vertex_idx = vertex_indices.z;
+		barycentric = vec3(0, 0, 1);
+	}
+
+	vertex_interp = vertices.data[vertex_idx].position;
+	uv_interp = vertices.data[vertex_idx].uv;
+	normal_interp = vec3(vertices.data[vertex_idx].normal_xy, vertices.data[vertex_idx].normal_z);
+
+	face_normal = -normalize(cross((vertices.data[vertex_indices.x].position - vertices.data[vertex_indices.y].position), (vertices.data[vertex_indices.x].position - vertices.data[vertex_indices.z].position)));
+
+	gl_Position = vec4((uv_interp + params.uv_offset) * 2.0 - 1.0, 0.0001, 1.0);
+	;
+}
+
+/* clang-format off */
+
+[fragment]
+
+#version 450
+
+VERSION_DEFINES
+
+#include "lm_common_inc.glsl"
+
+
+layout(push_constant, binding = 0, std430) uniform Params {
+	vec2 atlas_size;
+	vec2 uv_offset;
+	vec3 to_cell_size;
+	uint base_triangle;
+	vec3 to_cell_offset;
+	float bias;
+	ivec3 grid_size;
+	uint pad2;
+} params;
+
+/* clang-format on */
+
+layout(location = 0) in vec3 vertex_interp;
+layout(location = 1) in vec3 normal_interp;
+layout(location = 2) in vec2 uv_interp;
+layout(location = 3) in vec3 barycentric;
+layout(location = 4) in flat uvec3 vertex_indices;
+layout(location = 5) in flat vec3 face_normal;
+
+layout(location = 0) out vec4 position;
+layout(location = 1) out vec4 normal;
+layout(location = 2) out vec4 unocclude;
+
+void main() {
+
+	vec3 vertex_pos = vertex_interp;
+
+	{
+		// smooth out vertex position by interpolating its projection in the 3 normal planes (normal plane is created by vertex pos and normal)
+		// because we don't want to interpolate inwards, normals found pointing inwards are pushed out.
+
+		vec3 pos_a = vertices.data[vertex_indices.x].position;
+		vec3 pos_b = vertices.data[vertex_indices.y].position;
+		vec3 pos_c = vertices.data[vertex_indices.z].position;
+		vec3 center = (pos_a + pos_b + pos_c) * 0.3333333;
+		vec3 norm_a = vec3(vertices.data[vertex_indices.x].normal_xy, vertices.data[vertex_indices.x].normal_z);
+		vec3 norm_b = vec3(vertices.data[vertex_indices.y].normal_xy, vertices.data[vertex_indices.y].normal_z);
+		vec3 norm_c = vec3(vertices.data[vertex_indices.z].normal_xy, vertices.data[vertex_indices.z].normal_z);
+
+		{
+			vec3 dir_a = normalize(pos_a - center);
+			float d_a = dot(dir_a, norm_a);
+			if (d_a < 0) {
+				//pointing inwards
+				norm_a = normalize(norm_a - dir_a * d_a);
+			}
+		}
+		{
+			vec3 dir_b = normalize(pos_b - center);
+			float d_b = dot(dir_b, norm_b);
+			if (d_b < 0) {
+				//pointing inwards
+				norm_b = normalize(norm_b - dir_b * d_b);
+			}
+		}
+		{
+			vec3 dir_c = normalize(pos_c - center);
+			float d_c = dot(dir_c, norm_c);
+			if (d_c < 0) {
+				//pointing inwards
+				norm_c = normalize(norm_c - dir_c * d_c);
+			}
+		}
+
+		float d_a = dot(norm_a, pos_a);
+		float d_b = dot(norm_b, pos_b);
+		float d_c = dot(norm_c, pos_c);
+
+		vec3 proj_a = vertex_pos - norm_a * (dot(norm_a, vertex_pos) - d_a);
+		vec3 proj_b = vertex_pos - norm_b * (dot(norm_b, vertex_pos) - d_b);
+		vec3 proj_c = vertex_pos - norm_c * (dot(norm_c, vertex_pos) - d_c);
+
+		vec3 smooth_position = proj_a * barycentric.x + proj_b * barycentric.y + proj_c * barycentric.z;
+
+		if (dot(face_normal, smooth_position) > dot(face_normal, vertex_pos)) { //only project outwards
+			vertex_pos = smooth_position;
+		}
+	}
+
+	{
+		// unocclusion technique based on:
+		// https://ndotl.wordpress.com/2018/08/29/baking-artifact-free-lightmaps/
+
+		/* compute texel size */
+		vec3 delta_uv = max(abs(dFdx(vertex_interp)), abs(dFdy(vertex_interp)));
+		float texel_size = max(delta_uv.x, max(delta_uv.y, delta_uv.z));
+		texel_size *= sqrt(2.0); //expand to unit box edge length (again, worst case)
+
+		unocclude.xyz = face_normal;
+		unocclude.w = texel_size;
+
+		//continued on lm_compute.glsl
+	}
+
+	position = vec4(vertex_pos, 1.0);
+	normal = vec4(normalize(normal_interp), 1.0);
+}

+ 64 - 0
modules/lightmapper_rd/register_types.cpp

@@ -0,0 +1,64 @@
+/*************************************************************************/
+/*  register_types.cpp                                                   */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "register_types.h"
+
+#include "core/project_settings.h"
+#include "lightmapper_rd.h"
+#include "scene/3d/lightmapper.h"
+
+#ifndef _3D_DISABLED
+static Lightmapper *create_lightmapper_rd() {
+	return memnew(LightmapperRD);
+}
+#endif
+
+void register_lightmapper_rd_types() {
+
+	GLOBAL_DEF("rendering/gpu_lightmapper/quality/low_quality_ray_count", 16);
+	GLOBAL_DEF("rendering/gpu_lightmapper/quality/medium_quality_ray_count", 64);
+	GLOBAL_DEF("rendering/gpu_lightmapper/quality/high_quality_ray_count", 256);
+	GLOBAL_DEF("rendering/gpu_lightmapper/quality/ultra_quality_ray_count", 1024);
+	GLOBAL_DEF("rendering/gpu_lightmapper/performance/max_rays_per_pass", 32);
+	GLOBAL_DEF("rendering/gpu_lightmapper/performance/region_size", 512);
+
+	GLOBAL_DEF("rendering/gpu_lightmapper/quality/low_quality_probe_ray_count", 64);
+	GLOBAL_DEF("rendering/gpu_lightmapper/quality/medium_quality_probe_ray_count", 256);
+	GLOBAL_DEF("rendering/gpu_lightmapper/quality/high_quality_probe_ray_count", 512);
+	GLOBAL_DEF("rendering/gpu_lightmapper/quality/ultra_quality_probe_ray_count", 2048);
+	GLOBAL_DEF("rendering/gpu_lightmapper/performance/max_rays_per_probe_pass", 64);
+#ifndef _3D_DISABLED
+	ClassDB::register_class<LightmapperRD>();
+	Lightmapper::create_gpu = create_lightmapper_rd;
+#endif
+}
+
+void unregister_lightmapper_rd_types() {
+}

+ 37 - 0
modules/lightmapper_rd/register_types.h

@@ -0,0 +1,37 @@
+/*************************************************************************/
+/*  register_types.h                                                     */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef LIGHTMAPPER_RD_REGISTER_TYPES_H
+#define LIGHTMAPPER_RD_REGISTER_TYPES_H
+
+void register_lightmapper_rd_types();
+void unregister_lightmapper_rd_types();
+
+#endif // XATLAS_UNWRAP_REGISTER_TYPES_H

+ 12 - 4
modules/tinyexr/image_saver_tinyexr.cpp

@@ -267,13 +267,21 @@ Error save_exr(const String &p_path, const Ref<Image> &p_img, bool p_grayscale)
 	header.channels = channel_infos;
 	header.channels = channel_infos;
 	header.pixel_types = pixel_types;
 	header.pixel_types = pixel_types;
 	header.requested_pixel_types = requested_pixel_types;
 	header.requested_pixel_types = requested_pixel_types;
+	header.compression_type = TINYEXR_COMPRESSIONTYPE_PIZ;
 
 
-	CharString utf8_filename = p_path.utf8();
-	const char *err;
-	int ret = SaveEXRImageToFile(&image, &header, utf8_filename.ptr(), &err);
-	if (ret != TINYEXR_SUCCESS) {
+	unsigned char *mem = nullptr;
+	const char *err = nullptr;
+
+	size_t bytes = SaveEXRImageToMemory(&image, &header, &mem, &err);
+
+	if (bytes == 0) {
 		print_error(String("Saving EXR failed. Error: {0}").format(varray(err)));
 		print_error(String("Saving EXR failed. Error: {0}").format(varray(err)));
 		return ERR_FILE_CANT_WRITE;
 		return ERR_FILE_CANT_WRITE;
+	} else {
+		FileAccessRef ref = FileAccess::open(p_path, FileAccess::WRITE);
+		ERR_FAIL_COND_V(!ref, ERR_FILE_CANT_WRITE);
+		ref->store_buffer(mem, bytes);
+		free(mem);
 	}
 	}
 
 
 	return OK;
 	return OK;

+ 1 - 0
modules/xatlas_unwrap/register_types.cpp

@@ -137,6 +137,7 @@ bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_ver
 
 
 	pack_options.maxChartSize = 4096;
 	pack_options.maxChartSize = 4096;
 	pack_options.blockAlign = true;
 	pack_options.blockAlign = true;
+	pack_options.padding = 1;
 	pack_options.texelsPerUnit = 1.0 / p_texel_size;
 	pack_options.texelsPerUnit = 1.0 / p_texel_size;
 
 
 	xatlas::Atlas *atlas = xatlas::Create();
 	xatlas::Atlas *atlas = xatlas::Create();

+ 23 - 23
platform/uwp/export/export.cpp

@@ -750,7 +750,7 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
 		return false;
 		return false;
 	}
 	}
 
 
-	bool _valid_image(const StreamTexture *p_image, int p_width, int p_height) const {
+	bool _valid_image(const StreamTexture2D *p_image, int p_width, int p_height) const {
 
 
 		if (!p_image) {
 		if (!p_image) {
 			return false;
 			return false;
@@ -887,22 +887,22 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
 	Vector<uint8_t> _get_image_data(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
 	Vector<uint8_t> _get_image_data(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
 
 
 		Vector<uint8_t> data;
 		Vector<uint8_t> data;
-		StreamTexture *image = nullptr;
+		StreamTexture2D *image = nullptr;
 
 
 		if (p_path.find("StoreLogo") != -1) {
 		if (p_path.find("StoreLogo") != -1) {
-			image = p_preset->get("images/store_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture>(((Object *)p_preset->get("images/store_logo")));
+			image = p_preset->get("images/store_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/store_logo")));
 		} else if (p_path.find("Square44x44Logo") != -1) {
 		} else if (p_path.find("Square44x44Logo") != -1) {
-			image = p_preset->get("images/square44x44_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture>(((Object *)p_preset->get("images/square44x44_logo")));
+			image = p_preset->get("images/square44x44_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square44x44_logo")));
 		} else if (p_path.find("Square71x71Logo") != -1) {
 		} else if (p_path.find("Square71x71Logo") != -1) {
-			image = p_preset->get("images/square71x71_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture>(((Object *)p_preset->get("images/square71x71_logo")));
+			image = p_preset->get("images/square71x71_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square71x71_logo")));
 		} else if (p_path.find("Square150x150Logo") != -1) {
 		} else if (p_path.find("Square150x150Logo") != -1) {
-			image = p_preset->get("images/square150x150_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture>(((Object *)p_preset->get("images/square150x150_logo")));
+			image = p_preset->get("images/square150x150_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square150x150_logo")));
 		} else if (p_path.find("Square310x310Logo") != -1) {
 		} else if (p_path.find("Square310x310Logo") != -1) {
-			image = p_preset->get("images/square310x310_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture>(((Object *)p_preset->get("images/square310x310_logo")));
+			image = p_preset->get("images/square310x310_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square310x310_logo")));
 		} else if (p_path.find("Wide310x150Logo") != -1) {
 		} else if (p_path.find("Wide310x150Logo") != -1) {
-			image = p_preset->get("images/wide310x150_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture>(((Object *)p_preset->get("images/wide310x150_logo")));
+			image = p_preset->get("images/wide310x150_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/wide310x150_logo")));
 		} else if (p_path.find("SplashScreen") != -1) {
 		} else if (p_path.find("SplashScreen") != -1) {
-			image = p_preset->get("images/splash_screen").is_zero() ? nullptr : Object::cast_to<StreamTexture>(((Object *)p_preset->get("images/splash_screen")));
+			image = p_preset->get("images/splash_screen").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/splash_screen")));
 		} else {
 		} else {
 			ERR_PRINT("Unable to load logo");
 			ERR_PRINT("Unable to load logo");
 		}
 		}
@@ -1066,13 +1066,13 @@ public:
 		r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/portrait_flipped"), true));
 		r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/portrait_flipped"), true));
 
 
 		r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "images/background_color"), "transparent"));
 		r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "images/background_color"), "transparent"));
-		r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/store_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture"), Variant()));
-		r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square44x44_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture"), Variant()));
-		r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square71x71_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture"), Variant()));
-		r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square150x150_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture"), Variant()));
-		r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square310x310_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture"), Variant()));
-		r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/wide310x150_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture"), Variant()));
-		r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/splash_screen", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture"), Variant()));
+		r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/store_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant()));
+		r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square44x44_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant()));
+		r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square71x71_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant()));
+		r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square150x150_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant()));
+		r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square310x310_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant()));
+		r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/wide310x150_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant()));
+		r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/splash_screen", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant()));
 
 
 		r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "tiles/show_name_on_square150x150"), false));
 		r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "tiles/show_name_on_square150x150"), false));
 		r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "tiles/show_name_on_wide310x150"), false));
 		r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "tiles/show_name_on_wide310x150"), false));
@@ -1173,37 +1173,37 @@ public:
 			err += TTR("Invalid background color.") + "\n";
 			err += TTR("Invalid background color.") + "\n";
 		}
 		}
 
 
-		if (!p_preset->get("images/store_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture>((Object *)p_preset->get("images/store_logo"))), 50, 50)) {
+		if (!p_preset->get("images/store_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/store_logo"))), 50, 50)) {
 			valid = false;
 			valid = false;
 			err += TTR("Invalid Store Logo image dimensions (should be 50x50).") + "\n";
 			err += TTR("Invalid Store Logo image dimensions (should be 50x50).") + "\n";
 		}
 		}
 
 
-		if (!p_preset->get("images/square44x44_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture>((Object *)p_preset->get("images/square44x44_logo"))), 44, 44)) {
+		if (!p_preset->get("images/square44x44_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/square44x44_logo"))), 44, 44)) {
 			valid = false;
 			valid = false;
 			err += TTR("Invalid square 44x44 logo image dimensions (should be 44x44).") + "\n";
 			err += TTR("Invalid square 44x44 logo image dimensions (should be 44x44).") + "\n";
 		}
 		}
 
 
-		if (!p_preset->get("images/square71x71_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture>((Object *)p_preset->get("images/square71x71_logo"))), 71, 71)) {
+		if (!p_preset->get("images/square71x71_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/square71x71_logo"))), 71, 71)) {
 			valid = false;
 			valid = false;
 			err += TTR("Invalid square 71x71 logo image dimensions (should be 71x71).") + "\n";
 			err += TTR("Invalid square 71x71 logo image dimensions (should be 71x71).") + "\n";
 		}
 		}
 
 
-		if (!p_preset->get("images/square150x150_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture>((Object *)p_preset->get("images/square150x150_logo"))), 150, 150)) {
+		if (!p_preset->get("images/square150x150_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/square150x150_logo"))), 150, 150)) {
 			valid = false;
 			valid = false;
 			err += TTR("Invalid square 150x150 logo image dimensions (should be 150x150).") + "\n";
 			err += TTR("Invalid square 150x150 logo image dimensions (should be 150x150).") + "\n";
 		}
 		}
 
 
-		if (!p_preset->get("images/square310x310_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture>((Object *)p_preset->get("images/square310x310_logo"))), 310, 310)) {
+		if (!p_preset->get("images/square310x310_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/square310x310_logo"))), 310, 310)) {
 			valid = false;
 			valid = false;
 			err += TTR("Invalid square 310x310 logo image dimensions (should be 310x310).") + "\n";
 			err += TTR("Invalid square 310x310 logo image dimensions (should be 310x310).") + "\n";
 		}
 		}
 
 
-		if (!p_preset->get("images/wide310x150_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture>((Object *)p_preset->get("images/wide310x150_logo"))), 310, 150)) {
+		if (!p_preset->get("images/wide310x150_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/wide310x150_logo"))), 310, 150)) {
 			valid = false;
 			valid = false;
 			err += TTR("Invalid wide 310x150 logo image dimensions (should be 310x150).") + "\n";
 			err += TTR("Invalid wide 310x150 logo image dimensions (should be 310x150).") + "\n";
 		}
 		}
 
 
-		if (!p_preset->get("images/splash_screen").is_zero() && !_valid_image((Object::cast_to<StreamTexture>((Object *)p_preset->get("images/splash_screen"))), 620, 300)) {
+		if (!p_preset->get("images/splash_screen").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/splash_screen"))), 620, 300)) {
 			valid = false;
 			valid = false;
 			err += TTR("Invalid splash screen image dimensions (should be 620x300).") + "\n";
 			err += TTR("Invalid splash screen image dimensions (should be 620x300).") + "\n";
 		}
 		}

File diff suppressed because it is too large
+ 972 - 403
scene/3d/baked_lightmap.cpp


+ 155 - 87
scene/3d/baked_lightmap.h

@@ -28,189 +28,257 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /*************************************************************************/
 /*************************************************************************/
 
 
-#if 0
 #ifndef BAKED_INDIRECT_LIGHT_H
 #ifndef BAKED_INDIRECT_LIGHT_H
 #define BAKED_INDIRECT_LIGHT_H
 #define BAKED_INDIRECT_LIGHT_H
 
 
-#include "multimesh_instance.h"
-#include "scene/3d/light.h"
-#include "scene/3d/visual_instance.h"
+#include "core/local_vector.h"
+#include "scene/3d/light_3d.h"
+#include "scene/3d/lightmapper.h"
+#include "scene/3d/mesh_instance_3d.h"
+#include "scene/3d/multimesh_instance_3d.h"
+#include "scene/3d/visual_instance_3d.h"
+#include "scene/resources/sky.h"
 
 
 class BakedLightmapData : public Resource {
 class BakedLightmapData : public Resource {
 	GDCLASS(BakedLightmapData, Resource);
 	GDCLASS(BakedLightmapData, Resource);
+	RES_BASE_EXTENSION("lmbake")
 
 
-	RID baked_light;
+	Ref<TextureLayered> light_texture;
+
+	bool uses_spherical_harmonics = false;
+	bool interior = false;
+
+	RID lightmap;
 	AABB bounds;
 	AABB bounds;
-	float energy;
-	int cell_subdiv;
-	Transform cell_space_xform;
 
 
 	struct User {
 	struct User {
 
 
 		NodePath path;
 		NodePath path;
-		Ref<Texture2D> lightmap;
-		int instance_index;
+		int32_t sub_instance;
+		Rect2 uv_scale;
+		int slice_index;
 	};
 	};
 
 
 	Vector<User> users;
 	Vector<User> users;
 
 
 	void _set_user_data(const Array &p_data);
 	void _set_user_data(const Array &p_data);
 	Array _get_user_data() const;
 	Array _get_user_data() const;
+	void _set_probe_data(const Dictionary &p_data);
+	Dictionary _get_probe_data() const;
 
 
 protected:
 protected:
 	static void _bind_methods();
 	static void _bind_methods();
 
 
 public:
 public:
-	void set_bounds(const AABB &p_bounds);
-	AABB get_bounds() const;
+	void add_user(const NodePath &p_path, const Rect2 &p_uv_scale, int p_slice_index, int32_t p_sub_instance = -1);
+	int get_user_count() const;
+	NodePath get_user_path(int p_user) const;
+	int32_t get_user_sub_instance(int p_user) const;
+	Rect2 get_user_lightmap_uv_scale(int p_user) const;
+	int get_user_lightmap_slice_index(int p_user) const;
+	void clear_users();
 
 
-	void set_octree(const Vector<uint8_t> &p_octree);
-	Vector<uint8_t> get_octree() const;
+	void set_light_texture(const Ref<TextureLayered> &p_light_texture);
+	Ref<TextureLayered> get_light_texture() const;
 
 
-	void set_cell_space_transform(const Transform &p_xform);
-	Transform get_cell_space_transform() const;
+	void set_uses_spherical_harmonics(bool p_enable);
+	bool is_using_spherical_harmonics() const;
 
 
-	void set_cell_subdiv(int p_cell_subdiv);
-	int get_cell_subdiv() const;
+	bool is_interior() const;
 
 
-	void set_energy(float p_energy);
-	float get_energy() const;
+	void set_capture_data(const AABB &p_bounds, bool p_interior, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree);
+	PackedVector3Array get_capture_points() const;
+	PackedColorArray get_capture_sh() const;
+	PackedInt32Array get_capture_tetrahedra() const;
+	PackedInt32Array get_capture_bsp_tree() const;
+	AABB get_capture_bounds() const;
 
 
-	void add_user(const NodePath &p_path, const Ref<Texture2D> &p_lightmap, int p_instance = -1);
-	int get_user_count() const;
-	NodePath get_user_path(int p_user) const;
-	Ref<Texture2D> get_user_lightmap(int p_user) const;
-	int get_user_instance(int p_user) const;
-	void clear_users();
+	void clear();
 
 
 	virtual RID get_rid() const;
 	virtual RID get_rid() const;
 	BakedLightmapData();
 	BakedLightmapData();
 	~BakedLightmapData();
 	~BakedLightmapData();
 };
 };
 
 
-class BakedLightmap : public VisualInstance {
-	GDCLASS(BakedLightmap, VisualInstance);
+class BakedLightmap : public VisualInstance3D {
+	GDCLASS(BakedLightmap, VisualInstance3D);
 
 
 public:
 public:
 	enum BakeQuality {
 	enum BakeQuality {
 		BAKE_QUALITY_LOW,
 		BAKE_QUALITY_LOW,
 		BAKE_QUALITY_MEDIUM,
 		BAKE_QUALITY_MEDIUM,
-		BAKE_QUALITY_HIGH
+		BAKE_QUALITY_HIGH,
+		BAKE_QUALITY_ULTRA,
 	};
 	};
-
-	enum BakeMode {
-		BAKE_MODE_CONE_TRACE,
-		BAKE_MODE_RAY_TRACE,
+	enum GenerateProbes {
+		GENERATE_PROBES_DISABLED,
+		GENERATE_PROBES_SUBDIV_4,
+		GENERATE_PROBES_SUBDIV_8,
+		GENERATE_PROBES_SUBDIV_16,
+		GENERATE_PROBES_SUBDIV_32,
 	};
 	};
 
 
 	enum BakeError {
 	enum BakeError {
 		BAKE_ERROR_OK,
 		BAKE_ERROR_OK,
+		BAKE_ERROR_NO_LIGHTMAPPER,
 		BAKE_ERROR_NO_SAVE_PATH,
 		BAKE_ERROR_NO_SAVE_PATH,
 		BAKE_ERROR_NO_MESHES,
 		BAKE_ERROR_NO_MESHES,
+		BAKE_ERROR_MESHES_INVALID,
 		BAKE_ERROR_CANT_CREATE_IMAGE,
 		BAKE_ERROR_CANT_CREATE_IMAGE,
 		BAKE_ERROR_USER_ABORTED
 		BAKE_ERROR_USER_ABORTED
 
 
 	};
 	};
 
 
-	typedef void (*BakeBeginFunc)(int);
-	typedef bool (*BakeStepFunc)(int, const String &);
-	typedef void (*BakeEndFunc)();
+	enum EnvironmentMode {
+		ENVIRONMENT_MODE_DISABLED,
+		ENVIRONMENT_MODE_SCENE,
+		ENVIRONMENT_MODE_CUSTOM_SKY,
+		ENVIRONMENT_MODE_CUSTOM_COLOR,
+	};
 
 
 private:
 private:
-	float bake_cell_size;
-	float capture_cell_size;
-	Vector3 extents;
-	float bake_default_texels_per_unit;
-	float propagation;
-	float energy;
 	BakeQuality bake_quality;
 	BakeQuality bake_quality;
-	BakeMode bake_mode;
-	bool hdr;
-	String image_path;
+	bool use_denoiser;
+	int bounces;
+	float bias;
+	int max_texture_size;
+	bool interior;
+	EnvironmentMode environment_mode;
+	Ref<Sky> environment_custom_sky;
+	Color environment_custom_color;
+	float environment_custom_energy;
+	bool directional;
+	GenerateProbes gen_probes;
 
 
 	Ref<BakedLightmapData> light_data;
 	Ref<BakedLightmapData> light_data;
 
 
-	struct PlotMesh {
-		Ref<Material> override_material;
-		Vector<Ref<Material> > instance_materials;
-		Ref<Mesh> mesh;
-		Transform local_xform;
-		NodePath path;
-		int instance_idx;
+	struct LightsFound {
+		Transform xform;
+		Light3D *light;
 	};
 	};
 
 
-	struct PlotLight {
-		Light *light;
-		Transform local_xform;
+	struct MeshesFound {
+		Transform xform;
+		NodePath node_path;
+		int32_t subindex;
+		Ref<Mesh> mesh;
+		int32_t lightmap_scale;
+		Vector<Ref<Material>> overrides;
 	};
 	};
 
 
-	void _find_meshes_and_lights(Node *p_at_node, List<PlotMesh> &plot_meshes, List<PlotLight> &plot_lights);
-
-	void _debug_bake();
+	void _find_meshes_and_lights(Node *p_at_node, Vector<MeshesFound> &meshes, Vector<LightsFound> &lights, Vector<Vector3> &probes);
 
 
 	void _assign_lightmaps();
 	void _assign_lightmaps();
 	void _clear_lightmaps();
 	void _clear_lightmaps();
 
 
-	static bool _bake_time(void *ud, float p_secs, float p_progress);
-
 	struct BakeTimeData {
 	struct BakeTimeData {
 		String text;
 		String text;
 		int pass;
 		int pass;
 		uint64_t last_step;
 		uint64_t last_step;
 	};
 	};
 
 
+	struct BSPSimplex {
+		int vertices[4];
+		int planes[4];
+	};
+
+	struct BSPNode {
+		static const int32_t EMPTY_LEAF = INT32_MIN;
+		Plane plane;
+		int32_t over = EMPTY_LEAF, under = EMPTY_LEAF;
+	};
+
+	int _bsp_get_simplex_side(const Vector<Vector3> &p_points, const LocalVector<BSPSimplex> &p_simplices, const Plane &p_plane, uint32_t p_simplex) const;
+	int32_t _compute_bsp_tree(const Vector<Vector3> &p_points, const LocalVector<Plane> &p_planes, LocalVector<int32_t> &planes_tested, const LocalVector<BSPSimplex> &p_simplices, const LocalVector<int32_t> &p_simplex_indices, LocalVector<BSPNode> &bsp_nodes);
+
+	struct BakeStepUD {
+		Lightmapper::BakeStepFunc func;
+		void *ud;
+		float from_percent;
+		float to_percent;
+	};
+
+	static bool _lightmap_bake_step_function(float p_completion, const String &p_text, void *ud, bool p_refresh);
+
+	struct GenProbesOctree {
+		Vector3i offset;
+		uint32_t size;
+		GenProbesOctree *children[8] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
+		~GenProbesOctree() {
+			for (int i = 0; i < 8; i++) {
+				if (children[i] != nullptr) {
+					memdelete(children[i]);
+				}
+			}
+		}
+	};
+
+	struct Vector3iHash {
+		_FORCE_INLINE_ static uint32_t hash(const Vector3i &p_vtx) {
+			uint32_t h = hash_djb2_one_32(p_vtx.x);
+			h = hash_djb2_one_32(p_vtx.y, h);
+			return hash_djb2_one_32(p_vtx.z, h);
+		}
+	};
+
+	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, Vector3iHash> &positions_used, const AABB &p_bounds);
+
 protected:
 protected:
+	void _validate_property(PropertyInfo &property) const;
 	static void _bind_methods();
 	static void _bind_methods();
 	void _notification(int p_what);
 	void _notification(int p_what);
 
 
 public:
 public:
-	static BakeBeginFunc bake_begin_function;
-	static BakeStepFunc bake_step_function;
-	static BakeEndFunc bake_end_function;
-
 	void set_light_data(const Ref<BakedLightmapData> &p_data);
 	void set_light_data(const Ref<BakedLightmapData> &p_data);
 	Ref<BakedLightmapData> get_light_data() const;
 	Ref<BakedLightmapData> get_light_data() const;
 
 
-	void set_bake_cell_size(float p_cell_size);
-	float get_bake_cell_size() const;
+	void set_bake_quality(BakeQuality p_quality);
+	BakeQuality get_bake_quality() const;
+
+	void set_use_denoiser(bool p_enable);
+	bool is_using_denoiser() const;
 
 
-	void set_capture_cell_size(float p_cell_size);
-	float get_capture_cell_size() const;
+	void set_directional(bool p_enable);
+	bool is_directional() const;
 
 
-	void set_extents(const Vector3 &p_extents);
-	Vector3 get_extents() const;
+	void set_interior(bool p_interior);
+	bool is_interior() const;
 
 
-	void set_bake_default_texels_per_unit(const float &p_bake_texels_per_unit);
-	float get_bake_default_texels_per_unit() const;
+	void set_environment_mode(EnvironmentMode p_mode);
+	EnvironmentMode get_environment_mode() const;
 
 
-	void set_propagation(float p_propagation);
-	float get_propagation() const;
+	void set_environment_custom_sky(const Ref<Sky> &p_sky);
+	Ref<Sky> get_environment_custom_sky() const;
 
 
-	void set_energy(float p_energy);
-	float get_energy() const;
+	void set_environment_custom_color(const Color &p_color);
+	Color get_environment_custom_color() const;
 
 
-	void set_bake_quality(BakeQuality p_quality);
-	BakeQuality get_bake_quality() const;
+	void set_environment_custom_energy(float p_energy);
+	float get_environment_custom_energy() const;
+
+	void set_bounces(int p_bounces);
+	int get_bounces() const;
 
 
-	void set_bake_mode(BakeMode p_mode);
-	BakeMode get_bake_mode() const;
+	void set_bias(float p_bias);
+	float get_bias() const;
 
 
-	void set_hdr(bool p_enable);
-	bool is_hdr() const;
+	void set_max_texture_size(int p_size);
+	int get_max_texture_size() const;
 
 
-	void set_image_path(const String &p_path);
-	String get_image_path() const;
+	void set_generate_probes(GenerateProbes p_generate_probes);
+	GenerateProbes get_generate_probes() const;
 
 
 	AABB get_aabb() const;
 	AABB get_aabb() const;
 	Vector<Face3> get_faces(uint32_t p_usage_flags) const;
 	Vector<Face3> get_faces(uint32_t p_usage_flags) const;
 
 
-	BakeError bake(Node *p_from_node, bool p_create_visual_debug = false);
+	BakeError bake(Node *p_from_node, String p_image_data_path = "", Lightmapper::BakeStepFunc p_bake_step = nullptr, void *p_bake_userdata = nullptr);
 	BakedLightmap();
 	BakedLightmap();
 };
 };
 
 
+VARIANT_ENUM_CAST(BakedLightmap::GenerateProbes);
 VARIANT_ENUM_CAST(BakedLightmap::BakeQuality);
 VARIANT_ENUM_CAST(BakedLightmap::BakeQuality);
-VARIANT_ENUM_CAST(BakedLightmap::BakeMode);
 VARIANT_ENUM_CAST(BakedLightmap::BakeError);
 VARIANT_ENUM_CAST(BakedLightmap::BakeError);
+VARIANT_ENUM_CAST(BakedLightmap::EnvironmentMode);
 
 
-#endif
 #endif // BAKED_INDIRECT_LIGHT_H
 #endif // BAKED_INDIRECT_LIGHT_H

+ 1 - 1
scene/3d/gi_probe.cpp

@@ -349,7 +349,7 @@ Vector3 GIProbe::get_extents() const {
 void GIProbe::_find_meshes(Node *p_at_node, List<PlotMesh> &plot_meshes) {
 void GIProbe::_find_meshes(Node *p_at_node, List<PlotMesh> &plot_meshes) {
 
 
 	MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_at_node);
 	MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_at_node);
-	if (mi && mi->get_flag(GeometryInstance3D::FLAG_USE_BAKED_LIGHT) && mi->is_visible_in_tree()) {
+	if (mi && mi->get_gi_mode() == GeometryInstance3D::GI_MODE_BAKED && mi->is_visible_in_tree()) {
 		Ref<Mesh> mesh = mi->get_mesh();
 		Ref<Mesh> mesh = mi->get_mesh();
 		if (mesh.is_valid()) {
 		if (mesh.is_valid()) {
 
 

+ 4 - 0
scene/3d/lightmap_probe.cpp

@@ -0,0 +1,4 @@
+#include "lightmap_probe.h"
+
+LightmapProbe::LightmapProbe() {
+}

+ 12 - 0
scene/3d/lightmap_probe.h

@@ -0,0 +1,12 @@
+#ifndef LIGHTMAP_PROBE_H
+#define LIGHTMAP_PROBE_H
+
+#include "scene/3d/node_3d.h"
+
+class LightmapProbe : public Node3D {
+	GDCLASS(LightmapProbe, Node3D)
+public:
+	LightmapProbe();
+};
+
+#endif // LIGHTMAP_PROBE_H

+ 37 - 0
scene/3d/lightmapper.cpp

@@ -0,0 +1,37 @@
+#include "lightmapper.h"
+
+LightmapDenoiser *(*LightmapDenoiser::create_function)() = nullptr;
+
+Ref<LightmapDenoiser> LightmapDenoiser::create() {
+	if (create_function) {
+		return Ref<LightmapDenoiser>(create_function());
+	}
+	return Ref<LightmapDenoiser>();
+}
+
+Lightmapper::CreateFunc Lightmapper::create_custom = nullptr;
+Lightmapper::CreateFunc Lightmapper::create_gpu = nullptr;
+Lightmapper::CreateFunc Lightmapper::create_cpu = nullptr;
+
+Ref<Lightmapper> Lightmapper::create() {
+	Lightmapper *lm = nullptr;
+	if (create_custom) {
+		lm = create_custom();
+	}
+
+	if (!lm && create_gpu) {
+		lm = create_gpu();
+	}
+
+	if (!lm && create_cpu) {
+		lm = create_cpu();
+	}
+	if (!lm) {
+		return Ref<Lightmapper>();
+	} else {
+		return Ref<Lightmapper>(lm);
+	}
+}
+
+Lightmapper::Lightmapper() {
+}

+ 90 - 0
scene/3d/lightmapper.h

@@ -0,0 +1,90 @@
+#ifndef LIGHTMAPPER_H
+#define LIGHTMAPPER_H
+
+#include "scene/resources/mesh.h"
+#include "servers/rendering/rendering_device.h"
+
+class LightmapDenoiser : public Reference {
+	GDCLASS(LightmapDenoiser, Reference)
+protected:
+	static LightmapDenoiser *(*create_function)();
+
+public:
+	virtual Ref<Image> denoise_image(const Ref<Image> &p_image) = 0;
+	static Ref<LightmapDenoiser> create();
+};
+
+class Lightmapper : public Reference {
+	GDCLASS(Lightmapper, Reference)
+public:
+	enum GenerateProbes {
+		GENERATE_PROBES_DISABLED,
+		GENERATE_PROBES_SUBDIV_4,
+		GENERATE_PROBES_SUBDIV_8,
+		GENERATE_PROBES_SUBDIV_16,
+		GENERATE_PROBES_SUBDIV_32,
+
+	};
+
+	enum LightType {
+		LIGHT_TYPE_DIRECTIONAL,
+		LIGHT_TYPE_OMNI,
+		LIGHT_TYPE_SPOT
+	};
+
+	enum BakeError {
+		BAKE_ERROR_LIGHTMAP_TOO_SMALL,
+		BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES,
+		BAKE_OK
+	};
+
+	enum BakeQuality {
+		BAKE_QUALITY_LOW,
+		BAKE_QUALITY_MEDIUM,
+		BAKE_QUALITY_HIGH,
+		BAKE_QUALITY_ULTRA,
+	};
+
+	typedef Lightmapper *(*CreateFunc)();
+
+	static CreateFunc create_custom;
+	static CreateFunc create_gpu;
+	static CreateFunc create_cpu;
+
+protected:
+public:
+	typedef bool (*BakeStepFunc)(float, const String &, void *, bool); //step index, step total, step description, userdata
+
+	struct MeshData {
+		//triangle data
+		Vector<Vector3> points;
+		Vector<Vector2> uv2;
+		Vector<Vector3> normal;
+		Ref<Image> albedo_on_uv2;
+		Ref<Image> emission_on_uv2;
+		Variant userdata;
+	};
+
+	virtual void add_mesh(const MeshData &p_mesh) = 0;
+	virtual void add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_angular_distance) = 0;
+	virtual void add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size) = 0;
+	virtual void add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size) = 0;
+	virtual void add_probe(const Vector3 &p_position) = 0;
+	virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_step_userdata = nullptr) = 0;
+
+	virtual int get_bake_texture_count() const = 0;
+	virtual Ref<Image> get_bake_texture(int p_index) const = 0;
+	virtual int get_bake_mesh_count() const = 0;
+	virtual Variant get_bake_mesh_userdata(int p_index) const = 0;
+	virtual Rect2 get_bake_mesh_uv_scale(int p_index) const = 0;
+	virtual int get_bake_mesh_texture_slice(int p_index) const = 0;
+	virtual int get_bake_probe_count() const = 0;
+	virtual Vector3 get_bake_probe_point(int p_probe) const = 0;
+	virtual Vector<Color> get_bake_probe_sh(int p_probe) const = 0;
+
+	static Ref<Lightmapper> create();
+
+	Lightmapper();
+};
+
+#endif // LIGHTMAPPER_H

+ 64 - 29
scene/3d/visual_instance_3d.cpp

@@ -239,7 +239,17 @@ bool GeometryInstance3D::_set(const StringName &p_name, const Variant &p_value)
 		set_shader_instance_uniform(*r, p_value);
 		set_shader_instance_uniform(*r, p_value);
 		return true;
 		return true;
 	}
 	}
+#ifndef DISABLE_DEPRECATED
+	if (p_name == SceneStringNames::get_singleton()->use_in_baked_light && bool(p_value)) {
+		set_gi_mode(GI_MODE_BAKED);
+		return true;
+	}
 
 
+	if (p_name == SceneStringNames::get_singleton()->use_dynamic_gi && bool(p_value)) {
+		set_gi_mode(GI_MODE_DYNAMIC);
+		return true;
+	}
+#endif
 	return false;
 	return false;
 }
 }
 
 
@@ -273,23 +283,6 @@ void GeometryInstance3D::_get_property_list(List<PropertyInfo> *p_list) const {
 	}
 	}
 }
 }
 
 
-void GeometryInstance3D::set_flag(Flags p_flag, bool p_value) {
-
-	ERR_FAIL_INDEX(p_flag, FLAG_MAX);
-	if (flags[p_flag] == p_value)
-		return;
-
-	flags[p_flag] = p_value;
-	RS::get_singleton()->instance_geometry_set_flag(get_instance(), (RS::InstanceFlags)p_flag, p_value);
-}
-
-bool GeometryInstance3D::get_flag(Flags p_flag) const {
-
-	ERR_FAIL_INDEX_V(p_flag, FLAG_MAX, false);
-
-	return flags[p_flag];
-}
-
 void GeometryInstance3D::set_cast_shadows_setting(ShadowCastingSetting p_shadow_casting_setting) {
 void GeometryInstance3D::set_cast_shadows_setting(ShadowCastingSetting p_shadow_casting_setting) {
 
 
 	shadow_casting_setting = p_shadow_casting_setting;
 	shadow_casting_setting = p_shadow_casting_setting;
@@ -335,14 +328,45 @@ void GeometryInstance3D::set_custom_aabb(AABB aabb) {
 	RS::get_singleton()->instance_set_custom_aabb(get_instance(), aabb);
 	RS::get_singleton()->instance_set_custom_aabb(get_instance(), aabb);
 }
 }
 
 
+void GeometryInstance3D::set_lightmap_scale(LightmapScale p_scale) {
+	ERR_FAIL_INDEX(p_scale, LIGHTMAP_SCALE_MAX);
+	lightmap_scale = p_scale;
+}
+
+GeometryInstance3D::LightmapScale GeometryInstance3D::get_lightmap_scale() const {
+	return lightmap_scale;
+}
+
+void GeometryInstance3D::set_gi_mode(GIMode p_mode) {
+
+	switch (p_mode) {
+		case GI_MODE_DISABLED: {
+			RS::get_singleton()->instance_geometry_set_flag(get_instance(), RS::INSTANCE_FLAG_USE_BAKED_LIGHT, false);
+			RS::get_singleton()->instance_geometry_set_flag(get_instance(), RS::INSTANCE_FLAG_USE_DYNAMIC_GI, false);
+		} break;
+		case GI_MODE_BAKED: {
+			RS::get_singleton()->instance_geometry_set_flag(get_instance(), RS::INSTANCE_FLAG_USE_BAKED_LIGHT, true);
+			RS::get_singleton()->instance_geometry_set_flag(get_instance(), RS::INSTANCE_FLAG_USE_DYNAMIC_GI, false);
+
+		} break;
+		case GI_MODE_DYNAMIC: {
+			RS::get_singleton()->instance_geometry_set_flag(get_instance(), RS::INSTANCE_FLAG_USE_BAKED_LIGHT, false);
+			RS::get_singleton()->instance_geometry_set_flag(get_instance(), RS::INSTANCE_FLAG_USE_DYNAMIC_GI, true);
+		} break;
+	}
+
+	gi_mode = p_mode;
+}
+
+GeometryInstance3D::GIMode GeometryInstance3D::get_gi_mode() const {
+	return gi_mode;
+}
+
 void GeometryInstance3D::_bind_methods() {
 void GeometryInstance3D::_bind_methods() {
 
 
 	ClassDB::bind_method(D_METHOD("set_material_override", "material"), &GeometryInstance3D::set_material_override);
 	ClassDB::bind_method(D_METHOD("set_material_override", "material"), &GeometryInstance3D::set_material_override);
 	ClassDB::bind_method(D_METHOD("get_material_override"), &GeometryInstance3D::get_material_override);
 	ClassDB::bind_method(D_METHOD("get_material_override"), &GeometryInstance3D::get_material_override);
 
 
-	ClassDB::bind_method(D_METHOD("set_flag", "flag", "value"), &GeometryInstance3D::set_flag);
-	ClassDB::bind_method(D_METHOD("get_flag", "flag"), &GeometryInstance3D::get_flag);
-
 	ClassDB::bind_method(D_METHOD("set_cast_shadows_setting", "shadow_casting_setting"), &GeometryInstance3D::set_cast_shadows_setting);
 	ClassDB::bind_method(D_METHOD("set_cast_shadows_setting", "shadow_casting_setting"), &GeometryInstance3D::set_cast_shadows_setting);
 	ClassDB::bind_method(D_METHOD("get_cast_shadows_setting"), &GeometryInstance3D::get_cast_shadows_setting);
 	ClassDB::bind_method(D_METHOD("get_cast_shadows_setting"), &GeometryInstance3D::get_cast_shadows_setting);
 
 
@@ -364,6 +388,12 @@ void GeometryInstance3D::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_extra_cull_margin", "margin"), &GeometryInstance3D::set_extra_cull_margin);
 	ClassDB::bind_method(D_METHOD("set_extra_cull_margin", "margin"), &GeometryInstance3D::set_extra_cull_margin);
 	ClassDB::bind_method(D_METHOD("get_extra_cull_margin"), &GeometryInstance3D::get_extra_cull_margin);
 	ClassDB::bind_method(D_METHOD("get_extra_cull_margin"), &GeometryInstance3D::get_extra_cull_margin);
 
 
+	ClassDB::bind_method(D_METHOD("set_lightmap_scale", "scale"), &GeometryInstance3D::set_lightmap_scale);
+	ClassDB::bind_method(D_METHOD("get_lightmap_scale"), &GeometryInstance3D::get_lightmap_scale);
+
+	ClassDB::bind_method(D_METHOD("set_gi_mode", "mode"), &GeometryInstance3D::set_gi_mode);
+	ClassDB::bind_method(D_METHOD("get_gi_mode"), &GeometryInstance3D::get_gi_mode);
+
 	ClassDB::bind_method(D_METHOD("set_custom_aabb", "aabb"), &GeometryInstance3D::set_custom_aabb);
 	ClassDB::bind_method(D_METHOD("set_custom_aabb", "aabb"), &GeometryInstance3D::set_custom_aabb);
 
 
 	ClassDB::bind_method(D_METHOD("get_aabb"), &GeometryInstance3D::get_aabb);
 	ClassDB::bind_method(D_METHOD("get_aabb"), &GeometryInstance3D::get_aabb);
@@ -372,8 +402,9 @@ void GeometryInstance3D::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material_override", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,StandardMaterial3D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE), "set_material_override", "get_material_override");
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material_override", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,StandardMaterial3D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE), "set_material_override", "get_material_override");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "cast_shadow", PROPERTY_HINT_ENUM, "Off,On,Double-Sided,Shadows Only"), "set_cast_shadows_setting", "get_cast_shadows_setting");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "cast_shadow", PROPERTY_HINT_ENUM, "Off,On,Double-Sided,Shadows Only"), "set_cast_shadows_setting", "get_cast_shadows_setting");
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "extra_cull_margin", PROPERTY_HINT_RANGE, "0,16384,0.01"), "set_extra_cull_margin", "get_extra_cull_margin");
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "extra_cull_margin", PROPERTY_HINT_RANGE, "0,16384,0.01"), "set_extra_cull_margin", "get_extra_cull_margin");
-	ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "use_in_baked_light"), "set_flag", "get_flag", FLAG_USE_BAKED_LIGHT);
-	ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "use_dynamic_gi"), "set_flag", "get_flag", FLAG_USE_DYNAMIC_GI);
+	ADD_GROUP("Global Illumination", "gi_");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_mode", PROPERTY_HINT_ENUM, "Disabled,Baked,Dynamic"), "set_gi_mode", "get_gi_mode");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_lightmap_scale", PROPERTY_HINT_ENUM, "1x,2x,4x,8x"), "set_lightmap_scale", "get_lightmap_scale");
 
 
 	ADD_GROUP("LOD", "lod_");
 	ADD_GROUP("LOD", "lod_");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_min_distance", PROPERTY_HINT_RANGE, "0,32768,0.01"), "set_lod_min_distance", "get_lod_min_distance");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_min_distance", PROPERTY_HINT_RANGE, "0,32768,0.01"), "set_lod_min_distance", "get_lod_min_distance");
@@ -388,10 +419,15 @@ void GeometryInstance3D::_bind_methods() {
 	BIND_ENUM_CONSTANT(SHADOW_CASTING_SETTING_DOUBLE_SIDED);
 	BIND_ENUM_CONSTANT(SHADOW_CASTING_SETTING_DOUBLE_SIDED);
 	BIND_ENUM_CONSTANT(SHADOW_CASTING_SETTING_SHADOWS_ONLY);
 	BIND_ENUM_CONSTANT(SHADOW_CASTING_SETTING_SHADOWS_ONLY);
 
 
-	BIND_ENUM_CONSTANT(FLAG_USE_BAKED_LIGHT);
-	BIND_ENUM_CONSTANT(FLAG_USE_DYNAMIC_GI);
-	BIND_ENUM_CONSTANT(FLAG_DRAW_NEXT_FRAME_IF_VISIBLE);
-	BIND_ENUM_CONSTANT(FLAG_MAX);
+	BIND_ENUM_CONSTANT(GI_MODE_DISABLED);
+	BIND_ENUM_CONSTANT(GI_MODE_BAKED);
+	BIND_ENUM_CONSTANT(GI_MODE_DYNAMIC);
+
+	BIND_ENUM_CONSTANT(LIGHTMAP_SCALE_1X);
+	BIND_ENUM_CONSTANT(LIGHTMAP_SCALE_2X);
+	BIND_ENUM_CONSTANT(LIGHTMAP_SCALE_4X);
+	BIND_ENUM_CONSTANT(LIGHTMAP_SCALE_8X);
+	BIND_ENUM_CONSTANT(LIGHTMAP_SCALE_MAX);
 }
 }
 
 
 GeometryInstance3D::GeometryInstance3D() {
 GeometryInstance3D::GeometryInstance3D() {
@@ -400,9 +436,8 @@ GeometryInstance3D::GeometryInstance3D() {
 	lod_min_hysteresis = 0;
 	lod_min_hysteresis = 0;
 	lod_max_hysteresis = 0;
 	lod_max_hysteresis = 0;
 
 
-	for (int i = 0; i < FLAG_MAX; i++) {
-		flags[i] = false;
-	}
+	gi_mode = GI_MODE_DISABLED;
+	lightmap_scale = LIGHTMAP_SCALE_1X;
 
 
 	shadow_casting_setting = SHADOW_CASTING_SETTING_ON;
 	shadow_casting_setting = SHADOW_CASTING_SETTING_ON;
 	extra_cull_margin = 0;
 	extra_cull_margin = 0;

+ 24 - 12
scene/3d/visual_instance_3d.h

@@ -85,13 +85,6 @@ class GeometryInstance3D : public VisualInstance3D {
 	GDCLASS(GeometryInstance3D, VisualInstance3D);
 	GDCLASS(GeometryInstance3D, VisualInstance3D);
 
 
 public:
 public:
-	enum Flags {
-		FLAG_USE_BAKED_LIGHT = RS::INSTANCE_FLAG_USE_BAKED_LIGHT,
-		FLAG_USE_DYNAMIC_GI = RS::INSTANCE_FLAG_USE_DYNAMIC_GI,
-		FLAG_DRAW_NEXT_FRAME_IF_VISIBLE = RS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE,
-		FLAG_MAX = RS::INSTANCE_FLAG_MAX,
-	};
-
 	enum ShadowCastingSetting {
 	enum ShadowCastingSetting {
 		SHADOW_CASTING_SETTING_OFF = RS::SHADOW_CASTING_SETTING_OFF,
 		SHADOW_CASTING_SETTING_OFF = RS::SHADOW_CASTING_SETTING_OFF,
 		SHADOW_CASTING_SETTING_ON = RS::SHADOW_CASTING_SETTING_ON,
 		SHADOW_CASTING_SETTING_ON = RS::SHADOW_CASTING_SETTING_ON,
@@ -99,8 +92,21 @@ public:
 		SHADOW_CASTING_SETTING_SHADOWS_ONLY = RS::SHADOW_CASTING_SETTING_SHADOWS_ONLY
 		SHADOW_CASTING_SETTING_SHADOWS_ONLY = RS::SHADOW_CASTING_SETTING_SHADOWS_ONLY
 	};
 	};
 
 
+	enum GIMode {
+		GI_MODE_DISABLED,
+		GI_MODE_BAKED,
+		GI_MODE_DYNAMIC
+	};
+
+	enum LightmapScale {
+		LIGHTMAP_SCALE_1X,
+		LIGHTMAP_SCALE_2X,
+		LIGHTMAP_SCALE_4X,
+		LIGHTMAP_SCALE_8X,
+		LIGHTMAP_SCALE_MAX,
+	};
+
 private:
 private:
-	bool flags[FLAG_MAX];
 	ShadowCastingSetting shadow_casting_setting;
 	ShadowCastingSetting shadow_casting_setting;
 	Ref<Material> material_override;
 	Ref<Material> material_override;
 	float lod_min_distance;
 	float lod_min_distance;
@@ -112,6 +118,8 @@ private:
 	mutable HashMap<StringName, StringName> instance_uniform_property_remap;
 	mutable HashMap<StringName, StringName> instance_uniform_property_remap;
 
 
 	float extra_cull_margin;
 	float extra_cull_margin;
+	LightmapScale lightmap_scale;
+	GIMode gi_mode;
 
 
 	const StringName *_instance_uniform_get_remap(const StringName p_name) const;
 	const StringName *_instance_uniform_get_remap(const StringName p_name) const;
 
 
@@ -124,9 +132,6 @@ protected:
 	static void _bind_methods();
 	static void _bind_methods();
 
 
 public:
 public:
-	void set_flag(Flags p_flag, bool p_value);
-	bool get_flag(Flags p_flag) const;
-
 	void set_cast_shadows_setting(ShadowCastingSetting p_shadow_casting_setting);
 	void set_cast_shadows_setting(ShadowCastingSetting p_shadow_casting_setting);
 	ShadowCastingSetting get_cast_shadows_setting() const;
 	ShadowCastingSetting get_cast_shadows_setting() const;
 
 
@@ -148,6 +153,12 @@ public:
 	void set_extra_cull_margin(float p_margin);
 	void set_extra_cull_margin(float p_margin);
 	float get_extra_cull_margin() const;
 	float get_extra_cull_margin() const;
 
 
+	void set_gi_mode(GIMode p_mode);
+	GIMode get_gi_mode() const;
+
+	void set_lightmap_scale(LightmapScale p_scale);
+	LightmapScale get_lightmap_scale() const;
+
 	void set_shader_instance_uniform(const StringName &p_uniform, const Variant &p_value);
 	void set_shader_instance_uniform(const StringName &p_uniform, const Variant &p_value);
 	Variant get_shader_instance_uniform(const StringName &p_uniform) const;
 	Variant get_shader_instance_uniform(const StringName &p_uniform) const;
 
 
@@ -156,7 +167,8 @@ public:
 	GeometryInstance3D();
 	GeometryInstance3D();
 };
 };
 
 
-VARIANT_ENUM_CAST(GeometryInstance3D::Flags);
 VARIANT_ENUM_CAST(GeometryInstance3D::ShadowCastingSetting);
 VARIANT_ENUM_CAST(GeometryInstance3D::ShadowCastingSetting);
+VARIANT_ENUM_CAST(GeometryInstance3D::LightmapScale);
+VARIANT_ENUM_CAST(GeometryInstance3D::GIMode);
 
 
 #endif
 #endif

+ 5 - 200
scene/3d/voxelizer.cpp

@@ -29,207 +29,12 @@
 /*************************************************************************/
 /*************************************************************************/
 
 
 #include "voxelizer.h"
 #include "voxelizer.h"
+#include "core/math/geometry.h"
 #include "core/os/os.h"
 #include "core/os/os.h"
 #include "core/os/threaded_array_processor.h"
 #include "core/os/threaded_array_processor.h"
 
 
 #include <stdlib.h>
 #include <stdlib.h>
 
 
-#define FINDMINMAX(x0, x1, x2, min, max) \
-	min = max = x0;                      \
-	if (x1 < min)                        \
-		min = x1;                        \
-	if (x1 > max)                        \
-		max = x1;                        \
-	if (x2 < min)                        \
-		min = x2;                        \
-	if (x2 > max)                        \
-		max = x2;
-
-static bool planeBoxOverlap(Vector3 normal, float d, Vector3 maxbox) {
-	int q;
-	Vector3 vmin, vmax;
-	for (q = 0; q <= 2; q++) {
-		if (normal[q] > 0.0f) {
-			vmin[q] = -maxbox[q];
-			vmax[q] = maxbox[q];
-		} else {
-			vmin[q] = maxbox[q];
-			vmax[q] = -maxbox[q];
-		}
-	}
-	if (normal.dot(vmin) + d > 0.0f)
-		return false;
-	if (normal.dot(vmax) + d >= 0.0f)
-		return true;
-
-	return false;
-}
-
-/*======================== X-tests ========================*/
-#define AXISTEST_X01(a, b, fa, fb)                 \
-	p0 = a * v0.y - b * v0.z;                      \
-	p2 = a * v2.y - b * v2.z;                      \
-	if (p0 < p2) {                                 \
-		min = p0;                                  \
-		max = p2;                                  \
-	} else {                                       \
-		min = p2;                                  \
-		max = p0;                                  \
-	}                                              \
-	rad = fa * boxhalfsize.y + fb * boxhalfsize.z; \
-	if (min > rad || max < -rad)                   \
-		return false;
-
-#define AXISTEST_X2(a, b, fa, fb)                  \
-	p0 = a * v0.y - b * v0.z;                      \
-	p1 = a * v1.y - b * v1.z;                      \
-	if (p0 < p1) {                                 \
-		min = p0;                                  \
-		max = p1;                                  \
-	} else {                                       \
-		min = p1;                                  \
-		max = p0;                                  \
-	}                                              \
-	rad = fa * boxhalfsize.y + fb * boxhalfsize.z; \
-	if (min > rad || max < -rad)                   \
-		return false;
-
-/*======================== Y-tests ========================*/
-#define AXISTEST_Y02(a, b, fa, fb)                 \
-	p0 = -a * v0.x + b * v0.z;                     \
-	p2 = -a * v2.x + b * v2.z;                     \
-	if (p0 < p2) {                                 \
-		min = p0;                                  \
-		max = p2;                                  \
-	} else {                                       \
-		min = p2;                                  \
-		max = p0;                                  \
-	}                                              \
-	rad = fa * boxhalfsize.x + fb * boxhalfsize.z; \
-	if (min > rad || max < -rad)                   \
-		return false;
-
-#define AXISTEST_Y1(a, b, fa, fb)                  \
-	p0 = -a * v0.x + b * v0.z;                     \
-	p1 = -a * v1.x + b * v1.z;                     \
-	if (p0 < p1) {                                 \
-		min = p0;                                  \
-		max = p1;                                  \
-	} else {                                       \
-		min = p1;                                  \
-		max = p0;                                  \
-	}                                              \
-	rad = fa * boxhalfsize.x + fb * boxhalfsize.z; \
-	if (min > rad || max < -rad)                   \
-		return false;
-
-/*======================== Z-tests ========================*/
-
-#define AXISTEST_Z12(a, b, fa, fb)                 \
-	p1 = a * v1.x - b * v1.y;                      \
-	p2 = a * v2.x - b * v2.y;                      \
-	if (p2 < p1) {                                 \
-		min = p2;                                  \
-		max = p1;                                  \
-	} else {                                       \
-		min = p1;                                  \
-		max = p2;                                  \
-	}                                              \
-	rad = fa * boxhalfsize.x + fb * boxhalfsize.y; \
-	if (min > rad || max < -rad)                   \
-		return false;
-
-#define AXISTEST_Z0(a, b, fa, fb)                  \
-	p0 = a * v0.x - b * v0.y;                      \
-	p1 = a * v1.x - b * v1.y;                      \
-	if (p0 < p1) {                                 \
-		min = p0;                                  \
-		max = p1;                                  \
-	} else {                                       \
-		min = p1;                                  \
-		max = p0;                                  \
-	}                                              \
-	rad = fa * boxhalfsize.x + fb * boxhalfsize.y; \
-	if (min > rad || max < -rad)                   \
-		return false;
-
-static bool fast_tri_box_overlap(const Vector3 &boxcenter, const Vector3 boxhalfsize, const Vector3 *triverts) {
-
-	/*    use separating axis theorem to test overlap between triangle and box */
-	/*    need to test for overlap in these directions: */
-	/*    1) the {x,y,z}-directions (actually, since we use the AABB of the triangle */
-	/*       we do not even need to test these) */
-	/*    2) normal of the triangle */
-	/*    3) crossproduct(edge from tri, {x,y,z}-directin) */
-	/*       this gives 3x3=9 more tests */
-	Vector3 v0, v1, v2;
-	float min, max, d, p0, p1, p2, rad, fex, fey, fez;
-	Vector3 normal, e0, e1, e2;
-
-	/* This is the fastest branch on Sun */
-	/* move everything so that the boxcenter is in (0,0,0) */
-
-	v0 = triverts[0] - boxcenter;
-	v1 = triverts[1] - boxcenter;
-	v2 = triverts[2] - boxcenter;
-
-	/* compute triangle edges */
-	e0 = v1 - v0; /* tri edge 0 */
-	e1 = v2 - v1; /* tri edge 1 */
-	e2 = v0 - v2; /* tri edge 2 */
-
-	/* Bullet 3:  */
-	/*  test the 9 tests first (this was faster) */
-	fex = Math::abs(e0.x);
-	fey = Math::abs(e0.y);
-	fez = Math::abs(e0.z);
-	AXISTEST_X01(e0.z, e0.y, fez, fey);
-	AXISTEST_Y02(e0.z, e0.x, fez, fex);
-	AXISTEST_Z12(e0.y, e0.x, fey, fex);
-
-	fex = Math::abs(e1.x);
-	fey = Math::abs(e1.y);
-	fez = Math::abs(e1.z);
-	AXISTEST_X01(e1.z, e1.y, fez, fey);
-	AXISTEST_Y02(e1.z, e1.x, fez, fex);
-	AXISTEST_Z0(e1.y, e1.x, fey, fex);
-
-	fex = Math::abs(e2.x);
-	fey = Math::abs(e2.y);
-	fez = Math::abs(e2.z);
-	AXISTEST_X2(e2.z, e2.y, fez, fey);
-	AXISTEST_Y1(e2.z, e2.x, fez, fex);
-	AXISTEST_Z12(e2.y, e2.x, fey, fex);
-
-	/* Bullet 1: */
-	/*  first test overlap in the {x,y,z}-directions */
-	/*  find min, max of the triangle each direction, and test for overlap in */
-	/*  that direction -- this is equivalent to testing a minimal AABB around */
-	/*  the triangle against the AABB */
-
-	/* test in X-direction */
-	FINDMINMAX(v0.x, v1.x, v2.x, min, max);
-	if (min > boxhalfsize.x || max < -boxhalfsize.x)
-		return false;
-
-	/* test in Y-direction */
-	FINDMINMAX(v0.y, v1.y, v2.y, min, max);
-	if (min > boxhalfsize.y || max < -boxhalfsize.y)
-		return false;
-
-	/* test in Z-direction */
-	FINDMINMAX(v0.z, v1.z, v2.z, min, max);
-	if (min > boxhalfsize.z || max < -boxhalfsize.z)
-		return false;
-
-	/* Bullet 2: */
-	/*  test if the box intersects the plane of the triangle */
-	/*  compute plane equation of triangle: normal*x+d=0 */
-	normal = e0.cross(e1);
-	d = -normal.dot(v0); /* plane eq: normal.x+d=0 */
-	return planeBoxOverlap(normal, d, boxhalfsize); /* if true, box and triangle overlaps */
-}
-
 static _FORCE_INLINE_ void get_uv_and_normal(const Vector3 &p_pos, const Vector3 *p_vtx, const Vector2 *p_uv, const Vector3 *p_normal, Vector2 &r_uv, Vector3 &r_normal) {
 static _FORCE_INLINE_ void get_uv_and_normal(const Vector3 &p_pos, const Vector3 *p_vtx, const Vector2 *p_uv, const Vector3 *p_normal, Vector2 &r_uv, Vector3 &r_normal) {
 
 
 	if (p_pos.distance_squared_to(p_vtx[0]) < CMP_EPSILON2) {
 	if (p_pos.distance_squared_to(p_vtx[0]) < CMP_EPSILON2) {
@@ -324,7 +129,7 @@ void Voxelizer::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, co
 				Vector3 half = (to - from) * 0.5;
 				Vector3 half = (to - from) * 0.5;
 
 
 				//is in this cell?
 				//is in this cell?
-				if (!fast_tri_box_overlap(from + half, half, p_vtx)) {
+				if (!Geometry::triangle_box_overlap(from + half, half, p_vtx)) {
 					continue; //face does not span this cell
 					continue; //face does not span this cell
 				}
 				}
 
 
@@ -467,7 +272,7 @@ void Voxelizer::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, co
 				//test_aabb.grow_by(test_aabb.get_longest_axis_size()*0.05); //grow a bit to avoid numerical error in real-time
 				//test_aabb.grow_by(test_aabb.get_longest_axis_size()*0.05); //grow a bit to avoid numerical error in real-time
 				Vector3 qsize = test_aabb.size * 0.5; //quarter size, for fast aabb test
 				Vector3 qsize = test_aabb.size * 0.5; //quarter size, for fast aabb test
 
 
-				if (!fast_tri_box_overlap(test_aabb.position + qsize, qsize, p_vtx)) {
+				if (!Geometry::triangle_box_overlap(test_aabb.position + qsize, qsize, p_vtx)) {
 					//if (!Face3(p_vtx[0],p_vtx[1],p_vtx[2]).intersects_aabb2(aabb)) {
 					//if (!Face3(p_vtx[0],p_vtx[1],p_vtx[2]).intersects_aabb2(aabb)) {
 					//does not fit in child, go on
 					//does not fit in child, go on
 					continue;
 					continue;
@@ -648,7 +453,7 @@ void Voxelizer::plot_mesh(const Transform &p_xform, Ref<Mesh> &p_mesh, const Vec
 				}
 				}
 
 
 				//test against original bounds
 				//test against original bounds
-				if (!fast_tri_box_overlap(original_bounds.position + original_bounds.size * 0.5, original_bounds.size * 0.5, vtxs))
+				if (!Geometry::triangle_box_overlap(original_bounds.position + original_bounds.size * 0.5, original_bounds.size * 0.5, vtxs))
 					continue;
 					continue;
 				//plot
 				//plot
 				_plot_face(0, 0, 0, 0, 0, vtxs, normal, uvs, material, po2_bounds);
 				_plot_face(0, 0, 0, 0, 0, vtxs, normal, uvs, material, po2_bounds);
@@ -681,7 +486,7 @@ void Voxelizer::plot_mesh(const Transform &p_xform, Ref<Mesh> &p_mesh, const Vec
 				}
 				}
 
 
 				//test against original bounds
 				//test against original bounds
-				if (!fast_tri_box_overlap(original_bounds.position + original_bounds.size * 0.5, original_bounds.size * 0.5, vtxs))
+				if (!Geometry::triangle_box_overlap(original_bounds.position + original_bounds.size * 0.5, original_bounds.size * 0.5, vtxs))
 					continue;
 					continue;
 				//plot face
 				//plot face
 				_plot_face(0, 0, 0, 0, 0, vtxs, normal, uvs, material, po2_bounds);
 				_plot_face(0, 0, 0, 0, 0, vtxs, normal, uvs, material, po2_bounds);

+ 13 - 5
scene/register_scene_types.cpp

@@ -193,6 +193,7 @@
 #include "scene/3d/gpu_particles_3d.h"
 #include "scene/3d/gpu_particles_3d.h"
 #include "scene/3d/immediate_geometry_3d.h"
 #include "scene/3d/immediate_geometry_3d.h"
 #include "scene/3d/light_3d.h"
 #include "scene/3d/light_3d.h"
+#include "scene/3d/lightmap_probe.h"
 #include "scene/3d/listener_3d.h"
 #include "scene/3d/listener_3d.h"
 #include "scene/3d/mesh_instance_3d.h"
 #include "scene/3d/mesh_instance_3d.h"
 #include "scene/3d/multimesh_instance_3d.h"
 #include "scene/3d/multimesh_instance_3d.h"
@@ -225,8 +226,8 @@ static Ref<ResourceFormatLoaderText> resource_loader_text;
 
 
 static Ref<ResourceFormatLoaderDynamicFont> resource_loader_dynamic_font;
 static Ref<ResourceFormatLoaderDynamicFont> resource_loader_dynamic_font;
 
 
-static Ref<ResourceFormatLoaderStreamTexture> resource_loader_stream_texture;
-static Ref<ResourceFormatLoaderTextureLayered> resource_loader_texture_layered;
+static Ref<ResourceFormatLoaderStreamTexture2D> resource_loader_stream_texture;
+static Ref<ResourceFormatLoaderStreamTextureLayered> resource_loader_texture_layered;
 
 
 static Ref<ResourceFormatLoaderBMFont> resource_loader_bmfont;
 static Ref<ResourceFormatLoaderBMFont> resource_loader_bmfont;
 
 
@@ -432,8 +433,9 @@ void register_scene_types() {
 	ClassDB::register_class<Decal>();
 	ClassDB::register_class<Decal>();
 	ClassDB::register_class<GIProbe>();
 	ClassDB::register_class<GIProbe>();
 	ClassDB::register_class<GIProbeData>();
 	ClassDB::register_class<GIProbeData>();
-	//ClassDB::register_class<BakedLightmap>();
-	//ClassDB::register_class<BakedLightmapData>();
+	ClassDB::register_class<BakedLightmap>();
+	ClassDB::register_class<BakedLightmapData>();
+	ClassDB::register_class<LightmapProbe>();
 	ClassDB::register_class<GPUParticles3D>();
 	ClassDB::register_class<GPUParticles3D>();
 	ClassDB::register_class<CPUParticles3D>();
 	ClassDB::register_class<CPUParticles3D>();
 	ClassDB::register_class<Position3D>();
 	ClassDB::register_class<Position3D>();
@@ -675,7 +677,7 @@ void register_scene_types() {
 	ClassDB::register_virtual_class<Texture>();
 	ClassDB::register_virtual_class<Texture>();
 	ClassDB::register_virtual_class<Texture2D>();
 	ClassDB::register_virtual_class<Texture2D>();
 	ClassDB::register_class<Sky>();
 	ClassDB::register_class<Sky>();
-	ClassDB::register_class<StreamTexture>();
+	ClassDB::register_class<StreamTexture2D>();
 	ClassDB::register_class<ImageTexture>();
 	ClassDB::register_class<ImageTexture>();
 	ClassDB::register_class<AtlasTexture>();
 	ClassDB::register_class<AtlasTexture>();
 	ClassDB::register_class<MeshTexture>();
 	ClassDB::register_class<MeshTexture>();
@@ -689,6 +691,11 @@ void register_scene_types() {
 	ClassDB::register_class<Cubemap>();
 	ClassDB::register_class<Cubemap>();
 	ClassDB::register_class<CubemapArray>();
 	ClassDB::register_class<CubemapArray>();
 	ClassDB::register_class<Texture2DArray>();
 	ClassDB::register_class<Texture2DArray>();
+	ClassDB::register_virtual_class<StreamTextureLayered>();
+	ClassDB::register_class<StreamCubemap>();
+	ClassDB::register_class<StreamCubemapArray>();
+	ClassDB::register_class<StreamTexture2DArray>();
+
 	ClassDB::register_class<Animation>();
 	ClassDB::register_class<Animation>();
 	ClassDB::register_virtual_class<Font>();
 	ClassDB::register_virtual_class<Font>();
 	ClassDB::register_class<BitmapFont>();
 	ClassDB::register_class<BitmapFont>();
@@ -867,6 +874,7 @@ void register_scene_types() {
 	ClassDB::add_compatibility_class("VisualShaderNodeScalarOp", "VisualShaderNodeFloatOp");
 	ClassDB::add_compatibility_class("VisualShaderNodeScalarOp", "VisualShaderNodeFloatOp");
 	ClassDB::add_compatibility_class("VisualShaderNodeScalarUniform", "VisualShaderNodeFloatUniform");
 	ClassDB::add_compatibility_class("VisualShaderNodeScalarUniform", "VisualShaderNodeFloatUniform");
 	ClassDB::add_compatibility_class("World", "World3D");
 	ClassDB::add_compatibility_class("World", "World3D");
+	ClassDB::add_compatibility_class("StreamTexture", "StreamTexture2D");
 
 
 #endif
 #endif
 
 

+ 3 - 3
scene/resources/mesh.cpp

@@ -472,11 +472,11 @@ Ref<Mesh> Mesh::create_outline(float p_margin) const {
 	return newmesh;
 	return newmesh;
 }
 }
 
 
-void Mesh::set_lightmap_size_hint(const Vector2 &p_size) {
+void Mesh::set_lightmap_size_hint(const Size2i &p_size) {
 	lightmap_size_hint = p_size;
 	lightmap_size_hint = p_size;
 }
 }
 
 
-Size2 Mesh::get_lightmap_size_hint() const {
+Size2i Mesh::get_lightmap_size_hint() const {
 	return lightmap_size_hint;
 	return lightmap_size_hint;
 }
 }
 
 
@@ -486,7 +486,7 @@ void Mesh::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_lightmap_size_hint"), &Mesh::get_lightmap_size_hint);
 	ClassDB::bind_method(D_METHOD("get_lightmap_size_hint"), &Mesh::get_lightmap_size_hint);
 	ClassDB::bind_method(D_METHOD("get_aabb"), &Mesh::get_aabb);
 	ClassDB::bind_method(D_METHOD("get_aabb"), &Mesh::get_aabb);
 
 
-	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "lightmap_size_hint"), "set_lightmap_size_hint", "get_lightmap_size_hint");
+	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "lightmap_size_hint"), "set_lightmap_size_hint", "get_lightmap_size_hint");
 
 
 	ClassDB::bind_method(D_METHOD("get_surface_count"), &Mesh::get_surface_count);
 	ClassDB::bind_method(D_METHOD("get_surface_count"), &Mesh::get_surface_count);
 	ClassDB::bind_method(D_METHOD("surface_get_arrays", "surf_idx"), &Mesh::surface_get_arrays);
 	ClassDB::bind_method(D_METHOD("surface_get_arrays", "surf_idx"), &Mesh::surface_get_arrays);

+ 3 - 3
scene/resources/mesh.h

@@ -43,7 +43,7 @@ class Mesh : public Resource {
 
 
 	mutable Ref<TriangleMesh> triangle_mesh; //cached
 	mutable Ref<TriangleMesh> triangle_mesh; //cached
 	mutable Vector<Vector3> debug_lines;
 	mutable Vector<Vector3> debug_lines;
-	Size2 lightmap_size_hint;
+	Size2i lightmap_size_hint;
 
 
 protected:
 protected:
 	static void _bind_methods();
 	static void _bind_methods();
@@ -138,8 +138,8 @@ public:
 
 
 	virtual AABB get_aabb() const = 0;
 	virtual AABB get_aabb() const = 0;
 
 
-	void set_lightmap_size_hint(const Vector2 &p_size);
-	Size2 get_lightmap_size_hint() const;
+	void set_lightmap_size_hint(const Size2i &p_size);
+	Size2i get_lightmap_size_hint() const;
 	void clear_cache() const;
 	void clear_cache() const;
 
 
 	typedef Vector<Vector<Face3>> (*ConvexDecompositionFunc)(const Vector<Face3> &);
 	typedef Vector<Vector<Face3>> (*ConvexDecompositionFunc)(const Vector<Face3> &);

+ 273 - 208
scene/resources/texture.cpp

@@ -355,7 +355,7 @@ ImageTexture::~ImageTexture() {
 
 
 //////////////////////////////////////////
 //////////////////////////////////////////
 
 
-Ref<Image> StreamTexture::load_image_from_file(FileAccess *f, int p_size_limit) {
+Ref<Image> StreamTexture2D::load_image_from_file(FileAccess *f, int p_size_limit) {
 
 
 	uint32_t data_format = f->get_32();
 	uint32_t data_format = f->get_32();
 	uint32_t w = f->get_16();
 	uint32_t w = f->get_16();
@@ -492,7 +492,7 @@ Ref<Image> StreamTexture::load_image_from_file(FileAccess *f, int p_size_limit)
 	return Ref<Image>();
 	return Ref<Image>();
 }
 }
 
 
-void StreamTexture::set_path(const String &p_path, bool p_take_over) {
+void StreamTexture2D::set_path(const String &p_path, bool p_take_over) {
 
 
 	if (texture.is_valid()) {
 	if (texture.is_valid()) {
 		RenderingServer::get_singleton()->texture_set_path(texture, p_path);
 		RenderingServer::get_singleton()->texture_set_path(texture, p_path);
@@ -501,40 +501,40 @@ void StreamTexture::set_path(const String &p_path, bool p_take_over) {
 	Resource::set_path(p_path, p_take_over);
 	Resource::set_path(p_path, p_take_over);
 }
 }
 
 
-void StreamTexture::_requested_3d(void *p_ud) {
+void StreamTexture2D::_requested_3d(void *p_ud) {
 
 
-	StreamTexture *st = (StreamTexture *)p_ud;
-	Ref<StreamTexture> stex(st);
+	StreamTexture2D *st = (StreamTexture2D *)p_ud;
+	Ref<StreamTexture2D> stex(st);
 	ERR_FAIL_COND(!request_3d_callback);
 	ERR_FAIL_COND(!request_3d_callback);
 	request_3d_callback(stex);
 	request_3d_callback(stex);
 }
 }
 
 
-void StreamTexture::_requested_roughness(void *p_ud, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_roughness_channel) {
+void StreamTexture2D::_requested_roughness(void *p_ud, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_roughness_channel) {
 
 
-	StreamTexture *st = (StreamTexture *)p_ud;
-	Ref<StreamTexture> stex(st);
+	StreamTexture2D *st = (StreamTexture2D *)p_ud;
+	Ref<StreamTexture2D> stex(st);
 	ERR_FAIL_COND(!request_roughness_callback);
 	ERR_FAIL_COND(!request_roughness_callback);
 	request_roughness_callback(stex, p_normal_path, p_roughness_channel);
 	request_roughness_callback(stex, p_normal_path, p_roughness_channel);
 }
 }
 
 
-void StreamTexture::_requested_normal(void *p_ud) {
+void StreamTexture2D::_requested_normal(void *p_ud) {
 
 
-	StreamTexture *st = (StreamTexture *)p_ud;
-	Ref<StreamTexture> stex(st);
+	StreamTexture2D *st = (StreamTexture2D *)p_ud;
+	Ref<StreamTexture2D> stex(st);
 	ERR_FAIL_COND(!request_normal_callback);
 	ERR_FAIL_COND(!request_normal_callback);
 	request_normal_callback(stex);
 	request_normal_callback(stex);
 }
 }
 
 
-StreamTexture::TextureFormatRequestCallback StreamTexture::request_3d_callback = nullptr;
-StreamTexture::TextureFormatRoughnessRequestCallback StreamTexture::request_roughness_callback = nullptr;
-StreamTexture::TextureFormatRequestCallback StreamTexture::request_normal_callback = nullptr;
+StreamTexture2D::TextureFormatRequestCallback StreamTexture2D::request_3d_callback = nullptr;
+StreamTexture2D::TextureFormatRoughnessRequestCallback StreamTexture2D::request_roughness_callback = nullptr;
+StreamTexture2D::TextureFormatRequestCallback StreamTexture2D::request_normal_callback = nullptr;
 
 
-Image::Format StreamTexture::get_format() const {
+Image::Format StreamTexture2D::get_format() const {
 
 
 	return format;
 	return format;
 }
 }
 
 
-Error StreamTexture::_load_data(const String &p_path, int &tw, int &th, int &tw_custom, int &th_custom, Ref<Image> &image, bool &r_request_3d, bool &r_request_normal, bool &r_request_roughness, int &mipmap_limit, int p_size_limit) {
+Error StreamTexture2D::_load_data(const String &p_path, int &tw, int &th, int &tw_custom, int &th_custom, Ref<Image> &image, bool &r_request_3d, bool &r_request_normal, bool &r_request_roughness, int &mipmap_limit, int p_size_limit) {
 
 
 	alpha_cache.unref();
 	alpha_cache.unref();
 
 
@@ -595,7 +595,7 @@ Error StreamTexture::_load_data(const String &p_path, int &tw, int &th, int &tw_
 	return OK;
 	return OK;
 }
 }
 
 
-Error StreamTexture::load(const String &p_path) {
+Error StreamTexture2D::load(const String &p_path) {
 
 
 	int lw, lh, lwc, lhc;
 	int lw, lh, lwc, lhc;
 	Ref<Image> image;
 	Ref<Image> image;
@@ -661,20 +661,20 @@ Error StreamTexture::load(const String &p_path) {
 	emit_changed();
 	emit_changed();
 	return OK;
 	return OK;
 }
 }
-String StreamTexture::get_load_path() const {
+String StreamTexture2D::get_load_path() const {
 
 
 	return path_to_file;
 	return path_to_file;
 }
 }
 
 
-int StreamTexture::get_width() const {
+int StreamTexture2D::get_width() const {
 
 
 	return w;
 	return w;
 }
 }
-int StreamTexture::get_height() const {
+int StreamTexture2D::get_height() const {
 
 
 	return h;
 	return h;
 }
 }
-RID StreamTexture::get_rid() const {
+RID StreamTexture2D::get_rid() const {
 
 
 	if (!texture.is_valid()) {
 	if (!texture.is_valid()) {
 		texture = RS::get_singleton()->texture_2d_placeholder_create();
 		texture = RS::get_singleton()->texture_2d_placeholder_create();
@@ -682,7 +682,7 @@ RID StreamTexture::get_rid() const {
 	return texture;
 	return texture;
 }
 }
 
 
-void StreamTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat) const {
+void StreamTexture2D::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat) const {
 
 
 	if ((w | h) == 0)
 	if ((w | h) == 0)
 		return;
 		return;
@@ -690,7 +690,7 @@ void StreamTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_
 	RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
 	RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
 	RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, Size2(w, h)), texture, false, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, p_texture_filter, p_texture_repeat);
 	RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, Size2(w, h)), texture, false, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, p_texture_filter, p_texture_repeat);
 }
 }
-void StreamTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat) const {
+void StreamTexture2D::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat) const {
 
 
 	if ((w | h) == 0)
 	if ((w | h) == 0)
 		return;
 		return;
@@ -698,7 +698,7 @@ void StreamTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_til
 	RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
 	RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
 	RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, texture, p_tile, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, p_texture_filter, p_texture_repeat);
 	RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, texture, p_tile, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, p_texture_filter, p_texture_repeat);
 }
 }
-void StreamTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat, bool p_clip_uv) const {
+void StreamTexture2D::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat, bool p_clip_uv) const {
 
 
 	if ((w | h) == 0)
 	if ((w | h) == 0)
 		return;
 		return;
@@ -707,12 +707,12 @@ void StreamTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, con
 	RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, texture, p_src_rect, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, p_clip_uv, p_texture_filter, p_texture_repeat);
 	RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, texture, p_src_rect, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, p_clip_uv, p_texture_filter, p_texture_repeat);
 }
 }
 
 
-bool StreamTexture::has_alpha() const {
+bool StreamTexture2D::has_alpha() const {
 
 
 	return false;
 	return false;
 }
 }
 
 
-Ref<Image> StreamTexture::get_data() const {
+Ref<Image> StreamTexture2D::get_data() const {
 
 
 	if (texture.is_valid()) {
 	if (texture.is_valid()) {
 		return RS::get_singleton()->texture_2d_get(texture);
 		return RS::get_singleton()->texture_2d_get(texture);
@@ -721,7 +721,7 @@ Ref<Image> StreamTexture::get_data() const {
 	}
 	}
 }
 }
 
 
-bool StreamTexture::is_pixel_opaque(int p_x, int p_y) const {
+bool StreamTexture2D::is_pixel_opaque(int p_x, int p_y) const {
 
 
 	if (!alpha_cache.is_valid()) {
 	if (!alpha_cache.is_valid()) {
 		Ref<Image> img = get_data();
 		Ref<Image> img = get_data();
@@ -757,7 +757,7 @@ bool StreamTexture::is_pixel_opaque(int p_x, int p_y) const {
 	return true;
 	return true;
 }
 }
 
 
-void StreamTexture::reload_from_file() {
+void StreamTexture2D::reload_from_file() {
 
 
 	String path = get_path();
 	String path = get_path();
 	if (!path.is_resource_file())
 	if (!path.is_resource_file())
@@ -771,34 +771,34 @@ void StreamTexture::reload_from_file() {
 	load(path);
 	load(path);
 }
 }
 
 
-void StreamTexture::_validate_property(PropertyInfo &property) const {
+void StreamTexture2D::_validate_property(PropertyInfo &property) const {
 }
 }
 
 
-void StreamTexture::_bind_methods() {
+void StreamTexture2D::_bind_methods() {
 
 
-	ClassDB::bind_method(D_METHOD("load", "path"), &StreamTexture::load);
-	ClassDB::bind_method(D_METHOD("get_load_path"), &StreamTexture::get_load_path);
+	ClassDB::bind_method(D_METHOD("load", "path"), &StreamTexture2D::load);
+	ClassDB::bind_method(D_METHOD("get_load_path"), &StreamTexture2D::get_load_path);
 
 
 	ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.stex"), "load", "get_load_path");
 	ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.stex"), "load", "get_load_path");
 }
 }
 
 
-StreamTexture::StreamTexture() {
+StreamTexture2D::StreamTexture2D() {
 
 
 	format = Image::FORMAT_MAX;
 	format = Image::FORMAT_MAX;
 	w = 0;
 	w = 0;
 	h = 0;
 	h = 0;
 }
 }
 
 
-StreamTexture::~StreamTexture() {
+StreamTexture2D::~StreamTexture2D() {
 
 
 	if (texture.is_valid()) {
 	if (texture.is_valid()) {
 		RS::get_singleton()->free(texture);
 		RS::get_singleton()->free(texture);
 	}
 	}
 }
 }
 
 
-RES ResourceFormatLoaderStreamTexture::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderStreamTexture2D::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
 
 
-	Ref<StreamTexture> st;
+	Ref<StreamTexture2D> st;
 	st.instance();
 	st.instance();
 	Error err = st->load(p_path);
 	Error err = st->load(p_path);
 	if (r_error)
 	if (r_error)
@@ -809,17 +809,17 @@ RES ResourceFormatLoaderStreamTexture::load(const String &p_path, const String &
 	return st;
 	return st;
 }
 }
 
 
-void ResourceFormatLoaderStreamTexture::get_recognized_extensions(List<String> *p_extensions) const {
+void ResourceFormatLoaderStreamTexture2D::get_recognized_extensions(List<String> *p_extensions) const {
 
 
 	p_extensions->push_back("stex");
 	p_extensions->push_back("stex");
 }
 }
-bool ResourceFormatLoaderStreamTexture::handles_type(const String &p_type) const {
-	return p_type == "StreamTexture";
+bool ResourceFormatLoaderStreamTexture2D::handles_type(const String &p_type) const {
+	return p_type == "StreamTexture2D";
 }
 }
-String ResourceFormatLoaderStreamTexture::get_resource_type(const String &p_path) const {
+String ResourceFormatLoaderStreamTexture2D::get_resource_type(const String &p_path) const {
 
 
 	if (p_path.get_extension().to_lower() == "stex")
 	if (p_path.get_extension().to_lower() == "stex")
-		return "StreamTexture";
+		return "StreamTexture2D";
 	return "";
 	return "";
 }
 }
 
 
@@ -1930,23 +1930,47 @@ AnimatedTexture::~AnimatedTexture() {
 }
 }
 ///////////////////////////////
 ///////////////////////////////
 
 
-Image::Format TextureLayered::get_format() const {
+void TextureLayered::_bind_methods() {
+
+	ClassDB::bind_method(D_METHOD("get_format"), &TextureLayered::get_format);
+	ClassDB::bind_method(D_METHOD("get_layered_type"), &TextureLayered::get_layered_type);
+	ClassDB::bind_method(D_METHOD("get_width"), &TextureLayered::get_width);
+	ClassDB::bind_method(D_METHOD("get_height"), &TextureLayered::get_height);
+	ClassDB::bind_method(D_METHOD("get_layers"), &TextureLayered::get_layers);
+	ClassDB::bind_method(D_METHOD("has_mipmaps"), &TextureLayered::has_mipmaps);
+	ClassDB::bind_method(D_METHOD("get_layer_data"), &TextureLayered::get_layer_data);
+
+	BIND_ENUM_CONSTANT(LAYERED_TYPE_2D_ARRAY);
+	BIND_ENUM_CONSTANT(LAYERED_TYPE_CUBEMAP);
+	BIND_ENUM_CONSTANT(LAYERED_TYPE_CUBEMAP_ARRAY);
+}
+
+///////////////////////////////
+Image::Format ImageTextureLayered::get_format() const {
 	return format;
 	return format;
 }
 }
 
 
-uint32_t TextureLayered::get_width() const {
+int ImageTextureLayered::get_width() const {
 	return width;
 	return width;
 }
 }
 
 
-uint32_t TextureLayered::get_height() const {
+int ImageTextureLayered::get_height() const {
 	return height;
 	return height;
 }
 }
 
 
-uint32_t TextureLayered::get_layers() const {
+int ImageTextureLayered::get_layers() const {
 	return layers;
 	return layers;
 }
 }
 
 
-Error TextureLayered::_create_from_images(const Array &p_images) {
+bool ImageTextureLayered::has_mipmaps() const {
+	return mipmaps;
+}
+
+ImageTextureLayered::LayeredType ImageTextureLayered::get_layered_type() const {
+	return layered_type;
+}
+
+Error ImageTextureLayered::_create_from_images(const Array &p_images) {
 	Vector<Ref<Image>> images;
 	Vector<Ref<Image>> images;
 	for (int i = 0; i < p_images.size(); i++) {
 	for (int i = 0; i < p_images.size(); i++) {
 		Ref<Image> img = p_images[i];
 		Ref<Image> img = p_images[i];
@@ -1957,7 +1981,7 @@ Error TextureLayered::_create_from_images(const Array &p_images) {
 	return create_from_images(images);
 	return create_from_images(images);
 }
 }
 
 
-Array TextureLayered::_get_images() const {
+Array ImageTextureLayered::_get_images() const {
 	Array images;
 	Array images;
 	for (int i = 0; i < layers; i++) {
 	for (int i = 0; i < layers; i++) {
 		images.push_back(get_layer_data(i));
 		images.push_back(get_layer_data(i));
@@ -1965,14 +1989,14 @@ Array TextureLayered::_get_images() const {
 	return images;
 	return images;
 }
 }
 
 
-Error TextureLayered::create_from_images(Vector<Ref<Image>> p_images) {
+Error ImageTextureLayered::create_from_images(Vector<Ref<Image>> p_images) {
 
 
 	int new_layers = p_images.size();
 	int new_layers = p_images.size();
 	ERR_FAIL_COND_V(new_layers == 0, ERR_INVALID_PARAMETER);
 	ERR_FAIL_COND_V(new_layers == 0, ERR_INVALID_PARAMETER);
-	if (layered_type == RS::TEXTURE_LAYERED_CUBEMAP) {
+	if (layered_type == LAYERED_TYPE_CUBEMAP) {
 		ERR_FAIL_COND_V_MSG(new_layers != 6, ERR_INVALID_PARAMETER,
 		ERR_FAIL_COND_V_MSG(new_layers != 6, ERR_INVALID_PARAMETER,
 				"Cubemaps require exactly 6 layers");
 				"Cubemaps require exactly 6 layers");
-	} else if (layered_type == RS::TEXTURE_LAYERED_CUBEMAP_ARRAY) {
+	} else if (layered_type == LAYERED_TYPE_CUBEMAP_ARRAY) {
 		ERR_FAIL_COND_V_MSG((new_layers % 6) != 0, ERR_INVALID_PARAMETER,
 		ERR_FAIL_COND_V_MSG((new_layers % 6) != 0, ERR_INVALID_PARAMETER,
 				"Cubemap array layers must be a multiple of 6");
 				"Cubemap array layers must be a multiple of 6");
 	}
 	}
@@ -1994,11 +2018,11 @@ Error TextureLayered::create_from_images(Vector<Ref<Image>> p_images) {
 	}
 	}
 
 
 	if (texture.is_valid()) {
 	if (texture.is_valid()) {
-		RID new_texture = RS::get_singleton()->texture_2d_layered_create(p_images, layered_type);
+		RID new_texture = RS::get_singleton()->texture_2d_layered_create(p_images, RS::TextureLayeredType(layered_type));
 		ERR_FAIL_COND_V(!new_texture.is_valid(), ERR_CANT_CREATE);
 		ERR_FAIL_COND_V(!new_texture.is_valid(), ERR_CANT_CREATE);
 		RS::get_singleton()->texture_replace(texture, new_texture);
 		RS::get_singleton()->texture_replace(texture, new_texture);
 	} else {
 	} else {
-		texture = RS::get_singleton()->texture_2d_layered_create(p_images, layered_type);
+		texture = RS::get_singleton()->texture_2d_layered_create(p_images, RS::TextureLayeredType(layered_type));
 		ERR_FAIL_COND_V(!texture.is_valid(), ERR_CANT_CREATE);
 		ERR_FAIL_COND_V(!texture.is_valid(), ERR_CANT_CREATE);
 	}
 	}
 
 
@@ -2010,7 +2034,7 @@ Error TextureLayered::create_from_images(Vector<Ref<Image>> p_images) {
 	return OK;
 	return OK;
 }
 }
 
 
-void TextureLayered::update_layer(const Ref<Image> &p_image, int p_layer) {
+void ImageTextureLayered::update_layer(const Ref<Image> &p_image, int p_layer) {
 	ERR_FAIL_COND(texture.is_valid());
 	ERR_FAIL_COND(texture.is_valid());
 	ERR_FAIL_COND(p_image.is_null());
 	ERR_FAIL_COND(p_image.is_null());
 	ERR_FAIL_COND(p_image->get_format() != format);
 	ERR_FAIL_COND(p_image->get_format() != format);
@@ -2020,19 +2044,19 @@ void TextureLayered::update_layer(const Ref<Image> &p_image, int p_layer) {
 	RS::get_singleton()->texture_2d_update(texture, p_image, p_layer);
 	RS::get_singleton()->texture_2d_update(texture, p_image, p_layer);
 }
 }
 
 
-Ref<Image> TextureLayered::get_layer_data(int p_layer) const {
+Ref<Image> ImageTextureLayered::get_layer_data(int p_layer) const {
 	ERR_FAIL_INDEX_V(p_layer, layers, Ref<Image>());
 	ERR_FAIL_INDEX_V(p_layer, layers, Ref<Image>());
 	return RS::get_singleton()->texture_2d_layer_get(texture, p_layer);
 	return RS::get_singleton()->texture_2d_layer_get(texture, p_layer);
 }
 }
 
 
-RID TextureLayered::get_rid() const {
+RID ImageTextureLayered::get_rid() const {
 	if (texture.is_null()) {
 	if (texture.is_null()) {
-		texture = RS::get_singleton()->texture_2d_layered_placeholder_create();
+		texture = RS::get_singleton()->texture_2d_layered_placeholder_create(RS::TextureLayeredType(layered_type));
 	}
 	}
 	return texture;
 	return texture;
 }
 }
 
 
-void TextureLayered::set_path(const String &p_path, bool p_take_over) {
+void ImageTextureLayered::set_path(const String &p_path, bool p_take_over) {
 	if (texture.is_valid()) {
 	if (texture.is_valid()) {
 		RS::get_singleton()->texture_set_path(texture, p_path);
 		RS::get_singleton()->texture_set_path(texture, p_path);
 	}
 	}
@@ -2040,24 +2064,17 @@ void TextureLayered::set_path(const String &p_path, bool p_take_over) {
 	Resource::set_path(p_path, p_take_over);
 	Resource::set_path(p_path, p_take_over);
 }
 }
 
 
-void TextureLayered::_bind_methods() {
-
-	ClassDB::bind_method(D_METHOD("get_format"), &TextureLayered::get_format);
-
-	ClassDB::bind_method(D_METHOD("get_width"), &TextureLayered::get_width);
-	ClassDB::bind_method(D_METHOD("get_height"), &TextureLayered::get_height);
-	ClassDB::bind_method(D_METHOD("get_layers"), &TextureLayered::get_layers);
+void ImageTextureLayered::_bind_methods() {
 
 
-	ClassDB::bind_method(D_METHOD("create_from_images", "images"), &TextureLayered::_create_from_images);
-	ClassDB::bind_method(D_METHOD("update_layer", "image", "layer"), &TextureLayered::update_layer);
-	ClassDB::bind_method(D_METHOD("get_layer_data", "layer"), &TextureLayered::get_layer_data);
+	ClassDB::bind_method(D_METHOD("create_from_images", "images"), &ImageTextureLayered::_create_from_images);
+	ClassDB::bind_method(D_METHOD("update_layer", "image", "layer"), &ImageTextureLayered::update_layer);
 
 
-	ClassDB::bind_method(D_METHOD("_get_images"), &TextureLayered::_get_images);
+	ClassDB::bind_method(D_METHOD("_get_images"), &ImageTextureLayered::_get_images);
 
 
 	ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_images", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_INTERNAL), "create_from_images", "_get_images");
 	ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_images", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_INTERNAL), "create_from_images", "_get_images");
 }
 }
 
 
-TextureLayered::TextureLayered(RenderingServer::TextureLayeredType p_layered_type) {
+ImageTextureLayered::ImageTextureLayered(LayeredType p_layered_type) {
 	layered_type = p_layered_type;
 	layered_type = p_layered_type;
 	format = Image::FORMAT_MAX;
 	format = Image::FORMAT_MAX;
 
 
@@ -2066,193 +2083,241 @@ TextureLayered::TextureLayered(RenderingServer::TextureLayeredType p_layered_typ
 	layers = 0;
 	layers = 0;
 }
 }
 
 
-TextureLayered::~TextureLayered() {
+ImageTextureLayered::~ImageTextureLayered() {
 	if (texture.is_valid()) {
 	if (texture.is_valid()) {
 		RS::get_singleton()->free(texture);
 		RS::get_singleton()->free(texture);
 	}
 	}
 }
 }
 
 
-RES ResourceFormatLoaderTextureLayered::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+///////////////////////////////////////////
+
+void StreamTextureLayered::set_path(const String &p_path, bool p_take_over) {
 
 
-	if (r_error) {
-		*r_error = ERR_CANT_OPEN;
+	if (texture.is_valid()) {
+		RenderingServer::get_singleton()->texture_set_path(texture, p_path);
 	}
 	}
 
 
-	Ref<TextureLayered> lt;
+	Resource::set_path(p_path, p_take_over);
+}
+
+Image::Format StreamTextureLayered::get_format() const {
 
 
-	if (p_path.ends_with("cube")) {
-		Ref<Cubemap> cube;
-		cube.instance();
-		lt = cube;
-	} else if (p_path.ends_with("cubearr")) {
-		Ref<CubemapArray> cubearr;
-		cubearr.instance();
-		lt = cubearr;
-	} else if (p_path.ends_with("tex2darr")) {
-		Ref<Texture2DArray> t2darr;
-		t2darr.instance();
-		lt = t2darr;
-	} else {
-		ERR_FAIL_V_MSG(RES(), "Unrecognized layered texture extension.");
+	return format;
+}
+
+Error StreamTextureLayered::_load_data(const String &p_path, Vector<Ref<Image>> &images, int &mipmap_limit, int p_size_limit) {
+
+	ERR_FAIL_COND_V(images.size() != 0, ERR_INVALID_PARAMETER);
+
+	FileAccessRef f = FileAccess::open(p_path, FileAccess::READ);
+	ERR_FAIL_COND_V(!f, ERR_CANT_OPEN);
+
+	uint8_t header[4];
+	f->get_buffer(header, 4);
+	if (header[0] != 'G' || header[1] != 'S' || header[2] != 'T' || header[3] != 'L') {
+		ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Stream texture layered file is corrupt (Bad header).");
 	}
 	}
 
 
-	FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
-	ERR_FAIL_COND_V_MSG(!f, RES(), "Cannot open file '" + p_path + "'.");
+	uint32_t version = f->get_32();
+
+	if (version > FORMAT_VERSION) {
+		ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Stream texture file is too new.");
+	}
 
 
-	char header[5] = { 0, 0, 0, 0, 0 };
-	f->get_buffer((uint8_t *)header, 4);
+	uint32_t layer_count = f->get_32(); //layer count
+	uint32_t type = f->get_32(); //layer count
+	ERR_FAIL_COND_V(type != layered_type, ERR_INVALID_DATA);
 
 
-	if (String(header) != "GDLT") {
-		f->close();
-		memdelete(f);
-		if (r_error) {
-			*r_error = ERR_FILE_CORRUPT;
-		}
-		// FIXME: It's bogus that we fail in both branches. Seen while rebasing
-		// vulkan branch on master branch.
-		ERR_FAIL_V_MSG(RES(), "Unrecognized layered texture.");
-	} else {
+	uint32_t df = f->get_32(); //data format
+	mipmap_limit = int(f->get_32());
+	//reserverd
+	f->get_32();
+	f->get_32();
+	f->get_32();
 
 
-		f->close();
-		memdelete(f);
-		ERR_FAIL_V_MSG(RES(), "Unrecognized layered texture file format '" + String((const char *)header) + "'.");
+	if (!(df & FORMAT_BIT_STREAM)) {
+		p_size_limit = 0;
 	}
 	}
 
 
-	int tw = f->get_32();
-	int th = f->get_32();
-	int td = f->get_32();
-	bool use_mipmaps = f->get_32() != 0; //texture flags (deprecated)
-	Image::Format format = Image::Format(f->get_32());
-	uint32_t compression = f->get_32(); // 0 - lossless (PNG), 1 - vram, 2 - uncompressed
+	images.resize(layer_count);
 
 
-	Vector<Ref<Image>> images;
-	for (int layer = 0; layer < td; layer++) {
+	for (uint32_t i = 0; i < layer_count; i++) {
+		Ref<Image> image = StreamTexture2D::load_image_from_file(f, p_size_limit);
+		ERR_FAIL_COND_V(image.is_null() || image->empty(), ERR_CANT_OPEN);
+		images.write[i] = image;
+	}
 
 
-		Ref<Image> image;
-		image.instance();
+	return OK;
+}
 
 
-		if (compression == COMPRESSION_LOSSLESS) {
-			//look for a PNG file inside
+Error StreamTextureLayered::load(const String &p_path) {
 
 
-			int mipmaps = f->get_32();
-			Vector<Ref<Image>> mipmap_images;
+	Vector<Ref<Image>> images;
 
 
-			for (int i = 0; i < mipmaps; i++) {
-				uint32_t size = f->get_32();
+	int mipmap_limit;
 
 
-				Vector<uint8_t> pv;
-				pv.resize(size);
-				{
-					uint8_t *w = pv.ptrw();
-					f->get_buffer(w, size);
-				}
+	Error err = _load_data(p_path, images, mipmap_limit);
+	if (err)
+		return err;
 
 
-				Ref<Image> img = Image::lossless_unpacker(pv);
+	if (texture.is_valid()) {
+		RID new_texture = RS::get_singleton()->texture_2d_layered_create(images, RS::TextureLayeredType(layered_type));
+		RS::get_singleton()->texture_replace(texture, new_texture);
+	} else {
+		texture = RS::get_singleton()->texture_2d_layered_create(images, RS::TextureLayeredType(layered_type));
+	}
 
 
-				if (img.is_null() || img->empty() || format != img->get_format()) {
-					if (r_error) {
-						*r_error = ERR_FILE_CORRUPT;
-					}
-					f->close();
-					memdelete(f);
-					ERR_FAIL_V(RES());
-				}
+	w = images[0]->get_width();
+	h = images[0]->get_height();
+	mipmaps = images[0]->has_mipmaps();
+	format = images[0]->get_format();
+	layers = images.size();
 
 
-				mipmap_images.push_back(img);
-			}
+	path_to_file = p_path;
 
 
-			if (mipmap_images.size() == 1) {
+	if (get_path() == String()) {
+		//temporarily set path if no path set for resource, helps find errors
+		RenderingServer::get_singleton()->texture_set_path(texture, p_path);
+	}
 
 
-				image = mipmap_images[0];
+	_change_notify();
+	emit_changed();
+	return OK;
+}
+String StreamTextureLayered::get_load_path() const {
 
 
-			} else {
-				int total_size = Image::get_image_data_size(tw, th, format, true);
-				Vector<uint8_t> img_data;
-				img_data.resize(total_size);
-
-				{
-					uint8_t *w = img_data.ptrw();
-
-					int ofs = 0;
-					for (int i = 0; i < mipmap_images.size(); i++) {
-
-						Vector<uint8_t> id = mipmap_images[i]->get_data();
-						int len = id.size();
-						const uint8_t *r = id.ptr();
-						copymem(&w[ofs], r, len);
-						ofs += len;
-					}
-				}
+	return path_to_file;
+}
 
 
-				image->create(tw, th, true, format, img_data);
-				if (image->empty()) {
-					if (r_error) {
-						*r_error = ERR_FILE_CORRUPT;
-					}
-					f->close();
-					memdelete(f);
-					ERR_FAIL_V(RES());
-				}
-			}
+int StreamTextureLayered::get_width() const {
 
 
-		} else {
+	return w;
+}
+int StreamTextureLayered::get_height() const {
 
 
-			//look for regular format
+	return h;
+}
+int StreamTextureLayered::get_layers() const {
 
 
-			int total_size = Image::get_image_data_size(tw, th, format, use_mipmaps);
+	return layers;
+}
+bool StreamTextureLayered::has_mipmaps() const {
+	return mipmaps;
+}
 
 
-			Vector<uint8_t> img_data;
-			img_data.resize(total_size);
+TextureLayered::LayeredType StreamTextureLayered::get_layered_type() const {
 
 
-			{
-				uint8_t *w = img_data.ptrw();
-				int bytes = f->get_buffer(w, total_size);
-				if (bytes != total_size) {
-					if (r_error) {
-						*r_error = ERR_FILE_CORRUPT;
-					}
-					f->close();
-					memdelete(f);
-					ERR_FAIL_V(RES());
-				}
-			}
+	return layered_type;
+}
 
 
-			image->create(tw, th, use_mipmaps, format, img_data);
-		}
+RID StreamTextureLayered::get_rid() const {
 
 
-		images.push_back(image);
+	if (!texture.is_valid()) {
+		texture = RS::get_singleton()->texture_2d_layered_placeholder_create(RS::TextureLayeredType(layered_type));
 	}
 	}
+	return texture;
+}
 
 
-	Error err = lt->create_from_images(images);
-	if (err != OK) {
-		*r_error = err;
-		return RES();
+Ref<Image> StreamTextureLayered::get_layer_data(int p_layer) const {
+
+	if (texture.is_valid()) {
+		return RS::get_singleton()->texture_2d_layer_get(texture, p_layer);
 	} else {
 	} else {
+		return Ref<Image>();
+	}
+}
+
+void StreamTextureLayered::reload_from_file() {
+
+	String path = get_path();
+	if (!path.is_resource_file())
+		return;
 
 
-		if (r_error)
-			*r_error = OK;
+	path = ResourceLoader::path_remap(path); //remap for translation
+	path = ResourceLoader::import_remap(path); //remap for import
+	if (!path.is_resource_file())
+		return;
+
+	load(path);
+}
+
+void StreamTextureLayered::_validate_property(PropertyInfo &property) const {
+}
+
+void StreamTextureLayered::_bind_methods() {
+
+	ClassDB::bind_method(D_METHOD("load", "path"), &StreamTextureLayered::load);
+	ClassDB::bind_method(D_METHOD("get_load_path"), &StreamTextureLayered::get_load_path);
+
+	ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.stex"), "load", "get_load_path");
+}
+
+StreamTextureLayered::StreamTextureLayered(LayeredType p_type) {
+
+	layered_type = p_type;
+	format = Image::FORMAT_MAX;
+	w = 0;
+	h = 0;
+	layers = 0;
+	mipmaps = false;
+}
+
+StreamTextureLayered::~StreamTextureLayered() {
+
+	if (texture.is_valid()) {
+		RS::get_singleton()->free(texture);
 	}
 	}
+}
 
 
-	return lt;
+/////////////////////////////////////////////////
+
+RES ResourceFormatLoaderStreamTextureLayered::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+
+	Ref<StreamTextureLayered> st;
+	if (p_path.get_extension().to_lower() == "stexarray") {
+		Ref<StreamTexture2DArray> s;
+		s.instance();
+		st = s;
+	} else if (p_path.get_extension().to_lower() == "scube") {
+		Ref<StreamCubemap> s;
+		s.instance();
+		st = s;
+	} else if (p_path.get_extension().to_lower() == "scubearray") {
+		Ref<StreamCubemapArray> s;
+		s.instance();
+		st = s;
+	} else {
+		if (r_error) {
+			*r_error = ERR_FILE_UNRECOGNIZED;
+		}
+		return RES();
+	}
+	Error err = st->load(p_path);
+	if (r_error)
+		*r_error = err;
+	if (err != OK)
+		return RES();
+
+	return st;
 }
 }
 
 
-void ResourceFormatLoaderTextureLayered::get_recognized_extensions(List<String> *p_extensions) const {
+void ResourceFormatLoaderStreamTextureLayered::get_recognized_extensions(List<String> *p_extensions) const {
 
 
-	p_extensions->push_back("cube");
-	p_extensions->push_back("cubearr");
-	p_extensions->push_back("tex2darr");
+	p_extensions->push_back("stexarray");
+	p_extensions->push_back("scube");
+	p_extensions->push_back("scubearray");
 }
 }
-bool ResourceFormatLoaderTextureLayered::handles_type(const String &p_type) const {
-	return p_type == "Texture2DArray" || p_type == "Cubemap" || p_type == "CubemapArray";
+bool ResourceFormatLoaderStreamTextureLayered::handles_type(const String &p_type) const {
+	return p_type == "StreamTexture2DArray" || p_type == "StreamCubemap" || p_type == "StreamCubemapArray";
 }
 }
-String ResourceFormatLoaderTextureLayered::get_resource_type(const String &p_path) const {
+String ResourceFormatLoaderStreamTextureLayered::get_resource_type(const String &p_path) const {
 
 
-	if (p_path.get_extension().to_lower() == "cube")
-		return "Cubemap";
-	if (p_path.get_extension().to_lower() == "cubearr")
-		return "CubemapArray";
-	if (p_path.get_extension().to_lower() == "tex2darr")
-		return "Texture2DArray";
+	if (p_path.get_extension().to_lower() == "stexarray")
+		return "StreamTexture2DArray";
+	if (p_path.get_extension().to_lower() == "scube")
+		return "StreamCubemap";
+	if (p_path.get_extension().to_lower() == "scubearray")
+		return "StreamCubemapArray";
 	return "";
 	return "";
 }
 }
 
 

+ 135 - 31
scene/resources/texture.h

@@ -132,9 +132,9 @@ public:
 	~ImageTexture();
 	~ImageTexture();
 };
 };
 
 
-class StreamTexture : public Texture2D {
+class StreamTexture2D : public Texture2D {
 
 
-	GDCLASS(StreamTexture, Texture2D);
+	GDCLASS(StreamTexture2D, Texture2D);
 
 
 public:
 public:
 	enum DataFormat {
 	enum DataFormat {
@@ -181,8 +181,8 @@ protected:
 public:
 public:
 	static Ref<Image> load_image_from_file(FileAccess *p_file, int p_size_limit);
 	static Ref<Image> load_image_from_file(FileAccess *p_file, int p_size_limit);
 
 
-	typedef void (*TextureFormatRequestCallback)(const Ref<StreamTexture> &);
-	typedef void (*TextureFormatRoughnessRequestCallback)(const Ref<StreamTexture> &, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_roughness_channel);
+	typedef void (*TextureFormatRequestCallback)(const Ref<StreamTexture2D> &);
+	typedef void (*TextureFormatRoughnessRequestCallback)(const Ref<StreamTexture2D> &, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_roughness_channel);
 
 
 	static TextureFormatRequestCallback request_3d_callback;
 	static TextureFormatRequestCallback request_3d_callback;
 	static TextureFormatRoughnessRequestCallback request_roughness_callback;
 	static TextureFormatRoughnessRequestCallback request_roughness_callback;
@@ -207,11 +207,11 @@ public:
 
 
 	virtual Ref<Image> get_data() const;
 	virtual Ref<Image> get_data() const;
 
 
-	StreamTexture();
-	~StreamTexture();
+	StreamTexture2D();
+	~StreamTexture2D();
 };
 };
 
 
-class ResourceFormatLoaderStreamTexture : public ResourceFormatLoader {
+class ResourceFormatLoaderStreamTexture2D : public ResourceFormatLoader {
 public:
 public:
 	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
 	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
 	virtual void get_recognized_extensions(List<String> *p_extensions) const;
 	virtual void get_recognized_extensions(List<String> *p_extensions) const;
@@ -349,10 +349,34 @@ public:
 };
 };
 
 
 class TextureLayered : public Texture {
 class TextureLayered : public Texture {
-
 	GDCLASS(TextureLayered, Texture);
 	GDCLASS(TextureLayered, Texture);
 
 
-	RS::TextureLayeredType layered_type;
+protected:
+	static void _bind_methods();
+
+public:
+	enum LayeredType {
+		LAYERED_TYPE_2D_ARRAY,
+		LAYERED_TYPE_CUBEMAP,
+		LAYERED_TYPE_CUBEMAP_ARRAY
+	};
+
+	virtual Image::Format get_format() const = 0;
+	virtual LayeredType get_layered_type() const = 0;
+	virtual int get_width() const = 0;
+	virtual int get_height() const = 0;
+	virtual int get_layers() const = 0;
+	virtual bool has_mipmaps() const = 0;
+	virtual Ref<Image> get_layer_data(int p_layer) const = 0;
+};
+
+VARIANT_ENUM_CAST(TextureLayered::LayeredType)
+
+class ImageTextureLayered : public TextureLayered {
+
+	GDCLASS(ImageTextureLayered, TextureLayered);
+
+	LayeredType layered_type;
 
 
 	mutable RID texture;
 	mutable RID texture;
 	Image::Format format;
 	Image::Format format;
@@ -370,57 +394,137 @@ protected:
 	static void _bind_methods();
 	static void _bind_methods();
 
 
 public:
 public:
-	Image::Format get_format() const;
-	uint32_t get_width() const;
-	uint32_t get_height() const;
-	uint32_t get_layers() const;
-	bool has_mipmaps() const;
+	virtual Image::Format get_format() const;
+	virtual int get_width() const;
+	virtual int get_height() const;
+	virtual int get_layers() const;
+	virtual bool has_mipmaps() const;
+	virtual LayeredType get_layered_type() const;
 
 
 	Error create_from_images(Vector<Ref<Image>> p_images);
 	Error create_from_images(Vector<Ref<Image>> p_images);
 	void update_layer(const Ref<Image> &p_image, int p_layer);
 	void update_layer(const Ref<Image> &p_image, int p_layer);
-	Ref<Image> get_layer_data(int p_layer) const;
+	virtual Ref<Image> get_layer_data(int p_layer) const;
 
 
 	virtual RID get_rid() const;
 	virtual RID get_rid() const;
 	virtual void set_path(const String &p_path, bool p_take_over = false);
 	virtual void set_path(const String &p_path, bool p_take_over = false);
 
 
-	TextureLayered(RS::TextureLayeredType p_layered_type);
-	~TextureLayered();
+	ImageTextureLayered(LayeredType p_layered_type);
+	~ImageTextureLayered();
 };
 };
 
 
-class Texture2DArray : public TextureLayered {
+class Texture2DArray : public ImageTextureLayered {
 
 
-	GDCLASS(Texture2DArray, TextureLayered)
+	GDCLASS(Texture2DArray, ImageTextureLayered)
 public:
 public:
 	Texture2DArray() :
 	Texture2DArray() :
-			TextureLayered(RS::TEXTURE_LAYERED_2D_ARRAY) {}
+			ImageTextureLayered(LAYERED_TYPE_2D_ARRAY) {}
 };
 };
 
 
-class Cubemap : public TextureLayered {
+class Cubemap : public ImageTextureLayered {
 
 
-	GDCLASS(Cubemap, TextureLayered);
+	GDCLASS(Cubemap, ImageTextureLayered);
 
 
 public:
 public:
 	Cubemap() :
 	Cubemap() :
-			TextureLayered(RS::TEXTURE_LAYERED_CUBEMAP) {}
+			ImageTextureLayered(LAYERED_TYPE_CUBEMAP) {}
 };
 };
 
 
-class CubemapArray : public TextureLayered {
+class CubemapArray : public ImageTextureLayered {
 
 
-	GDCLASS(CubemapArray, TextureLayered);
+	GDCLASS(CubemapArray, ImageTextureLayered);
 
 
 public:
 public:
 	CubemapArray() :
 	CubemapArray() :
-			TextureLayered(RS::TEXTURE_LAYERED_CUBEMAP_ARRAY) {}
+			ImageTextureLayered(LAYERED_TYPE_CUBEMAP_ARRAY) {}
 };
 };
 
 
-class ResourceFormatLoaderTextureLayered : public ResourceFormatLoader {
+class StreamTextureLayered : public TextureLayered {
+
+	GDCLASS(StreamTextureLayered, TextureLayered);
+
 public:
 public:
-	enum Compression {
-		COMPRESSION_LOSSLESS,
-		COMPRESSION_VRAM,
-		COMPRESSION_UNCOMPRESSED
+	enum DataFormat {
+		DATA_FORMAT_IMAGE,
+		DATA_FORMAT_LOSSLESS,
+		DATA_FORMAT_LOSSY,
+		DATA_FORMAT_BASIS_UNIVERSAL,
+	};
+
+	enum {
+		FORMAT_VERSION = 1
 	};
 	};
 
 
+	enum FormatBits {
+		FORMAT_MASK_IMAGE_FORMAT = (1 << 20) - 1,
+		FORMAT_BIT_LOSSLESS = 1 << 20,
+		FORMAT_BIT_LOSSY = 1 << 21,
+		FORMAT_BIT_STREAM = 1 << 22,
+		FORMAT_BIT_HAS_MIPMAPS = 1 << 23,
+	};
+
+private:
+	Error _load_data(const String &p_path, Vector<Ref<Image>> &images, int &mipmap_limit, int p_size_limit = 0);
+	String path_to_file;
+	mutable RID texture;
+	Image::Format format;
+	int w, h, layers;
+	bool mipmaps;
+	LayeredType layered_type;
+
+	virtual void reload_from_file();
+
+protected:
+	static void _bind_methods();
+	void _validate_property(PropertyInfo &property) const;
+
+public:
+	Image::Format get_format() const;
+	Error load(const String &p_path);
+	String get_load_path() const;
+	virtual LayeredType get_layered_type() const;
+
+	int get_width() const;
+	int get_height() const;
+	int get_layers() const;
+	virtual bool has_mipmaps() const;
+	virtual RID get_rid() const;
+
+	virtual void set_path(const String &p_path, bool p_take_over);
+
+	virtual Ref<Image> get_layer_data(int p_layer) const;
+
+	StreamTextureLayered(LayeredType p_layered_type);
+	~StreamTextureLayered();
+};
+
+class StreamTexture2DArray : public StreamTextureLayered {
+
+	GDCLASS(StreamTexture2DArray, StreamTextureLayered)
+public:
+	StreamTexture2DArray() :
+			StreamTextureLayered(LAYERED_TYPE_2D_ARRAY) {}
+};
+
+class StreamCubemap : public StreamTextureLayered {
+
+	GDCLASS(StreamCubemap, StreamTextureLayered);
+
+public:
+	StreamCubemap() :
+			StreamTextureLayered(LAYERED_TYPE_CUBEMAP) {}
+};
+
+class StreamCubemapArray : public StreamTextureLayered {
+
+	GDCLASS(StreamCubemapArray, StreamTextureLayered);
+
+public:
+	StreamCubemapArray() :
+			StreamTextureLayered(LAYERED_TYPE_CUBEMAP_ARRAY) {}
+};
+
+class ResourceFormatLoaderStreamTextureLayered : public ResourceFormatLoader {
+public:
 	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
 	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
 	virtual void get_recognized_extensions(List<String> *p_extensions) const;
 	virtual void get_recognized_extensions(List<String> *p_extensions) const;
 	virtual bool handles_type(const String &p_type) const;
 	virtual bool handles_type(const String &p_type) const;

+ 5 - 0
scene/scene_string_names.cpp

@@ -205,4 +205,9 @@ SceneStringNames::SceneStringNames() {
 
 
 	shader_overrides_group = StaticCString::create("_shader_overrides_group_");
 	shader_overrides_group = StaticCString::create("_shader_overrides_group_");
 	shader_overrides_group_active = StaticCString::create("_shader_overrides_group_active_");
 	shader_overrides_group_active = StaticCString::create("_shader_overrides_group_active_");
+
+#ifndef DISABLE_DEPRECATED
+	use_in_baked_light = StaticCString::create("use_in_baked_light");
+	use_dynamic_gi = StaticCString::create("use_dynamic_gi");
+#endif
 }
 }

+ 4 - 0
scene/scene_string_names.h

@@ -210,6 +210,10 @@ public:
 	StringName shader_overrides_group;
 	StringName shader_overrides_group;
 	StringName shader_overrides_group_active;
 	StringName shader_overrides_group_active;
 
 
+#ifndef DISABLE_DEPRECATED
+	StringName use_in_baked_light;
+	StringName use_dynamic_gi;
+#endif
 	enum {
 	enum {
 		MAX_MATERIALS = 32
 		MAX_MATERIALS = 32
 	};
 	};

+ 34 - 31
servers/rendering/rasterizer.h

@@ -57,6 +57,7 @@ public:
 	virtual void sky_set_radiance_size(RID p_sky, int p_radiance_size) = 0;
 	virtual void sky_set_radiance_size(RID p_sky, int p_radiance_size) = 0;
 	virtual void sky_set_mode(RID p_sky, RS::SkyMode p_samples) = 0;
 	virtual void sky_set_mode(RID p_sky, RS::SkyMode p_samples) = 0;
 	virtual void sky_set_material(RID p_sky, RID p_material) = 0;
 	virtual void sky_set_material(RID p_sky, RID p_material) = 0;
+	virtual Ref<Image> sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size) = 0;
 
 
 	/* ENVIRONMENT API */
 	/* ENVIRONMENT API */
 
 
@@ -94,6 +95,8 @@ public:
 	virtual void environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_end, float p_depth_curve, bool p_transmit, float p_transmit_curve) = 0;
 	virtual void environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_end, float p_depth_curve, bool p_transmit, float p_transmit_curve) = 0;
 	virtual void environment_set_fog_height(RID p_env, bool p_enable, float p_min_height, float p_max_height, float p_height_curve) = 0;
 	virtual void environment_set_fog_height(RID p_env, bool p_enable, float p_min_height, float p_max_height, float p_height_curve) = 0;
 
 
+	virtual Ref<Image> environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) = 0;
+
 	virtual bool is_environment(RID p_env) const = 0;
 	virtual bool is_environment(RID p_env) const = 0;
 	virtual RS::EnvironmentBG environment_get_background(RID p_env) const = 0;
 	virtual RS::EnvironmentBG environment_get_background(RID p_env) const = 0;
 	virtual int environment_get_canvas_max_layer(RID p_env) const = 0;
 	virtual int environment_get_canvas_max_layer(RID p_env) const = 0;
@@ -160,9 +163,11 @@ public:
 
 
 		SelfList<InstanceBase> dependency_item;
 		SelfList<InstanceBase> dependency_item;
 
 
-		InstanceBase *lightmap_capture;
-		RID lightmap;
-		Vector<Color> lightmap_capture_data; //in a array (12 values) to avoid wasting space if unused. Alpha is unused, but needed to send to shader
+		InstanceBase *lightmap;
+		Rect2 lightmap_uv_scale;
+		int lightmap_slice_index;
+		uint32_t lightmap_cull_index;
+		Vector<Color> lightmap_sh; //spherical harmonic
 
 
 		AABB aabb;
 		AABB aabb;
 		AABB transformed_aabb;
 		AABB transformed_aabb;
@@ -178,8 +183,8 @@ public:
 		bool instance_allocated_shader_parameters = false;
 		bool instance_allocated_shader_parameters = false;
 		int32_t instance_allocated_shader_parameters_offset = -1;
 		int32_t instance_allocated_shader_parameters_offset = -1;
 
 
-		virtual void dependency_deleted(RID p_dependency) = 0;
-		virtual void dependency_changed(bool p_aabb, bool p_dependencies) = 0;
+		virtual void dependency_deleted(RID p_dependency) {}
+		virtual void dependency_changed(bool p_aabb, bool p_dependencies) {}
 
 
 		Set<InstanceDependency *> dependencies;
 		Set<InstanceDependency *> dependencies;
 
 
@@ -233,7 +238,9 @@ public:
 			baked_light = false;
 			baked_light = false;
 			dynamic_gi = false;
 			dynamic_gi = false;
 			redraw_if_visible = false;
 			redraw_if_visible = false;
-			lightmap_capture = nullptr;
+			lightmap_slice_index = 0;
+			lightmap = nullptr;
+			lightmap_cull_index = 0;
 		}
 		}
 
 
 		virtual ~InstanceBase() {
 		virtual ~InstanceBase() {
@@ -268,7 +275,7 @@ public:
 	virtual bool gi_probe_needs_update(RID p_probe) const = 0;
 	virtual bool gi_probe_needs_update(RID p_probe) const = 0;
 	virtual void gi_probe_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, int p_dynamic_object_count, InstanceBase **p_dynamic_objects) = 0;
 	virtual void gi_probe_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, int p_dynamic_object_count, InstanceBase **p_dynamic_objects) = 0;
 
 
-	virtual void render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID *p_decal_cull_result, int p_decal_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass) = 0;
+	virtual void render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID *p_decal_cull_result, int p_decal_cull_count, InstanceBase **p_lightmap_cull_result, int p_lightmap_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass) = 0;
 
 
 	virtual void render_shadow(RID p_light, RID p_shadow_atlas, int p_pass, InstanceBase **p_cull_result, int p_cull_count) = 0;
 	virtual void render_shadow(RID p_light, RID p_shadow_atlas, int p_pass, InstanceBase **p_cull_result, int p_cull_count) = 0;
 	virtual void render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region) = 0;
 	virtual void render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region) = 0;
@@ -286,6 +293,8 @@ public:
 	virtual void sub_surface_scattering_set_quality(RS::SubSurfaceScatteringQuality p_quality) = 0;
 	virtual void sub_surface_scattering_set_quality(RS::SubSurfaceScatteringQuality p_quality) = 0;
 	virtual void sub_surface_scattering_set_scale(float p_scale, float p_depth_scale) = 0;
 	virtual void sub_surface_scattering_set_scale(float p_scale, float p_depth_scale) = 0;
 
 
+	virtual TypedArray<Image> bake_render_uv2(RID p_base, const Vector<RID> &p_material_overrides, const Size2i &p_image_size) = 0;
+
 	virtual bool free(RID p_rid) = 0;
 	virtual bool free(RID p_rid) = 0;
 
 
 	virtual void update() = 0;
 	virtual void update() = 0;
@@ -311,7 +320,7 @@ public:
 
 
 	//these two APIs can be used together or in combination with the others.
 	//these two APIs can be used together or in combination with the others.
 	virtual RID texture_2d_placeholder_create() = 0;
 	virtual RID texture_2d_placeholder_create() = 0;
-	virtual RID texture_2d_layered_placeholder_create() = 0;
+	virtual RID texture_2d_layered_placeholder_create(RenderingServer::TextureLayeredType p_layered_type) = 0;
 	virtual RID texture_3d_placeholder_create() = 0;
 	virtual RID texture_3d_placeholder_create() = 0;
 
 
 	virtual Ref<Image> texture_2d_get(RID p_texture) const = 0;
 	virtual Ref<Image> texture_2d_get(RID p_texture) const = 0;
@@ -593,29 +602,21 @@ public:
 
 
 	/* LIGHTMAP CAPTURE */
 	/* LIGHTMAP CAPTURE */
 
 
-	struct LightmapCaptureOctree {
-
-		enum {
-			CHILD_EMPTY = 0xFFFFFFFF
-		};
-
-		uint16_t light[6][3]; //anisotropic light
-		float alpha;
-		uint32_t children[8];
-	};
-
-	virtual RID lightmap_capture_create() = 0;
-	virtual void lightmap_capture_set_bounds(RID p_capture, const AABB &p_bounds) = 0;
-	virtual AABB lightmap_capture_get_bounds(RID p_capture) const = 0;
-	virtual void lightmap_capture_set_octree(RID p_capture, const Vector<uint8_t> &p_octree) = 0;
-	virtual Vector<uint8_t> lightmap_capture_get_octree(RID p_capture) const = 0;
-	virtual void lightmap_capture_set_octree_cell_transform(RID p_capture, const Transform &p_xform) = 0;
-	virtual Transform lightmap_capture_get_octree_cell_transform(RID p_capture) const = 0;
-	virtual void lightmap_capture_set_octree_cell_subdiv(RID p_capture, int p_subdiv) = 0;
-	virtual int lightmap_capture_get_octree_cell_subdiv(RID p_capture) const = 0;
-	virtual void lightmap_capture_set_energy(RID p_capture, float p_energy) = 0;
-	virtual float lightmap_capture_get_energy(RID p_capture) const = 0;
-	virtual const Vector<LightmapCaptureOctree> *lightmap_capture_get_octree_ptr(RID p_capture) const = 0;
+	virtual RID lightmap_create() = 0;
+
+	virtual void lightmap_set_textures(RID p_lightmap, RID p_light, bool p_uses_spherical_haromics) = 0;
+	virtual void lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) = 0;
+	virtual void lightmap_set_probe_interior(RID p_lightmap, bool p_interior) = 0;
+	virtual void lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) = 0;
+	virtual PackedVector3Array lightmap_get_probe_capture_points(RID p_lightmap) const = 0;
+	virtual PackedColorArray lightmap_get_probe_capture_sh(RID p_lightmap) const = 0;
+	virtual PackedInt32Array lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const = 0;
+	virtual PackedInt32Array lightmap_get_probe_capture_bsp_tree(RID p_lightmap) const = 0;
+	virtual AABB lightmap_get_aabb(RID p_lightmap) const = 0;
+	virtual void lightmap_tap_sh_light(RID p_lightmap, const Vector3 &p_point, Color *r_sh) = 0;
+	virtual bool lightmap_is_interior(RID p_lightmap) const = 0;
+	virtual void lightmap_set_probe_capture_update_speed(float p_speed) = 0;
+	virtual float lightmap_get_probe_capture_update_speed() const = 0;
 
 
 	/* PARTICLES */
 	/* PARTICLES */
 
 
@@ -1370,6 +1371,8 @@ public:
 
 
 	virtual void end_frame(bool p_swap_buffers) = 0;
 	virtual void end_frame(bool p_swap_buffers) = 0;
 	virtual void finalize() = 0;
 	virtual void finalize() = 0;
+	virtual uint64_t get_frame_number() const = 0;
+	virtual float get_frame_delta_time() const = 0;
 
 
 	virtual bool is_low_end() const = 0;
 	virtual bool is_low_end() const = 0;
 
 

+ 2 - 2
servers/rendering/rasterizer_rd/rasterizer_canvas_rd.cpp

@@ -1539,8 +1539,8 @@ void RasterizerCanvasRD::canvas_render_items(RID p_to_render_target, Item *p_ite
 					}
 					}
 				}
 				}
 
 
-				if (md->last_frame != RasterizerRD::get_frame_number()) {
-					md->last_frame = RasterizerRD::get_frame_number();
+				if (md->last_frame != RasterizerRD::singleton->get_frame_number()) {
+					md->last_frame = RasterizerRD::singleton->get_frame_number();
 					if (!RD::get_singleton()->uniform_set_is_valid(md->uniform_set)) {
 					if (!RD::get_singleton()->uniform_set_is_valid(md->uniform_set)) {
 						// uniform set may be gone because a dependency was erased. In this case, it will happen
 						// uniform set may be gone because a dependency was erased. In this case, it will happen
 						// if a texture is deleted, so just re-create it.
 						// if a texture is deleted, so just re-create it.

+ 29 - 1
servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp

@@ -282,6 +282,30 @@ void RasterizerEffectsRD::copy_to_rect(RID p_source_rd_texture, RID p_dest_textu
 	RD::get_singleton()->compute_list_end();
 	RD::get_singleton()->compute_list_end();
 }
 }
 
 
+void RasterizerEffectsRD::copy_cubemap_to_panorama(RID p_source_cube, RID p_dest_panorama, const Size2i &p_panorama_size, float p_lod, bool p_is_array) {
+
+	zeromem(&copy.push_constant, sizeof(CopyPushConstant));
+
+	copy.push_constant.section[0] = 0;
+	copy.push_constant.section[1] = 0;
+	copy.push_constant.section[2] = p_panorama_size.width;
+	copy.push_constant.section[3] = p_panorama_size.height;
+	copy.push_constant.target[0] = 0;
+	copy.push_constant.target[1] = 0;
+	copy.push_constant.camera_z_far = p_lod;
+
+	int32_t x_groups = (p_panorama_size.width - 1) / 8 + 1;
+	int32_t y_groups = (p_panorama_size.height - 1) / 8 + 1;
+
+	RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+	RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, copy.pipelines[p_is_array ? COPY_MODE_CUBE_ARRAY_TO_PANORAMA : COPY_MODE_CUBE_TO_PANORAMA]);
+	RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_cube), 0);
+	RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_dest_panorama), 3);
+	RD::get_singleton()->compute_list_set_push_constant(compute_list, &copy.push_constant, sizeof(CopyPushConstant));
+	RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 1);
+	RD::get_singleton()->compute_list_end();
+}
+
 void RasterizerEffectsRD::copy_depth_to_rect_and_linearize(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, float p_z_near, float p_z_far) {
 void RasterizerEffectsRD::copy_depth_to_rect_and_linearize(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, float p_z_near, float p_z_far) {
 
 
 	zeromem(&copy.push_constant, sizeof(CopyPushConstant));
 	zeromem(&copy.push_constant, sizeof(CopyPushConstant));
@@ -1202,7 +1226,9 @@ void RasterizerEffectsRD::render_sky(RD::DrawListID p_list, float p_time, RID p_
 	RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, p_pipeline->get_render_pipeline(RD::INVALID_ID, fb_format));
 	RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, p_pipeline->get_render_pipeline(RD::INVALID_ID, fb_format));
 
 
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_samplers, 0);
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_samplers, 0);
-	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_uniform_set, 1);
+	if (p_uniform_set.is_valid()) { //material may not have uniform set
+		RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_uniform_set, 1);
+	}
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_texture_set, 2);
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_texture_set, 2);
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_lights, 3);
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_lights, 3);
 
 
@@ -1226,6 +1252,8 @@ RasterizerEffectsRD::RasterizerEffectsRD() {
 		copy_modes.push_back("\n#define MODE_SIMPLE_COPY_DEPTH\n");
 		copy_modes.push_back("\n#define MODE_SIMPLE_COPY_DEPTH\n");
 		copy_modes.push_back("\n#define MODE_MIPMAP\n");
 		copy_modes.push_back("\n#define MODE_MIPMAP\n");
 		copy_modes.push_back("\n#define MODE_LINEARIZE_DEPTH_COPY\n");
 		copy_modes.push_back("\n#define MODE_LINEARIZE_DEPTH_COPY\n");
+		copy_modes.push_back("\n#define MODE_CUBEMAP_TO_PANORAMA\n");
+		copy_modes.push_back("\n#define MODE_CUBEMAP_ARRAY_TO_PANORAMA\n");
 
 
 		copy.shader.initialize(copy_modes);
 		copy.shader.initialize(copy_modes);
 		zeromem(&copy.push_constant, sizeof(CopyPushConstant));
 		zeromem(&copy.push_constant, sizeof(CopyPushConstant));

+ 3 - 0
servers/rendering/rasterizer_rd/rasterizer_effects_rd.h

@@ -66,6 +66,8 @@ class RasterizerEffectsRD {
 		COPY_MODE_SIMPLY_COPY_DEPTH,
 		COPY_MODE_SIMPLY_COPY_DEPTH,
 		COPY_MODE_MIPMAP,
 		COPY_MODE_MIPMAP,
 		COPY_MODE_LINEARIZE_DEPTH,
 		COPY_MODE_LINEARIZE_DEPTH,
+		COPY_MODE_CUBE_TO_PANORAMA,
+		COPY_MODE_CUBE_ARRAY_TO_PANORAMA,
 		COPY_MODE_MAX,
 		COPY_MODE_MAX,
 
 
 	};
 	};
@@ -564,6 +566,7 @@ class RasterizerEffectsRD {
 public:
 public:
 	void copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_alpha_to_zero = false);
 	void copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_alpha_to_zero = false);
 	void copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_all_source = false, bool p_8_bit_dst = false);
 	void copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_all_source = false, bool p_8_bit_dst = false);
+	void copy_cubemap_to_panorama(RID p_source_cube, RID p_dest_panorama, const Size2i &p_panorama_size, float p_lod, bool p_is_array);
 	void copy_depth_to_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false);
 	void copy_depth_to_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false);
 	void copy_depth_to_rect_and_linearize(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, float p_z_near, float p_z_far);
 	void copy_depth_to_rect_and_linearize(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, float p_z_near, float p_z_far);
 	void copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y = false, bool p_panorama = false);
 	void copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y = false, bool p_panorama = false);

+ 5 - 1
servers/rendering/rasterizer_rd/rasterizer_rd.cpp

@@ -79,6 +79,7 @@ void RasterizerRD::blit_render_targets_to_screen(DisplayServer::WindowID p_scree
 
 
 void RasterizerRD::begin_frame(double frame_step) {
 void RasterizerRD::begin_frame(double frame_step) {
 	frame++;
 	frame++;
+	delta = frame_step;
 	time += frame_step;
 	time += frame_step;
 
 
 	double time_roll_over = GLOBAL_GET("rendering/limits/time/time_rollover_secs");
 	double time_roll_over = GLOBAL_GET("rendering/limits/time/time_rollover_secs");
@@ -157,7 +158,7 @@ void RasterizerRD::initialize() {
 }
 }
 
 
 ThreadWorkPool RasterizerRD::thread_work_pool;
 ThreadWorkPool RasterizerRD::thread_work_pool;
-uint32_t RasterizerRD::frame = 1;
+uint64_t RasterizerRD::frame = 1;
 
 
 void RasterizerRD::finalize() {
 void RasterizerRD::finalize() {
 
 
@@ -173,7 +174,10 @@ void RasterizerRD::finalize() {
 	RD::get_singleton()->free(copy_viewports_sampler);
 	RD::get_singleton()->free(copy_viewports_sampler);
 }
 }
 
 
+RasterizerRD *RasterizerRD::singleton = nullptr;
+
 RasterizerRD::RasterizerRD() {
 RasterizerRD::RasterizerRD() {
+	singleton = this;
 	thread_work_pool.init();
 	thread_work_pool.init();
 	time = 0;
 	time = 0;
 
 

+ 5 - 2
servers/rendering/rasterizer_rd/rasterizer_rd.h

@@ -53,8 +53,9 @@ protected:
 	Map<RID, RID> render_target_descriptors;
 	Map<RID, RID> render_target_descriptors;
 
 
 	double time;
 	double time;
+	float delta;
 
 
-	static uint32_t frame;
+	static uint64_t frame;
 
 
 public:
 public:
 	RasterizerStorage *get_storage() { return storage; }
 	RasterizerStorage *get_storage() { return storage; }
@@ -71,7 +72,8 @@ public:
 	void end_frame(bool p_swap_buffers);
 	void end_frame(bool p_swap_buffers);
 	void finalize();
 	void finalize();
 
 
-	static _ALWAYS_INLINE_ uint64_t get_frame_number() { return frame; }
+	_ALWAYS_INLINE_ uint64_t get_frame_number() const { return frame; }
+	_ALWAYS_INLINE_ float get_frame_delta_time() const { return delta; }
 
 
 	static Error is_viable() {
 	static Error is_viable() {
 		return OK;
 		return OK;
@@ -89,6 +91,7 @@ public:
 
 
 	static ThreadWorkPool thread_work_pool;
 	static ThreadWorkPool thread_work_pool;
 
 
+	static RasterizerRD *singleton;
 	RasterizerRD();
 	RasterizerRD();
 	~RasterizerRD() {}
 	~RasterizerRD() {}
 };
 };

+ 193 - 28
servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp

@@ -67,18 +67,18 @@ static _FORCE_INLINE_ void store_basis_3x4(const Basis &p_mtx, float *p_array) {
 	p_array[11] = 0;
 	p_array[11] = 0;
 }
 }
 
 
-static _FORCE_INLINE_ void store_transform_3x3(const Transform &p_mtx, float *p_array) {
-	p_array[0] = p_mtx.basis.elements[0][0];
-	p_array[1] = p_mtx.basis.elements[1][0];
-	p_array[2] = p_mtx.basis.elements[2][0];
+static _FORCE_INLINE_ void store_transform_3x3(const Basis &p_mtx, float *p_array) {
+	p_array[0] = p_mtx.elements[0][0];
+	p_array[1] = p_mtx.elements[1][0];
+	p_array[2] = p_mtx.elements[2][0];
 	p_array[3] = 0;
 	p_array[3] = 0;
-	p_array[4] = p_mtx.basis.elements[0][1];
-	p_array[5] = p_mtx.basis.elements[1][1];
-	p_array[6] = p_mtx.basis.elements[2][1];
+	p_array[4] = p_mtx.elements[0][1];
+	p_array[5] = p_mtx.elements[1][1];
+	p_array[6] = p_mtx.elements[2][1];
 	p_array[7] = 0;
 	p_array[7] = 0;
-	p_array[8] = p_mtx.basis.elements[0][2];
-	p_array[9] = p_mtx.basis.elements[1][2];
-	p_array[10] = p_mtx.basis.elements[2][2];
+	p_array[8] = p_mtx.elements[0][2];
+	p_array[9] = p_mtx.elements[1][2];
+	p_array[10] = p_mtx.elements[2][2];
 	p_array[11] = 0;
 	p_array[11] = 0;
 }
 }
 
 
@@ -841,6 +841,8 @@ bool RasterizerSceneHighEndRD::free(RID p_rid) {
 
 
 void RasterizerSceneHighEndRD::_fill_instances(RenderList::Element **p_elements, int p_element_count, bool p_for_depth) {
 void RasterizerSceneHighEndRD::_fill_instances(RenderList::Element **p_elements, int p_element_count, bool p_for_depth) {
 
 
+	uint32_t lightmap_captures_used = 0;
+
 	for (int i = 0; i < p_element_count; i++) {
 	for (int i = 0; i < p_element_count; i++) {
 
 
 		const RenderList::Element *e = p_elements[i];
 		const RenderList::Element *e = p_elements[i];
@@ -898,6 +900,7 @@ void RasterizerSceneHighEndRD::_fill_instances(RenderList::Element **p_elements,
 
 
 				if (written == 0) {
 				if (written == 0) {
 					id.gi_offset = index;
 					id.gi_offset = index;
+					id.flags |= INSTANCE_DATA_FLAG_USE_GIPROBE;
 					written = 1;
 					written = 1;
 				} else {
 				} else {
 					id.gi_offset = index << 16;
 					id.gi_offset = index << 16;
@@ -910,17 +913,53 @@ void RasterizerSceneHighEndRD::_fill_instances(RenderList::Element **p_elements,
 			} else if (written == 1) {
 			} else if (written == 1) {
 				id.gi_offset |= 0xFFFF0000;
 				id.gi_offset |= 0xFFFF0000;
 			}
 			}
+		} else if (e->instance->lightmap) {
+
+			int32_t lightmap_index = storage->lightmap_get_array_index(e->instance->lightmap->base);
+			if (lightmap_index >= 0) {
+				id.gi_offset = lightmap_index;
+				id.gi_offset |= e->instance->lightmap_slice_index << 12;
+				id.gi_offset |= e->instance->lightmap_cull_index << 20;
+				id.lightmap_uv_scale[0] = e->instance->lightmap_uv_scale.position.x;
+				id.lightmap_uv_scale[1] = e->instance->lightmap_uv_scale.position.y;
+				id.lightmap_uv_scale[2] = e->instance->lightmap_uv_scale.size.width;
+				id.lightmap_uv_scale[3] = e->instance->lightmap_uv_scale.size.height;
+				id.flags |= INSTANCE_DATA_FLAG_USE_LIGHTMAP;
+				if (storage->lightmap_uses_spherical_harmonics(e->instance->lightmap->base)) {
+					id.flags |= INSTANCE_DATA_FLAG_USE_SH_LIGHTMAP;
+				}
+			} else {
+				id.gi_offset = 0xFFFFFFFF;
+			}
+		} else if (!e->instance->lightmap_sh.empty()) {
+			if (lightmap_captures_used < scene_state.max_lightmap_captures) {
+
+				const Color *src_capture = e->instance->lightmap_sh.ptr();
+				LightmapCaptureData &lcd = scene_state.lightmap_captures[lightmap_captures_used];
+				for (int j = 0; j < 9; j++) {
+					lcd.sh[j * 4 + 0] = src_capture[j].r;
+					lcd.sh[j * 4 + 1] = src_capture[j].g;
+					lcd.sh[j * 4 + 2] = src_capture[j].b;
+					lcd.sh[j * 4 + 3] = src_capture[j].a;
+				}
+				id.flags |= INSTANCE_DATA_FLAG_USE_LIGHTMAP_CAPTURE;
+				id.gi_offset = lightmap_captures_used;
+				lightmap_captures_used++;
+			}
 		} else {
 		} else {
 			id.gi_offset = 0xFFFFFFFF;
 			id.gi_offset = 0xFFFFFFFF;
 		}
 		}
 	}
 	}
 
 
 	RD::get_singleton()->buffer_update(scene_state.instance_buffer, 0, sizeof(InstanceData) * p_element_count, scene_state.instances, true);
 	RD::get_singleton()->buffer_update(scene_state.instance_buffer, 0, sizeof(InstanceData) * p_element_count, scene_state.instances, true);
+	if (lightmap_captures_used) {
+		RD::get_singleton()->buffer_update(scene_state.lightmap_capture_buffer, 0, sizeof(LightmapCaptureData) * lightmap_captures_used, scene_state.lightmap_captures, true);
+	}
 }
 }
 
 
 /// RENDERING ///
 /// RENDERING ///
 
 
-void RasterizerSceneHighEndRD::_render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderList::Element **p_elements, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, bool p_no_gi, RID p_radiance_uniform_set, RID p_render_buffers_uniform_set) {
+void RasterizerSceneHighEndRD::_render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderList::Element **p_elements, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, bool p_no_gi, RID p_radiance_uniform_set, RID p_render_buffers_uniform_set, bool p_force_wireframe, const Vector2 &p_uv_offset) {
 
 
 	RD::DrawListID draw_list = p_draw_list;
 	RD::DrawListID draw_list = p_draw_list;
 	RD::FramebufferFormatID framebuffer_format = p_framebuffer_Format;
 	RD::FramebufferFormatID framebuffer_format = p_framebuffer_Format;
@@ -949,6 +988,8 @@ void RasterizerSceneHighEndRD::_render_list(RenderingDevice::DrawListID p_draw_l
 
 
 	PushConstant push_constant;
 	PushConstant push_constant;
 	zeromem(&push_constant, sizeof(PushConstant));
 	zeromem(&push_constant, sizeof(PushConstant));
+	push_constant.bake_uv2_offset[0] = p_uv_offset.x;
+	push_constant.bake_uv2_offset[1] = p_uv_offset.y;
 
 
 	for (int i = 0; i < p_element_count; i++) {
 	for (int i = 0; i < p_element_count; i++) {
 
 
@@ -961,7 +1002,7 @@ void RasterizerSceneHighEndRD::_render_list(RenderingDevice::DrawListID p_draw_l
 		//find cull variant
 		//find cull variant
 		ShaderData::CullVariant cull_variant;
 		ShaderData::CullVariant cull_variant;
 
 
-		if ((p_pass_mode == PASS_MODE_SHADOW || p_pass_mode == PASS_MODE_SHADOW_DP) && e->instance->cast_shadows == RS::SHADOW_CASTING_SETTING_DOUBLE_SIDED) {
+		if (p_pass_mode == PASS_MODE_DEPTH_MATERIAL || ((p_pass_mode == PASS_MODE_SHADOW || p_pass_mode == PASS_MODE_SHADOW_DP) && e->instance->cast_shadows == RS::SHADOW_CASTING_SETTING_DOUBLE_SIDED)) {
 			cull_variant = ShaderData::CULL_VARIANT_DOUBLE_SIDED;
 			cull_variant = ShaderData::CULL_VARIANT_DOUBLE_SIDED;
 		} else {
 		} else {
 			bool mirror = e->instance->mirror;
 			bool mirror = e->instance->mirror;
@@ -1080,7 +1121,7 @@ void RasterizerSceneHighEndRD::_render_list(RenderingDevice::DrawListID p_draw_l
 			prev_index_array_rd = index_array_rd;
 			prev_index_array_rd = index_array_rd;
 		}
 		}
 
 
-		RID pipeline_rd = pipeline->get_render_pipeline(vertex_format, framebuffer_format);
+		RID pipeline_rd = pipeline->get_render_pipeline(vertex_format, framebuffer_format, p_force_wireframe);
 
 
 		if (pipeline_rd != prev_pipeline_rd) {
 		if (pipeline_rd != prev_pipeline_rd) {
 			// checking with prev shader does not make so much sense, as
 			// checking with prev shader does not make so much sense, as
@@ -1255,6 +1296,7 @@ void RasterizerSceneHighEndRD::_setup_environment(RID p_environment, const Camer
 
 
 		scene_state.ubo.use_ambient_cubemap = false;
 		scene_state.ubo.use_ambient_cubemap = false;
 		scene_state.ubo.use_reflection_cubemap = false;
 		scene_state.ubo.use_reflection_cubemap = false;
+		scene_state.ubo.ssao_enabled = false;
 	}
 	}
 
 
 	scene_state.ubo.roughness_limiter_enabled = p_opaque_render_buffers && screen_space_roughness_limiter_is_active();
 	scene_state.ubo.roughness_limiter_enabled = p_opaque_render_buffers && screen_space_roughness_limiter_is_active();
@@ -1271,8 +1313,6 @@ void RasterizerSceneHighEndRD::_add_geometry(InstanceBase *p_instance, uint32_t
 	if (unlikely(get_debug_draw_mode() != RS::VIEWPORT_DEBUG_DRAW_DISABLED)) {
 	if (unlikely(get_debug_draw_mode() != RS::VIEWPORT_DEBUG_DRAW_DISABLED)) {
 		if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_OVERDRAW) {
 		if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_OVERDRAW) {
 			m_src = overdraw_material;
 			m_src = overdraw_material;
-		} else if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME) {
-			m_src = wireframe_material;
 		} else if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_LIGHTING) {
 		} else if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_LIGHTING) {
 			m_src = default_material;
 			m_src = default_material;
 		}
 		}
@@ -1374,7 +1414,7 @@ void RasterizerSceneHighEndRD::_add_geometry_with_material(InstanceBase *p_insta
 	e->geometry_index = p_geometry_index;
 	e->geometry_index = p_geometry_index;
 	e->material_index = e->material->index;
 	e->material_index = e->material->index;
 	e->uses_instancing = e->instance->base_type == RS::INSTANCE_MULTIMESH;
 	e->uses_instancing = e->instance->base_type == RS::INSTANCE_MULTIMESH;
-	e->uses_lightmap = e->instance->lightmap.is_valid();
+	e->uses_lightmap = e->instance->lightmap != nullptr || !e->instance->lightmap_sh.empty();
 	e->uses_vct = e->instance->gi_probe_instances.size();
 	e->uses_vct = e->instance->gi_probe_instances.size();
 	e->shader_index = e->shader_index;
 	e->shader_index = e->shader_index;
 	e->depth_layer = e->instance->depth_layer;
 	e->depth_layer = e->instance->depth_layer;
@@ -1575,6 +1615,26 @@ void RasterizerSceneHighEndRD::_setup_reflections(RID *p_reflection_probe_cull_r
 	}
 	}
 }
 }
 
 
+void RasterizerSceneHighEndRD::_setup_lightmaps(InstanceBase **p_lightmap_cull_result, int p_lightmap_cull_count, const Transform &p_cam_transform) {
+
+	uint32_t lightmaps_used = 0;
+	for (int i = 0; i < p_lightmap_cull_count; i++) {
+		if (i >= (int)scene_state.max_lightmaps) {
+			break;
+		}
+
+		InstanceBase *lm = p_lightmap_cull_result[i];
+		Basis to_lm = lm->transform.basis.inverse() * p_cam_transform.basis;
+		to_lm = to_lm.inverse().transposed(); //will transform normals
+		store_transform_3x3(to_lm, scene_state.lightmaps[i].normal_xform);
+		lm->lightmap_cull_index = i;
+		lightmaps_used++;
+	}
+	if (lightmaps_used > 0) {
+		RD::get_singleton()->buffer_update(scene_state.lightmap_buffer, 0, sizeof(LightmapData) * lightmaps_used, scene_state.lightmaps, true);
+	}
+}
+
 void RasterizerSceneHighEndRD::_setup_gi_probes(RID *p_gi_probe_probe_cull_result, int p_gi_probe_probe_cull_count, const Transform &p_camera_transform) {
 void RasterizerSceneHighEndRD::_setup_gi_probes(RID *p_gi_probe_probe_cull_result, int p_gi_probe_probe_cull_count, const Transform &p_camera_transform) {
 
 
 	int index = 0;
 	int index = 0;
@@ -2118,7 +2178,7 @@ void RasterizerSceneHighEndRD::_setup_decals(const RID *p_decal_instances, int p
 	}
 	}
 }
 }
 
 
-void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID *p_decal_cull_result, int p_decal_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, const Color &p_default_bg_color) {
+void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID *p_decal_cull_result, int p_decal_cull_count, InstanceBase **p_lightmap_cull_result, int p_lightmap_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, const Color &p_default_bg_color) {
 
 
 	RenderBufferDataHighEnd *render_buffer = nullptr;
 	RenderBufferDataHighEnd *render_buffer = nullptr;
 	if (p_render_buffer.is_valid()) {
 	if (p_render_buffer.is_valid()) {
@@ -2238,6 +2298,7 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor
 	_setup_decals(p_decal_cull_result, p_decal_cull_count, p_cam_transform.affine_inverse());
 	_setup_decals(p_decal_cull_result, p_decal_cull_count, p_cam_transform.affine_inverse());
 	_setup_reflections(p_reflection_probe_cull_result, p_reflection_probe_cull_count, p_cam_transform.affine_inverse(), p_environment);
 	_setup_reflections(p_reflection_probe_cull_result, p_reflection_probe_cull_count, p_cam_transform.affine_inverse(), p_environment);
 	_setup_gi_probes(p_gi_probe_cull_result, p_gi_probe_cull_count, p_cam_transform);
 	_setup_gi_probes(p_gi_probe_cull_result, p_gi_probe_cull_count, p_cam_transform);
+	_setup_lightmaps(p_lightmap_cull_result, p_lightmap_cull_count, p_cam_transform);
 	_setup_environment(p_environment, p_cam_projection, p_cam_transform, p_reflection_probe, p_reflection_probe.is_valid(), screen_pixel_size, p_shadow_atlas, !p_reflection_probe.is_valid(), p_default_bg_color, p_cam_projection.get_z_near(), p_cam_projection.get_z_far(), false);
 	_setup_environment(p_environment, p_cam_projection, p_cam_transform, p_reflection_probe, p_reflection_probe.is_valid(), screen_pixel_size, p_shadow_atlas, !p_reflection_probe.is_valid(), p_default_bg_color, p_cam_projection.get_z_near(), p_cam_projection.get_z_far(), false);
 
 
 	cluster_builder.bake_cluster(); //bake to cluster
 	cluster_builder.bake_cluster(); //bake to cluster
@@ -2338,7 +2399,7 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor
 
 
 		bool finish_depth = using_ssao;
 		bool finish_depth = using_ssao;
 		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(depth_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, finish_depth ? RD::FINAL_ACTION_READ : RD::FINAL_ACTION_CONTINUE, depth_pass_clear);
 		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(depth_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, finish_depth ? RD::FINAL_ACTION_READ : RD::FINAL_ACTION_CONTINUE, depth_pass_clear);
-		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(depth_framebuffer), render_list.elements, render_list.element_count, false, depth_pass_mode, render_buffer == nullptr, radiance_uniform_set, RID());
+		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(depth_framebuffer), render_list.elements, render_list.element_count, false, depth_pass_mode, render_buffer == nullptr, radiance_uniform_set, RID(), get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME);
 		RD::get_singleton()->draw_list_end();
 		RD::get_singleton()->draw_list_end();
 
 
 		if (render_buffer && render_buffer->msaa != RS::VIEWPORT_MSAA_DISABLED) {
 		if (render_buffer && render_buffer->msaa != RS::VIEWPORT_MSAA_DISABLED) {
@@ -2394,7 +2455,7 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor
 
 
 		RID framebuffer = using_separate_specular ? opaque_specular_framebuffer : opaque_framebuffer;
 		RID framebuffer = using_separate_specular ? opaque_specular_framebuffer : opaque_framebuffer;
 		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, keep_color ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CLEAR, will_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, depth_pre_pass ? (using_ssao ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CONTINUE) : RD::INITIAL_ACTION_CLEAR, will_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, c, 1.0, 0);
 		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, keep_color ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CLEAR, will_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, depth_pre_pass ? (using_ssao ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CONTINUE) : RD::INITIAL_ACTION_CLEAR, will_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, c, 1.0, 0);
-		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(framebuffer), render_list.elements, render_list.element_count, false, using_separate_specular ? PASS_MODE_COLOR_SPECULAR : PASS_MODE_COLOR, render_buffer == nullptr, radiance_uniform_set, render_buffers_uniform_set);
+		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(framebuffer), render_list.elements, render_list.element_count, false, using_separate_specular ? PASS_MODE_COLOR_SPECULAR : PASS_MODE_COLOR, render_buffer == nullptr, radiance_uniform_set, render_buffers_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME);
 		RD::get_singleton()->draw_list_end();
 		RD::get_singleton()->draw_list_end();
 
 
 		if (will_continue_color && using_separate_specular) {
 		if (will_continue_color && using_separate_specular) {
@@ -2472,7 +2533,7 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor
 
 
 	{
 	{
 		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(alpha_framebuffer, can_continue_color ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, can_continue_depth ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ);
 		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(alpha_framebuffer, can_continue_color ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, can_continue_depth ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ);
-		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(alpha_framebuffer), &render_list.elements[render_list.max_elements - render_list.alpha_element_count], render_list.alpha_element_count, false, PASS_MODE_COLOR, render_buffer == nullptr, radiance_uniform_set, render_buffers_uniform_set);
+		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(alpha_framebuffer), &render_list.elements[render_list.max_elements - render_list.alpha_element_count], render_list.alpha_element_count, false, PASS_MODE_COLOR, render_buffer == nullptr, radiance_uniform_set, render_buffers_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME);
 		RD::get_singleton()->draw_list_end();
 		RD::get_singleton()->draw_list_end();
 	}
 	}
 
 
@@ -2517,13 +2578,14 @@ void RasterizerSceneHighEndRD::_render_shadow(RID p_framebuffer, InstanceBase **
 }
 }
 
 
 void RasterizerSceneHighEndRD::_render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region) {
 void RasterizerSceneHighEndRD::_render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region) {
-	RENDER_TIMESTAMP("Setup Rendering Shadow");
+	RENDER_TIMESTAMP("Setup Rendering Material");
 
 
 	_update_render_base_uniform_set();
 	_update_render_base_uniform_set();
 
 
 	render_pass++;
 	render_pass++;
 
 
 	scene_state.ubo.dual_paraboloid_side = 0;
 	scene_state.ubo.dual_paraboloid_side = 0;
+	scene_state.ubo.material_uv2_mode = true;
 
 
 	_setup_environment(RID(), p_cam_projection, p_cam_transform, RID(), true, Vector2(1, 1), RID(), false, Color(), 0, 0);
 	_setup_environment(RID(), p_cam_projection, p_cam_transform, RID(), true, Vector2(1, 1), RID(), false, Color(), 0, 0);
 
 
@@ -2554,6 +2616,67 @@ void RasterizerSceneHighEndRD::_render_material(const Transform &p_cam_transform
 	}
 	}
 }
 }
 
 
+void RasterizerSceneHighEndRD::_render_uv2(InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region) {
+	RENDER_TIMESTAMP("Setup Rendering UV2");
+
+	_update_render_base_uniform_set();
+
+	render_pass++;
+
+	scene_state.ubo.dual_paraboloid_side = 0;
+	scene_state.ubo.material_uv2_mode = true;
+
+	_setup_environment(RID(), CameraMatrix(), Transform(), RID(), true, Vector2(1, 1), RID(), false, Color(), 0, 0);
+
+	render_list.clear();
+
+	PassMode pass_mode = PASS_MODE_DEPTH_MATERIAL;
+	_fill_render_list(p_cull_result, p_cull_count, pass_mode, true);
+
+	_setup_view_dependant_uniform_set(RID(), RID());
+
+	RENDER_TIMESTAMP("Render Material");
+
+	render_list.sort_by_key(false);
+
+	_fill_instances(render_list.elements, render_list.element_count, true);
+
+	{
+		//regular forward for now
+		Vector<Color> clear;
+		clear.push_back(Color(0, 0, 0, 0));
+		clear.push_back(Color(0, 0, 0, 0));
+		clear.push_back(Color(0, 0, 0, 0));
+		clear.push_back(Color(0, 0, 0, 0));
+		clear.push_back(Color(0, 0, 0, 0));
+		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, clear, 1.0, 0, p_region);
+
+		const int uv_offset_count = 9;
+		static const Vector2 uv_offsets[uv_offset_count] = {
+			Vector2(-1, 1),
+			Vector2(1, 1),
+			Vector2(1, -1),
+			Vector2(-1, -1),
+			Vector2(-1, 0),
+			Vector2(1, 0),
+			Vector2(0, -1),
+			Vector2(0, 1),
+			Vector2(0, 0),
+
+		};
+
+		for (int i = 0; i < uv_offset_count; i++) {
+			Vector2 ofs = uv_offsets[i];
+			ofs.x /= p_region.size.width;
+			ofs.y /= p_region.size.height;
+			_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), render_list.elements, render_list.element_count, true, pass_mode, true, RID(), RID(), true, ofs); //first wireframe, for pseudo conservative
+		}
+		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), render_list.elements, render_list.element_count, true, pass_mode, true, RID(), RID(), false); //second regular triangles
+
+		RD::get_singleton()->draw_list_end();
+	}
+}
+
 void RasterizerSceneHighEndRD::_base_uniforms_changed() {
 void RasterizerSceneHighEndRD::_base_uniforms_changed() {
 
 
 	if (!render_base_uniform_set.is_null() && RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set)) {
 	if (!render_base_uniform_set.is_null() && RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set)) {
@@ -2564,12 +2687,14 @@ void RasterizerSceneHighEndRD::_base_uniforms_changed() {
 
 
 void RasterizerSceneHighEndRD::_update_render_base_uniform_set() {
 void RasterizerSceneHighEndRD::_update_render_base_uniform_set() {
 
 
-	if (render_base_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set)) {
+	if (render_base_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set) || (lightmap_texture_array_version != storage->lightmap_array_get_version())) {
 
 
 		if (render_base_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set)) {
 		if (render_base_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set)) {
 			RD::get_singleton()->free(render_base_uniform_set);
 			RD::get_singleton()->free(render_base_uniform_set);
 		}
 		}
 
 
+		lightmap_texture_array_version = storage->lightmap_array_get_version();
+
 		Vector<RD::Uniform> uniforms;
 		Vector<RD::Uniform> uniforms;
 
 
 		{
 		{
@@ -2685,6 +2810,27 @@ void RasterizerSceneHighEndRD::_update_render_base_uniform_set() {
 		{
 		{
 			RD::Uniform u;
 			RD::Uniform u;
 			u.binding = 10;
 			u.binding = 10;
+			u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+			u.ids.push_back(scene_state.lightmap_buffer);
+			uniforms.push_back(u);
+		}
+		{
+			RD::Uniform u;
+			u.binding = 11;
+			u.type = RD::UNIFORM_TYPE_TEXTURE;
+			u.ids = storage->lightmap_array_get_textures();
+			uniforms.push_back(u);
+		}
+		{
+			RD::Uniform u;
+			u.binding = 12;
+			u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+			u.ids.push_back(scene_state.lightmap_capture_buffer);
+			uniforms.push_back(u);
+		}
+		{
+			RD::Uniform u;
+			u.binding = 13;
 			u.type = RD::UNIFORM_TYPE_TEXTURE;
 			u.type = RD::UNIFORM_TYPE_TEXTURE;
 			RID decal_atlas = storage->decal_atlas_get_texture();
 			RID decal_atlas = storage->decal_atlas_get_texture();
 			u.ids.push_back(decal_atlas);
 			u.ids.push_back(decal_atlas);
@@ -2692,7 +2838,7 @@ void RasterizerSceneHighEndRD::_update_render_base_uniform_set() {
 		}
 		}
 		{
 		{
 			RD::Uniform u;
 			RD::Uniform u;
-			u.binding = 11;
+			u.binding = 14;
 			u.type = RD::UNIFORM_TYPE_TEXTURE;
 			u.type = RD::UNIFORM_TYPE_TEXTURE;
 			RID decal_atlas = storage->decal_atlas_get_texture_srgb();
 			RID decal_atlas = storage->decal_atlas_get_texture_srgb();
 			u.ids.push_back(decal_atlas);
 			u.ids.push_back(decal_atlas);
@@ -2700,7 +2846,7 @@ void RasterizerSceneHighEndRD::_update_render_base_uniform_set() {
 		}
 		}
 		{
 		{
 			RD::Uniform u;
 			RD::Uniform u;
-			u.binding = 12;
+			u.binding = 15;
 			u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
 			u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
 			u.ids.push_back(scene_state.decal_buffer);
 			u.ids.push_back(scene_state.decal_buffer);
 			uniforms.push_back(u);
 			uniforms.push_back(u);
@@ -2708,14 +2854,14 @@ void RasterizerSceneHighEndRD::_update_render_base_uniform_set() {
 
 
 		{
 		{
 			RD::Uniform u;
 			RD::Uniform u;
-			u.binding = 13;
+			u.binding = 16;
 			u.type = RD::UNIFORM_TYPE_TEXTURE;
 			u.type = RD::UNIFORM_TYPE_TEXTURE;
 			u.ids.push_back(cluster_builder.get_cluster_texture());
 			u.ids.push_back(cluster_builder.get_cluster_texture());
 			uniforms.push_back(u);
 			uniforms.push_back(u);
 		}
 		}
 		{
 		{
 			RD::Uniform u;
 			RD::Uniform u;
-			u.binding = 14;
+			u.binding = 17;
 			u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
 			u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
 			u.ids.push_back(cluster_builder.get_cluster_indices_buffer());
 			u.ids.push_back(cluster_builder.get_cluster_indices_buffer());
 			uniforms.push_back(u);
 			uniforms.push_back(u);
@@ -2723,7 +2869,7 @@ void RasterizerSceneHighEndRD::_update_render_base_uniform_set() {
 
 
 		{
 		{
 			RD::Uniform u;
 			RD::Uniform u;
-			u.binding = 15;
+			u.binding = 18;
 			u.type = RD::UNIFORM_TYPE_TEXTURE;
 			u.type = RD::UNIFORM_TYPE_TEXTURE;
 			if (directional_shadow_get_texture().is_valid()) {
 			if (directional_shadow_get_texture().is_valid()) {
 				u.ids.push_back(directional_shadow_get_texture());
 				u.ids.push_back(directional_shadow_get_texture());
@@ -2736,7 +2882,7 @@ void RasterizerSceneHighEndRD::_update_render_base_uniform_set() {
 		{
 		{
 			RD::Uniform u;
 			RD::Uniform u;
 			u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
 			u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
-			u.binding = 16;
+			u.binding = 19;
 			u.ids.push_back(storage->global_variables_get_storage_buffer());
 			u.ids.push_back(storage->global_variables_get_storage_buffer());
 			uniforms.push_back(u);
 			uniforms.push_back(u);
 		}
 		}
@@ -2951,7 +3097,21 @@ RasterizerSceneHighEndRD::RasterizerSceneHighEndRD(RasterizerStorageRD *p_storag
 			scene_state.gi_probe_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(GIProbeData) * scene_state.max_gi_probes);
 			scene_state.gi_probe_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(GIProbeData) * scene_state.max_gi_probes);
 			defines += "\n#define MAX_GI_PROBES " + itos(scene_state.max_gi_probes) + "\n";
 			defines += "\n#define MAX_GI_PROBES " + itos(scene_state.max_gi_probes) + "\n";
 		}
 		}
+		{
+			//lightmaps
+			scene_state.max_lightmaps = storage->lightmap_array_get_size();
+			defines += "\n#define MAX_LIGHTMAP_TEXTURES " + itos(scene_state.max_lightmaps) + "\n";
+			defines += "\n#define MAX_LIGHTMAPS " + itos(scene_state.max_lightmaps) + "\n";
 
 
+			scene_state.lightmaps = memnew_arr(LightmapData, scene_state.max_lightmaps);
+			scene_state.lightmap_buffer = RD::get_singleton()->storage_buffer_create(sizeof(LightmapData) * scene_state.max_lightmaps);
+		}
+		{
+			//captures
+			scene_state.max_lightmap_captures = 2048;
+			scene_state.lightmap_captures = memnew_arr(LightmapCaptureData, scene_state.max_lightmap_captures);
+			scene_state.lightmap_capture_buffer = RD::get_singleton()->storage_buffer_create(sizeof(LightmapCaptureData) * scene_state.max_lightmap_captures);
+		}
 		{ //decals
 		{ //decals
 			scene_state.max_decals = MIN(1024 * 1024, uniform_max_size) / sizeof(DecalData); //1mb of decals
 			scene_state.max_decals = MIN(1024 * 1024, uniform_max_size) / sizeof(DecalData); //1mb of decals
 			uint32_t decal_buffer_size = scene_state.max_decals * sizeof(DecalData);
 			uint32_t decal_buffer_size = scene_state.max_decals * sizeof(DecalData);
@@ -2959,6 +3119,11 @@ RasterizerSceneHighEndRD::RasterizerSceneHighEndRD(RasterizerStorageRD *p_storag
 			scene_state.decal_buffer = RD::get_singleton()->storage_buffer_create(decal_buffer_size);
 			scene_state.decal_buffer = RD::get_singleton()->storage_buffer_create(decal_buffer_size);
 		}
 		}
 
 
+		{
+
+			defines += "\n#define MATERIAL_UNIFORM_SET " + itos(MATERIAL_UNIFORM_SET) + "\n";
+		}
+
 		Vector<String> shader_versions;
 		Vector<String> shader_versions;
 		shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n");
 		shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n");
 		shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_DUAL_PARABOLOID\n");
 		shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_DUAL_PARABOLOID\n");

+ 33 - 3
servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h

@@ -193,7 +193,8 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 
 
 	struct PushConstant {
 	struct PushConstant {
 		uint32_t index;
 		uint32_t index;
-		uint32_t pad[3];
+		uint32_t pad;
+		float bake_uv2_offset[2];
 	};
 	};
 
 
 	/* Framebuffer */
 	/* Framebuffer */
@@ -241,6 +242,8 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 	RID render_base_uniform_set;
 	RID render_base_uniform_set;
 	RID view_dependant_uniform_set;
 	RID view_dependant_uniform_set;
 
 
+	uint64_t lightmap_texture_array_version = 0xFFFFFFFF;
+
 	virtual void _base_uniforms_changed();
 	virtual void _base_uniforms_changed();
 	void _render_buffers_clear_uniform_set(RenderBufferDataHighEnd *rb);
 	void _render_buffers_clear_uniform_set(RenderBufferDataHighEnd *rb);
 	virtual void _render_buffers_uniform_set_changed(RID p_render_buffers);
 	virtual void _render_buffers_uniform_set_changed(RID p_render_buffers);
@@ -331,6 +334,10 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 		uint32_t pad[1];
 		uint32_t pad[1];
 	};
 	};
 
 
+	struct LightmapData {
+		float normal_xform[12];
+	};
+
 	struct DecalData {
 	struct DecalData {
 		float xform[16];
 		float xform[16];
 		float inv_extents[3];
 		float inv_extents[3];
@@ -349,7 +356,15 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 		float normal_fade;
 		float normal_fade;
 	};
 	};
 
 
+	struct LightmapCaptureData {
+		float sh[9 * 4];
+	};
+
 	enum {
 	enum {
+		INSTANCE_DATA_FLAG_USE_LIGHTMAP_CAPTURE = 1 << 8,
+		INSTANCE_DATA_FLAG_USE_LIGHTMAP = 1 << 9,
+		INSTANCE_DATA_FLAG_USE_SH_LIGHTMAP = 1 << 10,
+		INSTANCE_DATA_FLAG_USE_GIPROBE = 1 << 11,
 		INSTANCE_DATA_FLAG_MULTIMESH = 1 << 12,
 		INSTANCE_DATA_FLAG_MULTIMESH = 1 << 12,
 		INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D = 1 << 13,
 		INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D = 1 << 13,
 		INSTANCE_DATA_FLAG_MULTIMESH_HAS_COLOR = 1 << 14,
 		INSTANCE_DATA_FLAG_MULTIMESH_HAS_COLOR = 1 << 14,
@@ -366,6 +381,7 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 		uint32_t instance_uniforms_ofs; //instance_offset in instancing/skeleton buffer
 		uint32_t instance_uniforms_ofs; //instance_offset in instancing/skeleton buffer
 		uint32_t gi_offset; //GI information when using lightmapping (VCT or lightmap)
 		uint32_t gi_offset; //GI information when using lightmapping (VCT or lightmap)
 		uint32_t mask;
 		uint32_t mask;
+		float lightmap_uv_scale[4];
 	};
 	};
 
 
 	struct SceneState {
 	struct SceneState {
@@ -418,6 +434,9 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 			uint32_t roughness_limiter_enabled;
 			uint32_t roughness_limiter_enabled;
 
 
 			float ao_color[4];
 			float ao_color[4];
+
+			uint32_t material_uv2_mode;
+			uint32_t pad_material[3];
 		};
 		};
 
 
 		UBO ubo;
 		UBO ubo;
@@ -434,6 +453,10 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 		RID gi_probe_buffer;
 		RID gi_probe_buffer;
 		uint32_t max_gi_probe_probes_per_instance;
 		uint32_t max_gi_probe_probes_per_instance;
 
 
+		LightmapData *lightmaps;
+		uint32_t max_lightmaps;
+		RID lightmap_buffer;
+
 		DecalData *decals;
 		DecalData *decals;
 		uint32_t max_decals;
 		uint32_t max_decals;
 		RID decal_buffer;
 		RID decal_buffer;
@@ -446,6 +469,10 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 		uint32_t max_directional_lights;
 		uint32_t max_directional_lights;
 		RID directional_light_buffer;
 		RID directional_light_buffer;
 
 
+		LightmapCaptureData *lightmap_captures;
+		uint32_t max_lightmap_captures;
+		RID lightmap_capture_buffer;
+
 		RID instance_buffer;
 		RID instance_buffer;
 		InstanceData *instances;
 		InstanceData *instances;
 		uint32_t max_instances;
 		uint32_t max_instances;
@@ -456,6 +483,7 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 		bool used_sss = false;
 		bool used_sss = false;
 		uint32_t current_shader_index = 0;
 		uint32_t current_shader_index = 0;
 		uint32_t current_material_index = 0;
 		uint32_t current_material_index = 0;
+
 	} scene_state;
 	} scene_state;
 
 
 	/* Render List */
 	/* Render List */
@@ -632,18 +660,20 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 	void _setup_decals(const RID *p_decal_instances, int p_decal_count, const Transform &p_camera_inverse_xform);
 	void _setup_decals(const RID *p_decal_instances, int p_decal_count, const Transform &p_camera_inverse_xform);
 	void _setup_reflections(RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, const Transform &p_camera_inverse_transform, RID p_environment);
 	void _setup_reflections(RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, const Transform &p_camera_inverse_transform, RID p_environment);
 	void _setup_gi_probes(RID *p_gi_probe_probe_cull_result, int p_gi_probe_probe_cull_count, const Transform &p_camera_transform);
 	void _setup_gi_probes(RID *p_gi_probe_probe_cull_result, int p_gi_probe_probe_cull_count, const Transform &p_camera_transform);
+	void _setup_lightmaps(InstanceBase **p_lightmap_cull_result, int p_lightmap_cull_count, const Transform &p_cam_transform);
 
 
 	void _fill_instances(RenderList::Element **p_elements, int p_element_count, bool p_for_depth);
 	void _fill_instances(RenderList::Element **p_elements, int p_element_count, bool p_for_depth);
-	void _render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderList::Element **p_elements, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, bool p_no_gi, RID p_radiance_uniform_set, RID p_render_buffers_uniform_set);
+	void _render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderList::Element **p_elements, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, bool p_no_gi, RID p_radiance_uniform_set, RID p_render_buffers_uniform_set, bool p_force_wireframe = false, const Vector2 &p_uv_offset = Vector2());
 	_FORCE_INLINE_ void _add_geometry(InstanceBase *p_instance, uint32_t p_surface, RID p_material, PassMode p_pass_mode, uint32_t p_geometry_index);
 	_FORCE_INLINE_ void _add_geometry(InstanceBase *p_instance, uint32_t p_surface, RID p_material, PassMode p_pass_mode, uint32_t p_geometry_index);
 	_FORCE_INLINE_ void _add_geometry_with_material(InstanceBase *p_instance, uint32_t p_surface, MaterialData *p_material, RID p_material_rid, PassMode p_pass_mode, uint32_t p_geometry_index);
 	_FORCE_INLINE_ void _add_geometry_with_material(InstanceBase *p_instance, uint32_t p_surface, MaterialData *p_material, RID p_material_rid, PassMode p_pass_mode, uint32_t p_geometry_index);
 
 
 	void _fill_render_list(InstanceBase **p_cull_result, int p_cull_count, PassMode p_pass_mode, bool p_no_gi);
 	void _fill_render_list(InstanceBase **p_cull_result, int p_cull_count, PassMode p_pass_mode, bool p_no_gi);
 
 
 protected:
 protected:
-	virtual void _render_scene(RID p_render_buffer, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID *p_decal_cull_result, int p_decal_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, const Color &p_default_bg_color);
+	virtual void _render_scene(RID p_render_buffer, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID *p_decal_cull_result, int p_decal_cull_count, InstanceBase **p_lightmap_cull_result, int p_lightmap_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, const Color &p_default_bg_color);
 	virtual void _render_shadow(RID p_framebuffer, InstanceBase **p_cull_result, int p_cull_count, const CameraMatrix &p_projection, const Transform &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake);
 	virtual void _render_shadow(RID p_framebuffer, InstanceBase **p_cull_result, int p_cull_count, const CameraMatrix &p_projection, const Transform &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake);
 	virtual void _render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region);
 	virtual void _render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region);
+	virtual void _render_uv2(InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region);
 
 
 public:
 public:
 	virtual void set_time(double p_time, double p_step);
 	virtual void set_time(double p_time, double p_step);

+ 171 - 2
servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp

@@ -263,7 +263,47 @@ void RasterizerSceneRD::sky_set_material(RID p_sky, RID p_material) {
 	Sky *sky = sky_owner.getornull(p_sky);
 	Sky *sky = sky_owner.getornull(p_sky);
 	ERR_FAIL_COND(!sky);
 	ERR_FAIL_COND(!sky);
 	sky->material = p_material;
 	sky->material = p_material;
+	_sky_invalidate(sky);
+}
+
+Ref<Image> RasterizerSceneRD::sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size) {
+
+	Sky *sky = sky_owner.getornull(p_sky);
+	ERR_FAIL_COND_V(!sky, Ref<Image>());
+
+	_update_dirty_skys();
+
+	if (sky->radiance.is_valid()) {
+
+		RD::TextureFormat tf;
+		tf.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT;
+		tf.width = p_size.width;
+		tf.height = p_size.height;
+		tf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT;
+
+		RID rad_tex = RD::get_singleton()->texture_create(tf, RD::TextureView());
+		storage->get_effects()->copy_cubemap_to_panorama(sky->radiance, rad_tex, p_size, p_bake_irradiance ? roughness_layers : 0, sky->reflection.layers.size() > 1);
+		Vector<uint8_t> data = RD::get_singleton()->texture_get_data(rad_tex, 0);
+		RD::get_singleton()->free(rad_tex);
+
+		Ref<Image> img;
+		img.instance();
+		img->create(p_size.width, p_size.height, false, Image::FORMAT_RGBAF, data);
+		for (int i = 0; i < p_size.width; i++) {
+			for (int j = 0; j < p_size.height; j++) {
+				Color c = img->get_pixel(i, j);
+				c.r *= p_energy;
+				c.g *= p_energy;
+				c.b *= p_energy;
+				img->set_pixel(i, j, c);
+			}
+		}
+		return img;
+	}
+
+	return Ref<Image>();
 }
 }
+
 void RasterizerSceneRD::_update_dirty_skys() {
 void RasterizerSceneRD::_update_dirty_skys() {
 
 
 	Sky *sky = dirty_sky_list;
 	Sky *sky = dirty_sky_list;
@@ -1336,6 +1376,43 @@ bool RasterizerSceneRD::is_environment(RID p_env) const {
 	return environment_owner.owns(p_env);
 	return environment_owner.owns(p_env);
 }
 }
 
 
+Ref<Image> RasterizerSceneRD::environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) {
+	Environent *env = environment_owner.getornull(p_env);
+	ERR_FAIL_COND_V(!env, Ref<Image>());
+
+	if (env->background == RS::ENV_BG_CAMERA_FEED || env->background == RS::ENV_BG_CANVAS || env->background == RS::ENV_BG_KEEP) {
+		return Ref<Image>(); //nothing to bake
+	}
+
+	if (env->background == RS::ENV_BG_CLEAR_COLOR || env->background == RS::ENV_BG_COLOR) {
+		Color color;
+		if (env->background == RS::ENV_BG_CLEAR_COLOR) {
+			color = storage->get_default_clear_color();
+		} else {
+			color = env->bg_color;
+		}
+		color.r *= env->bg_energy;
+		color.g *= env->bg_energy;
+		color.b *= env->bg_energy;
+
+		Ref<Image> ret;
+		ret.instance();
+		ret->create(p_size.width, p_size.height, false, Image::FORMAT_RGBAF);
+		for (int i = 0; i < p_size.width; i++) {
+			for (int j = 0; j < p_size.height; j++) {
+				ret->set_pixel(i, j, color);
+			}
+		}
+		return ret;
+	}
+
+	if (env->background == RS::ENV_BG_SKY && env->sky.is_valid()) {
+		return sky_bake_panorama(env->sky, env->bg_energy, p_bake_irradiance, p_size);
+	}
+
+	return Ref<Image>();
+}
+
 ////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////
 
 
 RID RasterizerSceneRD::reflection_atlas_create() {
 RID RasterizerSceneRD::reflection_atlas_create() {
@@ -3741,7 +3818,7 @@ RasterizerSceneRD::RenderBufferData *RasterizerSceneRD::render_buffers_get_data(
 	return rb->data;
 	return rb->data;
 }
 }
 
 
-void RasterizerSceneRD::render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID *p_decal_cull_result, int p_decal_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass) {
+void RasterizerSceneRD::render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID *p_decal_cull_result, int p_decal_cull_count, InstanceBase **p_lightmap_cull_result, int p_lightmap_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass) {
 
 
 	Color clear_color;
 	Color clear_color;
 	if (p_render_buffers.is_valid()) {
 	if (p_render_buffers.is_valid()) {
@@ -3752,7 +3829,7 @@ void RasterizerSceneRD::render_scene(RID p_render_buffers, const Transform &p_ca
 		clear_color = storage->get_default_clear_color();
 		clear_color = storage->get_default_clear_color();
 	}
 	}
 
 
-	_render_scene(p_render_buffers, p_cam_transform, p_cam_projection, p_cam_ortogonal, p_cull_result, p_cull_count, p_light_cull_result, p_light_cull_count, p_reflection_probe_cull_result, p_reflection_probe_cull_count, p_gi_probe_cull_result, p_gi_probe_cull_count, p_decal_cull_result, p_decal_cull_count, p_environment, p_camera_effects, p_shadow_atlas, p_reflection_atlas, p_reflection_probe, p_reflection_probe_pass, clear_color);
+	_render_scene(p_render_buffers, p_cam_transform, p_cam_projection, p_cam_ortogonal, p_cull_result, p_cull_count, p_light_cull_result, p_light_cull_count, p_reflection_probe_cull_result, p_reflection_probe_cull_count, p_gi_probe_cull_result, p_gi_probe_cull_count, p_decal_cull_result, p_decal_cull_count, p_lightmap_cull_result, p_lightmap_cull_count, p_environment, p_camera_effects, p_shadow_atlas, p_reflection_atlas, p_reflection_probe, p_reflection_probe_pass, clear_color);
 
 
 	if (p_render_buffers.is_valid()) {
 	if (p_render_buffers.is_valid()) {
 		RENDER_TIMESTAMP("Tonemap");
 		RENDER_TIMESTAMP("Tonemap");
@@ -4079,6 +4156,98 @@ float RasterizerSceneRD::screen_space_roughness_limiter_get_curve() const {
 	return screen_space_roughness_limiter_curve;
 	return screen_space_roughness_limiter_curve;
 }
 }
 
 
+TypedArray<Image> RasterizerSceneRD::bake_render_uv2(RID p_base, const Vector<RID> &p_material_overrides, const Size2i &p_image_size) {
+
+	RD::TextureFormat tf;
+	tf.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
+	tf.width = p_image_size.width; // Always 64x64
+	tf.height = p_image_size.height;
+	tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT;
+
+	RID albedo_alpha_tex = RD::get_singleton()->texture_create(tf, RD::TextureView());
+	RID normal_tex = RD::get_singleton()->texture_create(tf, RD::TextureView());
+	RID orm_tex = RD::get_singleton()->texture_create(tf, RD::TextureView());
+
+	tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
+	RID emission_tex = RD::get_singleton()->texture_create(tf, RD::TextureView());
+
+	tf.format = RD::DATA_FORMAT_R32_SFLOAT;
+	RID depth_write_tex = RD::get_singleton()->texture_create(tf, RD::TextureView());
+
+	tf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT;
+	tf.format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D32_SFLOAT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? RD::DATA_FORMAT_D32_SFLOAT : RD::DATA_FORMAT_X8_D24_UNORM_PACK32;
+	RID depth_tex = RD::get_singleton()->texture_create(tf, RD::TextureView());
+
+	Vector<RID> fb_tex;
+	fb_tex.push_back(albedo_alpha_tex);
+	fb_tex.push_back(normal_tex);
+	fb_tex.push_back(orm_tex);
+	fb_tex.push_back(emission_tex);
+	fb_tex.push_back(depth_write_tex);
+	fb_tex.push_back(depth_tex);
+
+	RID fb = RD::get_singleton()->framebuffer_create(fb_tex);
+
+	//RID sampled_light;
+
+	InstanceBase ins;
+
+	ins.base_type = RSG::storage->get_base_type(p_base);
+	ins.base = p_base;
+	ins.materials.resize(RSG::storage->mesh_get_surface_count(p_base));
+	for (int i = 0; i < ins.materials.size(); i++) {
+		if (i < p_material_overrides.size()) {
+			ins.materials.write[i] = p_material_overrides[i];
+		}
+	}
+
+	InstanceBase *cull = &ins;
+	_render_uv2(&cull, 1, fb, Rect2i(0, 0, p_image_size.width, p_image_size.height));
+
+	TypedArray<Image> ret;
+
+	{
+		PackedByteArray data = RD::get_singleton()->texture_get_data(albedo_alpha_tex, 0);
+		Ref<Image> img;
+		img.instance();
+		img->create(p_image_size.width, p_image_size.height, false, Image::FORMAT_RGBA8, data);
+		RD::get_singleton()->free(albedo_alpha_tex);
+		ret.push_back(img);
+	}
+
+	{
+		PackedByteArray data = RD::get_singleton()->texture_get_data(normal_tex, 0);
+		Ref<Image> img;
+		img.instance();
+		img->create(p_image_size.width, p_image_size.height, false, Image::FORMAT_RGBA8, data);
+		RD::get_singleton()->free(normal_tex);
+		ret.push_back(img);
+	}
+
+	{
+		PackedByteArray data = RD::get_singleton()->texture_get_data(orm_tex, 0);
+		Ref<Image> img;
+		img.instance();
+		img->create(p_image_size.width, p_image_size.height, false, Image::FORMAT_RGBA8, data);
+		RD::get_singleton()->free(orm_tex);
+		ret.push_back(img);
+	}
+
+	{
+		PackedByteArray data = RD::get_singleton()->texture_get_data(emission_tex, 0);
+		Ref<Image> img;
+		img.instance();
+		img->create(p_image_size.width, p_image_size.height, false, Image::FORMAT_RGBAH, data);
+		RD::get_singleton()->free(emission_tex);
+		ret.push_back(img);
+	}
+
+	RD::get_singleton()->free(depth_write_tex);
+	RD::get_singleton()->free(depth_tex);
+
+	return ret;
+}
+
 RasterizerSceneRD *RasterizerSceneRD::singleton = nullptr;
 RasterizerSceneRD *RasterizerSceneRD::singleton = nullptr;
 
 
 RasterizerSceneRD::RasterizerSceneRD(RasterizerStorageRD *p_storage) {
 RasterizerSceneRD::RasterizerSceneRD(RasterizerStorageRD *p_storage) {

+ 8 - 2
servers/rendering/rasterizer_rd/rasterizer_scene_rd.h

@@ -80,9 +80,10 @@ protected:
 	};
 	};
 	virtual RenderBufferData *_create_render_buffer_data() = 0;
 	virtual RenderBufferData *_create_render_buffer_data() = 0;
 
 
-	virtual void _render_scene(RID p_render_buffer, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID *p_decal_cull_result, int p_decal_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, const Color &p_default_color) = 0;
+	virtual void _render_scene(RID p_render_buffer, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID *p_decal_cull_result, int p_decal_cull_count, InstanceBase **p_lightmap_cull_result, int p_lightmap_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, const Color &p_default_color) = 0;
 	virtual void _render_shadow(RID p_framebuffer, InstanceBase **p_cull_result, int p_cull_count, const CameraMatrix &p_projection, const Transform &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool use_dp_flip, bool p_use_pancake) = 0;
 	virtual void _render_shadow(RID p_framebuffer, InstanceBase **p_cull_result, int p_cull_count, const CameraMatrix &p_projection, const Transform &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool use_dp_flip, bool p_use_pancake) = 0;
 	virtual void _render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region) = 0;
 	virtual void _render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region) = 0;
+	virtual void _render_uv2(InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region) = 0;
 
 
 	virtual void _debug_giprobe(RID p_gi_probe, RenderingDevice::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform, bool p_lighting, bool p_emission, float p_alpha);
 	virtual void _debug_giprobe(RID p_gi_probe, RenderingDevice::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform, bool p_lighting, bool p_emission, float p_alpha);
 
 
@@ -843,6 +844,7 @@ public:
 	void sky_set_radiance_size(RID p_sky, int p_radiance_size);
 	void sky_set_radiance_size(RID p_sky, int p_radiance_size);
 	void sky_set_mode(RID p_sky, RS::SkyMode p_mode);
 	void sky_set_mode(RID p_sky, RS::SkyMode p_mode);
 	void sky_set_material(RID p_sky, RID p_material);
 	void sky_set_material(RID p_sky, RID p_material);
+	Ref<Image> sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size);
 
 
 	RID sky_get_radiance_texture_rd(RID p_sky) const;
 	RID sky_get_radiance_texture_rd(RID p_sky) const;
 	RID sky_get_radiance_uniform_set_rd(RID p_sky, RID p_shader, int p_set) const;
 	RID sky_get_radiance_uniform_set_rd(RID p_sky, RID p_shader, int p_set) const;
@@ -900,6 +902,8 @@ public:
 	void environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_end, float p_depth_curve, bool p_transmit, float p_transmit_curve) {}
 	void environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_end, float p_depth_curve, bool p_transmit, float p_transmit_curve) {}
 	void environment_set_fog_height(RID p_env, bool p_enable, float p_min_height, float p_max_height, float p_height_curve) {}
 	void environment_set_fog_height(RID p_env, bool p_enable, float p_min_height, float p_max_height, float p_height_curve) {}
 
 
+	virtual Ref<Image> environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size);
+
 	virtual RID camera_effects_create();
 	virtual RID camera_effects_create();
 
 
 	virtual void camera_effects_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter);
 	virtual void camera_effects_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter);
@@ -1194,7 +1198,7 @@ public:
 	RID render_buffers_get_ao_texture(RID p_render_buffers);
 	RID render_buffers_get_ao_texture(RID p_render_buffers);
 	RID render_buffers_get_back_buffer_texture(RID p_render_buffers);
 	RID render_buffers_get_back_buffer_texture(RID p_render_buffers);
 
 
-	void render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID *p_decal_cull_result, int p_decal_cull_count, RID p_environment, RID p_shadow_atlas, RID p_camera_effects, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass);
+	void render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID *p_decal_cull_result, int p_decal_cull_count, InstanceBase **p_lightmap_cull_result, int p_lightmap_cull_count, RID p_environment, RID p_shadow_atlas, RID p_camera_effects, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass);
 
 
 	void render_shadow(RID p_light, RID p_shadow_atlas, int p_pass, InstanceBase **p_cull_result, int p_cull_count);
 	void render_shadow(RID p_light, RID p_shadow_atlas, int p_pass, InstanceBase **p_cull_result, int p_cull_count);
 
 
@@ -1235,6 +1239,8 @@ public:
 	int get_roughness_layers() const;
 	int get_roughness_layers() const;
 	bool is_using_radiance_cubemap_array() const;
 	bool is_using_radiance_cubemap_array() const;
 
 
+	virtual TypedArray<Image> bake_render_uv2(RID p_base, const Vector<RID> &p_material_overrides, const Size2i &p_image_size);
+
 	virtual bool free(RID p_rid);
 	virtual bool free(RID p_rid);
 
 
 	virtual void update();
 	virtual void update();

+ 456 - 108
servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp

@@ -610,7 +610,113 @@ RID RasterizerStorageRD::texture_2d_create(const Ref<Image> &p_image) {
 
 
 RID RasterizerStorageRD::texture_2d_layered_create(const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) {
 RID RasterizerStorageRD::texture_2d_layered_create(const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) {
 
 
-	return RID();
+	ERR_FAIL_COND_V(p_layers.size() == 0, RID());
+
+	ERR_FAIL_COND_V(p_layered_type == RS::TEXTURE_LAYERED_CUBEMAP && p_layers.size() != 6, RID());
+	ERR_FAIL_COND_V(p_layered_type == RS::TEXTURE_LAYERED_CUBEMAP_ARRAY && (p_layers.size() < 6 || (p_layers.size() % 6) != 0), RID());
+
+	TextureToRDFormat ret_format;
+	Vector<Ref<Image>> images;
+	{
+		int valid_width = 0;
+		int valid_height = 0;
+		bool valid_mipmaps = false;
+		Image::Format valid_format = Image::FORMAT_MAX;
+
+		for (int i = 0; i < p_layers.size(); i++) {
+			ERR_FAIL_COND_V(p_layers[i]->empty(), RID());
+
+			if (i == 0) {
+				valid_width = p_layers[i]->get_width();
+				valid_height = p_layers[i]->get_height();
+				valid_format = p_layers[i]->get_format();
+				valid_mipmaps = p_layers[i]->has_mipmaps();
+			} else {
+				ERR_FAIL_COND_V(p_layers[i]->get_width() != valid_width, RID());
+				ERR_FAIL_COND_V(p_layers[i]->get_height() != valid_height, RID());
+				ERR_FAIL_COND_V(p_layers[i]->get_format() != valid_format, RID());
+				ERR_FAIL_COND_V(p_layers[i]->has_mipmaps() != valid_mipmaps, RID());
+			}
+
+			images.push_back(_validate_texture_format(p_layers[i], ret_format));
+		}
+	}
+
+	Texture texture;
+
+	texture.type = Texture::TYPE_LAYERED;
+	texture.layered_type = p_layered_type;
+
+	texture.width = p_layers[0]->get_width();
+	texture.height = p_layers[0]->get_height();
+	texture.layers = p_layers.size();
+	texture.mipmaps = p_layers[0]->get_mipmap_count() + 1;
+	texture.depth = 1;
+	texture.format = p_layers[0]->get_format();
+	texture.validated_format = images[0]->get_format();
+
+	switch (p_layered_type) {
+		case RS::TEXTURE_LAYERED_2D_ARRAY: {
+			texture.rd_type = RD::TEXTURE_TYPE_2D_ARRAY;
+		} break;
+		case RS::TEXTURE_LAYERED_CUBEMAP: {
+			texture.rd_type = RD::TEXTURE_TYPE_CUBE;
+		} break;
+		case RS::TEXTURE_LAYERED_CUBEMAP_ARRAY: {
+			texture.rd_type = RD::TEXTURE_TYPE_CUBE_ARRAY;
+		} break;
+	}
+
+	texture.rd_format = ret_format.format;
+	texture.rd_format_srgb = ret_format.format_srgb;
+
+	RD::TextureFormat rd_format;
+	RD::TextureView rd_view;
+	{ //attempt register
+		rd_format.format = texture.rd_format;
+		rd_format.width = texture.width;
+		rd_format.height = texture.height;
+		rd_format.depth = 1;
+		rd_format.array_layers = texture.layers;
+		rd_format.mipmaps = texture.mipmaps;
+		rd_format.type = texture.rd_type;
+		rd_format.samples = RD::TEXTURE_SAMPLES_1;
+		rd_format.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT;
+		if (texture.rd_format_srgb != RD::DATA_FORMAT_MAX) {
+			rd_format.shareable_formats.push_back(texture.rd_format);
+			rd_format.shareable_formats.push_back(texture.rd_format_srgb);
+		}
+	}
+	{
+		rd_view.swizzle_r = ret_format.swizzle_r;
+		rd_view.swizzle_g = ret_format.swizzle_g;
+		rd_view.swizzle_b = ret_format.swizzle_b;
+		rd_view.swizzle_a = ret_format.swizzle_a;
+	}
+	Vector<Vector<uint8_t>> data_slices;
+	for (int i = 0; i < images.size(); i++) {
+		Vector<uint8_t> data = images[i]->get_data(); //use image data
+		data_slices.push_back(data);
+	}
+	texture.rd_texture = RD::get_singleton()->texture_create(rd_format, rd_view, data_slices);
+	ERR_FAIL_COND_V(texture.rd_texture.is_null(), RID());
+	if (texture.rd_format_srgb != RD::DATA_FORMAT_MAX) {
+		rd_view.format_override = texture.rd_format_srgb;
+		texture.rd_texture_srgb = RD::get_singleton()->texture_create_shared(rd_view, texture.rd_texture);
+		if (texture.rd_texture_srgb.is_null()) {
+			RD::get_singleton()->free(texture.rd_texture);
+			ERR_FAIL_COND_V(texture.rd_texture_srgb.is_null(), RID());
+		}
+	}
+
+	//used for 2D, overridable
+	texture.width_2d = texture.width;
+	texture.height_2d = texture.height;
+	texture.is_render_target = false;
+	texture.rd_view = rd_view;
+	texture.is_proxy = false;
+
+	return texture_owner.make_rid(texture);
 }
 }
 RID RasterizerStorageRD::texture_3d_create(const Vector<Ref<Image>> &p_slices) {
 RID RasterizerStorageRD::texture_3d_create(const Vector<Ref<Image>> &p_slices) {
 
 
@@ -729,9 +835,31 @@ RID RasterizerStorageRD::texture_2d_placeholder_create() {
 
 
 	return texture_2d_create(image);
 	return texture_2d_create(image);
 }
 }
-RID RasterizerStorageRD::texture_2d_layered_placeholder_create() {
+RID RasterizerStorageRD::texture_2d_layered_placeholder_create(RS::TextureLayeredType p_layered_type) {
 
 
-	return RID();
+	//this could be better optimized to reuse an existing image , done this way
+	//for now to get it working
+	Ref<Image> image;
+	image.instance();
+	image->create(4, 4, false, Image::FORMAT_RGBA8);
+
+	for (int i = 0; i < 4; i++) {
+		for (int j = 0; j < 4; j++) {
+			image->set_pixel(i, j, Color(1, 0, 1, 1));
+		}
+	}
+
+	Vector<Ref<Image>> images;
+	if (p_layered_type == RS::TEXTURE_LAYERED_2D_ARRAY) {
+		images.push_back(image);
+	} else {
+		//cube
+		for (int i = 0; i < 6; i++) {
+			images.push_back(image);
+		}
+	}
+
+	return texture_2d_layered_create(images, p_layered_type);
 }
 }
 RID RasterizerStorageRD::texture_3d_placeholder_create() {
 RID RasterizerStorageRD::texture_3d_placeholder_create() {
 
 
@@ -4139,6 +4267,180 @@ RID RasterizerStorageRD::gi_probe_get_sdf_texture(RID p_gi_probe) {
 
 
 	return gi_probe->sdf_texture;
 	return gi_probe->sdf_texture;
 }
 }
+/* LIGHTMAP API */
+
+RID RasterizerStorageRD::lightmap_create() {
+	return lightmap_owner.make_rid(Lightmap());
+}
+
+void RasterizerStorageRD::lightmap_set_textures(RID p_lightmap, RID p_light, bool p_uses_spherical_haromics) {
+
+	Lightmap *lm = lightmap_owner.getornull(p_lightmap);
+	ERR_FAIL_COND(!lm);
+
+	lightmap_array_version++;
+
+	//erase lightmap users
+	if (lm->light_texture.is_valid()) {
+		Texture *t = texture_owner.getornull(lm->light_texture);
+		if (t) {
+			t->lightmap_users.erase(p_lightmap);
+		}
+	}
+
+	Texture *t = texture_owner.getornull(p_light);
+	lm->light_texture = p_light;
+	lm->uses_spherical_harmonics = p_uses_spherical_haromics;
+
+	RID default_2d_array = default_rd_textures[DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE];
+	if (!t) {
+
+		if (using_lightmap_array) {
+			if (lm->array_index >= 0) {
+				lightmap_textures.write[lm->array_index] = default_2d_array;
+				lm->array_index = -1;
+			}
+		}
+
+		return;
+	}
+
+	t->lightmap_users.insert(p_lightmap);
+
+	if (using_lightmap_array) {
+		if (lm->array_index < 0) {
+			//not in array, try to put in array
+			for (int i = 0; i < lightmap_textures.size(); i++) {
+				if (lightmap_textures[i] == default_2d_array) {
+					lm->array_index = i;
+					break;
+				}
+			}
+		}
+		ERR_FAIL_COND_MSG(lm->array_index < 0, "Maximum amount of lightmaps in use (" + itos(lightmap_textures.size()) + ") has been exceeded, lightmap will nod display properly.");
+
+		lightmap_textures.write[lm->array_index] = t->rd_texture;
+	}
+}
+
+void RasterizerStorageRD::lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) {
+	Lightmap *lm = lightmap_owner.getornull(p_lightmap);
+	ERR_FAIL_COND(!lm);
+	lm->bounds = p_bounds;
+}
+
+void RasterizerStorageRD::lightmap_set_probe_interior(RID p_lightmap, bool p_interior) {
+
+	Lightmap *lm = lightmap_owner.getornull(p_lightmap);
+	ERR_FAIL_COND(!lm);
+	lm->interior = p_interior;
+}
+
+void RasterizerStorageRD::lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) {
+
+	Lightmap *lm = lightmap_owner.getornull(p_lightmap);
+	ERR_FAIL_COND(!lm);
+
+	if (p_points.size()) {
+		ERR_FAIL_COND(p_points.size() * 9 != p_point_sh.size());
+		ERR_FAIL_COND((p_tetrahedra.size() % 4) != 0);
+		ERR_FAIL_COND((p_bsp_tree.size() % 6) != 0);
+	}
+
+	lm->points = p_points;
+	lm->bsp_tree = p_bsp_tree;
+	lm->point_sh = p_point_sh;
+	lm->tetrahedra = p_tetrahedra;
+}
+
+PackedVector3Array RasterizerStorageRD::lightmap_get_probe_capture_points(RID p_lightmap) const {
+
+	Lightmap *lm = lightmap_owner.getornull(p_lightmap);
+	ERR_FAIL_COND_V(!lm, PackedVector3Array());
+
+	return lm->points;
+}
+PackedColorArray RasterizerStorageRD::lightmap_get_probe_capture_sh(RID p_lightmap) const {
+	Lightmap *lm = lightmap_owner.getornull(p_lightmap);
+	ERR_FAIL_COND_V(!lm, PackedColorArray());
+	return lm->point_sh;
+}
+PackedInt32Array RasterizerStorageRD::lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const {
+	Lightmap *lm = lightmap_owner.getornull(p_lightmap);
+	ERR_FAIL_COND_V(!lm, PackedInt32Array());
+	return lm->tetrahedra;
+}
+PackedInt32Array RasterizerStorageRD::lightmap_get_probe_capture_bsp_tree(RID p_lightmap) const {
+	Lightmap *lm = lightmap_owner.getornull(p_lightmap);
+	ERR_FAIL_COND_V(!lm, PackedInt32Array());
+	return lm->bsp_tree;
+}
+
+void RasterizerStorageRD::lightmap_set_probe_capture_update_speed(float p_speed) {
+	lightmap_probe_capture_update_speed = p_speed;
+}
+
+void RasterizerStorageRD::lightmap_tap_sh_light(RID p_lightmap, const Vector3 &p_point, Color *r_sh) {
+	Lightmap *lm = lightmap_owner.getornull(p_lightmap);
+	ERR_FAIL_COND(!lm);
+
+	for (int i = 0; i < 9; i++) {
+		r_sh[i] = Color(0, 0, 0, 0);
+	}
+
+	if (!lm->points.size() || !lm->bsp_tree.size() || !lm->tetrahedra.size()) {
+		return;
+	}
+
+	static_assert(sizeof(Lightmap::BSP) == 24);
+
+	const Lightmap::BSP *bsp = (const Lightmap::BSP *)lm->bsp_tree.ptr();
+	int32_t node = 0;
+	while (node >= 0) {
+
+		if (Plane(bsp[node].plane[0], bsp[node].plane[1], bsp[node].plane[2], bsp[node].plane[3]).is_point_over(p_point)) {
+#ifdef DEBUG_ENABLED
+			ERR_FAIL_COND(bsp[node].over >= 0 && bsp[node].over < node);
+#endif
+
+			node = bsp[node].over;
+		} else {
+#ifdef DEBUG_ENABLED
+			ERR_FAIL_COND(bsp[node].under >= 0 && bsp[node].under < node);
+#endif
+			node = bsp[node].under;
+		}
+	}
+
+	if (node == Lightmap::BSP::EMPTY_LEAF) {
+		return; //nothing could be done
+	}
+
+	node = ABS(node) - 1;
+
+	uint32_t *tetrahedron = (uint32_t *)&lm->tetrahedra[node * 4];
+	Vector3 points[4] = { lm->points[tetrahedron[0]], lm->points[tetrahedron[1]], lm->points[tetrahedron[2]], lm->points[tetrahedron[3]] };
+	const Color *sh_colors[4]{ &lm->point_sh[tetrahedron[0] * 9], &lm->point_sh[tetrahedron[1] * 9], &lm->point_sh[tetrahedron[2] * 9], &lm->point_sh[tetrahedron[3] * 9] };
+	Color barycentric = Geometry::tetrahedron_get_barycentric_coords(points[0], points[1], points[2], points[3], p_point);
+
+	for (int i = 0; i < 4; i++) {
+		float c = CLAMP(barycentric[i], 0.0, 1.0);
+		for (int j = 0; j < 9; j++) {
+			r_sh[j] += sh_colors[i][j] * c;
+		}
+	}
+}
+
+bool RasterizerStorageRD::lightmap_is_interior(RID p_lightmap) const {
+	const Lightmap *lm = lightmap_owner.getornull(p_lightmap);
+	ERR_FAIL_COND_V(!lm, false);
+	return lm->interior;
+}
+AABB RasterizerStorageRD::lightmap_get_aabb(RID p_lightmap) const {
+	const Lightmap *lm = lightmap_owner.getornull(p_lightmap);
+	ERR_FAIL_COND_V(!lm, AABB());
+	return lm->bounds;
+}
 
 
 /* RENDER TARGET API */
 /* RENDER TARGET API */
 
 
@@ -4491,6 +4793,9 @@ void RasterizerStorageRD::base_update_dependency(RID p_base, RasterizerScene::In
 	} else if (gi_probe_owner.owns(p_base)) {
 	} else if (gi_probe_owner.owns(p_base)) {
 		GIProbe *gip = gi_probe_owner.getornull(p_base);
 		GIProbe *gip = gi_probe_owner.getornull(p_base);
 		p_instance->update_dependency(&gip->instance_dependency);
 		p_instance->update_dependency(&gip->instance_dependency);
+	} else if (lightmap_owner.owns(p_base)) {
+		Lightmap *lm = lightmap_owner.getornull(p_base);
+		p_instance->update_dependency(&lm->instance_dependency);
 	} else if (light_owner.owns(p_base)) {
 	} else if (light_owner.owns(p_base)) {
 		Light *l = light_owner.getornull(p_base);
 		Light *l = light_owner.getornull(p_base);
 		p_instance->update_dependency(&l->instance_dependency);
 		p_instance->update_dependency(&l->instance_dependency);
@@ -4525,6 +4830,9 @@ RS::InstanceType RasterizerStorageRD::get_base_type(RID p_rid) const {
 	if (light_owner.owns(p_rid)) {
 	if (light_owner.owns(p_rid)) {
 		return RS::INSTANCE_LIGHT;
 		return RS::INSTANCE_LIGHT;
 	}
 	}
+	if (lightmap_owner.owns(p_rid)) {
+		return RS::INSTANCE_LIGHTMAP;
+	}
 
 
 	return RS::INSTANCE_NONE;
 	return RS::INSTANCE_NONE;
 }
 }
@@ -4678,7 +4986,7 @@ void RasterizerStorageRD::_update_decal_atlas() {
 			DecalAtlas::Texture *t = decal_atlas.textures.getptr(items[i].texture);
 			DecalAtlas::Texture *t = decal_atlas.textures.getptr(items[i].texture);
 			t->uv_rect.position = items[i].pos * border + Vector2i(border / 2, border / 2);
 			t->uv_rect.position = items[i].pos * border + Vector2i(border / 2, border / 2);
 			t->uv_rect.size = items[i].pixel_size;
 			t->uv_rect.size = items[i].pixel_size;
-			//print_line("blitrect: " + t->uv_rect);
+
 			t->uv_rect.position /= Size2(decal_atlas.size);
 			t->uv_rect.position /= Size2(decal_atlas.size);
 			t->uv_rect.size /= Size2(decal_atlas.size);
 			t->uv_rect.size /= Size2(decal_atlas.size);
 		}
 		}
@@ -5563,6 +5871,11 @@ bool RasterizerStorageRD::free(RID p_rid) {
 		GIProbe *gi_probe = gi_probe_owner.getornull(p_rid);
 		GIProbe *gi_probe = gi_probe_owner.getornull(p_rid);
 		gi_probe->instance_dependency.instance_notify_deleted(p_rid);
 		gi_probe->instance_dependency.instance_notify_deleted(p_rid);
 		gi_probe_owner.free(p_rid);
 		gi_probe_owner.free(p_rid);
+	} else if (lightmap_owner.owns(p_rid)) {
+		lightmap_set_textures(p_rid, RID(), false);
+		Lightmap *lightmap = lightmap_owner.getornull(p_rid);
+		lightmap->instance_dependency.instance_notify_deleted(p_rid);
+		lightmap_owner.free(p_rid);
 
 
 	} else if (light_owner.owns(p_rid)) {
 	} else if (light_owner.owns(p_rid)) {
 
 
@@ -5801,6 +6114,32 @@ RasterizerStorageRD::RasterizerStorageRD() {
 		}
 		}
 	}
 	}
 
 
+	{ //create default array
+
+		RD::TextureFormat tformat;
+		tformat.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
+		tformat.width = 4;
+		tformat.height = 4;
+		tformat.array_layers = 1;
+		tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT;
+		tformat.type = RD::TEXTURE_TYPE_2D_ARRAY;
+
+		Vector<uint8_t> pv;
+		pv.resize(16 * 4);
+		for (int i = 0; i < 16; i++) {
+			pv.set(i * 4 + 0, 255);
+			pv.set(i * 4 + 1, 255);
+			pv.set(i * 4 + 2, 255);
+			pv.set(i * 4 + 3, 255);
+		}
+
+		{
+			Vector<Vector<uint8_t>> vpv;
+			vpv.push_back(pv);
+			default_rd_textures[DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE] = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv);
+		}
+	}
+
 	//default samplers
 	//default samplers
 	for (int i = 1; i < RS::CANVAS_ITEM_TEXTURE_FILTER_MAX; i++) {
 	for (int i = 1; i < RS::CANVAS_ITEM_TEXTURE_FILTER_MAX; i++) {
 		for (int j = 1; j < RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX; j++) {
 		for (int j = 1; j < RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX; j++) {
@@ -5872,124 +6211,133 @@ RasterizerStorageRD::RasterizerStorageRD() {
 	//default rd buffers
 	//default rd buffers
 	{
 	{
 
 
-		//vertex
+		Vector<uint8_t> buffer;
 		{
 		{
 
 
-				Vector<uint8_t> buffer;
+			buffer.resize(sizeof(float) * 3);
+			{
+				uint8_t *w = buffer.ptrw();
+				float *fptr = (float *)w;
+				fptr[0] = 0.0;
+				fptr[1] = 0.0;
+				fptr[2] = 0.0;
+			}
+			mesh_default_rd_buffers[DEFAULT_RD_BUFFER_VERTEX] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
+		}
 
 
-	buffer.resize(sizeof(float) * 3);
-	{
-		uint8_t *w = buffer.ptrw();
-		float *fptr = (float *)w;
-		fptr[0] = 0.0;
-		fptr[1] = 0.0;
-		fptr[2] = 0.0;
-	}
-	mesh_default_rd_buffers[DEFAULT_RD_BUFFER_VERTEX] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
-}
+		{ //normal
+			buffer.resize(sizeof(float) * 3);
+			{
+				uint8_t *w = buffer.ptrw();
+				float *fptr = (float *)w;
+				fptr[0] = 1.0;
+				fptr[1] = 0.0;
+				fptr[2] = 0.0;
+			}
+			mesh_default_rd_buffers[DEFAULT_RD_BUFFER_NORMAL] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
+		}
 
 
-{ //normal
-	Vector<uint8_t> buffer;
-	buffer.resize(sizeof(float) * 3);
-	{
-		uint8_t *w = buffer.ptrw();
-		float *fptr = (float *)w;
-		fptr[0] = 1.0;
-		fptr[1] = 0.0;
-		fptr[2] = 0.0;
-	}
-	mesh_default_rd_buffers[DEFAULT_RD_BUFFER_NORMAL] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
-}
+		{ //tangent
+			buffer.resize(sizeof(float) * 4);
+			{
+				uint8_t *w = buffer.ptrw();
+				float *fptr = (float *)w;
+				fptr[0] = 1.0;
+				fptr[1] = 0.0;
+				fptr[2] = 0.0;
+				fptr[3] = 0.0;
+			}
+			mesh_default_rd_buffers[DEFAULT_RD_BUFFER_TANGENT] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
+		}
 
 
-{ //tangent
-	Vector<uint8_t> buffer;
-	buffer.resize(sizeof(float) * 4);
-	{
-		uint8_t *w = buffer.ptrw();
-		float *fptr = (float *)w;
-		fptr[0] = 1.0;
-		fptr[1] = 0.0;
-		fptr[2] = 0.0;
-		fptr[3] = 0.0;
-	}
-	mesh_default_rd_buffers[DEFAULT_RD_BUFFER_TANGENT] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
-}
+		{ //color
+			buffer.resize(sizeof(float) * 4);
+			{
+				uint8_t *w = buffer.ptrw();
+				float *fptr = (float *)w;
+				fptr[0] = 1.0;
+				fptr[1] = 1.0;
+				fptr[2] = 1.0;
+				fptr[3] = 1.0;
+			}
+			mesh_default_rd_buffers[DEFAULT_RD_BUFFER_COLOR] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
+		}
 
 
-{ //color
-	Vector<uint8_t> buffer;
-	buffer.resize(sizeof(float) * 4);
-	{
-		uint8_t *w = buffer.ptrw();
-		float *fptr = (float *)w;
-		fptr[0] = 1.0;
-		fptr[1] = 1.0;
-		fptr[2] = 1.0;
-		fptr[3] = 1.0;
-	}
-	mesh_default_rd_buffers[DEFAULT_RD_BUFFER_COLOR] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
-}
+		{ //tex uv 1
+			buffer.resize(sizeof(float) * 2);
+			{
+				uint8_t *w = buffer.ptrw();
+				float *fptr = (float *)w;
+				fptr[0] = 0.0;
+				fptr[1] = 0.0;
+			}
+			mesh_default_rd_buffers[DEFAULT_RD_BUFFER_TEX_UV] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
+		}
+		{ //tex uv 2
+			buffer.resize(sizeof(float) * 2);
+			{
+				uint8_t *w = buffer.ptrw();
+				float *fptr = (float *)w;
+				fptr[0] = 0.0;
+				fptr[1] = 0.0;
+			}
+			mesh_default_rd_buffers[DEFAULT_RD_BUFFER_TEX_UV2] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
+		}
 
 
-{ //tex uv 1
-	Vector<uint8_t> buffer;
-	buffer.resize(sizeof(float) * 2);
-	{
-		uint8_t *w = buffer.ptrw();
-		float *fptr = (float *)w;
-		fptr[0] = 0.0;
-		fptr[1] = 0.0;
-	}
-	mesh_default_rd_buffers[DEFAULT_RD_BUFFER_TEX_UV] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
-}
-{ //tex uv 2
-	Vector<uint8_t> buffer;
-	buffer.resize(sizeof(float) * 2);
-	{
-		uint8_t *w = buffer.ptrw();
-		float *fptr = (float *)w;
-		fptr[0] = 0.0;
-		fptr[1] = 0.0;
+		{ //bones
+			buffer.resize(sizeof(uint32_t) * 4);
+			{
+				uint8_t *w = buffer.ptrw();
+				uint32_t *fptr = (uint32_t *)w;
+				fptr[0] = 0;
+				fptr[1] = 0;
+				fptr[2] = 0;
+				fptr[3] = 0;
+			}
+			mesh_default_rd_buffers[DEFAULT_RD_BUFFER_BONES] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
+		}
+
+		{ //weights
+			buffer.resize(sizeof(float) * 4);
+			{
+				uint8_t *w = buffer.ptrw();
+				float *fptr = (float *)w;
+				fptr[0] = 0.0;
+				fptr[1] = 0.0;
+				fptr[2] = 0.0;
+				fptr[3] = 0.0;
+			}
+			mesh_default_rd_buffers[DEFAULT_RD_BUFFER_WEIGHTS] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
+		}
 	}
 	}
-	mesh_default_rd_buffers[DEFAULT_RD_BUFFER_TEX_UV2] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
-}
 
 
-{ //bones
-	Vector<uint8_t> buffer;
-	buffer.resize(sizeof(uint32_t) * 4);
 	{
 	{
-		uint8_t *w = buffer.ptrw();
-		uint32_t *fptr = (uint32_t *)w;
-		fptr[0] = 0;
-		fptr[1] = 0;
-		fptr[2] = 0;
-		fptr[3] = 0;
+		Vector<String> sdf_versions;
+		sdf_versions.push_back(""); //one only
+		giprobe_sdf_shader.initialize(sdf_versions);
+		giprobe_sdf_shader_version = giprobe_sdf_shader.version_create();
+		giprobe_sdf_shader.version_set_compute_code(giprobe_sdf_shader_version, "", "", "", Vector<String>());
+		giprobe_sdf_shader_version_shader = giprobe_sdf_shader.version_get_shader(giprobe_sdf_shader_version, 0);
+		giprobe_sdf_shader_pipeline = RD::get_singleton()->compute_pipeline_create(giprobe_sdf_shader_version_shader);
 	}
 	}
-	mesh_default_rd_buffers[DEFAULT_RD_BUFFER_BONES] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
-}
 
 
-{ //weights
-	Vector<uint8_t> buffer;
-	buffer.resize(sizeof(float) * 4);
-	{
-		uint8_t *w = buffer.ptrw();
-		float *fptr = (float *)w;
-		fptr[0] = 0.0;
-		fptr[1] = 0.0;
-		fptr[2] = 0.0;
-		fptr[3] = 0.0;
+	using_lightmap_array = true; // high end
+	if (using_lightmap_array) {
+
+		uint32_t textures_per_stage = RD::get_singleton()->limit_get(RD::LIMIT_MAX_TEXTURES_PER_SHADER_STAGE);
+
+		if (textures_per_stage <= 256) {
+			lightmap_textures.resize(32);
+		} else {
+			lightmap_textures.resize(1024);
+		}
+
+		for (int i = 0; i < lightmap_textures.size(); i++) {
+			lightmap_textures.write[i] = default_rd_textures[DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE];
+		}
 	}
 	}
-	mesh_default_rd_buffers[DEFAULT_RD_BUFFER_WEIGHTS] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
-}
-}
 
 
-{
-	Vector<String> sdf_versions;
-	sdf_versions.push_back(""); //one only
-	giprobe_sdf_shader.initialize(sdf_versions);
-	giprobe_sdf_shader_version = giprobe_sdf_shader.version_create();
-	giprobe_sdf_shader.version_set_compute_code(giprobe_sdf_shader_version, "", "", "", Vector<String>());
-	giprobe_sdf_shader_version_shader = giprobe_sdf_shader.version_get_shader(giprobe_sdf_shader_version, 0);
-	giprobe_sdf_shader_pipeline = RD::get_singleton()->compute_pipeline_create(giprobe_sdf_shader_version_shader);
-}
+	lightmap_probe_capture_update_speed = GLOBAL_GET("rendering/lightmapper/probe_capture_update_speed");
 }
 }
 
 
 RasterizerStorageRD::~RasterizerStorageRD() {
 RasterizerStorageRD::~RasterizerStorageRD() {

+ 77 - 16
servers/rendering/rasterizer_rd/rasterizer_storage_rd.h

@@ -92,6 +92,7 @@ public:
 		DEFAULT_RD_TEXTURE_CUBEMAP_BLACK,
 		DEFAULT_RD_TEXTURE_CUBEMAP_BLACK,
 		DEFAULT_RD_TEXTURE_CUBEMAP_ARRAY_BLACK,
 		DEFAULT_RD_TEXTURE_CUBEMAP_ARRAY_BLACK,
 		DEFAULT_RD_TEXTURE_3D_WHITE,
 		DEFAULT_RD_TEXTURE_3D_WHITE,
+		DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE,
 		DEFAULT_RD_TEXTURE_MAX
 		DEFAULT_RD_TEXTURE_MAX
 	};
 	};
 
 
@@ -118,6 +119,7 @@ private:
 		};
 		};
 
 
 		Type type;
 		Type type;
+		RS::TextureLayeredType layered_type = RS::TEXTURE_LAYERED_2D_ARRAY;
 
 
 		RenderingDevice::TextureType rd_type;
 		RenderingDevice::TextureType rd_type;
 		RID rd_texture;
 		RID rd_texture;
@@ -147,6 +149,7 @@ private:
 
 
 		RID proxy_to;
 		RID proxy_to;
 		Vector<RID> proxies;
 		Vector<RID> proxies;
+		Set<RID> lightmap_users;
 
 
 		RS::TextureDetectCallback detect_3d_callback = nullptr;
 		RS::TextureDetectCallback detect_3d_callback = nullptr;
 		void *detect_3d_callback_ud = nullptr;
 		void *detect_3d_callback_ud = nullptr;
@@ -524,6 +527,40 @@ private:
 
 
 	mutable RID_Owner<GIProbe> gi_probe_owner;
 	mutable RID_Owner<GIProbe> gi_probe_owner;
 
 
+	/* REFLECTION PROBE */
+
+	struct Lightmap {
+
+		RID light_texture;
+		bool uses_spherical_harmonics = false;
+		bool interior = false;
+		AABB bounds = AABB(Vector3(), Vector3(1, 1, 1));
+		int32_t array_index = -1; //unassigned
+		PackedVector3Array points;
+		PackedColorArray point_sh;
+		PackedInt32Array tetrahedra;
+		PackedInt32Array bsp_tree;
+
+		struct BSP {
+			static const int32_t EMPTY_LEAF = INT32_MIN;
+			float plane[4];
+			int32_t over = EMPTY_LEAF, under = EMPTY_LEAF;
+		};
+
+		RasterizerScene::InstanceDependency instance_dependency;
+	};
+
+	bool using_lightmap_array; //high end uses this
+	/* for high end */
+
+	Vector<RID> lightmap_textures;
+
+	uint64_t lightmap_array_version = 0;
+
+	mutable RID_Owner<Lightmap> lightmap_owner;
+
+	float lightmap_probe_capture_update_speed = 4;
+
 	/* RENDER TARGET */
 	/* RENDER TARGET */
 
 
 	struct RenderTarget {
 	struct RenderTarget {
@@ -653,7 +690,7 @@ public:
 
 
 	//these two APIs can be used together or in combination with the others.
 	//these two APIs can be used together or in combination with the others.
 	virtual RID texture_2d_placeholder_create();
 	virtual RID texture_2d_placeholder_create();
-	virtual RID texture_2d_layered_placeholder_create();
+	virtual RID texture_2d_layered_placeholder_create(RenderingServer::TextureLayeredType p_layered_type);
 	virtual RID texture_3d_placeholder_create();
 	virtual RID texture_3d_placeholder_create();
 
 
 	virtual Ref<Image> texture_2d_get(RID p_texture) const;
 	virtual Ref<Image> texture_2d_get(RID p_texture) const;
@@ -1270,23 +1307,47 @@ public:
 
 
 	/* LIGHTMAP CAPTURE */
 	/* LIGHTMAP CAPTURE */
 
 
-	void lightmap_capture_set_bounds(RID p_capture, const AABB &p_bounds) {}
-	AABB lightmap_capture_get_bounds(RID p_capture) const { return AABB(); }
-	void lightmap_capture_set_octree(RID p_capture, const Vector<uint8_t> &p_octree) {}
-	RID lightmap_capture_create() {
-		return RID();
+	virtual RID lightmap_create();
+
+	virtual void lightmap_set_textures(RID p_lightmap, RID p_light, bool p_uses_spherical_haromics);
+	virtual void lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds);
+	virtual void lightmap_set_probe_interior(RID p_lightmap, bool p_interior);
+	virtual void lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree);
+	virtual PackedVector3Array lightmap_get_probe_capture_points(RID p_lightmap) const;
+	virtual PackedColorArray lightmap_get_probe_capture_sh(RID p_lightmap) const;
+	virtual PackedInt32Array lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const;
+	virtual PackedInt32Array lightmap_get_probe_capture_bsp_tree(RID p_lightmap) const;
+	virtual AABB lightmap_get_aabb(RID p_lightmap) const;
+	virtual bool lightmap_is_interior(RID p_lightmap) const;
+	virtual void lightmap_tap_sh_light(RID p_lightmap, const Vector3 &p_point, Color *r_sh);
+	virtual void lightmap_set_probe_capture_update_speed(float p_speed);
+	_FORCE_INLINE_ float lightmap_get_probe_capture_update_speed() const {
+		return lightmap_probe_capture_update_speed;
+	}
+
+	_FORCE_INLINE_ int32_t lightmap_get_array_index(RID p_lightmap) const {
+		ERR_FAIL_COND_V(!using_lightmap_array, -1); //only for arrays
+		const Lightmap *lm = lightmap_owner.getornull(p_lightmap);
+		return lm->array_index;
+	}
+	_FORCE_INLINE_ bool lightmap_uses_spherical_harmonics(RID p_lightmap) const {
+		ERR_FAIL_COND_V(!using_lightmap_array, false); //only for arrays
+		const Lightmap *lm = lightmap_owner.getornull(p_lightmap);
+		return lm->uses_spherical_harmonics;
 	}
 	}
-	Vector<uint8_t> lightmap_capture_get_octree(RID p_capture) const {
-		return Vector<uint8_t>();
+	_FORCE_INLINE_ uint64_t lightmap_array_get_version() const {
+		ERR_FAIL_COND_V(!using_lightmap_array, 0); //only for arrays
+		return lightmap_array_version;
 	}
 	}
-	void lightmap_capture_set_octree_cell_transform(RID p_capture, const Transform &p_xform) {}
-	Transform lightmap_capture_get_octree_cell_transform(RID p_capture) const { return Transform(); }
-	void lightmap_capture_set_octree_cell_subdiv(RID p_capture, int p_subdiv) {}
-	int lightmap_capture_get_octree_cell_subdiv(RID p_capture) const { return 0; }
-	void lightmap_capture_set_energy(RID p_capture, float p_energy) {}
-	float lightmap_capture_get_energy(RID p_capture) const { return 0.0; }
-	const Vector<LightmapCaptureOctree> *lightmap_capture_get_octree_ptr(RID p_capture) const {
-		return nullptr;
+
+	_FORCE_INLINE_ int lightmap_array_get_size() const {
+		ERR_FAIL_COND_V(!using_lightmap_array, 0); //only for arrays
+		return lightmap_textures.size();
+	}
+
+	_FORCE_INLINE_ const Vector<RID> &lightmap_array_get_textures() const {
+		ERR_FAIL_COND_V(!using_lightmap_array, lightmap_textures); //only for arrays
+		return lightmap_textures;
 	}
 	}
 
 
 	/* PARTICLES */
 	/* PARTICLES */

+ 6 - 2
servers/rendering/rasterizer_rd/render_pipeline_vertex_format_cache_rd.cpp

@@ -31,16 +31,20 @@
 #include "render_pipeline_vertex_format_cache_rd.h"
 #include "render_pipeline_vertex_format_cache_rd.h"
 #include "core/os/memory.h"
 #include "core/os/memory.h"
 
 
-RID RenderPipelineVertexFormatCacheRD::_generate_version(RD::VertexFormatID p_vertex_format_id, RD::FramebufferFormatID p_framebuffer_format_id) {
+RID RenderPipelineVertexFormatCacheRD::_generate_version(RD::VertexFormatID p_vertex_format_id, RD::FramebufferFormatID p_framebuffer_format_id, bool p_wireframe) {
 
 
 	RD::PipelineMultisampleState multisample_state_version = multisample_state;
 	RD::PipelineMultisampleState multisample_state_version = multisample_state;
 	multisample_state_version.sample_count = RD::get_singleton()->framebuffer_format_get_texture_samples(p_framebuffer_format_id);
 	multisample_state_version.sample_count = RD::get_singleton()->framebuffer_format_get_texture_samples(p_framebuffer_format_id);
 
 
-	RID pipeline = RD::get_singleton()->render_pipeline_create(shader, p_framebuffer_format_id, p_vertex_format_id, render_primitive, rasterization_state, multisample_state_version, depth_stencil_state, blend_state, dynamic_state_flags);
+	RD::PipelineRasterizationState raster_state_version = rasterization_state;
+	raster_state_version.wireframe = p_wireframe;
+
+	RID pipeline = RD::get_singleton()->render_pipeline_create(shader, p_framebuffer_format_id, p_vertex_format_id, render_primitive, raster_state_version, multisample_state_version, depth_stencil_state, blend_state, dynamic_state_flags);
 	ERR_FAIL_COND_V(pipeline.is_null(), RID());
 	ERR_FAIL_COND_V(pipeline.is_null(), RID());
 	versions = (Version *)memrealloc(versions, sizeof(Version) * (version_count + 1));
 	versions = (Version *)memrealloc(versions, sizeof(Version) * (version_count + 1));
 	versions[version_count].framebuffer_id = p_framebuffer_format_id;
 	versions[version_count].framebuffer_id = p_framebuffer_format_id;
 	versions[version_count].vertex_id = p_vertex_format_id;
 	versions[version_count].vertex_id = p_vertex_format_id;
+	versions[version_count].wireframe = p_wireframe;
 	versions[version_count].pipeline = pipeline;
 	versions[version_count].pipeline = pipeline;
 	version_count++;
 	version_count++;
 	return pipeline;
 	return pipeline;

+ 5 - 4
servers/rendering/rasterizer_rd/render_pipeline_vertex_format_cache_rd.h

@@ -51,13 +51,14 @@ class RenderPipelineVertexFormatCacheRD {
 	struct Version {
 	struct Version {
 		RD::VertexFormatID vertex_id;
 		RD::VertexFormatID vertex_id;
 		RD::FramebufferFormatID framebuffer_id;
 		RD::FramebufferFormatID framebuffer_id;
+		bool wireframe;
 		RID pipeline;
 		RID pipeline;
 	};
 	};
 
 
 	Version *versions;
 	Version *versions;
 	uint32_t version_count;
 	uint32_t version_count;
 
 
-	RID _generate_version(RD::VertexFormatID p_vertex_format_id, RD::FramebufferFormatID p_framebuffer_format_id);
+	RID _generate_version(RD::VertexFormatID p_vertex_format_id, RD::FramebufferFormatID p_framebuffer_format_id, bool p_wireframe);
 
 
 	void _clear();
 	void _clear();
 
 
@@ -65,7 +66,7 @@ public:
 	void setup(RID p_shader, RD::RenderPrimitive p_primitive, const RD::PipelineRasterizationState &p_rasterization_state, RD::PipelineMultisampleState p_multisample, const RD::PipelineDepthStencilState &p_depth_stencil_state, const RD::PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags = 0);
 	void setup(RID p_shader, RD::RenderPrimitive p_primitive, const RD::PipelineRasterizationState &p_rasterization_state, RD::PipelineMultisampleState p_multisample, const RD::PipelineDepthStencilState &p_depth_stencil_state, const RD::PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags = 0);
 	void update_shader(RID p_shader);
 	void update_shader(RID p_shader);
 
 
-	_FORCE_INLINE_ RID get_render_pipeline(RD::VertexFormatID p_vertex_format_id, RD::FramebufferFormatID p_framebuffer_format_id) {
+	_FORCE_INLINE_ RID get_render_pipeline(RD::VertexFormatID p_vertex_format_id, RD::FramebufferFormatID p_framebuffer_format_id, bool p_wireframe = false) {
 #ifdef DEBUG_ENABLED
 #ifdef DEBUG_ENABLED
 		ERR_FAIL_COND_V_MSG(shader.is_null(), RID(),
 		ERR_FAIL_COND_V_MSG(shader.is_null(), RID(),
 				"Attempted to use an unused shader variant (shader is null),");
 				"Attempted to use an unused shader variant (shader is null),");
@@ -74,13 +75,13 @@ public:
 		spin_lock.lock();
 		spin_lock.lock();
 		RID result;
 		RID result;
 		for (uint32_t i = 0; i < version_count; i++) {
 		for (uint32_t i = 0; i < version_count; i++) {
-			if (versions[i].vertex_id == p_vertex_format_id && versions[i].framebuffer_id == p_framebuffer_format_id) {
+			if (versions[i].vertex_id == p_vertex_format_id && versions[i].framebuffer_id == p_framebuffer_format_id && versions[i].wireframe == p_wireframe) {
 				result = versions[i].pipeline;
 				result = versions[i].pipeline;
 				spin_lock.unlock();
 				spin_lock.unlock();
 				return result;
 				return result;
 			}
 			}
 		}
 		}
-		result = _generate_version(p_vertex_format_id, p_framebuffer_format_id);
+		result = _generate_version(p_vertex_format_id, p_framebuffer_format_id, p_wireframe);
 		spin_lock.unlock();
 		spin_lock.unlock();
 		return result;
 		return result;
 	}
 	}

+ 5 - 0
servers/rendering/rasterizer_rd/shader_compiler_rd.cpp

@@ -120,8 +120,11 @@ static int _get_datatype_size(SL::DataType p_type) {
 			return 16;
 			return 16;
 		case SL::TYPE_SAMPLERCUBE:
 		case SL::TYPE_SAMPLERCUBE:
 			return 16;
 			return 16;
+		case SL::TYPE_SAMPLERCUBEARRAY:
+			return 16;
 		case SL::TYPE_STRUCT:
 		case SL::TYPE_STRUCT:
 			return 0;
 			return 0;
+
 		case SL::TYPE_MAX: {
 		case SL::TYPE_MAX: {
 			ERR_FAIL_V(0);
 			ERR_FAIL_V(0);
 		};
 		};
@@ -194,6 +197,8 @@ static int _get_datatype_alignment(SL::DataType p_type) {
 			return 16;
 			return 16;
 		case SL::TYPE_SAMPLERCUBE:
 		case SL::TYPE_SAMPLERCUBE:
 			return 16;
 			return 16;
+		case SL::TYPE_SAMPLERCUBEARRAY:
+			return 16;
 		case SL::TYPE_STRUCT:
 		case SL::TYPE_STRUCT:
 			return 0;
 			return 0;
 		case SL::TYPE_MAX: {
 		case SL::TYPE_MAX: {

+ 28 - 1
servers/rendering/rasterizer_rd/shaders/copy.glsl

@@ -39,7 +39,13 @@ layout(push_constant, binding = 1, std430) uniform Params {
 }
 }
 params;
 params;
 
 
+#ifdef MODE_CUBEMAP_ARRAY_TO_PANORAMA
+layout(set = 0, binding = 0) uniform samplerCubeArray source_color;
+#elif defined(MODE_CUBEMAP_TO_PANORAMA)
+layout(set = 0, binding = 0) uniform samplerCube source_color;
+#else
 layout(set = 0, binding = 0) uniform sampler2D source_color;
 layout(set = 0, binding = 0) uniform sampler2D source_color;
+#endif
 
 
 #ifdef GLOW_USE_AUTO_EXPOSURE
 #ifdef GLOW_USE_AUTO_EXPOSURE
 layout(set = 1, binding = 0) uniform sampler2D source_auto_exposure;
 layout(set = 1, binding = 0) uniform sampler2D source_auto_exposure;
@@ -57,7 +63,7 @@ void main() {
 
 
 	// Pixel being shaded
 	// Pixel being shaded
 	ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
 	ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
-	if (any(greaterThan(pos, params.section.zw))) { //too large, do nothing
+	if (any(greaterThanEqual(pos, params.section.zw))) { //too large, do nothing
 		return;
 		return;
 	}
 	}
 
 
@@ -217,4 +223,25 @@ void main() {
 
 
 	imageStore(dest_buffer, pos + params.target, color);
 	imageStore(dest_buffer, pos + params.target, color);
 #endif
 #endif
+
+#if defined(MODE_CUBEMAP_TO_PANORAMA) || defined(MODE_CUBEMAP_ARRAY_TO_PANORAMA)
+
+	const float PI = 3.14159265359;
+	vec2 uv = vec2(pos) / vec2(params.section.zw);
+	uv.y = 1.0 - uv.y;
+	float phi = uv.x * 2.0 * PI;
+	float theta = uv.y * PI;
+
+	vec3 normal;
+	normal.x = sin(phi) * sin(theta) * -1.0;
+	normal.y = cos(theta);
+	normal.z = cos(phi) * sin(theta) * -1.0;
+
+#ifdef MODE_CUBEMAP_TO_PANORAMA
+	vec4 color = textureLod(source_color, normal, params.camera_z_far); //the biggest the lod the least the acne
+#else
+	vec4 color = textureLod(source_color, vec4(normal, params.camera_z_far), 0.0); //the biggest the lod the least the acne
+#endif
+	imageStore(dest_buffer, pos + params.target, color);
+#endif
 }
 }

+ 89 - 28
servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl

@@ -22,7 +22,7 @@ layout(location = 3) in vec4 color_attrib;
 
 
 layout(location = 4) in vec2 uv_attrib;
 layout(location = 4) in vec2 uv_attrib;
 
 
-#if defined(UV2_USED) || defined(USE_LIGHTMAP)
+#if defined(UV2_USED) || defined(USE_LIGHTMAP) || defined(MODE_RENDER_MATERIAL)
 layout(location = 5) in vec2 uv2_attrib;
 layout(location = 5) in vec2 uv2_attrib;
 #endif
 #endif
 
 
@@ -49,7 +49,7 @@ layout(location = 6) out vec3 binormal_interp;
 #endif
 #endif
 
 
 #ifdef USE_MATERIAL_UNIFORMS
 #ifdef USE_MATERIAL_UNIFORMS
-layout(set = 5, binding = 0, std140) uniform MaterialUniforms{
+layout(set = MATERIAL_UNIFORM_SET, binding = 0, std140) uniform MaterialUniforms{
 	/* clang-format off */
 	/* clang-format off */
 MATERIAL_UNIFORMS
 MATERIAL_UNIFORMS
 	/* clang-format on */
 	/* clang-format on */
@@ -263,6 +263,14 @@ VERTEX_SHADER_CODE
 		}
 		}
 	}
 	}
 #endif
 #endif
+
+#ifdef MODE_RENDER_MATERIAL
+	if (scene_data.material_uv2_mode) {
+		gl_Position.xy = (uv2_attrib.xy + draw_call.bake_uv2_offset) * 2.0 - 1.0;
+		gl_Position.z = 0.00001;
+		gl_Position.w = 1.0;
+	}
+#endif
 }
 }
 
 
 /* clang-format off */
 /* clang-format off */
@@ -315,7 +323,7 @@ layout(location = 8) in float dp_clip;
 #endif
 #endif
 
 
 #ifdef USE_MATERIAL_UNIFORMS
 #ifdef USE_MATERIAL_UNIFORMS
-layout(set = 5, binding = 0, std140) uniform MaterialUniforms{
+layout(set = MATERIAL_UNIFORM_SET, binding = 0, std140) uniform MaterialUniforms{
 	/* clang-format off */
 	/* clang-format off */
 MATERIAL_UNIFORMS
 MATERIAL_UNIFORMS
 	/* clang-format on */
 	/* clang-format on */
@@ -1917,42 +1925,96 @@ FRAGMENT_SHADER_CODE
 #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
 #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
 	//gi probes
 	//gi probes
 
 
+#ifdef USE_LIGHTMAP
+
 	//lightmap
 	//lightmap
+	if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_LIGHTMAP_CAPTURE)) { //has lightmap capture
+		uint index = instances.data[instance_index].gi_offset;
+
+		vec3 wnormal = mat3(scene_data.camera_matrix) * normal;
+		const float c1 = 0.429043;
+		const float c2 = 0.511664;
+		const float c3 = 0.743125;
+		const float c4 = 0.886227;
+		const float c5 = 0.247708;
+		ambient_light += (c1 * lightmap_captures.data[index].sh[8].rgb * (wnormal.x * wnormal.x - wnormal.y * wnormal.y) +
+						  c3 * lightmap_captures.data[index].sh[6].rgb * wnormal.z * wnormal.z +
+						  c4 * lightmap_captures.data[index].sh[0].rgb -
+						  c5 * lightmap_captures.data[index].sh[6].rgb +
+						  2.0 * c1 * lightmap_captures.data[index].sh[4].rgb * wnormal.x * wnormal.y +
+						  2.0 * c1 * lightmap_captures.data[index].sh[7].rgb * wnormal.x * wnormal.z +
+						  2.0 * c1 * lightmap_captures.data[index].sh[5].rgb * wnormal.y * wnormal.z +
+						  2.0 * c2 * lightmap_captures.data[index].sh[3].rgb * wnormal.x +
+						  2.0 * c2 * lightmap_captures.data[index].sh[1].rgb * wnormal.y +
+						  2.0 * c2 * lightmap_captures.data[index].sh[2].rgb * wnormal.z);
+
+	} else if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_LIGHTMAP)) { // has actual lightmap
+		bool uses_sh = bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_SH_LIGHTMAP);
+		uint ofs = instances.data[instance_index].gi_offset & 0xFFF;
+		vec3 uvw;
+		uvw.xy = uv2 * instances.data[instance_index].lightmap_uv_scale.zw + instances.data[instance_index].lightmap_uv_scale.xy;
+		uvw.z = float((instances.data[instance_index].gi_offset >> 12) & 0xFF);
+
+		if (uses_sh) {
+			uvw.z *= 4.0; //SH textures use 4 times more data
+			vec3 lm_light_l0 = textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb;
+			vec3 lm_light_l1n1 = textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb;
+			vec3 lm_light_l1_0 = textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb;
+			vec3 lm_light_l1p1 = textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb;
+
+			uint idx = instances.data[instance_index].gi_offset >> 20;
+			vec3 n = normalize(lightmaps.data[idx].normal_xform * normal);
+
+			ambient_light += lm_light_l0 * 0.282095f;
+			ambient_light += lm_light_l1n1 * 0.32573 * n.y;
+			ambient_light += lm_light_l1_0 * 0.32573 * n.z;
+			ambient_light += lm_light_l1p1 * 0.32573 * n.x;
+			if (metallic > 0.01) { // since the more direct bounced light is lost, we can kind of fake it with this trick
+				vec3 r = reflect(normalize(-vertex), normal);
+				specular_light += lm_light_l1n1 * 0.32573 * r.y;
+				specular_light += lm_light_l1_0 * 0.32573 * r.z;
+				specular_light += lm_light_l1p1 * 0.32573 * r.x;
+			}
+
+		} else {
 
 
+			ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw, 0.0).rgb;
+		}
+	}
+#endif
 	//lightmap capture
 	//lightmap capture
 
 
 #ifdef USE_VOXEL_CONE_TRACING
 #ifdef USE_VOXEL_CONE_TRACING
-	{ // process giprobes
-		uint index1 = instances.data[instance_index].gi_offset & 0xFFFF;
-		if (index1 != 0xFFFF) {
-			vec3 ref_vec = normalize(reflect(normalize(vertex), normal));
-			//find arbitrary tangent and bitangent, then build a matrix
-			vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);
-			vec3 tangent = normalize(cross(v0, normal));
-			vec3 bitangent = normalize(cross(tangent, normal));
-			mat3 normal_mat = mat3(tangent, bitangent, normal);
+	if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_GIPROBE)) { // process giprobes
 
 
-			vec4 amb_accum = vec4(0.0);
-			vec4 spec_accum = vec4(0.0);
-			gi_probe_compute(index1, vertex, normal, ref_vec, normal_mat, roughness * roughness, ambient_light, specular_light, spec_accum, amb_accum);
+		uint index1 = instances.data[instance_index].gi_offset & 0xFFFF;
+		vec3 ref_vec = normalize(reflect(normalize(vertex), normal));
+		//find arbitrary tangent and bitangent, then build a matrix
+		vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);
+		vec3 tangent = normalize(cross(v0, normal));
+		vec3 bitangent = normalize(cross(tangent, normal));
+		mat3 normal_mat = mat3(tangent, bitangent, normal);
 
 
-			uint index2 = instances.data[instance_index].gi_offset >> 16;
+		vec4 amb_accum = vec4(0.0);
+		vec4 spec_accum = vec4(0.0);
+		gi_probe_compute(index1, vertex, normal, ref_vec, normal_mat, roughness * roughness, ambient_light, specular_light, spec_accum, amb_accum);
 
 
-			if (index2 != 0xFFFF) {
-				gi_probe_compute(index2, vertex, normal, ref_vec, normal_mat, roughness * roughness, ambient_light, specular_light, spec_accum, amb_accum);
-			}
+		uint index2 = instances.data[instance_index].gi_offset >> 16;
 
 
-			if (amb_accum.a > 0.0) {
-				amb_accum.rgb /= amb_accum.a;
-			}
+		if (index2 != 0xFFFF) {
+			gi_probe_compute(index2, vertex, normal, ref_vec, normal_mat, roughness * roughness, ambient_light, specular_light, spec_accum, amb_accum);
+		}
 
 
-			if (spec_accum.a > 0.0) {
-				spec_accum.rgb /= spec_accum.a;
-			}
+		if (amb_accum.a > 0.0) {
+			amb_accum.rgb /= amb_accum.a;
+		}
 
 
-			specular_light = spec_accum.rgb;
-			ambient_light = amb_accum.rgb;
+		if (spec_accum.a > 0.0) {
+			spec_accum.rgb /= spec_accum.a;
 		}
 		}
+
+		specular_light = spec_accum.rgb;
+		ambient_light = amb_accum.rgb;
 	}
 	}
 #endif
 #endif
 
 
@@ -2424,7 +2486,6 @@ FRAGMENT_SHADER_CODE
 	ao_light_affect = mix(1.0, ao, ao_light_affect);
 	ao_light_affect = mix(1.0, ao, ao_light_affect);
 	specular_light = mix(scene_data.ao_color.rgb, specular_light, ao_light_affect);
 	specular_light = mix(scene_data.ao_color.rgb, specular_light, ao_light_affect);
 	diffuse_light = mix(scene_data.ao_color.rgb, diffuse_light, ao_light_affect);
 	diffuse_light = mix(scene_data.ao_color.rgb, diffuse_light, ao_light_affect);
-
 #else
 #else
 
 
 	if (scene_data.ssao_enabled) {
 	if (scene_data.ssao_enabled) {

Some files were not shown because too many files changed in this diff