diff options
28 files changed, 484 insertions, 462 deletions
diff --git a/doc/classes/ParticleProcessMaterial.xml b/doc/classes/ParticleProcessMaterial.xml index 6d549e1b67..5497effc75 100644 --- a/doc/classes/ParticleProcessMaterial.xml +++ b/doc/classes/ParticleProcessMaterial.xml @@ -282,7 +282,8 @@ Minimum equivalent of [member tangential_accel_max]. </member> <member name="turbulence_enabled" type="bool" setter="set_turbulence_enabled" getter="get_turbulence_enabled" default="false"> - Enables and disables Turbulence for the particle system. + If [code]true[/code], enables turbulence for the particle system. Turbulence can be used to vary particle movement according to its position (based on a 3D noise pattern). In 3D, [GPUParticlesAttractorVectorField3D] with [NoiseTexture3D] can be used as an alternative to turbulence that works in world space and with multiple particle systems reacting in the same way. + [b]Note:[/b] Enabling turbulence has a high performance cost on the GPU. Only enable turbulence on a few particle systems at once at most, and consider disabling it when targeting mobile/web platforms. </member> <member name="turbulence_influence_max" type="float" setter="set_param_max" getter="get_param_max" default="0.1"> Maximum turbulence influence on each particle. @@ -296,11 +297,11 @@ Each particle's amount of turbulence will be influenced along this [CurveTexture] over its life time. </member> <member name="turbulence_initial_displacement_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0"> - Maximum displacement of each particles spawn position by the turbulence. + Maximum displacement of each particle's spawn position by the turbulence. The actual amount of displacement will be a factor of the underlying turbulence multiplied by a random value between [member turbulence_initial_displacement_min] and [member turbulence_initial_displacement_max]. </member> <member name="turbulence_initial_displacement_min" type="float" setter="set_param_min" getter="get_param_min" default="0.0"> - Minimum displacement of each particles spawn position by the turbulence. + Minimum displacement of each particle's spawn position by the turbulence. The actual amount of displacement will be a factor of the underlying turbulence multiplied by a random value between [member turbulence_initial_displacement_min] and [member turbulence_initial_displacement_max]. </member> <member name="turbulence_noise_scale" type="float" setter="set_turbulence_noise_scale" getter="get_turbulence_noise_scale" default="9.0"> @@ -312,10 +313,10 @@ A value of [code]Vector3(0.0, 0.0, 0.0)[/code] will freeze the turbulence pattern in place. </member> <member name="turbulence_noise_speed_random" type="float" setter="set_turbulence_noise_speed_random" getter="get_turbulence_noise_speed_random" default="0.0"> - Use to influence the noise speed in a random pattern. This helps to break up visible movement patterns. + Use to influence the noise speed in a random pattern. This helps break up visible movement patterns. </member> <member name="turbulence_noise_strength" type="float" setter="set_turbulence_noise_strength" getter="get_turbulence_noise_strength" default="1.0"> - The turbulence noise strength. Increasing this will result in a stronger, more contrasting, noise pattern. + The turbulence noise strength. Increasing this will result in a stronger, more contrasting noise pattern. </member> </members> <constants> diff --git a/editor/plugins/material_editor_plugin.cpp b/editor/plugins/material_editor_plugin.cpp index 2fdebc7c7f..404711e074 100644 --- a/editor/plugins/material_editor_plugin.cpp +++ b/editor/plugins/material_editor_plugin.cpp @@ -164,9 +164,19 @@ void MaterialEditor::_button_pressed(Node *p_button) { MaterialEditor::MaterialEditor() { // canvas item + vc_2d = memnew(SubViewportContainer); + vc_2d->set_stretch(true); + add_child(vc_2d); + vc_2d->set_anchors_and_offsets_preset(PRESET_FULL_RECT); + + viewport_2d = memnew(SubViewport); + vc_2d->add_child(viewport_2d); + viewport_2d->set_disable_input(true); + viewport_2d->set_transparent_background(true); + layout_2d = memnew(HBoxContainer); layout_2d->set_alignment(BoxContainer::ALIGNMENT_CENTER); - add_child(layout_2d); + viewport_2d->add_child(layout_2d); layout_2d->set_anchors_and_offsets_preset(PRESET_FULL_RECT); rect_instance = memnew(ColorRect); diff --git a/editor/plugins/material_editor_plugin.h b/editor/plugins/material_editor_plugin.h index deb1211c54..ac81bdc7c7 100644 --- a/editor/plugins/material_editor_plugin.h +++ b/editor/plugins/material_editor_plugin.h @@ -51,6 +51,8 @@ class MaterialEditor : public Control { Vector2 rot; + SubViewportContainer *vc_2d = nullptr; + SubViewport *viewport_2d = nullptr; HBoxContainer *layout_2d = nullptr; ColorRect *rect_instance = nullptr; diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 96166dab3f..9e231a41c3 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -276,6 +276,8 @@ void SceneTreeDock::_replace_with_branch_scene(const String &p_file, Node *base) return; } + instantiated_scene->set_unique_name_in_owner(base->is_unique_name_in_owner()); + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(TTR("Replace with Branch Scene")); diff --git a/modules/noise/doc_classes/Noise.xml b/modules/noise/doc_classes/Noise.xml index a5cdb5bcbc..c075b5b629 100644 --- a/modules/noise/doc_classes/Noise.xml +++ b/modules/noise/doc_classes/Noise.xml @@ -15,13 +15,24 @@ <return type="Image" /> <param index="0" name="width" type="int" /> <param index="1" name="height" type="int" /> + <param index="2" name="invert" type="bool" default="false" /> + <param index="3" name="in_3d_space" type="bool" default="false" /> + <param index="4" name="normalize" type="bool" default="true" /> + <description> + Returns an [Image] containing 2D noise values. + [b]Note:[/b] With [param normalize] set to [code]false[/code], the default implementation expects the noise generator to return values in the range [code]-1.0[/code] to [code]1.0[/code]. + </description> + </method> + <method name="get_image_3d" qualifiers="const"> + <return type="Image[]" /> + <param index="0" name="width" type="int" /> + <param index="1" name="height" type="int" /> <param index="2" name="depth" type="int" /> <param index="3" name="invert" type="bool" default="false" /> - <param index="4" name="in_3d_space" type="bool" default="false" /> - <param index="5" name="normalize" type="bool" default="true" /> + <param index="4" name="normalize" type="bool" default="true" /> <description> - Returns a 2D [Image] noise image. - Note: With [param normalize] set to [code]false[/code] the default implementation expects the noise generator to return values in the range [code]-1.0[/code] to [code]1.0[/code]. + Returns an [Array] of [Image]s containing 3D noise values for use with [method ImageTexture3D.create]. + [b]Note:[/b] With [param normalize] set to [code]false[/code], the default implementation expects the noise generator to return values in the range [code]-1.0[/code] to [code]1.0[/code]. </description> </method> <method name="get_noise_1d" qualifiers="const"> @@ -66,14 +77,26 @@ <return type="Image" /> <param index="0" name="width" type="int" /> <param index="1" name="height" type="int" /> + <param index="2" name="invert" type="bool" default="false" /> + <param index="3" name="in_3d_space" type="bool" default="false" /> + <param index="4" name="skirt" type="float" default="0.1" /> + <param index="5" name="normalize" type="bool" default="true" /> + <description> + Returns an [Image] containing seamless 2D noise values. + [b]Note:[/b] With [param normalize] set to [code]false[/code], the default implementation expects the noise generator to return values in the range [code]-1.0[/code] to [code]1.0[/code]. + </description> + </method> + <method name="get_seamless_image_3d" qualifiers="const"> + <return type="Image[]" /> + <param index="0" name="width" type="int" /> + <param index="1" name="height" type="int" /> <param index="2" name="depth" type="int" /> <param index="3" name="invert" type="bool" default="false" /> - <param index="4" name="in_3d_space" type="bool" default="false" /> - <param index="5" name="skirt" type="float" default="0.1" /> - <param index="6" name="normalize" type="bool" default="true" /> + <param index="4" name="skirt" type="float" default="0.1" /> + <param index="5" name="normalize" type="bool" default="true" /> <description> - Returns a seamless 2D [Image] noise image. - Note: With [param normalize] set to [code]false[/code] the default implementation expects the noise generator to return values in the range [code]-1.0[/code] to [code]1.0[/code]. + Returns an [Array] of [Image]s containing seamless 3D noise values for use with [method ImageTexture3D.create]. + [b]Note:[/b] With [param normalize] set to [code]false[/code], the default implementation expects the noise generator to return values in the range [code]-1.0[/code] to [code]1.0[/code]. </description> </method> </methods> diff --git a/modules/noise/doc_classes/NoiseTexture3D.xml b/modules/noise/doc_classes/NoiseTexture3D.xml index 0b385d9b9c..7394e7ff08 100644 --- a/modules/noise/doc_classes/NoiseTexture3D.xml +++ b/modules/noise/doc_classes/NoiseTexture3D.xml @@ -5,13 +5,12 @@ </brief_description> <description> Uses [FastNoiseLite] or other libraries to fill the texture data of your desired size. - The class uses [Thread]s to generate the texture data internally, so [method Texture3D.get_data] may return [code]null[/code] if the generation process has not completed yet. In that case, you need to wait for the texture to be generated before accessing the image and the generated byte data: + The class uses [Thread]s to generate the texture data internally, so [method Texture3D.get_data] may return [code]null[/code] if the generation process has not completed yet. In that case, you need to wait for the texture to be generated before accessing the image: [codeblock] var texture = NoiseTexture3D.new() texture.noise = FastNoiseLite.new() await texture.changed var data = texture.get_data() - var image = data[0] [/codeblock] </description> <tutorials> diff --git a/modules/noise/fastnoise_lite.cpp b/modules/noise/fastnoise_lite.cpp index 224c082c0f..4aea98c4de 100644 --- a/modules/noise/fastnoise_lite.cpp +++ b/modules/noise/fastnoise_lite.cpp @@ -416,7 +416,7 @@ void FastNoiseLite::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "noise_type", PROPERTY_HINT_ENUM, "Simplex,Simplex Smooth,Cellular,Perlin,Value Cubic,Value"), "set_noise_type", "get_noise_type"); ADD_PROPERTY(PropertyInfo(Variant::INT, "seed"), "set_seed", "get_seed"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frequency", PROPERTY_HINT_RANGE, ".001,1"), "set_frequency", "get_frequency"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frequency", PROPERTY_HINT_RANGE, ".0001,1,.0001"), "set_frequency", "get_frequency"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "offset", PROPERTY_HINT_RANGE, "-999999999,999999999,0.01"), "set_offset", "get_offset"); ADD_GROUP("Fractal", "fractal_"); diff --git a/modules/noise/noise.cpp b/modules/noise/noise.cpp index b8c1587ec3..1115d92f58 100644 --- a/modules/noise/noise.cpp +++ b/modules/noise/noise.cpp @@ -32,21 +32,40 @@ #include <float.h> -Ref<Image> Noise::get_seamless_image(int p_width, int p_height, int p_depth, bool p_invert, bool p_in_3d_space, real_t p_blend_skirt, bool p_normalize) const { - ERR_FAIL_COND_V(p_width <= 0 || p_height <= 0, Ref<Image>()); +Vector<Ref<Image>> Noise::_get_seamless_image(int p_width, int p_height, int p_depth, bool p_invert, bool p_in_3d_space, real_t p_blend_skirt, bool p_normalize) const { + ERR_FAIL_COND_V(p_width <= 0 || p_height <= 0 || p_depth <= 0, Vector<Ref<Image>>()); int skirt_width = MAX(1, p_width * p_blend_skirt); int skirt_height = MAX(1, p_height * p_blend_skirt); + int skirt_depth = MAX(1, p_depth * p_blend_skirt); int src_width = p_width + skirt_width; int src_height = p_height + skirt_height; + int src_depth = p_depth + skirt_depth; + + Vector<Ref<Image>> src = _get_image(src_width, src_height, src_depth, p_invert, p_in_3d_space, p_normalize); + bool grayscale = (src[0]->get_format() == Image::FORMAT_L8); - Ref<Image> src = get_image(src_width, src_height, p_depth, p_invert, p_in_3d_space, p_normalize); - bool grayscale = (src->get_format() == Image::FORMAT_L8); if (grayscale) { - return _generate_seamless_image<uint8_t>(src, p_width, p_height, p_invert, p_blend_skirt); + return _generate_seamless_image<uint8_t>(src, p_width, p_height, p_depth, p_invert, p_blend_skirt); } else { - return _generate_seamless_image<uint32_t>(src, p_width, p_height, p_invert, p_blend_skirt); + return _generate_seamless_image<uint32_t>(src, p_width, p_height, p_depth, p_invert, p_blend_skirt); + } +} + +Ref<Image> Noise::get_seamless_image(int p_width, int p_height, bool p_invert, bool p_in_3d_space, real_t p_blend_skirt, bool p_normalize) const { + Vector<Ref<Image>> images = _get_seamless_image(p_width, p_height, 1, p_invert, p_in_3d_space, p_blend_skirt, p_normalize); + return images[0]; +} + +TypedArray<Image> Noise::get_seamless_image_3d(int p_width, int p_height, int p_depth, bool p_invert, real_t p_blend_skirt, bool p_normalize) const { + Vector<Ref<Image>> images = _get_seamless_image(p_width, p_height, p_depth, p_invert, true, p_blend_skirt, p_normalize); + + TypedArray<Image> ret; + ret.resize(images.size()); + for (int i = 0; i < images.size(); i++) { + ret[i] = images[i]; } + return ret; } // Template specialization for faster grayscale blending. @@ -58,61 +77,104 @@ uint8_t Noise::_alpha_blend<uint8_t>(uint8_t p_bg, uint8_t p_fg, int p_alpha) co return (uint8_t)((alpha * p_fg + inv_alpha * p_bg) >> 8); } -Ref<Image> Noise::get_image(int p_width, int p_height, int p_depth, bool p_invert, bool p_in_3d_space, bool p_normalize) const { - ERR_FAIL_COND_V(p_width <= 0 || p_height <= 0, Ref<Image>()); - - Vector<uint8_t> data; - data.resize(p_width * p_height); +Vector<Ref<Image>> Noise::_get_image(int p_width, int p_height, int p_depth, bool p_invert, bool p_in_3d_space, bool p_normalize) const { + ERR_FAIL_COND_V(p_width <= 0 || p_height <= 0 || p_depth <= 0, Vector<Ref<Image>>()); - uint8_t *wd8 = data.ptrw(); + Vector<Ref<Image>> images; + images.resize(p_depth); if (p_normalize) { // Get all values and identify min/max values. - Vector<real_t> values; - values.resize(p_width * p_height); + LocalVector<real_t> values; + values.resize(p_width * p_height * p_depth); + real_t min_val = FLT_MAX; real_t max_val = -FLT_MAX; - for (int y = 0, i = 0; y < p_height; y++) { - for (int x = 0; x < p_width; x++, i++) { - values.set(i, p_in_3d_space ? get_noise_3d(x, y, p_depth) : get_noise_2d(x, y)); - if (values[i] > max_val) { - max_val = values[i]; - } - if (values[i] < min_val) { - min_val = values[i]; + int idx = 0; + for (int d = 0; d < p_depth; d++) { + for (int y = 0; y < p_height; y++) { + for (int x = 0; x < p_width; x++) { + values[idx] = p_in_3d_space ? get_noise_3d(x, y, d) : get_noise_2d(x, y); + if (values[idx] > max_val) { + max_val = values[idx]; + } + if (values[idx] < min_val) { + min_val = values[idx]; + } + idx++; } } } + idx = 0; // Normalize values and write to texture. - uint8_t ivalue; - for (int i = 0, x = 0; i < p_height; i++) { - for (int j = 0; j < p_width; j++, x++) { - if (max_val == min_val) { - ivalue = 0; - } else { - ivalue = static_cast<uint8_t>(CLAMP((values[x] - min_val) / (max_val - min_val) * 255.f, 0, 255)); - } - - if (p_invert) { - ivalue = 255 - ivalue; + for (int d = 0; d < p_depth; d++) { + Vector<uint8_t> data; + data.resize(p_width * p_height); + + uint8_t *wd8 = data.ptrw(); + uint8_t ivalue; + + for (int y = 0; y < p_height; y++) { + for (int x = 0; x < p_width; x++) { + if (max_val == min_val) { + ivalue = 0; + } else { + ivalue = static_cast<uint8_t>(CLAMP((values[idx] - min_val) / (max_val - min_val) * 255.f, 0, 255)); + } + + if (p_invert) { + ivalue = 255 - ivalue; + } + + wd8[x + y * p_width] = ivalue; + idx++; } - - wd8[x] = ivalue; } + Ref<Image> img = memnew(Image(p_width, p_height, false, Image::FORMAT_L8, data)); + images.write[d] = img; } } else { // Without normalization, the expected range of the noise function is [-1, 1]. - uint8_t ivalue; - for (int y = 0, i = 0; y < p_height; y++) { - for (int x = 0; x < p_width; x++, i++) { - float value = (p_in_3d_space ? get_noise_3d(x, y, p_depth) : get_noise_2d(x, y)); - ivalue = static_cast<uint8_t>(CLAMP(value * 127.5f + 127.5f, 0.0f, 255.0f)); - wd8[i] = p_invert ? (255 - ivalue) : ivalue; + + for (int d = 0; d < p_depth; d++) { + Vector<uint8_t> data; + data.resize(p_width * p_height); + + uint8_t *wd8 = data.ptrw(); + + uint8_t ivalue; + int idx = 0; + for (int y = 0; y < p_height; y++) { + for (int x = 0; x < p_width; x++) { + float value = (p_in_3d_space ? get_noise_3d(x, y, d) : get_noise_2d(x, y)); + ivalue = static_cast<uint8_t>(CLAMP(value * 127.5f + 127.5f, 0.0f, 255.0f)); + wd8[idx] = p_invert ? (255 - ivalue) : ivalue; + idx++; + } } + + Ref<Image> img = memnew(Image(p_width, p_height, false, Image::FORMAT_L8, data)); + images.write[d] = img; } } - return memnew(Image(p_width, p_height, false, Image::FORMAT_L8, data)); + return images; +} + +Ref<Image> Noise::get_image(int p_width, int p_height, bool p_invert, bool p_in_3d_space, bool p_normalize) const { + Vector<Ref<Image>> images = _get_image(p_width, p_height, 1, p_invert, p_in_3d_space, p_normalize); + return images[0]; +} + +TypedArray<Image> Noise::get_image_3d(int p_width, int p_height, int p_depth, bool p_invert, bool p_normalize) const { + Vector<Ref<Image>> images = _get_image(p_width, p_height, p_depth, p_invert, true, p_normalize); + + TypedArray<Image> ret; + ret.resize(images.size()); + for (int i = 0; i < images.size(); i++) { + ret[i] = images[i]; + } + return ret; } void Noise::_bind_methods() { @@ -124,6 +186,8 @@ void Noise::_bind_methods() { ClassDB::bind_method(D_METHOD("get_noise_3dv", "v"), &Noise::get_noise_3dv); // Textures. - ClassDB::bind_method(D_METHOD("get_image", "width", "height", "depth", "invert", "in_3d_space", "normalize"), &Noise::get_image, DEFVAL(false), DEFVAL(false), DEFVAL(true)); - ClassDB::bind_method(D_METHOD("get_seamless_image", "width", "height", "depth", "invert", "in_3d_space", "skirt", "normalize"), &Noise::get_seamless_image, DEFVAL(false), DEFVAL(false), DEFVAL(0.1), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("get_image", "width", "height", "invert", "in_3d_space", "normalize"), &Noise::get_image, DEFVAL(false), DEFVAL(false), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("get_seamless_image", "width", "height", "invert", "in_3d_space", "skirt", "normalize"), &Noise::get_seamless_image, DEFVAL(false), DEFVAL(false), DEFVAL(0.1), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("get_image_3d", "width", "height", "depth", "invert", "normalize"), &Noise::get_image_3d, DEFVAL(false), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("get_seamless_image_3d", "width", "height", "depth", "invert", "skirt", "normalize"), &Noise::get_seamless_image_3d, DEFVAL(false), DEFVAL(0.1), DEFVAL(true)); } diff --git a/modules/noise/noise.h b/modules/noise/noise.h index 856bef8c31..6c49c12bc2 100644 --- a/modules/noise/noise.h +++ b/modules/noise/noise.h @@ -32,6 +32,7 @@ #define NOISE_H #include "core/io/image.h" +#include "core/variant/typed_array.h" class Noise : public Resource { GDCLASS(Noise, Resource); @@ -81,7 +82,7 @@ class Noise : public Resource { }; template <typename T> - Ref<Image> _generate_seamless_image(Ref<Image> p_src, int p_width, int p_height, bool p_invert, real_t p_blend_skirt) const { + Vector<Ref<Image>> _generate_seamless_image(Vector<Ref<Image>> p_src, int p_width, int p_height, int p_depth, bool p_invert, real_t p_blend_skirt) const { /* To make a seamless image, we swap the quadrants so the edges are perfect matches. We initially get a 10% larger image so we have an overlap we can use to blend over the seams. @@ -101,7 +102,7 @@ class Noise : public Resource { on Source it's translated to corner of Q1/s3 unless the ALT_XY modulo moves it to Q4 */ - ERR_FAIL_COND_V(p_blend_skirt < 0, Ref<Image>()); + ERR_FAIL_COND_V(p_blend_skirt < 0, Vector<Ref<Image>>()); int skirt_width = MAX(1, p_width * p_blend_skirt); int skirt_height = MAX(1, p_height * p_blend_skirt); @@ -112,83 +113,139 @@ class Noise : public Resource { int skirt_edge_x = half_width + skirt_width; int skirt_edge_y = half_height + skirt_height; - Vector<uint8_t> dest; - dest.resize(p_width * p_height * Image::get_format_pixel_size(p_src->get_format())); - - img_buff<T> rd_src = { - (T *)p_src->get_data().ptr(), - src_width, src_height, - half_width, half_height, - p_width, p_height - }; - - // `wr` is setup for straight x/y coordinate array access. - img_buff<T> wr = { - (T *)dest.ptrw(), - p_width, p_height, - 0, 0, 0, 0 - }; - // `rd_dest` is a readable pointer to `wr`, i.e. what has already been written to the output buffer. - img_buff<T> rd_dest = { - (T *)dest.ptr(), - p_width, p_height, - 0, 0, 0, 0 - }; + Image::Format format = p_src[0]->get_format(); + int pixel_size = Image::get_format_pixel_size(format); + + Vector<Ref<Image>> images; + images.resize(p_src.size()); + + // First blend across x and y for all slices. + for (int d = 0; d < images.size(); d++) { + Vector<uint8_t> dest; + dest.resize(p_width * p_height * pixel_size); + + img_buff<T> rd_src = { + (T *)p_src[d]->get_data().ptr(), + src_width, src_height, + half_width, half_height, + p_width, p_height + }; + + // `wr` is setup for straight x/y coordinate array access. + img_buff<T> wr = { + (T *)dest.ptrw(), + p_width, p_height, + 0, 0, 0, 0 + }; + // `rd_dest` is a readable pointer to `wr`, i.e. what has already been written to the output buffer. + img_buff<T> rd_dest = { + (T *)dest.ptr(), + p_width, p_height, + 0, 0, 0, 0 + }; + + // Swap the quadrants to make edges seamless. + for (int y = 0; y < p_height; y++) { + for (int x = 0; x < p_width; x++) { + // rd_src has a half offset and the shorter modulo ignores the skirt. + // It reads and writes in Q1-4 order (see map above), skipping the skirt. + wr(x, y) = rd_src(x, y, img_buff<T>::ALT_XY); + } + } - // Swap the quadrants to make edges seamless. - for (int y = 0; y < p_height; y++) { - for (int x = 0; x < p_width; x++) { - // rd_src has a half offset and the shorter modulo ignores the skirt. - // It reads and writes in Q1-4 order (see map above), skipping the skirt. - wr(x, y) = rd_src(x, y, img_buff<T>::ALT_XY); + // Blend the vertical skirt over the middle seam. + for (int x = half_width; x < skirt_edge_x; x++) { + int alpha = 255 * (1 - Math::smoothstep(0.1f, 0.9f, float(x - half_width) / float(skirt_width))); + for (int y = 0; y < p_height; y++) { + // Skip the center square + if (y == half_height) { + y = skirt_edge_y - 1; + } else { + // Starts reading at s2, ALT_Y skips s3, and continues with s1. + wr(x, y) = _alpha_blend<T>(rd_dest(x, y), rd_src(x, y, img_buff<T>::ALT_Y), alpha); + } + } } - } - // Blend the vertical skirt over the middle seam. - for (int x = half_width; x < skirt_edge_x; x++) { - int alpha = 255 * (1 - Math::smoothstep(0.1f, 0.9f, float(x - half_width) / float(skirt_width))); - for (int y = 0; y < p_height; y++) { - // Skip the center square - if (y == half_height) { - y = skirt_edge_y - 1; - } else { - // Starts reading at s2, ALT_Y skips s3, and continues with s1. - wr(x, y) = _alpha_blend<T>(rd_dest(x, y), rd_src(x, y, img_buff<T>::ALT_Y), alpha); + // Blend the horizontal skirt over the middle seam. + for (int y = half_height; y < skirt_edge_y; y++) { + int alpha = 255 * (1 - Math::smoothstep(0.1f, 0.9f, float(y - half_height) / float(skirt_height))); + for (int x = 0; x < p_width; x++) { + // Skip the center square + if (x == half_width) { + x = skirt_edge_x - 1; + } else { + // Starts reading at s4, skips s3, continues with s5. + wr(x, y) = _alpha_blend<T>(rd_dest(x, y), rd_src(x, y, img_buff<T>::ALT_X), alpha); + } } } - } - // Blend the horizontal skirt over the middle seam. - for (int y = half_height; y < skirt_edge_y; y++) { - int alpha = 255 * (1 - Math::smoothstep(0.1f, 0.9f, float(y - half_height) / float(skirt_height))); - for (int x = 0; x < p_width; x++) { - // Skip the center square - if (x == half_width) { - x = skirt_edge_x - 1; - } else { - // Starts reading at s4, skips s3, continues with s5. - wr(x, y) = _alpha_blend<T>(rd_dest(x, y), rd_src(x, y, img_buff<T>::ALT_X), alpha); + // Fill in the center square. Wr starts at the top left of Q4, which is the equivalent of the top left of s3, unless a modulo is used. + for (int y = half_height; y < skirt_edge_y; y++) { + for (int x = half_width; x < skirt_edge_x; x++) { + int xpos = 255 * (1 - Math::smoothstep(0.1f, 0.9f, float(x - half_width) / float(skirt_width))); + int ypos = 255 * (1 - Math::smoothstep(0.1f, 0.9f, float(y - half_height) / float(skirt_height))); + + // Blend s3(Q1) onto s5(Q2) for the top half. + T top_blend = _alpha_blend<T>(rd_src(x, y, img_buff<T>::ALT_X), rd_src(x, y, img_buff<T>::DEFAULT), xpos); + // Blend s1(Q3) onto Q4 for the bottom half. + T bottom_blend = _alpha_blend<T>(rd_src(x, y, img_buff<T>::ALT_XY), rd_src(x, y, img_buff<T>::ALT_Y), xpos); + // Blend the top half onto the bottom half. + wr(x, y) = _alpha_blend<T>(bottom_blend, top_blend, ypos); } } + Ref<Image> image = memnew(Image(p_width, p_height, false, format, dest)); + p_src.write[d].unref(); + images.write[d] = image; } - // Fill in the center square. Wr starts at the top left of Q4, which is the equivalent of the top left of s3, unless a modulo is used. - for (int y = half_height; y < skirt_edge_y; y++) { - for (int x = half_width; x < skirt_edge_x; x++) { - int xpos = 255 * (1 - Math::smoothstep(0.1f, 0.9f, float(x - half_width) / float(skirt_width))); - int ypos = 255 * (1 - Math::smoothstep(0.1f, 0.9f, float(y - half_height) / float(skirt_height))); - - // Blend s3(Q1) onto s5(Q2) for the top half. - T top_blend = _alpha_blend<T>(rd_src(x, y, img_buff<T>::ALT_X), rd_src(x, y, img_buff<T>::DEFAULT), xpos); - // Blend s1(Q3) onto Q4 for the bottom half. - T bottom_blend = _alpha_blend<T>(rd_src(x, y, img_buff<T>::ALT_XY), rd_src(x, y, img_buff<T>::ALT_Y), xpos); - // Blend the top half onto the bottom half. - wr(x, y) = _alpha_blend<T>(bottom_blend, top_blend, ypos); + // Now blend across z. + if (p_depth > 1) { + int skirt_depth = MAX(1, p_depth * p_blend_skirt); + int half_depth = p_depth * 0.5; + int skirt_edge_z = half_depth + skirt_depth; + + // Swap halves on depth. + for (int i = 0; i < half_depth; i++) { + Ref<Image> img = images[i]; + images.write[i] = images[i + half_depth]; + images.write[i + half_depth] = img; } + + Vector<Ref<Image>> new_images = images; + new_images.resize(p_depth); + + // Scale seamless generation to third dimension. + for (int z = half_depth; z < skirt_edge_z; z++) { + int alpha = 255 * (1 - Math::smoothstep(0.1f, 0.9f, float(z - half_depth) / float(skirt_depth))); + + Vector<uint8_t> img = images[z % p_depth]->get_data(); + Vector<uint8_t> skirt = images[(z - half_depth) + p_depth]->get_data(); + + Vector<uint8_t> dest; + dest.resize(images[0]->get_width() * images[0]->get_height() * Image::get_format_pixel_size(images[0]->get_format())); + + for (int i = 0; i < img.size(); i++) { + uint8_t fg, bg, out; + + fg = skirt[i]; + bg = img[i]; + + uint16_t a = alpha + 1; + uint16_t inv_a = 256 - alpha; + + out = (uint8_t)((a * fg + inv_a * bg) >> 8); + + dest.write[i] = out; + } + + Ref<Image> new_image = memnew(Image(images[0]->get_width(), images[0]->get_height(), false, images[0]->get_format(), dest)); + new_images.write[z % p_depth] = new_image; + } + return new_images; } - Ref<Image> image = memnew(Image(p_width, p_height, false, p_src->get_format(), dest)); - p_src.unref(); - return image; + return images; } template <typename T> @@ -233,8 +290,13 @@ public: virtual real_t get_noise_3dv(Vector3 p_v) const = 0; virtual real_t get_noise_3d(real_t p_x, real_t p_y, real_t p_z) const = 0; - virtual Ref<Image> get_image(int p_width, int p_height, int p_depth, bool p_invert = false, bool p_in_3d_space = false, bool p_normalize = true) const; - virtual Ref<Image> get_seamless_image(int p_width, int p_height, int p_depth, bool p_invert = false, bool p_in_3d_space = false, real_t p_blend_skirt = 0.1, bool p_normalize = true) const; + Vector<Ref<Image>> _get_image(int p_width, int p_height, int p_depth, bool p_invert = false, bool p_in_3d_space = false, bool p_normalize = true) const; + virtual Ref<Image> get_image(int p_width, int p_height, bool p_invert = false, bool p_in_3d_space = false, bool p_normalize = true) const; + virtual TypedArray<Image> get_image_3d(int p_width, int p_height, int p_depth, bool p_invert = false, bool p_normalize = true) const; + + Vector<Ref<Image>> _get_seamless_image(int p_width, int p_height, int p_depth, bool p_invert = false, bool p_in_3d_space = false, real_t p_blend_skirt = 0.1, bool p_normalize = true) const; + virtual Ref<Image> get_seamless_image(int p_width, int p_height, bool p_invert = false, bool p_in_3d_space = false, real_t p_blend_skirt = 0.1, bool p_normalize = true) const; + virtual TypedArray<Image> get_seamless_image_3d(int p_width, int p_height, int p_depth, bool p_invert = false, real_t p_blend_skirt = 0.1, bool p_normalize = true) const; }; #endif // NOISE_H diff --git a/modules/noise/noise_texture_2d.cpp b/modules/noise/noise_texture_2d.cpp index a31f77a38d..e4b2e0b4ac 100644 --- a/modules/noise/noise_texture_2d.cpp +++ b/modules/noise/noise_texture_2d.cpp @@ -162,9 +162,9 @@ Ref<Image> NoiseTexture2D::_generate_texture() { Ref<Image> new_image; if (seamless) { - new_image = ref_noise->get_seamless_image(size.x, size.y, 0, invert, in_3d_space, seamless_blend_skirt, normalize); + new_image = ref_noise->get_seamless_image(size.x, size.y, invert, in_3d_space, seamless_blend_skirt, normalize); } else { - new_image = ref_noise->get_image(size.x, size.y, 0, invert, in_3d_space, normalize); + new_image = ref_noise->get_image(size.x, size.y, invert, in_3d_space, normalize); } if (color_ramp.is_valid()) { new_image = _modulate_with_gradient(new_image, color_ramp); diff --git a/modules/noise/noise_texture_3d.cpp b/modules/noise/noise_texture_3d.cpp index 58403397de..25d75b8ffb 100644 --- a/modules/noise/noise_texture_3d.cpp +++ b/modules/noise/noise_texture_3d.cpp @@ -44,7 +44,9 @@ NoiseTexture3D::~NoiseTexture3D() { if (texture.is_valid()) { RS::get_singleton()->free(texture); } - noise_thread.wait_to_finish(); + if (noise_thread.is_started()) { + noise_thread.wait_to_finish(); + } } void NoiseTexture3D::_bind_methods() { @@ -147,18 +149,9 @@ TypedArray<Image> NoiseTexture3D::_generate_texture() { Vector<Ref<Image>> images; if (seamless) { - images = _get_seamless(width, height, depth, invert, seamless_blend_skirt); + images = ref_noise->_get_seamless_image(width, height, depth, invert, true, seamless_blend_skirt, normalize); } else { - images.resize(depth); - - for (int i = 0; i < images.size(); i++) { - images.write[i] = ref_noise->get_image(width, height, i, invert, true, false); - } - } - - // Normalize on whole texture at once rather than on each image individually as it would result in visible artifacts on z (depth) axis. - if (normalize) { - images = _normalize(images); + images = ref_noise->_get_image(width, height, depth, invert, true, normalize); } if (color_ramp.is_valid()) { @@ -177,116 +170,6 @@ TypedArray<Image> NoiseTexture3D::_generate_texture() { return new_data; } -Vector<Ref<Image>> NoiseTexture3D::_get_seamless(int p_width, int p_height, int p_depth, bool p_invert, real_t p_blend_skirt) { - // Prevent memdelete due to unref() on other thread. - Ref<Noise> ref_noise = noise; - - if (ref_noise.is_null()) { - return Vector<Ref<Image>>(); - } - - int skirt_depth = MAX(1, p_depth * p_blend_skirt); - int src_depth = p_depth + skirt_depth; - - Vector<Ref<Image>> images; - images.resize(src_depth); - - for (int i = 0; i < src_depth; i++) { - images.write[i] = ref_noise->get_seamless_image(p_width, p_height, i, p_invert, true, p_blend_skirt, false); - } - - int half_depth = p_depth * 0.5; - int skirt_edge_z = half_depth + skirt_depth; - - // swap halves on depth. - for (int i = 0; i < half_depth; i++) { - Ref<Image> img = images[i]; - images.write[i] = images[i + half_depth]; - images.write[i + half_depth] = img; - } - - Vector<Ref<Image>> new_images = images; - new_images.resize(p_depth); - - // scale seamless generation to third dimension. - for (int z = half_depth; z < skirt_edge_z; z++) { - int alpha = 255 * (1 - Math::smoothstep(0.1f, 0.9f, float(z - half_depth) / float(skirt_depth))); - - Vector<uint8_t> img = images[z % p_depth]->get_data(); - Vector<uint8_t> skirt = images[(z - half_depth) + p_depth]->get_data(); - - Vector<uint8_t> dest; - dest.resize(images[0]->get_width() * images[0]->get_height() * Image::get_format_pixel_size(images[0]->get_format())); - - for (int i = 0; i < img.size(); i++) { - uint8_t fg, bg, out; - - fg = skirt[i]; - bg = img[i]; - - uint16_t a; - uint16_t inv_a; - - a = alpha + 1; - inv_a = 256 - alpha; - - out = (uint8_t)((a * fg + inv_a * bg) >> 8); - - dest.write[i] = out; - } - - Ref<Image> new_image = memnew(Image(images[0]->get_width(), images[0]->get_height(), false, images[0]->get_format(), dest)); - new_images.write[z % p_depth] = new_image; - } - - return new_images; -} - -Vector<Ref<Image>> NoiseTexture3D::_normalize(Vector<Ref<Image>> p_images) { - real_t min_val = FLT_MAX; - real_t max_val = -FLT_MAX; - - int w = p_images[0]->get_width(); - int h = p_images[0]->get_height(); - - for (int i = 0; i < p_images.size(); i++) { - Vector<uint8_t> data = p_images[i]->get_data(); - - for (int j = 0; j < data.size(); j++) { - if (data[j] > max_val) { - max_val = data[j]; - } - if (data[j] < min_val) { - min_val = data[j]; - } - } - } - - Vector<Ref<Image>> new_images; - new_images.resize(p_images.size()); - - for (int i = 0; i < p_images.size(); i++) { - Vector<uint8_t> data = p_images[i]->get_data(); - - for (int j = 0; j < data.size(); j++) { - uint8_t value; - - if (max_val == min_val) { - value = 0; - } else { - value = static_cast<uint8_t>(CLAMP((data[j] - min_val) / (max_val - min_val) * 255.f, 0, 255)); - } - - data.write[j] = value; - } - - Ref<Image> new_image = memnew(Image(w, h, false, Image::FORMAT_L8, data)); - new_images.write[i] = new_image; - } - - return new_images; -} - Ref<Image> NoiseTexture3D::_modulate_with_gradient(Ref<Image> p_image, Ref<Gradient> p_gradient) { int w = p_image->get_width(); int h = p_image->get_height(); diff --git a/modules/noise/noise_texture_3d.h b/modules/noise/noise_texture_3d.h index b5dab10321..397711ca98 100644 --- a/modules/noise/noise_texture_3d.h +++ b/modules/noise/noise_texture_3d.h @@ -68,8 +68,6 @@ private: void _update_texture(); void _set_texture_data(const TypedArray<Image> &p_data); - Vector<Ref<Image>> _get_seamless(int p_width, int p_height, int p_depth, bool p_invert, real_t p_blend_skirt); - Vector<Ref<Image>> _normalize(Vector<Ref<Image>> p_images); Ref<Image> _modulate_with_gradient(Ref<Image> p_image, Ref<Gradient> p_gradient); protected: diff --git a/modules/noise/tests/test_fastnoise_lite.h b/modules/noise/tests/test_fastnoise_lite.h index db489c6672..0a435c6a5c 100644 --- a/modules/noise/tests/test_fastnoise_lite.h +++ b/modules/noise/tests/test_fastnoise_lite.h @@ -605,7 +605,7 @@ TEST_CASE("[FastNoiseLite] Generating seamless 2D images (11x11px) and compare t noise.set_cellular_jitter(0.0); SUBCASE("Blend skirt 0.0") { - Ref<Image> img = noise.get_seamless_image(11, 11, 0, false, false, 0.0); + Ref<Image> img = noise.get_seamless_image(11, 11, false, false, 0.0); Ref<Image> ref_img_1 = memnew(Image); ref_img_1->set_data(11, 11, false, Image::FORMAT_L8, ref_img_1_data); @@ -614,7 +614,7 @@ TEST_CASE("[FastNoiseLite] Generating seamless 2D images (11x11px) and compare t } SUBCASE("Blend skirt 0.1") { - Ref<Image> img = noise.get_seamless_image(11, 11, 0, false, false, 0.1); + Ref<Image> img = noise.get_seamless_image(11, 11, false, false, 0.1); Ref<Image> ref_img_2 = memnew(Image); ref_img_2->set_data(11, 11, false, Image::FORMAT_L8, ref_img_2_data); @@ -623,7 +623,7 @@ TEST_CASE("[FastNoiseLite] Generating seamless 2D images (11x11px) and compare t } SUBCASE("Blend skirt 1.0") { - Ref<Image> img = noise.get_seamless_image(11, 11, 0, false, false, 0.1); + Ref<Image> img = noise.get_seamless_image(11, 11, false, false, 0.1); Ref<Image> ref_img_3 = memnew(Image); ref_img_3->set_data(11, 11, false, Image::FORMAT_L8, ref_img_3_data); diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index 71339c9443..18091649e3 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -446,39 +446,29 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *en Object *obj = ObjectDB::get_instance(ObjectID(ID)); ERR_FAIL_NULL(obj); - int res = env->PushLocalFrame(16); - ERR_FAIL_COND(res != 0); - String str_method = jstring_to_string(method, env); int count = env->GetArrayLength(params); + Variant *vlist = (Variant *)alloca(sizeof(Variant) * count); - Variant **vptr = (Variant **)alloca(sizeof(Variant *) * count); + const Variant **vptr = (const Variant **)alloca(sizeof(Variant *) * count); + for (int i = 0; i < count; i++) { jobject jobj = env->GetObjectArrayElement(params, i); - Variant v; - if (jobj) { - v = _jobject_to_variant(env, jobj); - } - memnew_placement(&vlist[i], Variant); - vlist[i] = v; + ERR_FAIL_NULL(jobj); + memnew_placement(&vlist[i], Variant(_jobject_to_variant(env, jobj))); vptr[i] = &vlist[i]; env->DeleteLocalRef(jobj); } Callable::CallError err; - obj->callp(str_method, (const Variant **)vptr, count, err); - - env->PopLocalFrame(nullptr); + obj->callp(str_method, vptr, count, err); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *env, jclass clazz, jlong ID, jstring method, jobjectArray params) { Object *obj = ObjectDB::get_instance(ObjectID(ID)); ERR_FAIL_NULL(obj); - int res = env->PushLocalFrame(16); - ERR_FAIL_COND(res != 0); - String str_method = jstring_to_string(method, env); int count = env->GetArrayLength(params); @@ -488,16 +478,13 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv * for (int i = 0; i < count; i++) { jobject jobj = env->GetObjectArrayElement(params, i); - if (jobj) { - args[i] = _jobject_to_variant(env, jobj); - } - env->DeleteLocalRef(jobj); + ERR_FAIL_NULL(jobj); + memnew_placement(&args[i], Variant(_jobject_to_variant(env, jobj))); argptrs[i] = &args[i]; + env->DeleteLocalRef(jobj); } - MessageQueue::get_singleton()->push_callp(obj, str_method, (const Variant **)argptrs, count); - - env->PopLocalFrame(nullptr); + MessageQueue::get_singleton()->push_callp(obj, str_method, argptrs, count); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jclass clazz, jstring p_permission, jboolean p_result) { diff --git a/platform/android/plugin/godot_plugin_jni.cpp b/platform/android/plugin/godot_plugin_jni.cpp index 4bb90cb971..843c015d49 100644 --- a/platform/android/plugin/godot_plugin_jni.cpp +++ b/platform/android/plugin/godot_plugin_jni.cpp @@ -120,7 +120,8 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeEmitS for (int i = 0; i < count; i++) { jobject j_param = env->GetObjectArrayElement(j_signal_params, i); - variant_params[i] = _jobject_to_variant(env, j_param); + ERR_FAIL_NULL(j_param); + memnew_placement(&variant_params[i], Variant(_jobject_to_variant(env, j_param))); args[i] = &variant_params[i]; env->DeleteLocalRef(j_param); } diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index c678b4bf02..1b59351b30 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -907,9 +907,7 @@ void RigidBody2D::_notification(int p_what) { } break; case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { - if (Engine::get_singleton()->is_editor_hint()) { - update_configuration_warnings(); - } + update_configuration_warnings(); } break; } #endif diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp index 19d1b83cab..6d8d60dcaa 100644 --- a/scene/3d/collision_object_3d.cpp +++ b/scene/3d/collision_object_3d.cpp @@ -83,13 +83,9 @@ void CollisionObject3D::_notification(int p_what) { _update_pickable(); } break; -#ifdef TOOLS_ENABLED case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { - if (Engine::get_singleton()->is_editor_hint()) { - update_configuration_warnings(); - } + update_configuration_warnings(); } break; -#endif case NOTIFICATION_TRANSFORM_CHANGED: { if (only_update_transform_changes) { diff --git a/scene/3d/collision_polygon_3d.cpp b/scene/3d/collision_polygon_3d.cpp index 53a61c1368..9a2ed00274 100644 --- a/scene/3d/collision_polygon_3d.cpp +++ b/scene/3d/collision_polygon_3d.cpp @@ -104,11 +104,7 @@ void CollisionPolygon3D::_notification(int p_what) { if (parent) { _update_in_shape_owner(true); } -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { - update_configuration_warnings(); - } -#endif + update_configuration_warnings(); } break; case NOTIFICATION_UNPARENTED: { diff --git a/scene/3d/collision_shape_3d.cpp b/scene/3d/collision_shape_3d.cpp index f1d918ad9b..b7f3b12c25 100644 --- a/scene/3d/collision_shape_3d.cpp +++ b/scene/3d/collision_shape_3d.cpp @@ -99,11 +99,7 @@ void CollisionShape3D::_notification(int p_what) { if (parent) { _update_in_shape_owner(true); } -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { - update_configuration_warnings(); - } -#endif + update_configuration_warnings(); } break; case NOTIFICATION_UNPARENTED: { diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp index 16c82bf6d2..18198b566e 100644 --- a/scene/3d/light_3d.cpp +++ b/scene/3d/light_3d.cpp @@ -284,6 +284,9 @@ void Light3D::_update_visibility() { void Light3D::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_TRANSFORM_CHANGED: { + update_configuration_warnings(); + } break; case NOTIFICATION_VISIBILITY_CHANGED: case NOTIFICATION_ENTER_TREE: { _update_visibility(); diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index b7d63258db..4be695d189 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -599,9 +599,7 @@ void RigidBody3D::_notification(int p_what) { } break; case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { - if (Engine::get_singleton()->is_editor_hint()) { - update_configuration_warnings(); - } + update_configuration_warnings(); } break; } #endif diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index 818e19ab29..1b148defc1 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -564,12 +564,12 @@ void TabContainer::add_child_notify(Node *p_child) { } void TabContainer::move_child_notify(Node *p_child) { + Container::move_child_notify(p_child); + if (p_child == tab_bar) { return; } - Container::move_child_notify(p_child); - Control *c = Object::cast_to<Control>(p_child); if (c && !c->is_set_as_top_level()) { int old_idx = -1; @@ -588,12 +588,12 @@ void TabContainer::move_child_notify(Node *p_child) { } void TabContainer::remove_child_notify(Node *p_child) { + Container::remove_child_notify(p_child); + if (p_child == tab_bar) { return; } - Container::remove_child_notify(p_child); - Control *c = Object::cast_to<Control>(p_child); if (!c || c->is_set_as_top_level()) { return; diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index ccb3ddee45..5ef3e09e3d 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -985,9 +985,21 @@ void SurfaceTool::create_from_blend_shape(const Ref<Mesh> &p_existing, int p_sur } ERR_FAIL_COND(shape_idx == -1); ERR_FAIL_COND(shape_idx >= arr.size()); - Array mesh = arr[shape_idx]; - ERR_FAIL_COND(mesh.size() != RS::ARRAY_MAX); - _create_list_from_arrays(arr[shape_idx], &vertex_array, &index_array, format); + Array blendshape_mesh_arrays = arr[shape_idx]; + ERR_FAIL_COND(blendshape_mesh_arrays.size() != RS::ARRAY_MAX); + + Array source_mesh_arrays = p_existing->surface_get_arrays(p_surface); + ERR_FAIL_COND(source_mesh_arrays.size() != RS::ARRAY_MAX); + + // Copy BlendShape vertex data over while keeping e.g. bones, weights, index from existing mesh intact. + source_mesh_arrays[RS::ARRAY_VERTEX] = blendshape_mesh_arrays[RS::ARRAY_VERTEX]; + source_mesh_arrays[RS::ARRAY_NORMAL] = blendshape_mesh_arrays[RS::ARRAY_NORMAL]; + source_mesh_arrays[RS::ARRAY_TANGENT] = blendshape_mesh_arrays[RS::ARRAY_TANGENT]; + + _create_list_from_arrays(source_mesh_arrays, &vertex_array, &index_array, format); + + material = p_existing->surface_get_material(p_surface); + format = p_existing->surface_get_format(p_surface); for (int j = 0; j < RS::ARRAY_CUSTOM_COUNT; j++) { if (format & custom_mask[j]) { diff --git a/servers/rendering/renderer_rd/environment/fog.cpp b/servers/rendering/renderer_rd/environment/fog.cpp index 57da55db4d..78b785153f 100644 --- a/servers/rendering/renderer_rd/environment/fog.cpp +++ b/servers/rendering/renderer_rd/environment/fog.cpp @@ -388,6 +388,37 @@ Fog::FogShaderData::~FogShaderData() { //////////////////////////////////////////////////////////////////////////////// // Volumetric Fog +bool Fog::VolumetricFog::sync_gi_dependent_sets_validity(bool p_ensure_freed) { + bool null = gi_dependent_sets.copy_uniform_set.is_null(); + bool valid = !null && RD::get_singleton()->uniform_set_is_valid(gi_dependent_sets.copy_uniform_set); + +#ifdef DEV_ENABLED + // It's all-or-nothing, or something else has changed that requires dev attention. + DEV_ASSERT(null == gi_dependent_sets.process_uniform_set_density.is_null()); + DEV_ASSERT(null == gi_dependent_sets.process_uniform_set.is_null()); + DEV_ASSERT(null == gi_dependent_sets.process_uniform_set2.is_null()); + DEV_ASSERT(valid == RD::get_singleton()->uniform_set_is_valid(gi_dependent_sets.process_uniform_set_density)); + DEV_ASSERT(valid == RD::get_singleton()->uniform_set_is_valid(gi_dependent_sets.process_uniform_set)); + DEV_ASSERT(valid == RD::get_singleton()->uniform_set_is_valid(gi_dependent_sets.process_uniform_set2)); +#endif + + if (valid) { + if (p_ensure_freed) { + RD::get_singleton()->free(gi_dependent_sets.copy_uniform_set); + RD::get_singleton()->free(gi_dependent_sets.process_uniform_set_density); + RD::get_singleton()->free(gi_dependent_sets.process_uniform_set); + RD::get_singleton()->free(gi_dependent_sets.process_uniform_set2); + valid = false; + } + } + + if (!valid && !null) { + gi_dependent_sets = {}; + } + + return valid; +} + void Fog::VolumetricFog::init(const Vector3i &fog_size, RID p_sky_shader) { width = fog_size.x; height = fog_size.y; @@ -464,17 +495,7 @@ Fog::VolumetricFog::~VolumetricFog() { RD::get_singleton()->free(fog_uniform_set); } - // At this point, due to cascade deletions, the sets may no longer be valid, but still they must work as a group. - gi_dependent_sets.valid = RD::get_singleton()->uniform_set_is_valid(gi_dependent_sets.process_uniform_set_density); -#ifdef DEV_ENABLED - gi_dependent_sets.assert_actual_validity(); -#endif - if (gi_dependent_sets.valid) { - RD::get_singleton()->free(gi_dependent_sets.copy_uniform_set); - RD::get_singleton()->free(gi_dependent_sets.process_uniform_set_density); - RD::get_singleton()->free(gi_dependent_sets.process_uniform_set); - RD::get_singleton()->free(gi_dependent_sets.process_uniform_set2); - } + sync_gi_dependent_sets_validity(true); if (sdfgi_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(sdfgi_uniform_set)) { RD::get_singleton()->free(sdfgi_uniform_set); @@ -717,10 +738,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P RD::get_singleton()->compute_list_end(); } -#ifdef DEV_ENABLED - fog->gi_dependent_sets.assert_actual_validity(); -#endif - if (!fog->gi_dependent_sets.valid) { + if (!fog->sync_gi_dependent_sets_validity()) { //re create uniform set if needed Vector<RD::Uniform> uniforms; Vector<RD::Uniform> copy_uniforms; @@ -932,8 +950,6 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P uniforms.remove_at(8); uniforms.write[7].set_id(0, aux7); fog->gi_dependent_sets.process_uniform_set_density = RD::get_singleton()->uniform_set_create(uniforms, volumetric_fog.process_shader.version_get_shader(volumetric_fog.process_shader_version, VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_DENSITY), 0); - - fog->gi_dependent_sets.valid = true; } bool using_sdfgi = RendererSceneRenderRD::get_singleton()->environment_get_volumetric_fog_gi_inject(p_settings.env) > 0.0001 && RendererSceneRenderRD::get_singleton()->environment_get_sdfgi_enabled(p_settings.env) && (p_settings.sdfgi.is_valid()); diff --git a/servers/rendering/renderer_rd/environment/fog.h b/servers/rendering/renderer_rd/environment/fog.h index 926da4026c..277389c596 100644 --- a/servers/rendering/renderer_rd/environment/fog.h +++ b/servers/rendering/renderer_rd/environment/fog.h @@ -303,21 +303,10 @@ public: RID fog_uniform_set; struct { - bool valid = false; RID copy_uniform_set; RID process_uniform_set_density; RID process_uniform_set; RID process_uniform_set2; - -#ifdef DEV_ENABLED - void assert_actual_validity() { - // It's all-or-nothing, or something else has changed that requires dev attention. - DEV_ASSERT(valid == RD::get_singleton()->uniform_set_is_valid(copy_uniform_set)); - DEV_ASSERT(valid == RD::get_singleton()->uniform_set_is_valid(process_uniform_set_density)); - DEV_ASSERT(valid == RD::get_singleton()->uniform_set_is_valid(process_uniform_set)); - DEV_ASSERT(valid == RD::get_singleton()->uniform_set_is_valid(process_uniform_set2)); - } -#endif } gi_dependent_sets; RID sdfgi_uniform_set; @@ -328,6 +317,8 @@ public: virtual void configure(RenderSceneBuffersRD *p_render_buffers) override{}; virtual void free_data() override{}; + bool sync_gi_dependent_sets_validity(bool p_ensure_freed = false); + void init(const Vector3i &fog_size, RID p_sky_shader); ~VolumetricFog(); }; diff --git a/servers/rendering/renderer_rd/environment/gi.cpp b/servers/rendering/renderer_rd/environment/gi.cpp index 52f09e1ccb..c2a018c7c6 100644 --- a/servers/rendering/renderer_rd/environment/gi.cpp +++ b/servers/rendering/renderer_rd/environment/gi.cpp @@ -3695,20 +3695,6 @@ void GI::setup_voxel_gi_instances(RenderDataRD *p_render_data, Ref<RenderSceneBu } rbgi->uniform_set[v] = RID(); } - if (p_render_buffers->has_custom_data(RB_SCOPE_FOG)) { - Ref<Fog::VolumetricFog> fog = p_render_buffers->get_custom_data(RB_SCOPE_FOG); - -#ifdef DEV_ENABLED - fog->gi_dependent_sets.assert_actual_validity(); -#endif - if (fog->gi_dependent_sets.valid) { - RD::get_singleton()->free(fog->gi_dependent_sets.copy_uniform_set); - RD::get_singleton()->free(fog->gi_dependent_sets.process_uniform_set_density); - RD::get_singleton()->free(fog->gi_dependent_sets.process_uniform_set); - RD::get_singleton()->free(fog->gi_dependent_sets.process_uniform_set2); - fog->gi_dependent_sets.valid = false; - } - } } if (p_voxel_gi_instances.size() > 0) { diff --git a/servers/rendering/renderer_rd/shaders/effects/cubemap_downsampler_raster.glsl b/servers/rendering/renderer_rd/shaders/effects/cubemap_downsampler_raster.glsl index 0828ffd921..b8c64d09f4 100644 --- a/servers/rendering/renderer_rd/shaders/effects/cubemap_downsampler_raster.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/cubemap_downsampler_raster.glsl @@ -55,109 +55,107 @@ void main() { // Converted from compute shader which uses absolute coordinates. // Could possibly simplify this float face_size = float(params.face_size); + float inv_face_size = 1.0 / face_size; + vec2 id = floor(uv_interp); - if (uv_interp.x < face_size && uv_interp.y < face_size) { - float inv_face_size = 1.0 / face_size; - - float u0 = (uv_interp.x * 2.0 + 1.0 - 0.75) * inv_face_size - 1.0; - float u1 = (uv_interp.x * 2.0 + 1.0 + 0.75) * inv_face_size - 1.0; - - float v0 = (uv_interp.y * 2.0 + 1.0 - 0.75) * -inv_face_size + 1.0; - float v1 = (uv_interp.y * 2.0 + 1.0 + 0.75) * -inv_face_size + 1.0; - - float weights[4]; - weights[0] = calcWeight(u0, v0); - weights[1] = calcWeight(u1, v0); - weights[2] = calcWeight(u0, v1); - weights[3] = calcWeight(u1, v1); - - const float wsum = 0.5 / (weights[0] + weights[1] + weights[2] + weights[3]); - for (int i = 0; i < 4; i++) { - weights[i] = weights[i] * wsum + .125; - } - - vec3 dir; - vec4 color; - switch (params.face_id) { - case 0: - get_dir_0(dir, u0, v0); - color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0]; - - get_dir_0(dir, u1, v0); - color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1]; - - get_dir_0(dir, u0, v1); - color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2]; - - get_dir_0(dir, u1, v1); - color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3]; - break; - case 1: - get_dir_1(dir, u0, v0); - color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0]; - - get_dir_1(dir, u1, v0); - color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1]; - - get_dir_1(dir, u0, v1); - color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2]; - - get_dir_1(dir, u1, v1); - color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3]; - break; - case 2: - get_dir_2(dir, u0, v0); - color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0]; - - get_dir_2(dir, u1, v0); - color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1]; - - get_dir_2(dir, u0, v1); - color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2]; - - get_dir_2(dir, u1, v1); - color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3]; - break; - case 3: - get_dir_3(dir, u0, v0); - color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0]; - - get_dir_3(dir, u1, v0); - color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1]; - - get_dir_3(dir, u0, v1); - color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2]; - - get_dir_3(dir, u1, v1); - color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3]; - break; - case 4: - get_dir_4(dir, u0, v0); - color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0]; - - get_dir_4(dir, u1, v0); - color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1]; - - get_dir_4(dir, u0, v1); - color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2]; - - get_dir_4(dir, u1, v1); - color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3]; - break; - default: - get_dir_5(dir, u0, v0); - color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0]; - - get_dir_5(dir, u1, v0); - color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1]; - - get_dir_5(dir, u0, v1); - color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2]; - - get_dir_5(dir, u1, v1); - color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3]; - break; - } - frag_color = color; + float u1 = (id.x * 2.0 + 1.0 + 0.75) * inv_face_size - 1.0; + float u0 = (id.x * 2.0 + 1.0 - 0.75) * inv_face_size - 1.0; + + float v0 = (id.y * 2.0 + 1.0 - 0.75) * -inv_face_size + 1.0; + float v1 = (id.y * 2.0 + 1.0 + 0.75) * -inv_face_size + 1.0; + + float weights[4]; + weights[0] = calcWeight(u0, v0); + weights[1] = calcWeight(u1, v0); + weights[2] = calcWeight(u0, v1); + weights[3] = calcWeight(u1, v1); + + const float wsum = 0.5 / (weights[0] + weights[1] + weights[2] + weights[3]); + for (int i = 0; i < 4; i++) { + weights[i] = weights[i] * wsum + .125; + } + + vec3 dir; + vec4 color; + switch (params.face_id) { + case 0: + get_dir_0(dir, u0, v0); + color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0]; + + get_dir_0(dir, u1, v0); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1]; + + get_dir_0(dir, u0, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2]; + + get_dir_0(dir, u1, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3]; + break; + case 1: + get_dir_1(dir, u0, v0); + color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0]; + + get_dir_1(dir, u1, v0); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1]; + + get_dir_1(dir, u0, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2]; + + get_dir_1(dir, u1, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3]; + break; + case 2: + get_dir_2(dir, u0, v0); + color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0]; + + get_dir_2(dir, u1, v0); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1]; + + get_dir_2(dir, u0, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2]; + + get_dir_2(dir, u1, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3]; + break; + case 3: + get_dir_3(dir, u0, v0); + color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0]; + + get_dir_3(dir, u1, v0); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1]; + + get_dir_3(dir, u0, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2]; + + get_dir_3(dir, u1, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3]; + break; + case 4: + get_dir_4(dir, u0, v0); + color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0]; + + get_dir_4(dir, u1, v0); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1]; + + get_dir_4(dir, u0, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2]; + + get_dir_4(dir, u1, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3]; + break; + default: + get_dir_5(dir, u0, v0); + color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0]; + + get_dir_5(dir, u1, v0); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1]; + + get_dir_5(dir, u0, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2]; + + get_dir_5(dir, u1, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3]; + break; } + frag_color = color; } diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.h b/servers/rendering/renderer_rd/storage_rd/light_storage.h index c36d1ef503..3360358169 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.h @@ -93,11 +93,11 @@ private: struct ShadowTransform { Projection camera; Transform3D transform; - float farplane; - float split; - float bias_scale; - float shadow_texel_size; - float range_begin; + float farplane = 0.0; + float split = 0.0; + float bias_scale = 0.0; + float shadow_texel_size = 0.0; + float range_begin = 0.0; Rect2 atlas_rect; Vector2 uv_scale; }; |
