summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/input/input_map.cpp1
-rw-r--r--core/templates/bin_sorted_array.h181
-rw-r--r--doc/classes/AnimationPlayer.xml3
-rw-r--r--doc/classes/Camera3D.xml11
-rw-r--r--doc/classes/Engine.xml1
-rw-r--r--doc/classes/GeometryInstance3D.xml26
-rw-r--r--doc/classes/Node3D.xml5
-rw-r--r--doc/classes/ProjectSettings.xml3
-rw-r--r--doc/classes/RenderingServer.xml54
-rw-r--r--editor/editor_file_dialog.cpp3
-rw-r--r--editor/editor_file_system.cpp19
-rw-r--r--editor/editor_node.cpp8
-rw-r--r--editor/editor_node.h2
-rw-r--r--editor/editor_paths.cpp104
-rw-r--r--editor/editor_paths.h28
-rw-r--r--editor/editor_settings.cpp125
-rw-r--r--editor/editor_settings.h1
-rw-r--r--editor/editor_themes.cpp33
-rw-r--r--editor/import/resource_importer_scene.cpp2
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp40
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h2
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp4
-rw-r--r--editor/plugins/node_3d_editor_plugin.h1
-rw-r--r--editor/plugins/theme_editor_plugin.cpp1473
-rw-r--r--editor/plugins/theme_editor_plugin.h115
-rw-r--r--editor/plugins/theme_editor_preview.cpp464
-rw-r--r--editor/plugins/theme_editor_preview.h118
-rw-r--r--editor/scene_tree_dock.cpp2
-rw-r--r--main/main.cpp12
-rw-r--r--misc/dist/osx_template.app/Contents/Info.plist2
-rw-r--r--misc/dist/osx_tools.app/Contents/Info.plist2
-rw-r--r--modules/gdnative/nativescript/api_generator.cpp25
-rw-r--r--modules/gdscript/gdscript.cpp14
-rw-r--r--modules/gdscript/gdscript.h2
-rw-r--r--modules/gdscript/gdscript_vm.cpp8
-rw-r--r--modules/mono/csharp_script.cpp14
-rw-r--r--modules/mono/csharp_script.h2
-rw-r--r--modules/tga/image_loader_tga.cpp22
-rw-r--r--modules/tga/image_loader_tga.h4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptFunctionState.xml2
-rw-r--r--modules/visual_script/visual_script.cpp2
-rw-r--r--platform/javascript/api/javascript_tools_editor_plugin.cpp2
-rw-r--r--platform/javascript/godot_js.h2
-rw-r--r--platform/javascript/javascript_singleton.cpp9
-rw-r--r--platform/javascript/js/libs/library_godot_display.js2
-rw-r--r--platform/javascript/js/libs/library_godot_fetch.js6
-rw-r--r--platform/javascript/js/libs/library_godot_javascript_singleton.js2
-rw-r--r--platform/javascript/package-lock.json1002
-rw-r--r--platform/javascript/package.json8
-rw-r--r--platform/osx/export/export.cpp4
-rw-r--r--scene/2d/tile_map.cpp7
-rw-r--r--scene/3d/camera_3d.cpp11
-rw-r--r--scene/3d/camera_3d.h1
-rw-r--r--scene/3d/node_3d.cpp62
-rw-r--r--scene/3d/node_3d.h12
-rw-r--r--scene/3d/visual_instance_3d.cpp66
-rw-r--r--scene/3d/visual_instance_3d.h30
-rw-r--r--scene/3d/xr_nodes.cpp6
-rw-r--r--scene/gui/code_edit.cpp3
-rw-r--r--scene/gui/graph_edit.cpp9
-rw-r--r--scene/gui/slider.cpp2
-rw-r--r--scene/gui/texture_button.cpp3
-rw-r--r--scene/resources/theme.h1
-rw-r--r--servers/rendering/renderer_rd/effects_rd.cpp26
-rw-r--r--servers/rendering/renderer_rd/effects_rd.h7
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.cpp2
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.h4
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp8
-rw-r--r--servers/rendering/renderer_rd/renderer_storage_rd.cpp8
-rw-r--r--servers/rendering/renderer_scene.h6
-rw-r--r--servers/rendering/renderer_scene_cull.cpp643
-rw-r--r--servers/rendering/renderer_scene_cull.h90
-rw-r--r--servers/rendering/renderer_viewport.cpp4
-rw-r--r--servers/rendering/rendering_server_default.h4
-rw-r--r--servers/rendering_server.cpp4
-rw-r--r--servers/rendering_server.h4
76 files changed, 3746 insertions, 1254 deletions
diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp
index 878ce820fb..5b96babe44 100644
--- a/core/input/input_map.cpp
+++ b/core/input/input_map.cpp
@@ -511,6 +511,7 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
// Text Backspace and Delete
inputs = List<Ref<InputEvent>>();
inputs.push_back(InputEventKey::create_reference(KEY_BACKSPACE));
+ inputs.push_back(InputEventKey::create_reference(KEY_BACKSPACE | KEY_MASK_SHIFT));
default_builtin_cache.insert("ui_text_backspace", inputs);
inputs = List<Ref<InputEvent>>();
diff --git a/core/templates/bin_sorted_array.h b/core/templates/bin_sorted_array.h
new file mode 100644
index 0000000000..be9d0b5475
--- /dev/null
+++ b/core/templates/bin_sorted_array.h
@@ -0,0 +1,181 @@
+/*************************************************************************/
+/* bin_sorted_array.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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 BIN_SORTED_ARRAY_H
+#define BIN_SORTED_ARRAY_H
+
+#include "core/templates/local_vector.h"
+#include "core/templates/paged_array.h"
+
+template <class T>
+class BinSortedArray {
+ PagedArray<T> array;
+ LocalVector<uint64_t> bin_limits;
+
+ // Implement if elements need to keep track of their own index in the array.
+ _FORCE_INLINE_ virtual void _update_idx(T &r_element, uint64_t p_idx) {}
+
+ _FORCE_INLINE_ void _swap(uint64_t p_a, uint64_t p_b) {
+ SWAP(array[p_a], array[p_b]);
+ _update_idx(array[p_a], p_a);
+ _update_idx(array[p_b], p_b);
+ }
+
+public:
+ uint64_t insert(T &p_element, uint64_t p_bin) {
+ array.push_back(p_element);
+ uint64_t new_idx = array.size() - 1;
+ _update_idx(p_element, new_idx);
+ bin_limits[0] = new_idx;
+ if (p_bin != 0) {
+ new_idx = move(new_idx, p_bin);
+ }
+ return new_idx;
+ }
+
+ uint64_t move(uint64_t p_idx, uint64_t p_bin) {
+ ERR_FAIL_COND_V(p_idx >= array.size(), -1);
+
+ uint64_t current_bin = bin_limits.size() - 1;
+ while (p_idx > bin_limits[current_bin]) {
+ current_bin--;
+ }
+
+ if (p_bin == current_bin) {
+ return p_idx;
+ }
+
+ uint64_t current_idx = p_idx;
+ if (p_bin > current_bin) {
+ while (p_bin > current_bin) {
+ uint64_t swap_idx = 0;
+
+ if (current_bin == bin_limits.size() - 1) {
+ bin_limits.push_back(0);
+ } else {
+ bin_limits[current_bin + 1]++;
+ swap_idx = bin_limits[current_bin + 1];
+ }
+
+ if (current_idx != swap_idx) {
+ _swap(current_idx, swap_idx);
+ current_idx = swap_idx;
+ }
+
+ current_bin++;
+ }
+ } else {
+ while (p_bin < current_bin) {
+ uint64_t swap_idx = bin_limits[current_bin];
+
+ if (current_idx != swap_idx) {
+ _swap(current_idx, swap_idx);
+ }
+
+ if (current_bin == bin_limits.size() - 1 && bin_limits[current_bin] == 0) {
+ bin_limits.resize(bin_limits.size() - 1);
+ } else {
+ bin_limits[current_bin]--;
+ }
+ current_idx = swap_idx;
+ current_bin--;
+ }
+ }
+
+ return current_idx;
+ }
+
+ void remove(uint64_t p_idx) {
+ ERR_FAIL_COND(p_idx >= array.size());
+ uint64_t new_idx = move(p_idx, 0);
+ uint64_t swap_idx = array.size() - 1;
+
+ if (new_idx != swap_idx) {
+ _swap(new_idx, swap_idx);
+ }
+
+ if (bin_limits[0] > 0) {
+ bin_limits[0]--;
+ }
+
+ array.pop_back();
+ }
+
+ void set_page_pool(PagedArrayPool<T> *p_page_pool) {
+ array.set_page_pool(p_page_pool);
+ }
+
+ _FORCE_INLINE_ const T &operator[](uint64_t p_index) const {
+ return array[p_index];
+ }
+
+ _FORCE_INLINE_ T &operator[](uint64_t p_index) {
+ return array[p_index];
+ }
+
+ int get_bin_count() {
+ if (array.size() == 0) {
+ return 0;
+ }
+ return bin_limits.size();
+ }
+
+ int get_bin_start(int p_bin) {
+ ERR_FAIL_COND_V(p_bin >= get_bin_count(), ~0U);
+ if ((unsigned int)p_bin == bin_limits.size() - 1) {
+ return 0;
+ }
+ return bin_limits[p_bin + 1] + 1;
+ }
+
+ int get_bin_size(int p_bin) {
+ ERR_FAIL_COND_V(p_bin >= get_bin_count(), 0);
+ if ((unsigned int)p_bin == bin_limits.size() - 1) {
+ return bin_limits[p_bin] + 1;
+ }
+ return bin_limits[p_bin] - bin_limits[p_bin + 1];
+ }
+
+ void reset() {
+ array.reset();
+ bin_limits.clear();
+ bin_limits.push_back(0);
+ }
+
+ BinSortedArray() {
+ bin_limits.push_back(0);
+ }
+
+ virtual ~BinSortedArray() {
+ reset();
+ }
+};
+
+#endif //BIN_SORTED_ARRAY_H
diff --git a/doc/classes/AnimationPlayer.xml b/doc/classes/AnimationPlayer.xml
index 7696f36009..8a94eee54c 100644
--- a/doc/classes/AnimationPlayer.xml
+++ b/doc/classes/AnimationPlayer.xml
@@ -275,7 +275,8 @@
<argument index="1" name="new_name" type="StringName">
</argument>
<description>
- If the currently being played animation changes, this signal will notify of such change.
+ Emitted when a queued animation plays after the previous animation was finished. See [method queue].
+ [b]Note:[/b] The signal is not emitted when the animation is changed via [method play] or from [AnimationTree].
</description>
</signal>
<signal name="animation_finished">
diff --git a/doc/classes/Camera3D.xml b/doc/classes/Camera3D.xml
index 30f6c2b951..016eb05308 100644
--- a/doc/classes/Camera3D.xml
+++ b/doc/classes/Camera3D.xml
@@ -55,10 +55,19 @@
<argument index="0" name="world_point" type="Vector3">
</argument>
<description>
- Returns [code]true[/code] if the given position is behind the camera.
+ Returns [code]true[/code] if the given position is behind the camera (the blue part of the linked diagram). [url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/camera3d_position_frustum.png]See this diagram[/url] for an overview of position query methods.
[b]Note:[/b] A position which returns [code]false[/code] may still be outside the camera's field of view.
</description>
</method>
+ <method name="is_position_in_frustum" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="world_point" type="Vector3">
+ </argument>
+ <description>
+ Returns [code]true[/code] if the given position is inside the camera's frustum (the green part of the linked diagram). [url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/camera3d_position_frustum.png]See this diagram[/url] for an overview of position query methods.
+ </description>
+ </method>
<method name="make_current">
<return type="void">
</return>
diff --git a/doc/classes/Engine.xml b/doc/classes/Engine.xml
index 1147b52102..ab480c50ab 100644
--- a/doc/classes/Engine.xml
+++ b/doc/classes/Engine.xml
@@ -171,6 +171,7 @@
</member>
<member name="physics_jitter_fix" type="float" setter="set_physics_jitter_fix" getter="get_physics_jitter_fix" default="0.5">
Controls how much physics ticks are synchronized with real time. For 0 or less, the ticks are synchronized. Such values are recommended for network games, where clock synchronization matters. Higher values cause higher deviation of in-game clock and real clock, but allows smoothing out framerate jitters. The default value of 0.5 should be fine for most; values above 2 could cause the game to react to dropped frames with a noticeable delay and are not recommended.
+ [b]Note:[/b] For best results, when using a custom physics interpolation solution, the physics jitter fix should be disabled by setting [member physics_jitter_fix] to [code]0[/code].
</member>
<member name="target_fps" type="int" setter="set_target_fps" getter="get_target_fps" default="0">
The desired frames per second. If the hardware cannot keep up, this setting may not be respected. A value of 0 means no limit.
diff --git a/doc/classes/GeometryInstance3D.xml b/doc/classes/GeometryInstance3D.xml
index b2c3bfc3ed..667ca2dacf 100644
--- a/doc/classes/GeometryInstance3D.xml
+++ b/doc/classes/GeometryInstance3D.xml
@@ -52,25 +52,21 @@
</member>
<member name="lod_bias" type="float" setter="set_lod_bias" getter="get_lod_bias" default="1.0">
</member>
- <member name="lod_max_distance" type="float" setter="set_lod_max_distance" getter="get_lod_max_distance" default="0.0">
- The GeometryInstance3D's max LOD distance.
- [b]Note:[/b] This property currently has no effect.
+ <member name="material_override" type="Material" setter="set_material_override" getter="get_material_override">
+ The material override for the whole geometry.
+ If a material is assigned to this property, it will be used instead of any material set in any material slot of the mesh.
</member>
- <member name="lod_max_hysteresis" type="float" setter="set_lod_max_hysteresis" getter="get_lod_max_hysteresis" default="0.0">
- The GeometryInstance3D's max LOD margin.
- [b]Note:[/b] This property currently has no effect.
+ <member name="visibility_range_begin" type="float" setter="set_visibility_range_begin" getter="get_visibility_range_begin" default="0.0">
+ Starting distance from which the GeometryInstance3D will be visible, taking [member visibility_range_begin_margin] into account as well. The default value of 0 is used to disable the range check.
</member>
- <member name="lod_min_distance" type="float" setter="set_lod_min_distance" getter="get_lod_min_distance" default="0.0">
- The GeometryInstance3D's min LOD distance.
- [b]Note:[/b] This property currently has no effect.
+ <member name="visibility_range_begin_margin" type="float" setter="set_visibility_range_begin_margin" getter="get_visibility_range_begin_margin" default="0.0">
+ Margin for the [member visibility_range_begin] threshold. The GeometryInstance3D will only change its visibility state when it goes over or under the [member visibility_range_begin] threshold by this amount.
</member>
- <member name="lod_min_hysteresis" type="float" setter="set_lod_min_hysteresis" getter="get_lod_min_hysteresis" default="0.0">
- The GeometryInstance3D's min LOD margin.
- [b]Note:[/b] This property currently has no effect.
+ <member name="visibility_range_end" type="float" setter="set_visibility_range_end" getter="get_visibility_range_end" default="0.0">
+ Distance from which the GeometryInstance3D will be hidden, taking [member visibility_range_end_margin] into account as well. The default value of 0 is used to disable the range check..
</member>
- <member name="material_override" type="Material" setter="set_material_override" getter="get_material_override">
- The material override for the whole geometry.
- If a material is assigned to this property, it will be used instead of any material set in any material slot of the mesh.
+ <member name="visibility_range_end_margin" type="float" setter="set_visibility_range_end_margin" getter="get_visibility_range_end_margin" default="0.0">
+ Margin for the [member visibility_range_end] threshold. The GeometryInstance3D will only change its visibility state when it goes over or under the [member visibility_range_end] threshold by this amount.
</member>
</members>
<constants>
diff --git a/doc/classes/Node3D.xml b/doc/classes/Node3D.xml
index a6237708c6..1c161803db 100644
--- a/doc/classes/Node3D.xml
+++ b/doc/classes/Node3D.xml
@@ -20,7 +20,7 @@
Forces the transform to update. Transform changes in physics are not instant for performance reasons. Transforms are accumulated and then set. Use this if you need an up-to-date transform when doing physics operations.
</description>
</method>
- <method name="get_parent_spatial" qualifiers="const">
+ <method name="get_parent_node_3d" qualifiers="const">
<return type="Node3D">
</return>
<description>
@@ -310,6 +310,9 @@
<member name="transform" type="Transform3D" setter="set_transform" getter="get_transform" default="Transform3D( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 )">
Local space [Transform3D] of this node, with respect to the parent node.
</member>
+ <member name="visibility_parent" type="NodePath" setter="set_visibility_parent" getter="get_visibility_parent" default="NodePath(&quot;&quot;)">
+ Defines the visibility range parent for this node and its subtree. The visibility parent must be a GeometryInstance3D. Any visual instance will only be visible if the visibility parent (and all of its visibility ancestors) is hidden by being closer to the camera than its own [member GeometryInstance3D.visibility_range_begin]. Nodes hidden via the [member Node3D.visible] property are essentially removed from the visibility dependency tree, so dependant instances will not take the hidden node or its ancestors into account.
+ </member>
<member name="visible" type="bool" setter="set_visible" getter="is_visible" default="true">
If [code]true[/code], this node is drawn. The node is only visible if all of its antecedents are visible as well (in other words, [method is_visible_in_tree] must return [code]true[/code]).
</member>
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 360ccc597e..851b88c142 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -1311,7 +1311,8 @@
[b]Note:[/b] This property is only read when the project starts. To change the physics FPS at runtime, set [member Engine.iterations_per_second] instead.
</member>
<member name="physics/common/physics_jitter_fix" type="float" setter="" getter="" default="0.5">
- Fix to improve physics jitter, specially on monitors where refresh rate is different than the physics FPS.
+ Controls how much physics ticks are synchronized with real time. For 0 or less, the ticks are synchronized. Such values are recommended for network games, where clock synchronization matters. Higher values cause higher deviation of in-game clock and real clock, but allows smoothing out framerate jitters. The default value of 0.5 should be fine for most; values above 2 could cause the game to react to dropped frames with a noticeable delay and are not recommended.
+ [b]Note:[/b] For best results, when using a custom physics interpolation solution, the physics jitter fix should be disabled by setting [member physics/common/physics_jitter_fix] to [code]0[/code].
[b]Note:[/b] This property is only read when the project starts. To change the physics FPS at runtime, set [member Engine.physics_jitter_fix] instead.
</member>
<member name="rendering/2d/sdf/oversize" type="int" setter="" getter="" default="1">
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index ba19176788..9b9644093f 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -1175,17 +1175,6 @@
Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
</description>
</method>
- <method name="instance_geometry_set_as_instance_lod">
- <return type="void">
- </return>
- <argument index="0" name="instance" type="RID">
- </argument>
- <argument index="1" name="as_lod_of_instance" type="RID">
- </argument>
- <description>
- Not implemented in Godot 3.x.
- </description>
- </method>
<method name="instance_geometry_set_cast_shadows_setting">
<return type="void">
</return>
@@ -1197,45 +1186,45 @@
Sets the shadow casting setting to one of [enum ShadowCastingSetting]. Equivalent to [member GeometryInstance3D.cast_shadow].
</description>
</method>
- <method name="instance_geometry_set_draw_range">
+ <method name="instance_geometry_set_flag">
<return type="void">
</return>
<argument index="0" name="instance" type="RID">
</argument>
- <argument index="1" name="min" type="float">
- </argument>
- <argument index="2" name="max" type="float">
- </argument>
- <argument index="3" name="min_margin" type="float">
+ <argument index="1" name="flag" type="int" enum="RenderingServer.InstanceFlags">
</argument>
- <argument index="4" name="max_margin" type="float">
+ <argument index="2" name="enabled" type="bool">
</argument>
<description>
- Not implemented in Godot 3.x.
+ Sets the flag for a given [enum InstanceFlags]. See [enum InstanceFlags] for more details.
</description>
</method>
- <method name="instance_geometry_set_flag">
+ <method name="instance_geometry_set_material_override">
<return type="void">
</return>
<argument index="0" name="instance" type="RID">
</argument>
- <argument index="1" name="flag" type="int" enum="RenderingServer.InstanceFlags">
- </argument>
- <argument index="2" name="enabled" type="bool">
+ <argument index="1" name="material" type="RID">
</argument>
<description>
- Sets the flag for a given [enum InstanceFlags]. See [enum InstanceFlags] for more details.
+ Sets a material that will override the material for all surfaces on the mesh associated with this instance. Equivalent to [member GeometryInstance3D.material_override].
</description>
</method>
- <method name="instance_geometry_set_material_override">
+ <method name="instance_geometry_set_visibility_range">
<return type="void">
</return>
<argument index="0" name="instance" type="RID">
</argument>
- <argument index="1" name="material" type="RID">
+ <argument index="1" name="min" type="float">
+ </argument>
+ <argument index="2" name="max" type="float">
+ </argument>
+ <argument index="3" name="min_margin" type="float">
+ </argument>
+ <argument index="4" name="max_margin" type="float">
</argument>
<description>
- Sets a material that will override the material for all surfaces on the mesh associated with this instance. Equivalent to [member GeometryInstance3D.material_override].
+ Sets the visibility range values for the given geometry instance. Equivalent to [member GeometryInstance3D.visibility_range_begin] and related properties.
</description>
</method>
<method name="instance_set_base">
@@ -1341,6 +1330,17 @@
Sets the world space transform of the instance. Equivalent to [member Node3D.transform].
</description>
</method>
+ <method name="instance_set_visibility_parent">
+ <return type="void">
+ </return>
+ <argument index="0" name="instance" type="RID">
+ </argument>
+ <argument index="1" name="parent" type="RID">
+ </argument>
+ <description>
+ Sets the visibility parent for the given instance. Equivalent to [member Node3D.visibility_parent].
+ </description>
+ </method>
<method name="instance_set_visible">
<return type="void">
</return>
diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp
index e7d536e08e..991d76ce72 100644
--- a/editor/editor_file_dialog.cpp
+++ b/editor/editor_file_dialog.cpp
@@ -294,6 +294,9 @@ void EditorFileDialog::_post_popup() {
if (res && name == "res://") {
name = "/";
} else {
+ if (name.ends_with("/")) {
+ name = name.substr(0, name.length() - 1);
+ }
name = name.get_file() + "/";
}
bool exists = dir_access->dir_exists(recentd[i]);
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index 050e55c624..c61b097ccd 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -1934,19 +1934,6 @@ void EditorFileSystem::_reimport_thread(uint32_t p_index, ImportThreadData *p_im
}
void EditorFileSystem::reimport_files(const Vector<String> &p_files) {
- {
- // Ensure that ProjectSettings::IMPORTED_FILES_PATH exists.
- DirAccess *da = DirAccess::open("res://");
- if (da->change_dir(ProjectSettings::IMPORTED_FILES_PATH) != OK) {
- Error err = da->make_dir_recursive(ProjectSettings::IMPORTED_FILES_PATH);
- if (err || da->change_dir(ProjectSettings::IMPORTED_FILES_PATH) != OK) {
- memdelete(da);
- ERR_FAIL_MSG("Failed to create '" + ProjectSettings::IMPORTED_FILES_PATH + "' folder.");
- }
- }
- memdelete(da);
- }
-
importing = true;
EditorProgress pr("reimport", TTR("(Re)Importing Assets"), p_files.size());
@@ -2177,13 +2164,9 @@ EditorFileSystem::EditorFileSystem() {
scanning_changes = false;
scanning_changes_done = false;
- DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
- if (da->change_dir(ProjectSettings::IMPORTED_FILES_PATH) != OK) {
- da->make_dir(ProjectSettings::IMPORTED_FILES_PATH);
- }
// This should probably also work on Unix and use the string it returns for FAT32 or exFAT
+ DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
using_fat32_or_exfat = (da->get_filesystem_type() == "FAT32" || da->get_filesystem_type() == "exFAT");
- memdelete(da);
scan_total = 0;
update_script_classes_queued.clear();
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index d8c5a14d4f..4f874687b4 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -2693,6 +2693,9 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
case FILE_EXPLORE_ANDROID_BUILD_TEMPLATES: {
OS::get_singleton()->shell_open("file://" + ProjectSettings::get_singleton()->get_resource_path().plus_file("android"));
} break;
+ case RUN_RELOAD_CURRENT_PROJECT: {
+ restart_editor();
+ } break;
case FILE_QUIT:
case RUN_PROJECT_MANAGER: {
if (!p_confirmed) {
@@ -3728,10 +3731,6 @@ bool EditorNode::is_scene_in_use(const String &p_path) {
return false;
}
-void EditorNode::register_editor_paths(bool p_for_project_manager) {
- EditorPaths::create(p_for_project_manager);
-}
-
void EditorNode::register_editor_types() {
ResourceLoader::set_timestamp_on_load(true);
ResourceSaver::set_timestamp_on_save(true);
@@ -6292,6 +6291,7 @@ EditorNode::EditorNode() {
tool_menu->add_item(TTR("Orphan Resource Explorer..."), TOOLS_ORPHAN_RESOURCES);
p->add_separator();
+ p->add_item(TTR("Reload Current Project"), RUN_RELOAD_CURRENT_PROJECT);
#ifdef OSX_ENABLED
p->add_shortcut(ED_SHORTCUT("editor/quit_to_project_list", TTR("Quit to Project List"), KEY_MASK_SHIFT + KEY_MASK_ALT + KEY_Q), RUN_PROJECT_MANAGER, true);
#else
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 9625b318e0..037ed263c5 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -164,6 +164,7 @@ private:
RUN_PLAY_CUSTOM_SCENE,
RUN_SETTINGS,
RUN_PROJECT_DATA_FOLDER,
+ RUN_RELOAD_CURRENT_PROJECT,
RUN_PROJECT_MANAGER,
RUN_VCS_SETTINGS,
RUN_VCS_SHUT_DOWN,
@@ -798,7 +799,6 @@ public:
Error export_preset(const String &p_preset, const String &p_path, bool p_debug, bool p_pack_only);
- static void register_editor_paths(bool p_for_project_manager);
static void register_editor_types();
static void unregister_editor_types();
diff --git a/editor/editor_paths.cpp b/editor/editor_paths.cpp
index ddba36c187..323707ec6c 100644
--- a/editor/editor_paths.cpp
+++ b/editor/editor_paths.cpp
@@ -29,8 +29,12 @@
/*************************************************************************/
#include "editor_paths.h"
+
+#include "core/config/engine.h"
+#include "core/config/project_settings.h"
#include "core/io/dir_access.h"
#include "core/os/os.h"
+#include "main/main.h" // For `is_project_manager`.
EditorPaths *EditorPaths::singleton = nullptr;
@@ -41,23 +45,32 @@ bool EditorPaths::are_paths_valid() const {
String EditorPaths::get_data_dir() const {
return data_dir;
}
+
String EditorPaths::get_config_dir() const {
return config_dir;
}
+
String EditorPaths::get_cache_dir() const {
return cache_dir;
}
+
+String EditorPaths::get_project_data_dir() const {
+ return project_data_dir;
+}
+
bool EditorPaths::is_self_contained() const {
return self_contained;
}
+
String EditorPaths::get_self_contained_file() const {
return self_contained_file;
}
-void EditorPaths::create(bool p_for_project_manager) {
+void EditorPaths::create() {
ERR_FAIL_COND(singleton != nullptr);
- memnew(EditorPaths(p_for_project_manager));
+ memnew(EditorPaths());
}
+
void EditorPaths::free() {
ERR_FAIL_COND(singleton == nullptr);
memdelete(singleton);
@@ -71,9 +84,10 @@ void EditorPaths::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_self_contained_file"), &EditorPaths::get_self_contained_file);
}
-EditorPaths::EditorPaths(bool p_for_project_mamanger) {
+EditorPaths::EditorPaths() {
singleton = this;
+ // Self-contained mode if a `._sc_` or `_sc_` file is present in executable dir.
String exe_path = OS::get_singleton()->get_executable_path().get_base_dir();
{
DirAccessRef d = DirAccess::create_for_path(exe_path);
@@ -100,13 +114,13 @@ EditorPaths::EditorPaths(bool p_for_project_mamanger) {
cache_path = exe_path;
cache_dir = data_dir.plus_file("cache");
} else {
- // Typically XDG_DATA_HOME or %APPDATA%
+ // Typically XDG_DATA_HOME or %APPDATA%.
data_path = OS::get_singleton()->get_data_path();
data_dir = data_path.plus_file(OS::get_singleton()->get_godot_dir_name());
- // Can be different from data_path e.g. on Linux or macOS
+ // Can be different from data_path e.g. on Linux or macOS.
config_path = OS::get_singleton()->get_config_path();
config_dir = config_path.plus_file(OS::get_singleton()->get_godot_dir_name());
- // Can be different from above paths, otherwise a subfolder of data_dir
+ // Can be different from above paths, otherwise a subfolder of data_dir.
cache_path = OS::get_singleton()->get_cache_path();
if (cache_path == data_path) {
cache_dir = data_dir.plus_file("cache");
@@ -116,37 +130,85 @@ EditorPaths::EditorPaths(bool p_for_project_mamanger) {
}
paths_valid = (data_path != "" && config_path != "" && cache_path != "");
+ ERR_FAIL_COND_MSG(!paths_valid, "Editor data, config, or cache paths are invalid.");
+
+ // Validate or create each dir and its relevant subdirectories.
- if (paths_valid) {
- DirAccessRef dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ DirAccessRef dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+
+ // Data dir.
+ {
if (dir->change_dir(data_dir) != OK) {
dir->make_dir_recursive(data_dir);
if (dir->change_dir(data_dir) != OK) {
- ERR_PRINT("Cannot create data directory!");
+ ERR_PRINT("Could not create editor data directory: " + data_dir);
paths_valid = false;
}
}
- // Validate/create cache dir
+ if (!dir->dir_exists("templates")) {
+ dir->make_dir("templates");
+ }
+ }
- if (dir->change_dir(EditorPaths::get_singleton()->get_cache_dir()) != OK) {
+ // Config dir.
+ {
+ if (dir->change_dir(config_dir) != OK) {
+ dir->make_dir_recursive(config_dir);
+ if (dir->change_dir(config_dir) != OK) {
+ ERR_PRINT("Could not create editor config directory: " + config_dir);
+ paths_valid = false;
+ }
+ }
+
+ if (!dir->dir_exists("text_editor_themes")) {
+ dir->make_dir("text_editor_themes");
+ }
+ if (!dir->dir_exists("script_templates")) {
+ dir->make_dir("script_templates");
+ }
+ if (!dir->dir_exists("feature_profiles")) {
+ dir->make_dir("feature_profiles");
+ }
+ }
+
+ // Cache dir.
+ {
+ if (dir->change_dir(cache_dir) != OK) {
dir->make_dir_recursive(cache_dir);
if (dir->change_dir(cache_dir) != OK) {
- ERR_PRINT("Cannot create cache directory!");
+ ERR_PRINT("Could not create editor cache directory: " + cache_dir);
+ paths_valid = false;
}
}
+ }
- if (p_for_project_mamanger) {
- Engine::get_singleton()->set_shader_cache_path(get_data_dir());
- } else {
- DirAccessRef dir2 = DirAccess::open("res://");
- if (dir2->change_dir(".godot") != OK) { //ensure the .godot subdir exists
- if (dir2->make_dir(".godot") != OK) {
- ERR_PRINT("Cannot create res://.godot directory!");
- }
+ // Validate or create project-specific editor data dir (`res://.godot`),
+ // including shader cache subdir.
+
+ if (Main::is_project_manager()) {
+ // Nothing to create, use shared editor data dir for shader cache.
+ Engine::get_singleton()->set_shader_cache_path(data_dir);
+ } else {
+ DirAccessRef dir_res = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ if (dir_res->change_dir(project_data_dir) != OK) {
+ dir_res->make_dir_recursive(project_data_dir);
+ if (dir_res->change_dir(project_data_dir) != OK) {
+ ERR_PRINT("Could not create project data directory (" + project_data_dir + ") in: " + dir_res->get_current_dir());
+ paths_valid = false;
}
+ }
+ Engine::get_singleton()->set_shader_cache_path(project_data_dir);
- Engine::get_singleton()->set_shader_cache_path("res://.godot");
+ // Editor metadata dir.
+ if (!dir_res->dir_exists("editor")) {
+ dir_res->make_dir("editor");
+ }
+ // Imported assets dir.
+ if (!dir_res->dir_exists(ProjectSettings::IMPORTED_FILES_PATH)) {
+ dir_res->make_dir(ProjectSettings::IMPORTED_FILES_PATH);
}
}
+
+ print_line("paths valid: " + itos((int)paths_valid));
}
diff --git a/editor/editor_paths.h b/editor/editor_paths.h
index c1be33f5c2..2c156b7c96 100644
--- a/editor/editor_paths.h
+++ b/editor/editor_paths.h
@@ -28,20 +28,22 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef EDITORPATHS_H
-#define EDITORPATHS_H
+#ifndef EDITOR_PATHS_H
+#define EDITOR_PATHS_H
-#include "core/config/engine.h"
+#include "core/object/class_db.h"
+#include "core/string/ustring.h"
class EditorPaths : public Object {
GDCLASS(EditorPaths, Object)
- bool paths_valid = false;
- String data_dir; //editor data dir
- String config_dir; //editor config dir
- String cache_dir; //editor cache dir
- bool self_contained = false; //true if running self contained
- String self_contained_file; //self contained file with configuration
+ bool paths_valid = false; // If any of the paths can't be created, this is false.
+ String data_dir; // Editor data (templates, shader cache, etc.).
+ String config_dir; // Editor config (settings, profiles, themes, etc.).
+ String cache_dir; // Editor cache (thumbnails, tmp generated files).
+ String project_data_dir = "res://.godot"; // Project-specific data (metadata, shader cache, etc.).
+ bool self_contained = false; // Self-contained means everything goes to `editor_data` dir.
+ String self_contained_file; // Self-contained file with configuration.
static EditorPaths *singleton;
@@ -54,6 +56,8 @@ public:
String get_data_dir() const;
String get_config_dir() const;
String get_cache_dir() const;
+ String get_project_data_dir() const;
+
bool is_self_contained() const;
String get_self_contained_file() const;
@@ -61,10 +65,10 @@ public:
return singleton;
}
- static void create(bool p_for_project_manager);
+ static void create();
static void free();
- EditorPaths(bool p_for_project_mamanger = false);
+ EditorPaths();
};
-#endif // EDITORPATHS_H
+#endif // EDITOR_PATHS_H
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index 949638f0c9..fa7980b95f 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -768,8 +768,8 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
for (int i = 0; i < list.size(); i++) {
String name = list[i].replace("/", "::");
set("projects/" + name, list[i]);
- };
- };
+ }
+ }
if (p_extra_config->has_section("presets")) {
List<String> keys;
@@ -779,9 +779,9 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
String key = E->get();
Variant val = p_extra_config->get_value("presets", key);
set(key, val);
- };
- };
- };
+ }
+ }
+ }
}
void EditorSettings::_load_godot2_text_editor_theme() {
@@ -871,9 +871,8 @@ static void _create_script_templates(const String &p_path) {
Dictionary templates = _get_builtin_script_templates();
List<Variant> keys;
templates.get_key_list(&keys);
- FileAccess *file = FileAccess::create(FileAccess::ACCESS_FILESYSTEM);
-
- DirAccess *dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ FileAccessRef file = FileAccess::create(FileAccess::ACCESS_FILESYSTEM);
+ DirAccessRef dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
dir->change_dir(p_path);
for (int i = 0; i < keys.size(); i++) {
if (!dir->file_exists(keys[i])) {
@@ -883,9 +882,6 @@ static void _create_script_templates(const String &p_path) {
file->close();
}
}
-
- memdelete(dir);
- memdelete(file);
}
// PUBLIC METHODS
@@ -895,103 +891,48 @@ EditorSettings *EditorSettings::get_singleton() {
}
void EditorSettings::create() {
+ // IMPORTANT: create() *must* create a valid EditorSettings singleton,
+ // as the rest of the engine code will assume it. As such, it should never
+ // return (incl. via ERR_FAIL) without initializing the singleton member.
+
if (singleton.ptr()) {
- return; //pointless
+ ERR_PRINT("Can't recreate EditorSettings as it already exists.");
+ return;
}
+ ClassDB::register_class<EditorSettings>(); // Otherwise it can't be unserialized.
+
+ String config_file_path;
Ref<ConfigFile> extra_config = memnew(ConfigFile);
+ if (!EditorPaths::get_singleton()) {
+ ERR_PRINT("Bug (please report): EditorPaths haven't been initialized, EditorSettings cannot be created properly.");
+ goto fail;
+ }
+
if (EditorPaths::get_singleton()->is_self_contained()) {
Error err = extra_config->load(EditorPaths::get_singleton()->get_self_contained_file());
if (err != OK) {
- ERR_PRINT("Can't load extra config from path :" + EditorPaths::get_singleton()->get_self_contained_file());
+ ERR_PRINT("Can't load extra config from path: " + EditorPaths::get_singleton()->get_self_contained_file());
}
}
- DirAccess *dir = nullptr;
-
- ClassDB::register_class<EditorSettings>(); //otherwise it can't be unserialized
-
- String config_file_path;
-
if (EditorPaths::get_singleton()->are_paths_valid()) {
- // Validate/create data dir and subdirectories
-
- String data_dir = EditorPaths::get_singleton()->get_data_dir();
-
- dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
- if (dir->change_dir(data_dir) != OK) {
- dir->make_dir_recursive(data_dir);
- if (dir->change_dir(data_dir) != OK) {
- ERR_PRINT("Cannot create data directory!");
- memdelete(dir);
- goto fail;
- }
- }
-
- if (dir->change_dir("templates") != OK) {
- dir->make_dir("templates");
- } else {
- dir->change_dir("..");
- }
+ _create_script_templates(EditorPaths::get_singleton()->get_config_dir().plus_file("script_templates"));
- // Validate/create config dir and subdirectories
-
- if (dir->change_dir(EditorPaths::get_singleton()->get_config_dir()) != OK) {
- dir->make_dir_recursive(EditorPaths::get_singleton()->get_config_dir());
- if (dir->change_dir(EditorPaths::get_singleton()->get_config_dir()) != OK) {
- ERR_PRINT("Cannot create config directory!");
- memdelete(dir);
- goto fail;
- }
- }
-
- if (dir->change_dir("text_editor_themes") != OK) {
- dir->make_dir("text_editor_themes");
- } else {
- dir->change_dir("..");
- }
-
- if (dir->change_dir("script_templates") != OK) {
- dir->make_dir("script_templates");
- } else {
- dir->change_dir("..");
- }
-
- if (dir->change_dir("feature_profiles") != OK) {
- dir->make_dir("feature_profiles");
- } else {
- dir->change_dir("..");
- }
-
- _create_script_templates(dir->get_current_dir().plus_file("script_templates"));
-
- {
- // Validate/create project-specific editor settings dir.
- DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
- if (da->change_dir(EditorSettings::PROJECT_EDITOR_SETTINGS_PATH) != OK) {
- Error err = da->make_dir_recursive(EditorSettings::PROJECT_EDITOR_SETTINGS_PATH);
- if (err || da->change_dir(EditorSettings::PROJECT_EDITOR_SETTINGS_PATH) != OK) {
- ERR_FAIL_MSG("Failed to create '" + EditorSettings::PROJECT_EDITOR_SETTINGS_PATH + "' folder.");
- }
- }
- }
-
- // Validate editor config file
+ // Validate editor config file.
+ DirAccessRef dir = DirAccess::open(EditorPaths::get_singleton()->get_config_dir());
String config_file_name = "editor_settings-" + itos(VERSION_MAJOR) + ".tres";
config_file_path = EditorPaths::get_singleton()->get_config_dir().plus_file(config_file_name);
if (!dir->file_exists(config_file_name)) {
- memdelete(dir);
goto fail;
}
- memdelete(dir);
-
singleton = ResourceLoader::load(config_file_path, "EditorSettings");
if (singleton.is_null()) {
- WARN_PRINT("Could not open config file.");
+ ERR_PRINT("Could not load editor settings from path: " + config_file_path);
goto fail;
}
@@ -1009,7 +950,6 @@ void EditorSettings::create() {
}
fail:
-
// patch init projects
String exe_path = OS::get_singleton()->get_executable_path().get_base_dir();
@@ -1017,9 +957,9 @@ fail:
Vector<String> list = extra_config->get_value("init_projects", "list");
for (int i = 0; i < list.size(); i++) {
list.write[i] = exe_path.plus_file(list[i]);
- };
+ }
extra_config->set_value("init_projects", "list", list);
- };
+ }
singleton = Ref<EditorSettings>(memnew(EditorSettings));
singleton->save_changed_setting = true;
@@ -1251,16 +1191,15 @@ void EditorSettings::add_property_hint(const PropertyInfo &p_hint) {
hints[p_hint.name] = p_hint;
}
-// Data directories
+// Editor data and config directories
+// EditorPaths::create() is responsible for the creation of these directories.
String EditorSettings::get_templates_dir() const {
return EditorPaths::get_singleton()->get_data_dir().plus_file("templates");
}
-// Config directories
-
String EditorSettings::get_project_settings_dir() const {
- return EditorSettings::PROJECT_EDITOR_SETTINGS_PATH;
+ return EditorPaths::get_singleton()->get_project_data_dir().plus_file("editor");
}
String EditorSettings::get_text_editor_themes_dir() const {
@@ -1275,8 +1214,6 @@ String EditorSettings::get_project_script_templates_dir() const {
return ProjectSettings::get_singleton()->get("editor/script/templates_search_path");
}
-// Cache directory
-
String EditorSettings::get_feature_profiles_dir() const {
return EditorPaths::get_singleton()->get_config_dir().plus_file("feature_profiles");
}
diff --git a/editor/editor_settings.h b/editor/editor_settings.h
index 4c361403a9..d6a9a9d1b9 100644
--- a/editor/editor_settings.h
+++ b/editor/editor_settings.h
@@ -47,7 +47,6 @@ class EditorSettings : public Resource {
_THREAD_SAFE_CLASS_
public:
- inline static const String PROJECT_EDITOR_SETTINGS_PATH = "res://.godot/editor";
struct Plugin {
EditorPlugin *instance = nullptr;
String path;
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 64f7500e8c..fa543b7455 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -960,14 +960,6 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
style_content_panel->set_border_color(dark_color_2);
theme->set_stylebox("panel", "TabContainer", style_content_panel);
- // this is the stylebox used in 3d and 2d viewports (no borders)
- Ref<StyleBoxFlat> style_content_panel_vp = style_content_panel->duplicate();
- style_content_panel_vp->set_default_margin(SIDE_LEFT, border_width * 2);
- style_content_panel_vp->set_default_margin(SIDE_TOP, default_margin_size * EDSCALE);
- style_content_panel_vp->set_default_margin(SIDE_RIGHT, border_width * 2);
- style_content_panel_vp->set_default_margin(SIDE_BOTTOM, border_width * 2);
- theme->set_stylebox("Content", "EditorStyles", style_content_panel_vp);
-
// These styleboxes can be used on tabs against the base color background (e.g. nested tabs).
Ref<StyleBoxFlat> style_tab_selected_odd = style_tab_selected->duplicate();
style_tab_selected_odd->set_bg_color(disabled_bg_color);
@@ -977,6 +969,22 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
style_content_panel_odd->set_bg_color(disabled_bg_color);
theme->set_stylebox("panel_odd", "TabContainer", style_content_panel_odd);
+ // This stylebox is used in 3d and 2d viewports (no borders).
+ Ref<StyleBoxFlat> style_content_panel_vp = style_content_panel->duplicate();
+ style_content_panel_vp->set_default_margin(SIDE_LEFT, border_width * 2);
+ style_content_panel_vp->set_default_margin(SIDE_TOP, default_margin_size * EDSCALE);
+ style_content_panel_vp->set_default_margin(SIDE_RIGHT, border_width * 2);
+ style_content_panel_vp->set_default_margin(SIDE_BOTTOM, border_width * 2);
+ theme->set_stylebox("Content", "EditorStyles", style_content_panel_vp);
+
+ // This stylebox is used by preview tabs in the Theme Editor.
+ Ref<StyleBoxFlat> style_theme_preview_tab = style_tab_selected_odd->duplicate();
+ style_theme_preview_tab->set_expand_margin_size(SIDE_BOTTOM, 5 * EDSCALE);
+ theme->set_stylebox("ThemeEditorPreviewFG", "EditorStyles", style_theme_preview_tab);
+ Ref<StyleBoxFlat> style_theme_preview_bg_tab = style_tab_unselected->duplicate();
+ style_theme_preview_bg_tab->set_expand_margin_size(SIDE_BOTTOM, 2 * EDSCALE);
+ theme->set_stylebox("ThemeEditorPreviewBG", "EditorStyles", style_theme_preview_bg_tab);
+
// Separators
theme->set_stylebox("separator", "HSeparator", make_line_stylebox(separator_color, MAX(Math::round(EDSCALE), border_width)));
theme->set_stylebox("separator", "VSeparator", make_line_stylebox(separator_color, MAX(Math::round(EDSCALE), border_width), 0, 0, true));
@@ -1346,6 +1354,15 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
style_info_3d_viewport->set_border_width_all(0);
theme->set_stylebox("Information3dViewport", "EditorStyles", style_info_3d_viewport);
+ // Theme editor.
+ theme->set_color("preview_picker_overlay_color", "ThemeEditor", Color(0.1, 0.1, 0.1, 0.25));
+ Color theme_preview_picker_bg_color = accent_color;
+ theme_preview_picker_bg_color.a = 0.2;
+ Ref<StyleBoxFlat> theme_preview_picker_sb = make_flat_stylebox(theme_preview_picker_bg_color, 0, 0, 0, 0);
+ theme_preview_picker_sb->set_border_color(accent_color);
+ theme_preview_picker_sb->set_border_width_all(1.0 * EDSCALE);
+ theme->set_stylebox("preview_picker_overlay", "ThemeEditor", theme_preview_picker_sb);
+
// adaptive script theme constants
// for comments and elements with lower relevance
const Color dim_color = Color(font_color.r, font_color.g, font_color.b, 0.5);
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
index e4d88850ea..08aa2fecbb 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -1213,7 +1213,7 @@ void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_m
Node3D *n = src_mesh_node;
while (n) {
xf = n->get_transform() * xf;
- n = n->get_parent_spatial();
+ n = n->get_parent_node_3d();
}
Vector<uint8_t> lightmap_cache;
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 0083876e69..5d248176c1 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -2292,22 +2292,38 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
// Start dragging
if (still_selected) {
// Drag the node(s) if requested
- List<CanvasItem *> selection2 = _get_edited_canvas_items();
+ drag_start_origin = click;
+ drag_type = DRAG_QUEUED;
+ }
+ // Select the item
+ return true;
+ }
+ }
+ }
- drag_selection.clear();
- for (int i = 0; i < selection2.size(); i++) {
- if (_is_node_movable(selection2[i], true)) {
- drag_selection.push_back(selection2[i]);
- }
- }
+ if (drag_type == DRAG_QUEUED) {
+ if (b.is_valid() && !b->is_pressed()) {
+ drag_type = DRAG_NONE;
+ return true;
+ }
+ if (m.is_valid()) {
+ Point2 click = transform.affine_inverse().xform(m->get_position());
+ bool movement_threshold_passed = drag_start_origin.distance_to(click) > 10 * EDSCALE;
+ if (m.is_valid() && movement_threshold_passed) {
+ List<CanvasItem *> selection2 = _get_edited_canvas_items();
- if (selection2.size() > 0) {
- drag_type = DRAG_MOVE;
- drag_from = click;
- _save_canvas_item_state(drag_selection);
+ drag_selection.clear();
+ for (int i = 0; i < selection2.size(); i++) {
+ if (_is_node_movable(selection2[i], true)) {
+ drag_selection.push_back(selection2[i]);
}
}
- // Select the item
+
+ if (selection2.size() > 0) {
+ drag_type = DRAG_MOVE;
+ drag_from = click;
+ _save_canvas_item_state(drag_selection);
+ }
return true;
}
}
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index 96a29e0c74..7b64d0cb5d 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -206,6 +206,7 @@ private:
DRAG_ANCHOR_BOTTOM_RIGHT,
DRAG_ANCHOR_BOTTOM_LEFT,
DRAG_ANCHOR_ALL,
+ DRAG_QUEUED,
DRAG_MOVE,
DRAG_MOVE_X,
DRAG_MOVE_Y,
@@ -379,6 +380,7 @@ private:
Control *top_ruler;
Control *left_ruler;
+ Point2 drag_start_origin;
DragType drag_type;
Point2 drag_from;
Point2 drag_to;
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 46c35291ac..84b39e782c 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -1244,6 +1244,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
_edit.mouse_pos = b->get_position();
+ _edit.original_mouse_pos = b->get_position();
_edit.snap = spatial_editor->is_snap_enabled();
_edit.mode = TRANSFORM_NONE;
@@ -1450,7 +1451,8 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
} else if (nav_scheme == NAVIGATION_MODO && m->is_alt_pressed()) {
nav_mode = NAVIGATION_ORBIT;
} else {
- if (clicked.is_valid()) {
+ bool movement_threshold_passed = _edit.original_mouse_pos.distance_to(_edit.mouse_pos) > 10 * EDSCALE;
+ if (clicked.is_valid() && movement_threshold_passed) {
if (!clicked_includes_current) {
_select_clicked(clicked_wants_append, true);
// Processing was deferred.
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index c68857cd7e..161d6a38a9 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -387,6 +387,7 @@ private:
Vector3 orig_gizmo_pos;
int edited_gizmo = 0;
Point2 mouse_pos;
+ Point2 original_mouse_pos;
bool snap = false;
Ref<EditorNode3DGizmo> gizmo;
int gizmo_handle = 0;
diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp
index d1652d3fce..6607bf6cad 100644
--- a/editor/plugins/theme_editor_plugin.cpp
+++ b/editor/plugins/theme_editor_plugin.cpp
@@ -30,12 +30,10 @@
#include "theme_editor_plugin.h"
-#include "core/io/file_access.h"
#include "core/os/keyboard.h"
-#include "core/version.h"
+#include "editor/editor_resource_picker.h"
#include "editor/editor_scale.h"
#include "editor/progress_dialog.h"
-#include "scene/gui/progress_bar.h"
void ThemeItemImportTree::_update_items_tree() {
import_items_tree->clear();
@@ -1209,7 +1207,7 @@ void ThemeItemEditorDialog::_close_dialog() {
}
void ThemeItemEditorDialog::_dialog_about_to_show() {
- ERR_FAIL_COND(edited_theme.is_null());
+ ERR_FAIL_COND_MSG(edited_theme.is_null(), "Invalid state of the Theme Editor; the Theme resource is missing.");
_update_edit_types();
@@ -1466,6 +1464,9 @@ void ThemeItemEditorDialog::_add_theme_type() {
edited_theme->add_color_type(edit_add_type_value->get_text());
edited_theme->add_constant_type(edit_add_type_value->get_text());
_update_edit_types();
+
+ // Force emit a change so that other parts of the editor can update.
+ edited_theme->emit_changed();
}
void ThemeItemEditorDialog::_add_theme_item(Theme::DataType p_data_type, String p_item_name, String p_item_type) {
@@ -1939,268 +1940,1310 @@ ThemeItemEditorDialog::ThemeItemEditorDialog() {
confirm_closing_dialog->connect("confirmed", callable_mp(this, &ThemeItemEditorDialog::_close_dialog));
}
+VBoxContainer *ThemeTypeEditor::_create_item_list(Theme::DataType p_data_type) {
+ VBoxContainer *items_tab = memnew(VBoxContainer);
+ items_tab->set_custom_minimum_size(Size2(0, 160) * EDSCALE);
+ data_type_tabs->add_child(items_tab);
+ data_type_tabs->set_tab_title(data_type_tabs->get_tab_count() - 1, "");
+
+ ScrollContainer *items_sc = memnew(ScrollContainer);
+ items_sc->set_v_size_flags(SIZE_EXPAND_FILL);
+ items_sc->set_enable_h_scroll(false);
+ items_tab->add_child(items_sc);
+ VBoxContainer *items_list = memnew(VBoxContainer);
+ items_list->set_h_size_flags(SIZE_EXPAND_FILL);
+ items_sc->add_child(items_list);
+
+ HBoxContainer *item_add_hb = memnew(HBoxContainer);
+ items_tab->add_child(item_add_hb);
+ LineEdit *item_add_edit = memnew(LineEdit);
+ item_add_edit->set_h_size_flags(SIZE_EXPAND_FILL);
+ item_add_hb->add_child(item_add_edit);
+ item_add_edit->connect("text_entered", callable_mp(this, &ThemeTypeEditor::_item_add_lineedit_cbk), varray(p_data_type, item_add_edit));
+ Button *item_add_button = memnew(Button);
+ item_add_button->set_text(TTR("Add"));
+ item_add_hb->add_child(item_add_button);
+ item_add_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_item_add_cbk), varray(p_data_type, item_add_edit));
+
+ return items_list;
+}
+
+void ThemeTypeEditor::_update_type_list() {
+ ERR_FAIL_COND(edited_theme.is_null());
+
+ if (updating) {
+ return;
+ }
+ updating = true;
+
+ Control *focused = get_focus_owner();
+ if (focused) {
+ if (focusables.has(focused)) {
+ // If focus is currently on one of the internal property editors, don't update.
+ updating = false;
+ return;
+ }
+
+ Node *focus_parent = focused->get_parent();
+ while (focus_parent) {
+ Control *c = Object::cast_to<Control>(focus_parent);
+ if (c && focusables.has(c)) {
+ // If focus is currently on one of the internal property editors, don't update.
+ updating = false;
+ return;
+ }
+
+ focus_parent = focus_parent->get_parent();
+ }
+ }
+
+ List<StringName> theme_types;
+ edited_theme->get_type_list(&theme_types);
+ theme_types.sort_custom<StringName::AlphCompare>();
+
+ theme_type_list->clear();
+
+ if (theme_types.size() > 0) {
+ theme_type_list->set_disabled(false);
+
+ bool item_reselected = false;
+ int e_idx = 0;
+ for (List<StringName>::Element *E = theme_types.front(); E; E = E->next()) {
+ Ref<Texture2D> item_icon;
+ if (E->get() == "") {
+ item_icon = get_theme_icon("NodeDisabled", "EditorIcons");
+ } else {
+ item_icon = EditorNode::get_singleton()->get_class_icon(E->get(), "NodeDisabled");
+ }
+ theme_type_list->add_icon_item(item_icon, E->get());
+
+ if (E->get() == edited_type) {
+ theme_type_list->select(e_idx);
+ item_reselected = true;
+ }
+ e_idx++;
+ }
+
+ if (!item_reselected) {
+ theme_type_list->select(0);
+ _list_type_selected(0);
+ } else {
+ _update_type_items();
+ }
+ } else {
+ theme_type_list->set_disabled(true);
+ theme_type_list->add_item(TTR("None"));
+
+ edited_type = "";
+ _update_type_items();
+ }
+
+ updating = false;
+}
+
+void ThemeTypeEditor::_update_type_list_debounced() {
+ update_debounce_timer->start();
+}
+
+void ThemeTypeEditor::_update_add_type_options(const String &p_filter) {
+ add_type_options->clear();
+
+ List<StringName> names;
+ Theme::get_default()->get_type_list(&names);
+ names.sort_custom<StringName::AlphCompare>();
+
+ for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
+ if (!p_filter.is_subsequence_ofi(String(E->get()))) {
+ continue;
+ }
+
+ Ref<Texture2D> item_icon;
+ if (E->get() == "") {
+ item_icon = get_theme_icon("NodeDisabled", "EditorIcons");
+ } else {
+ item_icon = EditorNode::get_singleton()->get_class_icon(E->get(), "NodeDisabled");
+ }
+
+ add_type_options->add_item(E->get(), item_icon);
+ }
+}
+
+OrderedHashMap<StringName, bool> ThemeTypeEditor::_get_type_items(String p_type_name, void (Theme::*get_list_func)(StringName, List<StringName> *) const, bool include_default) {
+ OrderedHashMap<StringName, bool> items;
+ List<StringName> names;
+
+ if (include_default) {
+ names.clear();
+ (Theme::get_default().operator->()->*get_list_func)(p_type_name, &names);
+ names.sort_custom<StringName::AlphCompare>();
+ for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
+ items[E->get()] = false;
+ }
+ }
+
+ {
+ names.clear();
+ (edited_theme.operator->()->*get_list_func)(p_type_name, &names);
+ names.sort_custom<StringName::AlphCompare>();
+ for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
+ items[E->get()] = true;
+ }
+ }
+
+ List<StringName> keys;
+ for (OrderedHashMap<StringName, bool>::Element E = items.front(); E; E = E.next()) {
+ keys.push_back(E.key());
+ }
+ keys.sort_custom<StringName::AlphCompare>();
+
+ OrderedHashMap<StringName, bool> ordered_items;
+ for (List<StringName>::Element *E = keys.front(); E; E = E->next()) {
+ ordered_items[E->get()] = items[E->get()];
+ }
+
+ return ordered_items;
+}
+
+HBoxContainer *ThemeTypeEditor::_create_property_control(Theme::DataType p_data_type, String p_item_name, bool p_editable) {
+ HBoxContainer *item_control = memnew(HBoxContainer);
+
+ HBoxContainer *item_name_container = memnew(HBoxContainer);
+ item_name_container->set_h_size_flags(SIZE_EXPAND_FILL);
+ item_name_container->set_stretch_ratio(2.0);
+ item_control->add_child(item_name_container);
+
+ Label *item_name = memnew(Label);
+ item_name->set_h_size_flags(SIZE_EXPAND_FILL);
+ item_name->set_clip_text(true);
+ item_name->set_text(p_item_name);
+ item_name->set_tooltip(p_item_name);
+ item_name_container->add_child(item_name);
+
+ if (p_editable) {
+ LineEdit *item_name_edit = memnew(LineEdit);
+ item_name_edit->set_h_size_flags(SIZE_EXPAND_FILL);
+ item_name_edit->set_text(p_item_name);
+ item_name_container->add_child(item_name_edit);
+ item_name_edit->connect("text_entered", callable_mp(this, &ThemeTypeEditor::_item_rename_entered), varray(p_data_type, p_item_name, item_name_container));
+ item_name_edit->hide();
+
+ Button *item_rename_button = memnew(Button);
+ item_rename_button->set_icon(get_theme_icon("Edit", "EditorIcons"));
+ item_rename_button->set_tooltip(TTR("Rename Item"));
+ item_rename_button->set_flat(true);
+ item_name_container->add_child(item_rename_button);
+ item_rename_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_item_rename_cbk), varray(p_data_type, p_item_name, item_name_container));
+
+ Button *item_remove_button = memnew(Button);
+ item_remove_button->set_icon(get_theme_icon("Remove", "EditorIcons"));
+ item_remove_button->set_tooltip(TTR("Remove Item"));
+ item_remove_button->set_flat(true);
+ item_name_container->add_child(item_remove_button);
+ item_remove_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_item_remove_cbk), varray(p_data_type, p_item_name));
+
+ Button *item_rename_confirm_button = memnew(Button);
+ item_rename_confirm_button->set_icon(get_theme_icon("ImportCheck", "EditorIcons"));
+ item_rename_confirm_button->set_tooltip(TTR("Confirm Item Rename"));
+ item_rename_confirm_button->set_flat(true);
+ item_name_container->add_child(item_rename_confirm_button);
+ item_rename_confirm_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_item_rename_confirmed), varray(p_data_type, p_item_name, item_name_container));
+ item_rename_confirm_button->hide();
+
+ Button *item_rename_cancel_button = memnew(Button);
+ item_rename_cancel_button->set_icon(get_theme_icon("ImportFail", "EditorIcons"));
+ item_rename_cancel_button->set_tooltip(TTR("Cancel Item Rename"));
+ item_rename_cancel_button->set_flat(true);
+ item_name_container->add_child(item_rename_cancel_button);
+ item_rename_cancel_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_item_rename_canceled), varray(p_data_type, p_item_name, item_name_container));
+ item_rename_cancel_button->hide();
+ } else {
+ item_name->add_theme_color_override("font_color", get_theme_color("disabled_font_color", "Editor"));
+
+ Button *item_override_button = memnew(Button);
+ item_override_button->set_icon(get_theme_icon("Add", "EditorIcons"));
+ item_override_button->set_tooltip(TTR("Override Item"));
+ item_override_button->set_flat(true);
+ item_name_container->add_child(item_override_button);
+ item_override_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_item_override_cbk), varray(p_data_type, p_item_name));
+ }
+
+ return item_control;
+}
+
+void ThemeTypeEditor::_add_focusable(Control *p_control) {
+ focusables.append(p_control);
+}
+
+void ThemeTypeEditor::_update_type_items() {
+ bool show_default = show_default_items_button->is_pressed();
+ List<StringName> names;
+
+ focusables.clear();
+
+ // Colors.
+ {
+ for (int i = color_items_list->get_child_count() - 1; i >= 0; i--) {
+ Node *node = color_items_list->get_child(i);
+ node->queue_delete();
+ color_items_list->remove_child(node);
+ }
+
+ OrderedHashMap<StringName, bool> color_items = _get_type_items(edited_type, &Theme::get_color_list, show_default);
+ for (OrderedHashMap<StringName, bool>::Element E = color_items.front(); E; E = E.next()) {
+ HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_COLOR, E.key(), E.get());
+ ColorPickerButton *item_editor = memnew(ColorPickerButton);
+ item_editor->set_h_size_flags(SIZE_EXPAND_FILL);
+ item_control->add_child(item_editor);
+
+ if (E.get()) {
+ item_editor->set_pick_color(edited_theme->get_color(E.key(), edited_type));
+ item_editor->connect("color_changed", callable_mp(this, &ThemeTypeEditor::_color_item_changed), varray(E.key()));
+ } else {
+ item_editor->set_pick_color(Theme::get_default()->get_color(E.key(), edited_type));
+ item_editor->set_disabled(true);
+ }
+
+ _add_focusable(item_editor);
+ color_items_list->add_child(item_control);
+ }
+ }
+
+ // Constants.
+ {
+ for (int i = constant_items_list->get_child_count() - 1; i >= 0; i--) {
+ Node *node = constant_items_list->get_child(i);
+ node->queue_delete();
+ constant_items_list->remove_child(node);
+ }
+
+ OrderedHashMap<StringName, bool> constant_items = _get_type_items(edited_type, &Theme::get_constant_list, show_default);
+ for (OrderedHashMap<StringName, bool>::Element E = constant_items.front(); E; E = E.next()) {
+ HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_CONSTANT, E.key(), E.get());
+ SpinBox *item_editor = memnew(SpinBox);
+ item_editor->set_h_size_flags(SIZE_EXPAND_FILL);
+ item_editor->set_min(-100000);
+ item_editor->set_max(100000);
+ item_editor->set_step(1);
+ item_editor->set_allow_lesser(true);
+ item_editor->set_allow_greater(true);
+ item_control->add_child(item_editor);
+
+ if (E.get()) {
+ item_editor->set_value(edited_theme->get_constant(E.key(), edited_type));
+ item_editor->connect("value_changed", callable_mp(this, &ThemeTypeEditor::_constant_item_changed), varray(E.key()));
+ } else {
+ item_editor->set_value(Theme::get_default()->get_constant(E.key(), edited_type));
+ item_editor->set_editable(false);
+ }
+
+ _add_focusable(item_editor);
+ constant_items_list->add_child(item_control);
+ }
+ }
+
+ // Fonts.
+ {
+ for (int i = font_items_list->get_child_count() - 1; i >= 0; i--) {
+ Node *node = font_items_list->get_child(i);
+ node->queue_delete();
+ font_items_list->remove_child(node);
+ }
+
+ OrderedHashMap<StringName, bool> font_items = _get_type_items(edited_type, &Theme::get_font_list, show_default);
+ for (OrderedHashMap<StringName, bool>::Element E = font_items.front(); E; E = E.next()) {
+ HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_FONT, E.key(), E.get());
+ EditorResourcePicker *item_editor = memnew(EditorResourcePicker);
+ item_editor->set_h_size_flags(SIZE_EXPAND_FILL);
+ item_editor->set_base_type("Font");
+ item_control->add_child(item_editor);
+
+ if (E.get()) {
+ if (edited_theme->has_font(E.key(), edited_type)) {
+ item_editor->set_edited_resource(edited_theme->get_font(E.key(), edited_type));
+ } else {
+ item_editor->set_edited_resource(RES());
+ }
+ item_editor->connect("resource_selected", callable_mp(this, &ThemeTypeEditor::_edit_resource_item), varray(item_control));
+ item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_font_item_changed), varray(E.key()));
+ } else {
+ if (Theme::get_default()->has_font(E.key(), edited_type)) {
+ item_editor->set_edited_resource(Theme::get_default()->get_font(E.key(), edited_type));
+ } else {
+ item_editor->set_edited_resource(RES());
+ }
+ item_editor->set_editable(false);
+ }
+
+ _add_focusable(item_editor);
+ font_items_list->add_child(item_control);
+ }
+ }
+
+ // Fonts sizes.
+ {
+ for (int i = font_size_items_list->get_child_count() - 1; i >= 0; i--) {
+ Node *node = font_size_items_list->get_child(i);
+ node->queue_delete();
+ font_size_items_list->remove_child(node);
+ }
+
+ OrderedHashMap<StringName, bool> font_size_items = _get_type_items(edited_type, &Theme::get_font_size_list, show_default);
+ for (OrderedHashMap<StringName, bool>::Element E = font_size_items.front(); E; E = E.next()) {
+ HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_FONT_SIZE, E.key(), E.get());
+ SpinBox *item_editor = memnew(SpinBox);
+ item_editor->set_h_size_flags(SIZE_EXPAND_FILL);
+ item_editor->set_min(-100000);
+ item_editor->set_max(100000);
+ item_editor->set_step(1);
+ item_editor->set_allow_lesser(true);
+ item_editor->set_allow_greater(true);
+ item_control->add_child(item_editor);
+
+ if (E.get()) {
+ item_editor->set_value(edited_theme->get_font_size(E.key(), edited_type));
+ item_editor->connect("value_changed", callable_mp(this, &ThemeTypeEditor::_font_size_item_changed), varray(E.key()));
+ } else {
+ item_editor->set_value(Theme::get_default()->get_font_size(E.key(), edited_type));
+ item_editor->set_editable(false);
+ }
+
+ _add_focusable(item_editor);
+ font_size_items_list->add_child(item_control);
+ }
+ }
+
+ // Icons.
+ {
+ for (int i = icon_items_list->get_child_count() - 1; i >= 0; i--) {
+ Node *node = icon_items_list->get_child(i);
+ node->queue_delete();
+ icon_items_list->remove_child(node);
+ }
+
+ OrderedHashMap<StringName, bool> icon_items = _get_type_items(edited_type, &Theme::get_icon_list, show_default);
+ for (OrderedHashMap<StringName, bool>::Element E = icon_items.front(); E; E = E.next()) {
+ HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_ICON, E.key(), E.get());
+ EditorResourcePicker *item_editor = memnew(EditorResourcePicker);
+ item_editor->set_h_size_flags(SIZE_EXPAND_FILL);
+ item_editor->set_base_type("Texture2D");
+ item_control->add_child(item_editor);
+
+ if (E.get()) {
+ if (edited_theme->has_icon(E.key(), edited_type)) {
+ item_editor->set_edited_resource(edited_theme->get_icon(E.key(), edited_type));
+ } else {
+ item_editor->set_edited_resource(RES());
+ }
+ item_editor->connect("resource_selected", callable_mp(this, &ThemeTypeEditor::_edit_resource_item), varray(item_control));
+ item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_icon_item_changed), varray(E.key()));
+ } else {
+ if (Theme::get_default()->has_icon(E.key(), edited_type)) {
+ item_editor->set_edited_resource(Theme::get_default()->get_icon(E.key(), edited_type));
+ } else {
+ item_editor->set_edited_resource(RES());
+ }
+ item_editor->set_editable(false);
+ }
+
+ _add_focusable(item_editor);
+ icon_items_list->add_child(item_control);
+ }
+ }
+
+ // Styleboxes.
+ {
+ for (int i = stylebox_items_list->get_child_count() - 1; i >= 0; i--) {
+ Node *node = stylebox_items_list->get_child(i);
+ node->queue_delete();
+ stylebox_items_list->remove_child(node);
+ }
+
+ if (leading_stylebox.pinned) {
+ HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_STYLEBOX, leading_stylebox.item_name, true);
+ EditorResourcePicker *item_editor = memnew(EditorResourcePicker);
+ item_editor->set_h_size_flags(SIZE_EXPAND_FILL);
+ item_editor->set_stretch_ratio(1.5);
+ item_editor->set_base_type("StyleBox");
+
+ Button *pin_leader_button = memnew(Button);
+ pin_leader_button->set_flat(true);
+ pin_leader_button->set_toggle_mode(true);
+ pin_leader_button->set_pressed(true);
+ pin_leader_button->set_icon(get_theme_icon("Pin", "EditorIcons"));
+ pin_leader_button->set_tooltip(TTR("Unpin this StyleBox as a main style."));
+ item_control->add_child(pin_leader_button);
+ pin_leader_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_unpin_leading_stylebox));
+
+ item_control->add_child(item_editor);
+
+ if (leading_stylebox.stylebox.is_valid()) {
+ item_editor->set_edited_resource(leading_stylebox.stylebox);
+ } else {
+ item_editor->set_edited_resource(RES());
+ }
+ item_editor->connect("resource_selected", callable_mp(this, &ThemeTypeEditor::_edit_resource_item), varray(item_control));
+ item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_stylebox_item_changed), varray(leading_stylebox.item_name));
+
+ stylebox_items_list->add_child(item_control);
+ stylebox_items_list->add_child(memnew(HSeparator));
+ }
+
+ OrderedHashMap<StringName, bool> stylebox_items = _get_type_items(edited_type, &Theme::get_stylebox_list, show_default);
+ for (OrderedHashMap<StringName, bool>::Element E = stylebox_items.front(); E; E = E.next()) {
+ if (leading_stylebox.pinned && leading_stylebox.item_name == E.key()) {
+ continue;
+ }
+
+ HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_STYLEBOX, E.key(), E.get());
+ EditorResourcePicker *item_editor = memnew(EditorResourcePicker);
+ item_editor->set_h_size_flags(SIZE_EXPAND_FILL);
+ item_editor->set_stretch_ratio(1.5);
+ item_editor->set_base_type("StyleBox");
+
+ if (E.get()) {
+ Ref<StyleBox> stylebox_value;
+ if (edited_theme->has_stylebox(E.key(), edited_type)) {
+ stylebox_value = edited_theme->get_stylebox(E.key(), edited_type);
+ item_editor->set_edited_resource(stylebox_value);
+ } else {
+ item_editor->set_edited_resource(RES());
+ }
+ item_editor->connect("resource_selected", callable_mp(this, &ThemeTypeEditor::_edit_resource_item), varray(item_control));
+ item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_stylebox_item_changed), varray(E.key()));
+
+ Button *pin_leader_button = memnew(Button);
+ pin_leader_button->set_flat(true);
+ pin_leader_button->set_toggle_mode(true);
+ pin_leader_button->set_icon(get_theme_icon("Pin", "EditorIcons"));
+ pin_leader_button->set_tooltip(TTR("Pin this StyleBox as a main style. Editing its properties will update the same properties in all other StyleBoxes of this type."));
+ item_control->add_child(pin_leader_button);
+ pin_leader_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_pin_leading_stylebox), varray(stylebox_value, E.key()));
+ } else {
+ if (Theme::get_default()->has_stylebox(E.key(), edited_type)) {
+ item_editor->set_edited_resource(Theme::get_default()->get_stylebox(E.key(), edited_type));
+ } else {
+ item_editor->set_edited_resource(RES());
+ }
+ item_editor->set_editable(false);
+ }
+
+ item_control->add_child(item_editor);
+ _add_focusable(item_editor);
+ stylebox_items_list->add_child(item_control);
+ }
+ }
+}
+
+void ThemeTypeEditor::_list_type_selected(int p_index) {
+ edited_type = theme_type_list->get_item_text(p_index);
+ _update_type_items();
+}
+
+void ThemeTypeEditor::_add_type_button_cbk() {
+ add_type_dialog->popup_centered(Size2(560, 420) * EDSCALE);
+ add_type_filter->grab_focus();
+}
+
+void ThemeTypeEditor::_add_type_filter_cbk(const String &p_value) {
+ _update_add_type_options(p_value);
+}
+
+void ThemeTypeEditor::_add_type_options_cbk(int p_index) {
+ add_type_filter->set_text(add_type_options->get_item_text(p_index));
+}
+
+void ThemeTypeEditor::_add_type_dialog_confirmed() {
+ select_type(add_type_filter->get_text().strip_edges());
+}
+
+void ThemeTypeEditor::_add_type_dialog_entered(const String &p_value) {
+ select_type(p_value.strip_edges());
+ add_type_dialog->hide();
+}
+
+void ThemeTypeEditor::_add_type_dialog_activated(int p_index) {
+ select_type(add_type_options->get_item_text(p_index));
+ add_type_dialog->hide();
+}
+
+void ThemeTypeEditor::_add_default_type_items() {
+ List<StringName> names;
+
+ updating = true;
+ // Prevent changes from immediatelly being reported while the operation is still ongoing.
+ edited_theme->_freeze_change_propagation();
+
+ {
+ names.clear();
+ Theme::get_default()->get_icon_list(edited_type, &names);
+ for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
+ if (!edited_theme->has_icon(E->get(), edited_type)) {
+ edited_theme->set_icon(E->get(), edited_type, Ref<Texture2D>());
+ }
+ }
+ }
+ {
+ names.clear();
+ Theme::get_default()->get_stylebox_list(edited_type, &names);
+ for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
+ if (!edited_theme->has_stylebox(E->get(), edited_type)) {
+ edited_theme->set_stylebox(E->get(), edited_type, Ref<StyleBox>());
+ }
+ }
+ }
+ {
+ names.clear();
+ Theme::get_default()->get_font_list(edited_type, &names);
+ for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
+ if (!edited_theme->has_font(E->get(), edited_type)) {
+ edited_theme->set_font(E->get(), edited_type, Ref<Font>());
+ }
+ }
+ }
+ {
+ names.clear();
+ Theme::get_default()->get_font_size_list(edited_type, &names);
+ for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
+ if (!edited_theme->has_font_size(E->get(), edited_type)) {
+ edited_theme->set_font_size(E->get(), edited_type, Theme::get_default()->get_font_size(E->get(), edited_type));
+ }
+ }
+ }
+ {
+ names.clear();
+ Theme::get_default()->get_color_list(edited_type, &names);
+ for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
+ if (!edited_theme->has_color(E->get(), edited_type)) {
+ edited_theme->set_color(E->get(), edited_type, Theme::get_default()->get_color(E->get(), edited_type));
+ }
+ }
+ }
+ {
+ names.clear();
+ Theme::get_default()->get_constant_list(edited_type, &names);
+ for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
+ if (!edited_theme->has_constant(E->get(), edited_type)) {
+ edited_theme->set_constant(E->get(), edited_type, Theme::get_default()->get_constant(E->get(), edited_type));
+ }
+ }
+ }
+
+ // Allow changes to be reported now that the operation is finished.
+ edited_theme->_unfreeze_and_propagate_changes();
+ updating = false;
+
+ _update_type_items();
+}
+
+void ThemeTypeEditor::_item_add_cbk(int p_data_type, Control *p_control) {
+ LineEdit *le = Object::cast_to<LineEdit>(p_control);
+ if (le->get_text().strip_edges().is_empty()) {
+ return;
+ }
+
+ String item_name = le->get_text().strip_edges();
+ switch (p_data_type) {
+ case Theme::DATA_TYPE_COLOR: {
+ edited_theme->set_color(item_name, edited_type, Color());
+ } break;
+ case Theme::DATA_TYPE_CONSTANT: {
+ edited_theme->set_constant(item_name, edited_type, 0);
+ } break;
+ case Theme::DATA_TYPE_FONT: {
+ edited_theme->set_font(item_name, edited_type, Ref<Font>());
+ } break;
+ case Theme::DATA_TYPE_FONT_SIZE: {
+ edited_theme->set_font_size(item_name, edited_type, -1);
+ } break;
+ case Theme::DATA_TYPE_ICON: {
+ edited_theme->set_icon(item_name, edited_type, Ref<Texture2D>());
+ } break;
+ case Theme::DATA_TYPE_STYLEBOX: {
+ edited_theme->set_stylebox(item_name, edited_type, Ref<StyleBox>());
+ } break;
+ }
+
+ le->set_text("");
+}
+
+void ThemeTypeEditor::_item_add_lineedit_cbk(String p_value, int p_data_type, Control *p_control) {
+ _item_add_cbk(p_data_type, p_control);
+}
+
+void ThemeTypeEditor::_item_override_cbk(int p_data_type, String p_item_name) {
+ switch (p_data_type) {
+ case Theme::DATA_TYPE_COLOR: {
+ edited_theme->set_color(p_item_name, edited_type, Theme::get_default()->get_color(p_item_name, edited_type));
+ } break;
+ case Theme::DATA_TYPE_CONSTANT: {
+ edited_theme->set_constant(p_item_name, edited_type, Theme::get_default()->get_constant(p_item_name, edited_type));
+ } break;
+ case Theme::DATA_TYPE_FONT: {
+ edited_theme->set_font(p_item_name, edited_type, Ref<Font>());
+ } break;
+ case Theme::DATA_TYPE_FONT_SIZE: {
+ edited_theme->set_font_size(p_item_name, edited_type, Theme::get_default()->get_font_size(p_item_name, edited_type));
+ } break;
+ case Theme::DATA_TYPE_ICON: {
+ edited_theme->set_icon(p_item_name, edited_type, Ref<Texture2D>());
+ } break;
+ case Theme::DATA_TYPE_STYLEBOX: {
+ edited_theme->set_stylebox(p_item_name, edited_type, Ref<StyleBox>());
+ } break;
+ }
+}
+
+void ThemeTypeEditor::_item_remove_cbk(int p_data_type, String p_item_name) {
+ switch (p_data_type) {
+ case Theme::DATA_TYPE_COLOR: {
+ edited_theme->clear_color(p_item_name, edited_type);
+ } break;
+ case Theme::DATA_TYPE_CONSTANT: {
+ edited_theme->clear_constant(p_item_name, edited_type);
+ } break;
+ case Theme::DATA_TYPE_FONT: {
+ edited_theme->clear_font(p_item_name, edited_type);
+ } break;
+ case Theme::DATA_TYPE_FONT_SIZE: {
+ edited_theme->clear_font_size(p_item_name, edited_type);
+ } break;
+ case Theme::DATA_TYPE_ICON: {
+ edited_theme->clear_icon(p_item_name, edited_type);
+ } break;
+ case Theme::DATA_TYPE_STYLEBOX: {
+ edited_theme->clear_stylebox(p_item_name, edited_type);
+ } break;
+ }
+}
+
+void ThemeTypeEditor::_item_rename_cbk(int p_data_type, String p_item_name, Control *p_control) {
+ // Label
+ Object::cast_to<Label>(p_control->get_child(0))->hide();
+ // Label buttons
+ Object::cast_to<Button>(p_control->get_child(2))->hide();
+ Object::cast_to<Button>(p_control->get_child(3))->hide();
+
+ // LineEdit
+ Object::cast_to<LineEdit>(p_control->get_child(1))->set_text(p_item_name);
+ Object::cast_to<LineEdit>(p_control->get_child(1))->show();
+ // LineEdit buttons
+ Object::cast_to<Button>(p_control->get_child(4))->show();
+ Object::cast_to<Button>(p_control->get_child(5))->show();
+}
+
+void ThemeTypeEditor::_item_rename_confirmed(int p_data_type, String p_item_name, Control *p_control) {
+ LineEdit *le = Object::cast_to<LineEdit>(p_control->get_child(1));
+ if (le->get_text().strip_edges().is_empty()) {
+ return;
+ }
+
+ String new_name = le->get_text().strip_edges();
+ if (new_name == p_item_name) {
+ _item_rename_canceled(p_data_type, p_item_name, p_control);
+ return;
+ }
+
+ switch (p_data_type) {
+ case Theme::DATA_TYPE_COLOR: {
+ edited_theme->rename_color(p_item_name, new_name, edited_type);
+ } break;
+ case Theme::DATA_TYPE_CONSTANT: {
+ edited_theme->rename_constant(p_item_name, new_name, edited_type);
+ } break;
+ case Theme::DATA_TYPE_FONT: {
+ edited_theme->rename_font(p_item_name, new_name, edited_type);
+ } break;
+ case Theme::DATA_TYPE_FONT_SIZE: {
+ edited_theme->rename_font_size(p_item_name, new_name, edited_type);
+ } break;
+ case Theme::DATA_TYPE_ICON: {
+ edited_theme->rename_icon(p_item_name, new_name, edited_type);
+ } break;
+ case Theme::DATA_TYPE_STYLEBOX: {
+ edited_theme->rename_stylebox(p_item_name, new_name, edited_type);
+ } break;
+ }
+}
+
+void ThemeTypeEditor::_item_rename_entered(String p_value, int p_data_type, String p_item_name, Control *p_control) {
+ _item_rename_confirmed(p_data_type, p_item_name, p_control);
+}
+
+void ThemeTypeEditor::_item_rename_canceled(int p_data_type, String p_item_name, Control *p_control) {
+ // LineEdit
+ Object::cast_to<LineEdit>(p_control->get_child(1))->hide();
+ // LineEdit buttons
+ Object::cast_to<Button>(p_control->get_child(4))->hide();
+ Object::cast_to<Button>(p_control->get_child(5))->hide();
+
+ // Label
+ Object::cast_to<Label>(p_control->get_child(0))->show();
+ // Label buttons
+ Object::cast_to<Button>(p_control->get_child(2))->show();
+ Object::cast_to<Button>(p_control->get_child(3))->show();
+}
+
+void ThemeTypeEditor::_color_item_changed(Color p_value, String p_item_name) {
+ edited_theme->set_color(p_item_name, edited_type, p_value);
+}
+
+void ThemeTypeEditor::_constant_item_changed(float p_value, String p_item_name) {
+ edited_theme->set_constant(p_item_name, edited_type, int(p_value));
+}
+
+void ThemeTypeEditor::_font_size_item_changed(float p_value, String p_item_name) {
+ edited_theme->set_font_size(p_item_name, edited_type, int(p_value));
+}
+
+void ThemeTypeEditor::_edit_resource_item(RES p_resource, Control *p_editor) {
+ EditorNode::get_singleton()->edit_resource(p_resource);
+}
+
+void ThemeTypeEditor::_font_item_changed(Ref<Font> p_value, String p_item_name) {
+ edited_theme->set_font(p_item_name, edited_type, p_value);
+}
+
+void ThemeTypeEditor::_icon_item_changed(Ref<Texture2D> p_value, String p_item_name) {
+ edited_theme->set_icon(p_item_name, edited_type, p_value);
+}
+
+void ThemeTypeEditor::_stylebox_item_changed(Ref<StyleBox> p_value, String p_item_name) {
+ edited_theme->set_stylebox(p_item_name, edited_type, p_value);
+
+ if (leading_stylebox.pinned && leading_stylebox.item_name == p_item_name) {
+ if (leading_stylebox.stylebox.is_valid()) {
+ leading_stylebox.stylebox->disconnect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
+ }
+
+ leading_stylebox.stylebox = p_value;
+ leading_stylebox.ref_stylebox = (p_value.is_valid() ? p_value->duplicate() : RES());
+ if (p_value.is_valid()) {
+ leading_stylebox.stylebox->connect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
+ }
+ }
+}
+
+void ThemeTypeEditor::_pin_leading_stylebox(Ref<StyleBox> p_stylebox, String p_item_name) {
+ if (leading_stylebox.stylebox.is_valid()) {
+ leading_stylebox.stylebox->disconnect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
+ }
+
+ LeadingStylebox leader;
+ leader.pinned = true;
+ leader.item_name = p_item_name;
+ leader.stylebox = p_stylebox;
+ leader.ref_stylebox = (p_stylebox.is_valid() ? p_stylebox->duplicate() : RES());
+
+ leading_stylebox = leader;
+ if (leading_stylebox.stylebox.is_valid()) {
+ leading_stylebox.stylebox->connect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
+ }
+
+ _update_type_items();
+}
+
+void ThemeTypeEditor::_unpin_leading_stylebox() {
+ if (leading_stylebox.stylebox.is_valid()) {
+ leading_stylebox.stylebox->disconnect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
+ }
+
+ LeadingStylebox leader;
+ leader.pinned = false;
+ leading_stylebox = leader;
+
+ _update_type_items();
+}
+
+void ThemeTypeEditor::_update_stylebox_from_leading() {
+ if (!leading_stylebox.pinned || leading_stylebox.stylebox.is_null()) {
+ return;
+ }
+
+ // Prevent changes from immediatelly being reported while the operation is still ongoing.
+ edited_theme->_freeze_change_propagation();
+
+ List<StringName> names;
+ edited_theme->get_stylebox_list(edited_type, &names);
+ List<Ref<StyleBox>> styleboxes;
+ for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
+ if (E->get() == leading_stylebox.item_name) {
+ continue;
+ }
+
+ Ref<StyleBox> sb = edited_theme->get_stylebox(E->get(), edited_type);
+ if (sb->get_class() == leading_stylebox.stylebox->get_class()) {
+ styleboxes.push_back(sb);
+ }
+ }
+
+ List<PropertyInfo> props;
+ leading_stylebox.stylebox->get_property_list(&props);
+ for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
+ if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) {
+ continue;
+ }
+
+ Variant value = leading_stylebox.stylebox->get(E->get().name);
+ Variant ref_value = leading_stylebox.ref_stylebox->get(E->get().name);
+ if (value == ref_value) {
+ continue;
+ }
+
+ for (List<Ref<StyleBox>>::Element *F = styleboxes.front(); F; F = F->next()) {
+ Ref<StyleBox> sb = F->get();
+ sb->set(E->get().name, value);
+ }
+ }
+
+ leading_stylebox.ref_stylebox = leading_stylebox.stylebox->duplicate();
+
+ // Allow changes to be reported now that the operation is finished.
+ edited_theme->_unfreeze_and_propagate_changes();
+}
+
+void ThemeTypeEditor::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
+ add_type_button->set_icon(get_theme_icon("Add", "EditorIcons"));
+
+ data_type_tabs->set_tab_icon(0, get_theme_icon("Color", "EditorIcons"));
+ data_type_tabs->set_tab_icon(1, get_theme_icon("MemberConstant", "EditorIcons"));
+ data_type_tabs->set_tab_icon(2, get_theme_icon("Font", "EditorIcons"));
+ data_type_tabs->set_tab_icon(3, get_theme_icon("FontSize", "EditorIcons"));
+ data_type_tabs->set_tab_icon(4, get_theme_icon("ImageTexture", "EditorIcons"));
+ data_type_tabs->set_tab_icon(5, get_theme_icon("StyleBoxFlat", "EditorIcons"));
+
+ data_type_tabs->add_theme_style_override("tab_selected", get_theme_stylebox("tab_selected_odd", "TabContainer"));
+ data_type_tabs->add_theme_style_override("panel", get_theme_stylebox("panel_odd", "TabContainer"));
+
+ _update_add_type_options();
+ } break;
+ }
+}
+
+void ThemeTypeEditor::set_edited_theme(const Ref<Theme> &p_theme) {
+ if (edited_theme.is_valid()) {
+ edited_theme->disconnect("changed", callable_mp(this, &ThemeTypeEditor::_update_type_list_debounced));
+ }
+
+ edited_theme = p_theme;
+ edited_theme->connect("changed", callable_mp(this, &ThemeTypeEditor::_update_type_list_debounced));
+ _update_type_list();
+}
+
+void ThemeTypeEditor::select_type(String p_type_name) {
+ edited_type = p_type_name;
+ bool type_exists = false;
+
+ for (int i = 0; i < theme_type_list->get_item_count(); i++) {
+ String type_name = theme_type_list->get_item_text(i);
+ if (type_name == edited_type) {
+ theme_type_list->select(i);
+ type_exists = true;
+ break;
+ }
+ }
+
+ if (type_exists) {
+ _update_type_items();
+ } else {
+ edited_theme->add_icon_type(edited_type);
+ edited_theme->add_stylebox_type(edited_type);
+ edited_theme->add_font_type(edited_type);
+ edited_theme->add_font_size_type(edited_type);
+ edited_theme->add_color_type(edited_type);
+ edited_theme->add_constant_type(edited_type);
+
+ _update_type_list();
+ }
+}
+
+ThemeTypeEditor::ThemeTypeEditor() {
+ VBoxContainer *main_vb = memnew(VBoxContainer);
+ add_child(main_vb);
+
+ HBoxContainer *type_list_hb = memnew(HBoxContainer);
+ main_vb->add_child(type_list_hb);
+
+ Label *type_list_label = memnew(Label);
+ type_list_label->set_text(TTR("Type:"));
+ type_list_hb->add_child(type_list_label);
+
+ theme_type_list = memnew(OptionButton);
+ theme_type_list->set_h_size_flags(SIZE_EXPAND_FILL);
+ type_list_hb->add_child(theme_type_list);
+ theme_type_list->connect("item_selected", callable_mp(this, &ThemeTypeEditor::_list_type_selected));
+
+ add_type_button = memnew(Button);
+ add_type_button->set_tooltip(TTR("Add Type"));
+ type_list_hb->add_child(add_type_button);
+ add_type_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_add_type_button_cbk));
+
+ add_type_dialog = memnew(ConfirmationDialog);
+ add_type_dialog->set_title(TTR("Add Item Type"));
+ type_list_hb->add_child(add_type_dialog);
+ add_type_dialog->connect("confirmed", callable_mp(this, &ThemeTypeEditor::_add_type_dialog_confirmed));
+
+ VBoxContainer *add_type_vb = memnew(VBoxContainer);
+ add_type_dialog->add_child(add_type_vb);
+
+ Label *add_type_filter_label = memnew(Label);
+ add_type_filter_label->set_text(TTR("Name:"));
+ add_type_vb->add_child(add_type_filter_label);
+ add_type_filter = memnew(LineEdit);
+ add_type_vb->add_child(add_type_filter);
+ add_type_filter->connect("text_changed", callable_mp(this, &ThemeTypeEditor::_add_type_filter_cbk));
+ add_type_filter->connect("text_entered", callable_mp(this, &ThemeTypeEditor::_add_type_dialog_entered));
+ Label *add_type_options_label = memnew(Label);
+ add_type_options_label->set_text(TTR("Node Types:"));
+ add_type_vb->add_child(add_type_options_label);
+ add_type_options = memnew(ItemList);
+ add_type_options->set_v_size_flags(SIZE_EXPAND_FILL);
+ add_type_vb->add_child(add_type_options);
+ add_type_options->connect("item_selected", callable_mp(this, &ThemeTypeEditor::_add_type_options_cbk));
+ add_type_options->connect("item_activated", callable_mp(this, &ThemeTypeEditor::_add_type_dialog_activated));
+
+ HBoxContainer *type_controls = memnew(HBoxContainer);
+ main_vb->add_child(type_controls);
+
+ show_default_items_button = memnew(CheckButton);
+ show_default_items_button->set_h_size_flags(SIZE_EXPAND_FILL);
+ show_default_items_button->set_text(TTR("Show Default"));
+ show_default_items_button->set_tooltip(TTR("Show default type items alongside items that have been overridden."));
+ show_default_items_button->set_pressed(true);
+ type_controls->add_child(show_default_items_button);
+ show_default_items_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_update_type_items));
+
+ Button *add_default_items_button = memnew(Button);
+ add_default_items_button->set_h_size_flags(SIZE_EXPAND_FILL);
+ add_default_items_button->set_text(TTR("Override All"));
+ add_default_items_button->set_tooltip(TTR("Override all default type items."));
+ type_controls->add_child(add_default_items_button);
+ add_default_items_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_add_default_type_items));
+
+ data_type_tabs = memnew(TabContainer);
+ main_vb->add_child(data_type_tabs);
+ data_type_tabs->set_v_size_flags(SIZE_EXPAND_FILL);
+ data_type_tabs->set_use_hidden_tabs_for_min_size(true);
+
+ color_items_list = _create_item_list(Theme::DATA_TYPE_COLOR);
+ constant_items_list = _create_item_list(Theme::DATA_TYPE_CONSTANT);
+ font_items_list = _create_item_list(Theme::DATA_TYPE_FONT);
+ font_size_items_list = _create_item_list(Theme::DATA_TYPE_FONT_SIZE);
+ icon_items_list = _create_item_list(Theme::DATA_TYPE_ICON);
+ stylebox_items_list = _create_item_list(Theme::DATA_TYPE_STYLEBOX);
+
+ update_debounce_timer = memnew(Timer);
+ update_debounce_timer->set_one_shot(true);
+ update_debounce_timer->set_wait_time(0.5);
+ update_debounce_timer->connect("timeout", callable_mp(this, &ThemeTypeEditor::_update_type_list));
+ add_child(update_debounce_timer);
+}
+
void ThemeEditor::edit(const Ref<Theme> &p_theme) {
+ if (theme == p_theme) {
+ return;
+ }
+
theme = p_theme;
+ theme_type_editor->set_edited_theme(p_theme);
theme_edit_dialog->set_edited_theme(p_theme);
- main_panel->set_theme(p_theme);
- main_container->set_theme(p_theme);
-}
-void ThemeEditor::_propagate_redraw(Control *p_at) {
- p_at->notification(NOTIFICATION_THEME_CHANGED);
- p_at->minimum_size_changed();
- p_at->update();
- for (int i = 0; i < p_at->get_child_count(); i++) {
- Control *a = Object::cast_to<Control>(p_at->get_child(i));
- if (a) {
- _propagate_redraw(a);
+ for (int i = 0; i < preview_tabs_content->get_child_count(); i++) {
+ ThemeEditorPreview *preview_tab = Object::cast_to<ThemeEditorPreview>(preview_tabs_content->get_child(i));
+ if (!preview_tab) {
+ continue;
}
+
+ preview_tab->set_preview_theme(p_theme);
}
+
+ theme_name->set_text(TTR("Theme") + ": " + theme->get_path().get_file());
+}
+
+Ref<Theme> ThemeEditor::get_edited_theme() {
+ return theme;
}
-void ThemeEditor::_refresh_interval() {
- _propagate_redraw(main_panel);
- _propagate_redraw(main_container);
+void ThemeEditor::_theme_save_button_cbk(bool p_save_as) {
+ ERR_FAIL_COND_MSG(theme.is_null(), "Invalid state of the Theme Editor; the Theme resource is missing.");
+
+ if (p_save_as) {
+ EditorNode::get_singleton()->save_resource_as(theme);
+ } else {
+ EditorNode::get_singleton()->save_resource(theme);
+ }
}
void ThemeEditor::_theme_edit_button_cbk() {
theme_edit_dialog->popup_centered(Size2(850, 760) * EDSCALE);
}
+void ThemeEditor::_add_preview_button_cbk() {
+ preview_scene_dialog->popup_file_dialog();
+}
+
+void ThemeEditor::_preview_scene_dialog_cbk(const String &p_path) {
+ SceneThemeEditorPreview *preview_tab = memnew(SceneThemeEditorPreview);
+ if (!preview_tab->set_preview_scene(p_path)) {
+ return;
+ }
+
+ _add_preview_tab(preview_tab, p_path.get_file(), get_theme_icon("PackedScene", "EditorIcons"));
+ preview_tab->connect("scene_invalidated", callable_mp(this, &ThemeEditor::_remove_preview_tab_invalid), varray(preview_tab));
+ preview_tab->connect("scene_reloaded", callable_mp(this, &ThemeEditor::_update_preview_tab), varray(preview_tab));
+}
+
+void ThemeEditor::_add_preview_tab(ThemeEditorPreview *p_preview_tab, const String &p_preview_name, const Ref<Texture2D> &p_icon) {
+ p_preview_tab->set_preview_theme(theme);
+
+ preview_tabs->add_tab(p_preview_name, p_icon);
+ preview_tabs_content->add_child(p_preview_tab);
+ preview_tabs->set_tab_right_button(preview_tabs->get_tab_count() - 1, EditorNode::get_singleton()->get_gui_base()->get_theme_icon("close", "Tabs"));
+ p_preview_tab->connect("control_picked", callable_mp(this, &ThemeEditor::_preview_control_picked));
+
+ preview_tabs->set_current_tab(preview_tabs->get_tab_count() - 1);
+}
+
+void ThemeEditor::_change_preview_tab(int p_tab) {
+ ERR_FAIL_INDEX_MSG(p_tab, preview_tabs_content->get_child_count(), "Attempting to open a preview tab that doesn't exist.");
+
+ for (int i = 0; i < preview_tabs_content->get_child_count(); i++) {
+ Control *c = Object::cast_to<Control>(preview_tabs_content->get_child(i));
+ if (!c) {
+ continue;
+ }
+
+ c->set_visible(i == p_tab);
+ }
+}
+
+void ThemeEditor::_remove_preview_tab(int p_tab) {
+ ERR_FAIL_INDEX_MSG(p_tab, preview_tabs_content->get_child_count(), "Attempting to remove a preview tab that doesn't exist.");
+
+ ThemeEditorPreview *preview_tab = Object::cast_to<ThemeEditorPreview>(preview_tabs_content->get_child(p_tab));
+ ERR_FAIL_COND_MSG(Object::cast_to<DefaultThemeEditorPreview>(preview_tab), "Attemptying to remove the default preview tab.");
+
+ if (preview_tab) {
+ preview_tab->disconnect("control_picked", callable_mp(this, &ThemeEditor::_preview_control_picked));
+ if (preview_tab->is_connected("scene_invalidated", callable_mp(this, &ThemeEditor::_remove_preview_tab_invalid))) {
+ preview_tab->disconnect("scene_invalidated", callable_mp(this, &ThemeEditor::_remove_preview_tab_invalid));
+ }
+ if (preview_tab->is_connected("scene_reloaded", callable_mp(this, &ThemeEditor::_update_preview_tab))) {
+ preview_tab->disconnect("scene_reloaded", callable_mp(this, &ThemeEditor::_update_preview_tab));
+ }
+
+ preview_tabs_content->remove_child(preview_tab);
+ preview_tabs->remove_tab(p_tab);
+ _change_preview_tab(preview_tabs->get_current_tab());
+ }
+}
+
+void ThemeEditor::_remove_preview_tab_invalid(Node *p_tab_control) {
+ int tab_index = p_tab_control->get_index();
+ _remove_preview_tab(tab_index);
+}
+
+void ThemeEditor::_update_preview_tab(Node *p_tab_control) {
+ if (!Object::cast_to<SceneThemeEditorPreview>(p_tab_control)) {
+ return;
+ }
+
+ int tab_index = p_tab_control->get_index();
+ SceneThemeEditorPreview *scene_preview = Object::cast_to<SceneThemeEditorPreview>(p_tab_control);
+ preview_tabs->set_tab_title(tab_index, scene_preview->get_preview_scene_path().get_file());
+}
+
+void ThemeEditor::_preview_control_picked(String p_class_name) {
+ theme_type_editor->select_type(p_class_name);
+}
+
void ThemeEditor::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_PROCESS: {
- time_left -= get_process_delta_time();
- if (time_left < 0) {
- time_left = 1.5;
- _refresh_interval();
- }
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
+ preview_tabs->add_theme_style_override("tab_selected", get_theme_stylebox("ThemeEditorPreviewFG", "EditorStyles"));
+ preview_tabs->add_theme_style_override("tab_unselected", get_theme_stylebox("ThemeEditorPreviewBG", "EditorStyles"));
+ preview_tabs_content->add_theme_style_override("panel", get_theme_stylebox("panel_odd", "TabContainer"));
+
+ add_preview_button->set_icon(get_theme_icon("Add", "EditorIcons"));
} break;
}
}
-void ThemeEditor::_bind_methods() {
-}
-
ThemeEditor::ThemeEditor() {
HBoxContainer *top_menu = memnew(HBoxContainer);
add_child(top_menu);
- top_menu->add_child(memnew(Label(TTR("Preview:"))));
+ theme_name = memnew(Label);
+ theme_name->set_text(TTR("Theme") + ": ");
+ top_menu->add_child(theme_name);
+
top_menu->add_spacer(false);
- theme_edit_button = memnew(Button);
- theme_edit_button->set_text(TTR("Manage Items"));
+ Button *theme_save_button = memnew(Button);
+ theme_save_button->set_text(TTR("Save"));
+ theme_save_button->set_flat(true);
+ theme_save_button->connect("pressed", callable_mp(this, &ThemeEditor::_theme_save_button_cbk), varray(false));
+ top_menu->add_child(theme_save_button);
+
+ Button *theme_save_as_button = memnew(Button);
+ theme_save_as_button->set_text(TTR("Save As..."));
+ theme_save_as_button->set_flat(true);
+ theme_save_as_button->connect("pressed", callable_mp(this, &ThemeEditor::_theme_save_button_cbk), varray(true));
+ top_menu->add_child(theme_save_as_button);
+
+ top_menu->add_child(memnew(VSeparator));
+
+ Button *theme_edit_button = memnew(Button);
+ theme_edit_button->set_text(TTR("Manage Items..."));
theme_edit_button->set_tooltip(TTR("Add, remove, organize and import Theme items."));
theme_edit_button->set_flat(true);
theme_edit_button->connect("pressed", callable_mp(this, &ThemeEditor::_theme_edit_button_cbk));
top_menu->add_child(theme_edit_button);
- ScrollContainer *scroll = memnew(ScrollContainer);
- add_child(scroll);
- scroll->set_enable_v_scroll(true);
- scroll->set_enable_h_scroll(true);
- scroll->set_v_size_flags(SIZE_EXPAND_FILL);
-
- MarginContainer *root_container = memnew(MarginContainer);
- scroll->add_child(root_container);
- root_container->set_theme(Theme::get_default());
- root_container->set_clip_contents(true);
- root_container->set_custom_minimum_size(Size2(700, 0) * EDSCALE);
- root_container->set_v_size_flags(SIZE_EXPAND_FILL);
- root_container->set_h_size_flags(SIZE_EXPAND_FILL);
-
- //// Preview Controls ////
-
- main_panel = memnew(Panel);
- root_container->add_child(main_panel);
-
- main_container = memnew(MarginContainer);
- root_container->add_child(main_container);
- main_container->add_theme_constant_override("margin_right", 4 * EDSCALE);
- main_container->add_theme_constant_override("margin_top", 4 * EDSCALE);
- main_container->add_theme_constant_override("margin_left", 4 * EDSCALE);
- main_container->add_theme_constant_override("margin_bottom", 4 * EDSCALE);
-
- HBoxContainer *main_hb = memnew(HBoxContainer);
- main_container->add_child(main_hb);
-
- VBoxContainer *first_vb = memnew(VBoxContainer);
- main_hb->add_child(first_vb);
- first_vb->set_h_size_flags(SIZE_EXPAND_FILL);
- first_vb->add_theme_constant_override("separation", 10 * EDSCALE);
-
- first_vb->add_child(memnew(Label("Label")));
-
- first_vb->add_child(memnew(Button("Button")));
- Button *bt = memnew(Button);
- bt->set_text(TTR("Toggle Button"));
- bt->set_toggle_mode(true);
- bt->set_pressed(true);
- first_vb->add_child(bt);
- bt = memnew(Button);
- bt->set_text(TTR("Disabled Button"));
- bt->set_disabled(true);
- first_vb->add_child(bt);
- Button *tb = memnew(Button);
- tb->set_flat(true);
- tb->set_text("Button");
- first_vb->add_child(tb);
-
- CheckButton *cb = memnew(CheckButton);
- cb->set_text("CheckButton");
- first_vb->add_child(cb);
- CheckBox *cbx = memnew(CheckBox);
- cbx->set_text("CheckBox");
- first_vb->add_child(cbx);
-
- MenuButton *test_menu_button = memnew(MenuButton);
- test_menu_button->set_text("MenuButton");
- test_menu_button->get_popup()->add_item(TTR("Item"));
- test_menu_button->get_popup()->add_item(TTR("Disabled Item"));
- test_menu_button->get_popup()->set_item_disabled(1, true);
- test_menu_button->get_popup()->add_separator();
- test_menu_button->get_popup()->add_check_item(TTR("Check Item"));
- test_menu_button->get_popup()->add_check_item(TTR("Checked Item"));
- test_menu_button->get_popup()->set_item_checked(4, true);
- test_menu_button->get_popup()->add_separator();
- test_menu_button->get_popup()->add_radio_check_item(TTR("Radio Item"));
- test_menu_button->get_popup()->add_radio_check_item(TTR("Checked Radio Item"));
- test_menu_button->get_popup()->set_item_checked(7, true);
- test_menu_button->get_popup()->add_separator(TTR("Named Sep."));
-
- PopupMenu *test_submenu = memnew(PopupMenu);
- test_menu_button->get_popup()->add_child(test_submenu);
- test_submenu->set_name("submenu");
- test_menu_button->get_popup()->add_submenu_item(TTR("Submenu"), "submenu");
- test_submenu->add_item(TTR("Subitem 1"));
- test_submenu->add_item(TTR("Subitem 2"));
- first_vb->add_child(test_menu_button);
-
- OptionButton *test_option_button = memnew(OptionButton);
- test_option_button->add_item("OptionButton");
- test_option_button->add_separator();
- test_option_button->add_item(TTR("Has"));
- test_option_button->add_item(TTR("Many"));
- test_option_button->add_item(TTR("Options"));
- first_vb->add_child(test_option_button);
- first_vb->add_child(memnew(ColorPickerButton));
-
- VBoxContainer *second_vb = memnew(VBoxContainer);
- second_vb->set_h_size_flags(SIZE_EXPAND_FILL);
- main_hb->add_child(second_vb);
- second_vb->add_theme_constant_override("separation", 10 * EDSCALE);
- LineEdit *le = memnew(LineEdit);
- le->set_text("LineEdit");
- second_vb->add_child(le);
- le = memnew(LineEdit);
- le->set_text(TTR("Disabled LineEdit"));
- le->set_editable(false);
- second_vb->add_child(le);
- TextEdit *te = memnew(TextEdit);
- te->set_text("TextEdit");
- te->set_custom_minimum_size(Size2(0, 100) * EDSCALE);
- second_vb->add_child(te);
- second_vb->add_child(memnew(SpinBox));
-
- HBoxContainer *vhb = memnew(HBoxContainer);
- second_vb->add_child(vhb);
- vhb->set_custom_minimum_size(Size2(0, 100) * EDSCALE);
- vhb->add_child(memnew(VSlider));
- VScrollBar *vsb = memnew(VScrollBar);
- vsb->set_page(25);
- vhb->add_child(vsb);
- vhb->add_child(memnew(VSeparator));
- VBoxContainer *hvb = memnew(VBoxContainer);
- vhb->add_child(hvb);
- hvb->set_alignment(ALIGN_CENTER);
- hvb->set_h_size_flags(SIZE_EXPAND_FILL);
- hvb->add_child(memnew(HSlider));
- HScrollBar *hsb = memnew(HScrollBar);
- hsb->set_page(25);
- hvb->add_child(hsb);
- HSlider *hs = memnew(HSlider);
- hs->set_editable(false);
- hvb->add_child(hs);
- hvb->add_child(memnew(HSeparator));
- ProgressBar *pb = memnew(ProgressBar);
- pb->set_value(50);
- hvb->add_child(pb);
-
- VBoxContainer *third_vb = memnew(VBoxContainer);
- third_vb->set_h_size_flags(SIZE_EXPAND_FILL);
- third_vb->add_theme_constant_override("separation", 10 * EDSCALE);
- main_hb->add_child(third_vb);
-
- TabContainer *tc = memnew(TabContainer);
- third_vb->add_child(tc);
- tc->set_custom_minimum_size(Size2(0, 135) * EDSCALE);
- Control *tcc = memnew(Control);
- tcc->set_name(TTR("Tab 1"));
- tc->add_child(tcc);
- tcc = memnew(Control);
- tcc->set_name(TTR("Tab 2"));
- tc->add_child(tcc);
- tcc = memnew(Control);
- tcc->set_name(TTR("Tab 3"));
- tc->add_child(tcc);
- tc->set_tab_disabled(2, true);
-
- Tree *test_tree = memnew(Tree);
- third_vb->add_child(test_tree);
- test_tree->set_custom_minimum_size(Size2(0, 175) * EDSCALE);
- test_tree->add_theme_constant_override("draw_relationship_lines", 1);
-
- TreeItem *item = test_tree->create_item();
- item->set_text(0, "Tree");
- item = test_tree->create_item(test_tree->get_root());
- item->set_text(0, "Item");
- item = test_tree->create_item(test_tree->get_root());
- item->set_editable(0, true);
- item->set_text(0, TTR("Editable Item"));
- TreeItem *sub_tree = test_tree->create_item(test_tree->get_root());
- sub_tree->set_text(0, TTR("Subtree"));
- item = test_tree->create_item(sub_tree);
- item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
- item->set_editable(0, true);
- item->set_text(0, "Check Item");
- item = test_tree->create_item(sub_tree);
- item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE);
- item->set_editable(0, true);
- item->set_range_config(0, 0, 20, 0.1);
- item->set_range(0, 2);
- item = test_tree->create_item(sub_tree);
- item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE);
- item->set_editable(0, true);
- item->set_text(0, TTR("Has,Many,Options"));
- item->set_range(0, 2);
-
- main_hb->add_theme_constant_override("separation", 20 * EDSCALE);
-
theme_edit_dialog = memnew(ThemeItemEditorDialog);
theme_edit_dialog->hide();
- add_child(theme_edit_dialog);
+ top_menu->add_child(theme_edit_dialog);
+
+ HSplitContainer *main_hs = memnew(HSplitContainer);
+ main_hs->set_v_size_flags(SIZE_EXPAND_FILL);
+ add_child(main_hs);
+
+ VBoxContainer *preview_tabs_vb = memnew(VBoxContainer);
+ preview_tabs_vb->set_h_size_flags(SIZE_EXPAND_FILL);
+ preview_tabs_vb->set_custom_minimum_size(Size2(520, 0) * EDSCALE);
+ preview_tabs_vb->add_theme_constant_override("separation", 2 * EDSCALE);
+ main_hs->add_child(preview_tabs_vb);
+ HBoxContainer *preview_tabbar_hb = memnew(HBoxContainer);
+ preview_tabs_vb->add_child(preview_tabbar_hb);
+ preview_tabs_content = memnew(PanelContainer);
+ preview_tabs_content->set_v_size_flags(SIZE_EXPAND_FILL);
+ preview_tabs_content->set_draw_behind_parent(true);
+ preview_tabs_vb->add_child(preview_tabs_content);
+
+ preview_tabs = memnew(Tabs);
+ preview_tabs->set_tab_align(Tabs::ALIGN_LEFT);
+ preview_tabs->set_h_size_flags(SIZE_EXPAND_FILL);
+ preview_tabbar_hb->add_child(preview_tabs);
+ preview_tabs->connect("tab_changed", callable_mp(this, &ThemeEditor::_change_preview_tab));
+ preview_tabs->connect("right_button_pressed", callable_mp(this, &ThemeEditor::_remove_preview_tab));
+
+ HBoxContainer *add_preview_button_hb = memnew(HBoxContainer);
+ preview_tabbar_hb->add_child(add_preview_button_hb);
+ add_preview_button = memnew(Button);
+ add_preview_button->set_text(TTR("Add Preview"));
+ add_preview_button_hb->add_child(add_preview_button);
+ add_preview_button->connect("pressed", callable_mp(this, &ThemeEditor::_add_preview_button_cbk));
+
+ DefaultThemeEditorPreview *default_preview_tab = memnew(DefaultThemeEditorPreview);
+ preview_tabs_content->add_child(default_preview_tab);
+ default_preview_tab->connect("control_picked", callable_mp(this, &ThemeEditor::_preview_control_picked));
+ preview_tabs->add_tab(TTR("Default Preview"));
+
+ preview_scene_dialog = memnew(EditorFileDialog);
+ preview_scene_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
+ preview_scene_dialog->set_title(TTR("Select UI Scene:"));
+ List<String> ext;
+ ResourceLoader::get_recognized_extensions_for_type("PackedScene", &ext);
+ for (List<String>::Element *E = ext.front(); E; E = E->next()) {
+ preview_scene_dialog->add_filter("*." + E->get() + "; Scene");
+ }
+ main_hs->add_child(preview_scene_dialog);
+ preview_scene_dialog->connect("file_selected", callable_mp(this, &ThemeEditor::_preview_scene_dialog_cbk));
+
+ theme_type_editor = memnew(ThemeTypeEditor);
+ main_hs->add_child(theme_type_editor);
+ theme_type_editor->set_custom_minimum_size(Size2(360, 0) * EDSCALE);
}
void ThemeEditorPlugin::edit(Object *p_node) {
if (Object::cast_to<Theme>(p_node)) {
theme_editor->edit(Object::cast_to<Theme>(p_node));
+ } else if (Object::cast_to<Font>(p_node) || Object::cast_to<StyleBox>(p_node) || Object::cast_to<Texture2D>(p_node)) {
+ // Do nothing, keep editing the existing theme.
} else {
theme_editor->edit(Ref<Theme>());
}
}
bool ThemeEditorPlugin::handles(Object *p_node) const {
- return p_node->is_class("Theme");
+ if (Object::cast_to<Theme>(p_node)) {
+ return true;
+ }
+
+ Ref<Theme> edited_theme = theme_editor->get_edited_theme();
+ if (edited_theme.is_null()) {
+ return false;
+ }
+
+ // If we are editing a theme already and this particular resource happens to belong to it,
+ // then we just keep editing it, despite not being able to directly handle it.
+ // This only goes one layer deep, but if required this can be extended to support, say, FontData inside of Font.
+ bool belongs_to_theme = false;
+
+ if (Object::cast_to<Font>(p_node)) {
+ Ref<Font> font_item = Object::cast_to<Font>(p_node);
+ List<StringName> types;
+ List<StringName> names;
+
+ edited_theme->get_font_type_list(&types);
+ for (List<StringName>::Element *E = types.front(); E; E = E->next()) {
+ names.clear();
+ edited_theme->get_font_list(E->get(), &names);
+
+ for (List<StringName>::Element *F = names.front(); F; F = F->next()) {
+ if (font_item == edited_theme->get_font(F->get(), E->get())) {
+ belongs_to_theme = true;
+ break;
+ }
+ }
+ }
+ } else if (Object::cast_to<StyleBox>(p_node)) {
+ Ref<StyleBox> stylebox_item = Object::cast_to<StyleBox>(p_node);
+ List<StringName> types;
+ List<StringName> names;
+
+ edited_theme->get_stylebox_type_list(&types);
+ for (List<StringName>::Element *E = types.front(); E; E = E->next()) {
+ names.clear();
+ edited_theme->get_stylebox_list(E->get(), &names);
+
+ for (List<StringName>::Element *F = names.front(); F; F = F->next()) {
+ if (stylebox_item == edited_theme->get_stylebox(F->get(), E->get())) {
+ belongs_to_theme = true;
+ break;
+ }
+ }
+ }
+ } else if (Object::cast_to<Texture2D>(p_node)) {
+ Ref<Texture2D> icon_item = Object::cast_to<Texture2D>(p_node);
+ List<StringName> types;
+ List<StringName> names;
+
+ edited_theme->get_icon_type_list(&types);
+ for (List<StringName>::Element *E = types.front(); E; E = E->next()) {
+ names.clear();
+ edited_theme->get_icon_list(E->get(), &names);
+
+ for (List<StringName>::Element *F = names.front(); F; F = F->next()) {
+ if (icon_item == edited_theme->get_icon(F->get(), E->get())) {
+ belongs_to_theme = true;
+ break;
+ }
+ }
+ }
+ }
+
+ return belongs_to_theme;
}
void ThemeEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
- theme_editor->set_process(true);
button->show();
editor->make_bottom_panel_item_visible(theme_editor);
} else {
- theme_editor->set_process(false);
if (theme_editor->is_visible_in_tree()) {
editor->hide_bottom_panel();
}
diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h
index c42ebf1a19..77baf46395 100644
--- a/editor/plugins/theme_editor_plugin.h
+++ b/editor/plugins/theme_editor_plugin.h
@@ -31,13 +31,13 @@
#ifndef THEME_EDITOR_PLUGIN_H
#define THEME_EDITOR_PLUGIN_H
-#include "scene/gui/check_box.h"
-#include "scene/gui/file_dialog.h"
#include "scene/gui/margin_container.h"
#include "scene/gui/option_button.h"
#include "scene/gui/scroll_container.h"
+#include "scene/gui/tabs.h"
#include "scene/gui/texture_rect.h"
#include "scene/resources/theme.h"
+#include "theme_editor_preview.h"
#include "editor/editor_node.h"
@@ -263,30 +263,123 @@ public:
ThemeItemEditorDialog();
};
+class ThemeTypeEditor : public MarginContainer {
+ GDCLASS(ThemeTypeEditor, MarginContainer);
+
+ Ref<Theme> edited_theme;
+ String edited_type;
+ bool updating = false;
+
+ struct LeadingStylebox {
+ bool pinned = false;
+ StringName item_name;
+ Ref<StyleBox> stylebox;
+ Ref<StyleBox> ref_stylebox;
+ };
+
+ LeadingStylebox leading_stylebox;
+
+ OptionButton *theme_type_list;
+ Button *add_type_button;
+ ConfirmationDialog *add_type_dialog;
+ LineEdit *add_type_filter;
+ ItemList *add_type_options;
+
+ CheckButton *show_default_items_button;
+
+ TabContainer *data_type_tabs;
+ VBoxContainer *color_items_list;
+ VBoxContainer *constant_items_list;
+ VBoxContainer *font_items_list;
+ VBoxContainer *font_size_items_list;
+ VBoxContainer *icon_items_list;
+ VBoxContainer *stylebox_items_list;
+
+ Vector<Control *> focusables;
+ Timer *update_debounce_timer;
+
+ VBoxContainer *_create_item_list(Theme::DataType p_data_type);
+ void _update_type_list();
+ void _update_type_list_debounced();
+ void _update_add_type_options(const String &p_filter = "");
+ OrderedHashMap<StringName, bool> _get_type_items(String p_type_name, void (Theme::*get_list_func)(StringName, List<StringName> *) const, bool include_default);
+ HBoxContainer *_create_property_control(Theme::DataType p_data_type, String p_item_name, bool p_editable);
+ void _add_focusable(Control *p_control);
+ void _update_type_items();
+
+ void _list_type_selected(int p_index);
+ void _select_type(String p_type_name);
+ void _add_type_button_cbk();
+ void _add_type_filter_cbk(const String &p_value);
+ void _add_type_options_cbk(int p_index);
+ void _add_type_dialog_confirmed();
+ void _add_type_dialog_entered(const String &p_value);
+ void _add_type_dialog_activated(int p_index);
+ void _add_default_type_items();
+
+ void _item_add_cbk(int p_data_type, Control *p_control);
+ void _item_add_lineedit_cbk(String p_value, int p_data_type, Control *p_control);
+ void _item_override_cbk(int p_data_type, String p_item_name);
+ void _item_remove_cbk(int p_data_type, String p_item_name);
+ void _item_rename_cbk(int p_data_type, String p_item_name, Control *p_control);
+ void _item_rename_confirmed(int p_data_type, String p_item_name, Control *p_control);
+ void _item_rename_entered(String p_value, int p_data_type, String p_item_name, Control *p_control);
+ void _item_rename_canceled(int p_data_type, String p_item_name, Control *p_control);
+
+ void _color_item_changed(Color p_value, String p_item_name);
+ void _constant_item_changed(float p_value, String p_item_name);
+ void _font_size_item_changed(float p_value, String p_item_name);
+ void _edit_resource_item(RES p_resource, Control *p_editor);
+ void _font_item_changed(Ref<Font> p_value, String p_item_name);
+ void _icon_item_changed(Ref<Texture2D> p_value, String p_item_name);
+ void _stylebox_item_changed(Ref<StyleBox> p_value, String p_item_name);
+ void _pin_leading_stylebox(Ref<StyleBox> p_stylebox, String p_item_name);
+ void _unpin_leading_stylebox();
+ void _update_stylebox_from_leading();
+
+protected:
+ void _notification(int p_what);
+
+public:
+ void set_edited_theme(const Ref<Theme> &p_theme);
+ void select_type(String p_type_name);
+
+ ThemeTypeEditor();
+};
+
class ThemeEditor : public VBoxContainer {
GDCLASS(ThemeEditor, VBoxContainer);
Ref<Theme> theme;
- double time_left = 0;
+ Tabs *preview_tabs;
+ PanelContainer *preview_tabs_content;
+ Button *add_preview_button;
+ EditorFileDialog *preview_scene_dialog;
- Button *theme_edit_button;
- ThemeItemEditorDialog *theme_edit_dialog;
+ ThemeTypeEditor *theme_type_editor;
- Panel *main_panel;
- MarginContainer *main_container;
- Tree *test_tree;
+ Label *theme_name;
+ ThemeItemEditorDialog *theme_edit_dialog;
+ void _theme_save_button_cbk(bool p_save_as);
void _theme_edit_button_cbk();
- void _propagate_redraw(Control *p_at);
- void _refresh_interval();
+
+ void _add_preview_button_cbk();
+ void _preview_scene_dialog_cbk(const String &p_path);
+ void _add_preview_tab(ThemeEditorPreview *p_preview_tab, const String &p_preview_name, const Ref<Texture2D> &p_icon);
+ void _change_preview_tab(int p_tab);
+ void _remove_preview_tab(int p_tab);
+ void _remove_preview_tab_invalid(Node *p_tab_control);
+ void _update_preview_tab(Node *p_tab_control);
+ void _preview_control_picked(String p_class_name);
protected:
void _notification(int p_what);
- static void _bind_methods();
public:
void edit(const Ref<Theme> &p_theme);
+ Ref<Theme> get_edited_theme();
ThemeEditor();
};
diff --git a/editor/plugins/theme_editor_preview.cpp b/editor/plugins/theme_editor_preview.cpp
new file mode 100644
index 0000000000..766f8508c1
--- /dev/null
+++ b/editor/plugins/theme_editor_preview.cpp
@@ -0,0 +1,464 @@
+/*************************************************************************/
+/* theme_editor_preview.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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 "theme_editor_preview.h"
+
+#include "core/input/input.h"
+#include "core/math/math_funcs.h"
+#include "scene/resources/packed_scene.h"
+
+#include "editor/editor_scale.h"
+
+void ThemeEditorPreview::set_preview_theme(const Ref<Theme> &p_theme) {
+ preview_content->set_theme(p_theme);
+}
+
+void ThemeEditorPreview::add_preview_overlay(Control *p_overlay) {
+ preview_overlay->add_child(p_overlay);
+ p_overlay->hide();
+}
+
+void ThemeEditorPreview::_propagate_redraw(Control *p_at) {
+ p_at->notification(NOTIFICATION_THEME_CHANGED);
+ p_at->minimum_size_changed();
+ p_at->update();
+ for (int i = 0; i < p_at->get_child_count(); i++) {
+ Control *a = Object::cast_to<Control>(p_at->get_child(i));
+ if (a) {
+ _propagate_redraw(a);
+ }
+ }
+}
+
+void ThemeEditorPreview::_refresh_interval() {
+ // In case the project settings have changed.
+ preview_bg->set_color(GLOBAL_GET("rendering/environment/defaults/default_clear_color"));
+
+ _propagate_redraw(preview_bg);
+ _propagate_redraw(preview_content);
+}
+
+void ThemeEditorPreview::_preview_visibility_changed() {
+ set_process(is_visible());
+}
+
+void ThemeEditorPreview::_picker_button_cbk() {
+ picker_overlay->set_visible(picker_button->is_pressed());
+}
+
+Control *ThemeEditorPreview::_find_hovered_control(Control *p_parent, Vector2 p_mouse_position) {
+ Control *found = nullptr;
+
+ for (int i = 0; i < p_parent->get_child_count(); i++) {
+ Control *cc = Object::cast_to<Control>(p_parent->get_child(i));
+ if (!cc || !cc->is_visible()) {
+ continue;
+ }
+
+ Rect2 crect = cc->get_rect();
+ if (crect.has_point(p_mouse_position)) {
+ // Check if there is a child control under mouse.
+ if (cc->get_child_count() > 0) {
+ found = _find_hovered_control(cc, p_mouse_position - cc->get_position());
+ }
+
+ // If there are no applicable children, use the control itself.
+ if (!found) {
+ found = cc;
+ }
+ break;
+ }
+ }
+
+ return found;
+}
+
+void ThemeEditorPreview::_draw_picker_overlay() {
+ if (!picker_button->is_pressed()) {
+ return;
+ }
+
+ picker_overlay->draw_rect(Rect2(Vector2(0.0, 0.0), picker_overlay->get_size()), get_theme_color("preview_picker_overlay_color", "ThemeEditor"));
+ if (hovered_control) {
+ Rect2 highlight_rect = hovered_control->get_global_rect();
+ highlight_rect.position = picker_overlay->get_global_transform().affine_inverse().xform(highlight_rect.position);
+
+ picker_overlay->draw_style_box(get_theme_stylebox("preview_picker_overlay", "ThemeEditor"), highlight_rect);
+ }
+}
+
+void ThemeEditorPreview::_gui_input_picker_overlay(const Ref<InputEvent> &p_event) {
+ if (!picker_button->is_pressed()) {
+ return;
+ }
+
+ Ref<InputEventMouseButton> mb = p_event;
+
+ if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (hovered_control) {
+ StringName theme_type = hovered_control->get_theme_custom_type();
+ if (theme_type == StringName()) {
+ theme_type = hovered_control->get_class_name();
+ }
+
+ emit_signal("control_picked", theme_type);
+ picker_button->set_pressed(false);
+ picker_overlay->set_visible(false);
+ }
+ }
+
+ Ref<InputEventMouseMotion> mm = p_event;
+
+ if (mm.is_valid()) {
+ Vector2 mp = preview_content->get_local_mouse_position();
+ hovered_control = _find_hovered_control(preview_content, mp);
+ picker_overlay->update();
+ }
+}
+
+void ThemeEditorPreview::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ if (is_visible_in_tree()) {
+ set_process(true);
+ }
+
+ connect("visibility_changed", callable_mp(this, &ThemeEditorPreview::_preview_visibility_changed));
+ [[fallthrough]];
+ }
+ case NOTIFICATION_THEME_CHANGED: {
+ picker_button->set_icon(get_theme_icon("ColorPick", "EditorIcons"));
+ } break;
+ case NOTIFICATION_PROCESS: {
+ time_left -= get_process_delta_time();
+ if (time_left < 0) {
+ time_left = 1.5;
+ _refresh_interval();
+ }
+ } break;
+ }
+}
+
+void ThemeEditorPreview::_bind_methods() {
+ ADD_SIGNAL(MethodInfo("control_picked", PropertyInfo(Variant::STRING, "class_name")));
+}
+
+ThemeEditorPreview::ThemeEditorPreview() {
+ preview_toolbar = memnew(HBoxContainer);
+ add_child(preview_toolbar);
+
+ picker_button = memnew(Button);
+ preview_toolbar->add_child(picker_button);
+ picker_button->set_flat(true);
+ picker_button->set_toggle_mode(true);
+ picker_button->set_tooltip(TTR("Toggle the control picker, allowing to visually select control types for edit."));
+ picker_button->connect("pressed", callable_mp(this, &ThemeEditorPreview::_picker_button_cbk));
+
+ MarginContainer *preview_body = memnew(MarginContainer);
+ preview_body->set_custom_minimum_size(Size2(480, 0) * EDSCALE);
+ preview_body->set_v_size_flags(SIZE_EXPAND_FILL);
+ add_child(preview_body);
+
+ ScrollContainer *preview_container = memnew(ScrollContainer);
+ preview_container->set_enable_v_scroll(true);
+ preview_container->set_enable_h_scroll(true);
+ preview_body->add_child(preview_container);
+
+ MarginContainer *preview_root = memnew(MarginContainer);
+ preview_container->add_child(preview_root);
+ preview_root->set_theme(Theme::get_default());
+ preview_root->set_clip_contents(true);
+ preview_root->set_custom_minimum_size(Size2(450, 0) * EDSCALE);
+ preview_root->set_v_size_flags(SIZE_EXPAND_FILL);
+ preview_root->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ preview_bg = memnew(ColorRect);
+ preview_bg->set_anchors_and_offsets_preset(PRESET_WIDE);
+ preview_bg->set_color(GLOBAL_GET("rendering/environment/defaults/default_clear_color"));
+ preview_root->add_child(preview_bg);
+
+ preview_content = memnew(MarginContainer);
+ preview_root->add_child(preview_content);
+ preview_content->add_theme_constant_override("margin_right", 4 * EDSCALE);
+ preview_content->add_theme_constant_override("margin_top", 4 * EDSCALE);
+ preview_content->add_theme_constant_override("margin_left", 4 * EDSCALE);
+ preview_content->add_theme_constant_override("margin_bottom", 4 * EDSCALE);
+
+ preview_overlay = memnew(MarginContainer);
+ preview_overlay->set_mouse_filter(MOUSE_FILTER_IGNORE);
+ preview_body->add_child(preview_overlay);
+
+ picker_overlay = memnew(Control);
+ add_preview_overlay(picker_overlay);
+ picker_overlay->connect("draw", callable_mp(this, &ThemeEditorPreview::_draw_picker_overlay));
+ picker_overlay->connect("gui_input", callable_mp(this, &ThemeEditorPreview::_gui_input_picker_overlay));
+}
+
+DefaultThemeEditorPreview::DefaultThemeEditorPreview() {
+ Panel *main_panel = memnew(Panel);
+ preview_content->add_child(main_panel);
+
+ MarginContainer *main_mc = memnew(MarginContainer);
+ main_mc->add_theme_constant_override("margin_right", 4 * EDSCALE);
+ main_mc->add_theme_constant_override("margin_top", 4 * EDSCALE);
+ main_mc->add_theme_constant_override("margin_left", 4 * EDSCALE);
+ main_mc->add_theme_constant_override("margin_bottom", 4 * EDSCALE);
+ preview_content->add_child(main_mc);
+
+ HBoxContainer *main_hb = memnew(HBoxContainer);
+ main_mc->add_child(main_hb);
+ main_hb->add_theme_constant_override("separation", 20 * EDSCALE);
+
+ VBoxContainer *first_vb = memnew(VBoxContainer);
+ main_hb->add_child(first_vb);
+ first_vb->set_h_size_flags(SIZE_EXPAND_FILL);
+ first_vb->add_theme_constant_override("separation", 10 * EDSCALE);
+
+ first_vb->add_child(memnew(Label("Label")));
+
+ first_vb->add_child(memnew(Button("Button")));
+ Button *bt = memnew(Button);
+ bt->set_text(TTR("Toggle Button"));
+ bt->set_toggle_mode(true);
+ bt->set_pressed(true);
+ first_vb->add_child(bt);
+ bt = memnew(Button);
+ bt->set_text(TTR("Disabled Button"));
+ bt->set_disabled(true);
+ first_vb->add_child(bt);
+ Button *tb = memnew(Button);
+ tb->set_flat(true);
+ tb->set_text("Button");
+ first_vb->add_child(tb);
+
+ CheckButton *cb = memnew(CheckButton);
+ cb->set_text("CheckButton");
+ first_vb->add_child(cb);
+ CheckBox *cbx = memnew(CheckBox);
+ cbx->set_text("CheckBox");
+ first_vb->add_child(cbx);
+
+ MenuButton *test_menu_button = memnew(MenuButton);
+ test_menu_button->set_text("MenuButton");
+ test_menu_button->get_popup()->add_item(TTR("Item"));
+ test_menu_button->get_popup()->add_item(TTR("Disabled Item"));
+ test_menu_button->get_popup()->set_item_disabled(1, true);
+ test_menu_button->get_popup()->add_separator();
+ test_menu_button->get_popup()->add_check_item(TTR("Check Item"));
+ test_menu_button->get_popup()->add_check_item(TTR("Checked Item"));
+ test_menu_button->get_popup()->set_item_checked(4, true);
+ test_menu_button->get_popup()->add_separator();
+ test_menu_button->get_popup()->add_radio_check_item(TTR("Radio Item"));
+ test_menu_button->get_popup()->add_radio_check_item(TTR("Checked Radio Item"));
+ test_menu_button->get_popup()->set_item_checked(7, true);
+ test_menu_button->get_popup()->add_separator(TTR("Named Sep."));
+
+ PopupMenu *test_submenu = memnew(PopupMenu);
+ test_menu_button->get_popup()->add_child(test_submenu);
+ test_submenu->set_name("submenu");
+ test_menu_button->get_popup()->add_submenu_item(TTR("Submenu"), "submenu");
+ test_submenu->add_item(TTR("Subitem 1"));
+ test_submenu->add_item(TTR("Subitem 2"));
+ first_vb->add_child(test_menu_button);
+
+ OptionButton *test_option_button = memnew(OptionButton);
+ test_option_button->add_item("OptionButton");
+ test_option_button->add_separator();
+ test_option_button->add_item(TTR("Has"));
+ test_option_button->add_item(TTR("Many"));
+ test_option_button->add_item(TTR("Options"));
+ first_vb->add_child(test_option_button);
+ first_vb->add_child(memnew(ColorPickerButton));
+
+ VBoxContainer *second_vb = memnew(VBoxContainer);
+ second_vb->set_h_size_flags(SIZE_EXPAND_FILL);
+ main_hb->add_child(second_vb);
+ second_vb->add_theme_constant_override("separation", 10 * EDSCALE);
+ LineEdit *le = memnew(LineEdit);
+ le->set_text("LineEdit");
+ second_vb->add_child(le);
+ le = memnew(LineEdit);
+ le->set_text(TTR("Disabled LineEdit"));
+ le->set_editable(false);
+ second_vb->add_child(le);
+ TextEdit *te = memnew(TextEdit);
+ te->set_text("TextEdit");
+ te->set_custom_minimum_size(Size2(0, 100) * EDSCALE);
+ second_vb->add_child(te);
+ second_vb->add_child(memnew(SpinBox));
+
+ HBoxContainer *vhb = memnew(HBoxContainer);
+ second_vb->add_child(vhb);
+ vhb->set_custom_minimum_size(Size2(0, 100) * EDSCALE);
+ vhb->add_child(memnew(VSlider));
+ VScrollBar *vsb = memnew(VScrollBar);
+ vsb->set_page(25);
+ vhb->add_child(vsb);
+ vhb->add_child(memnew(VSeparator));
+ VBoxContainer *hvb = memnew(VBoxContainer);
+ vhb->add_child(hvb);
+ hvb->set_alignment(BoxContainer::ALIGN_CENTER);
+ hvb->set_h_size_flags(SIZE_EXPAND_FILL);
+ hvb->add_child(memnew(HSlider));
+ HScrollBar *hsb = memnew(HScrollBar);
+ hsb->set_page(25);
+ hvb->add_child(hsb);
+ HSlider *hs = memnew(HSlider);
+ hs->set_editable(false);
+ hvb->add_child(hs);
+ hvb->add_child(memnew(HSeparator));
+ ProgressBar *pb = memnew(ProgressBar);
+ pb->set_value(50);
+ hvb->add_child(pb);
+
+ VBoxContainer *third_vb = memnew(VBoxContainer);
+ third_vb->set_h_size_flags(SIZE_EXPAND_FILL);
+ third_vb->add_theme_constant_override("separation", 10 * EDSCALE);
+ main_hb->add_child(third_vb);
+
+ TabContainer *tc = memnew(TabContainer);
+ third_vb->add_child(tc);
+ tc->set_custom_minimum_size(Size2(0, 135) * EDSCALE);
+ Control *tcc = memnew(Control);
+ tcc->set_name(TTR("Tab 1"));
+ tc->add_child(tcc);
+ tcc = memnew(Control);
+ tcc->set_name(TTR("Tab 2"));
+ tc->add_child(tcc);
+ tcc = memnew(Control);
+ tcc->set_name(TTR("Tab 3"));
+ tc->add_child(tcc);
+ tc->set_tab_disabled(2, true);
+
+ Tree *test_tree = memnew(Tree);
+ third_vb->add_child(test_tree);
+ test_tree->set_custom_minimum_size(Size2(0, 175) * EDSCALE);
+
+ TreeItem *item = test_tree->create_item();
+ item->set_text(0, "Tree");
+ item = test_tree->create_item(test_tree->get_root());
+ item->set_text(0, "Item");
+ item = test_tree->create_item(test_tree->get_root());
+ item->set_editable(0, true);
+ item->set_text(0, TTR("Editable Item"));
+ TreeItem *sub_tree = test_tree->create_item(test_tree->get_root());
+ sub_tree->set_text(0, TTR("Subtree"));
+ item = test_tree->create_item(sub_tree);
+ item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+ item->set_editable(0, true);
+ item->set_text(0, "Check Item");
+ item = test_tree->create_item(sub_tree);
+ item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE);
+ item->set_editable(0, true);
+ item->set_range_config(0, 0, 20, 0.1);
+ item->set_range(0, 2);
+ item = test_tree->create_item(sub_tree);
+ item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE);
+ item->set_editable(0, true);
+ item->set_text(0, TTR("Has,Many,Options"));
+ item->set_range(0, 2);
+}
+
+void SceneThemeEditorPreview::_reload_scene() {
+ if (loaded_scene.is_null()) {
+ return;
+ }
+
+ if (loaded_scene->get_path().is_empty() || !ResourceLoader::exists(loaded_scene->get_path())) {
+ EditorNode::get_singleton()->show_warning(TTR("Invalid path, the PackedScene resource was probably moved or removed."));
+ emit_signal("scene_invalidated");
+ return;
+ }
+
+ for (int i = preview_content->get_child_count() - 1; i >= 0; i--) {
+ Node *node = preview_content->get_child(i);
+ node->queue_delete();
+ preview_content->remove_child(node);
+ }
+
+ Node *instance = loaded_scene->instance();
+ if (!instance || !Object::cast_to<Control>(instance)) {
+ EditorNode::get_singleton()->show_warning(TTR("Invalid PackedScene resource, must have a Control node at its root."));
+ emit_signal("scene_invalidated");
+ return;
+ }
+
+ preview_content->add_child(instance);
+ emit_signal("scene_reloaded");
+}
+
+void SceneThemeEditorPreview::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
+ reload_scene_button->set_icon(get_theme_icon("Reload", "EditorIcons"));
+ } break;
+ }
+}
+
+void SceneThemeEditorPreview::_bind_methods() {
+ ADD_SIGNAL(MethodInfo("scene_invalidated"));
+ ADD_SIGNAL(MethodInfo("scene_reloaded"));
+}
+
+bool SceneThemeEditorPreview::set_preview_scene(const String &p_path) {
+ loaded_scene = ResourceLoader::load(p_path);
+ if (loaded_scene.is_null()) {
+ EditorNode::get_singleton()->show_warning(TTR("Invalid file, not a PackedScene resource."));
+ return false;
+ }
+
+ Node *instance = loaded_scene->instance();
+ if (!instance || !Object::cast_to<Control>(instance)) {
+ EditorNode::get_singleton()->show_warning(TTR("Invalid PackedScene resource, must have a Control node at its root."));
+ return false;
+ }
+
+ preview_content->add_child(instance);
+ return true;
+}
+
+String SceneThemeEditorPreview::get_preview_scene_path() const {
+ if (loaded_scene.is_null()) {
+ return "";
+ }
+
+ return loaded_scene->get_path();
+}
+
+SceneThemeEditorPreview::SceneThemeEditorPreview() {
+ preview_toolbar->add_child(memnew(VSeparator));
+
+ reload_scene_button = memnew(Button);
+ reload_scene_button->set_flat(true);
+ reload_scene_button->set_tooltip(TTR("Reload the scene to reflect its most actual state."));
+ preview_toolbar->add_child(reload_scene_button);
+ reload_scene_button->connect("pressed", callable_mp(this, &SceneThemeEditorPreview::_reload_scene));
+}
diff --git a/editor/plugins/theme_editor_preview.h b/editor/plugins/theme_editor_preview.h
new file mode 100644
index 0000000000..efb7e424d4
--- /dev/null
+++ b/editor/plugins/theme_editor_preview.h
@@ -0,0 +1,118 @@
+/*************************************************************************/
+/* theme_editor_preview.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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 THEME_EDITOR_PREVIEW_H
+#define THEME_EDITOR_PREVIEW_H
+
+#include "scene/gui/box_container.h"
+#include "scene/gui/check_box.h"
+#include "scene/gui/check_button.h"
+#include "scene/gui/color_picker.h"
+#include "scene/gui/color_rect.h"
+#include "scene/gui/label.h"
+#include "scene/gui/margin_container.h"
+#include "scene/gui/menu_button.h"
+#include "scene/gui/option_button.h"
+#include "scene/gui/panel.h"
+#include "scene/gui/progress_bar.h"
+#include "scene/gui/scroll_container.h"
+#include "scene/gui/separator.h"
+#include "scene/gui/spin_box.h"
+#include "scene/gui/tab_container.h"
+#include "scene/gui/text_edit.h"
+#include "scene/gui/tree.h"
+#include "scene/resources/theme.h"
+
+#include "editor/editor_node.h"
+
+class ThemeEditorPreview : public VBoxContainer {
+ GDCLASS(ThemeEditorPreview, VBoxContainer);
+
+ ColorRect *preview_bg;
+ MarginContainer *preview_overlay;
+ Control *picker_overlay;
+ Control *hovered_control = nullptr;
+
+ double time_left = 0;
+
+ void _propagate_redraw(Control *p_at);
+ void _refresh_interval();
+ void _preview_visibility_changed();
+
+ void _picker_button_cbk();
+ Control *_find_hovered_control(Control *p_parent, Vector2 p_mouse_position);
+
+ void _draw_picker_overlay();
+ void _gui_input_picker_overlay(const Ref<InputEvent> &p_event);
+
+protected:
+ HBoxContainer *preview_toolbar;
+ MarginContainer *preview_content;
+ Button *picker_button;
+
+ void add_preview_overlay(Control *p_overlay);
+
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ void set_preview_theme(const Ref<Theme> &p_theme);
+
+ ThemeEditorPreview();
+};
+
+class DefaultThemeEditorPreview : public ThemeEditorPreview {
+ GDCLASS(DefaultThemeEditorPreview, ThemeEditorPreview);
+
+public:
+ DefaultThemeEditorPreview();
+};
+
+class SceneThemeEditorPreview : public ThemeEditorPreview {
+ GDCLASS(SceneThemeEditorPreview, ThemeEditorPreview);
+
+ Ref<PackedScene> loaded_scene;
+
+ Button *reload_scene_button;
+
+ void _reload_scene();
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ bool set_preview_scene(const String &p_path);
+ String get_preview_scene_path() const;
+
+ SceneThemeEditorPreview();
+};
+
+#endif // THEME_EDITOR_PREVIEW_H
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 8d6b7f3389..8d59b478ba 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -2059,6 +2059,8 @@ void SceneTreeDock::_selection_changed() {
_tool_selected(TOOL_MULTI_EDIT);
} else if (selection_size == 0) {
editor->push_item(nullptr);
+ } else {
+ editor->push_item(EditorNode::get_singleton()->get_editor_selection()->get_selection().front()->value());
}
_update_script_button();
diff --git a/main/main.cpp b/main/main.cpp
index 13a9986564..f1905fc47b 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -1336,13 +1336,13 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
Engine::get_singleton()->set_iterations_per_second(GLOBAL_DEF_BASIC("physics/common/physics_fps", 60));
ProjectSettings::get_singleton()->set_custom_property_info("physics/common/physics_fps",
PropertyInfo(Variant::INT, "physics/common/physics_fps",
- PROPERTY_HINT_RANGE, "1,120,1,or_greater"));
+ PROPERTY_HINT_RANGE, "1,1000,1"));
Engine::get_singleton()->set_physics_jitter_fix(GLOBAL_DEF("physics/common/physics_jitter_fix", 0.5));
Engine::get_singleton()->set_target_fps(GLOBAL_DEF("debug/settings/fps/force_fps", 0));
ProjectSettings::get_singleton()->set_custom_property_info("debug/settings/fps/force_fps",
PropertyInfo(Variant::INT,
"debug/settings/fps/force_fps",
- PROPERTY_HINT_RANGE, "0,120,1,or_greater"));
+ PROPERTY_HINT_RANGE, "0,1000,1"));
GLOBAL_DEF("debug/settings/stdout/print_fps", false);
GLOBAL_DEF("debug/settings/stdout/verbose_stdout", false);
@@ -1453,7 +1453,7 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
#ifdef TOOLS_ENABLED
if (editor || project_manager) {
- EditorNode::register_editor_paths(project_manager);
+ EditorPaths::create();
}
#endif
@@ -2379,10 +2379,8 @@ bool Main::start() {
}
// 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());
}
#endif
}
diff --git a/misc/dist/osx_template.app/Contents/Info.plist b/misc/dist/osx_template.app/Contents/Info.plist
index aad24935d0..8e221df946 100644
--- a/misc/dist/osx_template.app/Contents/Info.plist
+++ b/misc/dist/osx_template.app/Contents/Info.plist
@@ -36,6 +36,8 @@
</array>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
+ <key>LSApplicationCategoryType</key>
+ <string>public.app-category.$app_category</string>
<key>LSMinimumSystemVersion</key>
<string>10.12</string>
<key>LSMinimumSystemVersionByArchitecture</key>
diff --git a/misc/dist/osx_tools.app/Contents/Info.plist b/misc/dist/osx_tools.app/Contents/Info.plist
index 1c682f339f..8e70d4c203 100644
--- a/misc/dist/osx_tools.app/Contents/Info.plist
+++ b/misc/dist/osx_tools.app/Contents/Info.plist
@@ -38,6 +38,8 @@
</array>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
+ <key>LSApplicationCategoryType</key>
+ <string>public.app-category.developer-tools</string>
<key>LSMinimumSystemVersion</key>
<string>10.12</string>
<key>LSMinimumSystemVersionByArchitecture</key>
diff --git a/modules/gdnative/nativescript/api_generator.cpp b/modules/gdnative/nativescript/api_generator.cpp
index 4ad8901f32..57717010f7 100644
--- a/modules/gdnative/nativescript/api_generator.cpp
+++ b/modules/gdnative/nativescript/api_generator.cpp
@@ -38,6 +38,7 @@
#include "core/object/class_db.h"
#include "core/string/string_builder.h"
#include "core/templates/pair.h"
+#include "core/variant/variant_parser.h"
// helper stuff
@@ -638,6 +639,7 @@ static List<String> generate_c_api_json(const List<ClassAPI> &p_api) {
// I'm sorry for the \t mess
List<String> source;
+ VariantWriter writer;
source.push_back("[\n");
@@ -682,7 +684,12 @@ static List<String> generate_c_api_json(const List<ClassAPI> &p_api) {
source.push_back("\t\t\t\t\t\t\"name\": \"" + e->get().argument_names[i] + "\",\n");
source.push_back("\t\t\t\t\t\t\"type\": \"" + e->get().argument_types[i] + "\",\n");
source.push_back(String("\t\t\t\t\t\t\"has_default_value\": ") + (e->get().default_arguments.has(i) ? "true" : "false") + ",\n");
- source.push_back("\t\t\t\t\t\t\"default_value\": \"" + (e->get().default_arguments.has(i) ? (String)e->get().default_arguments[i] : "") + "\"\n");
+ String default_value;
+ if (e->get().default_arguments.has(i)) {
+ writer.write_to_string(e->get().default_arguments[i], default_value);
+ default_value = default_value.replace("\n", "").json_escape();
+ }
+ source.push_back("\t\t\t\t\t\t\"default_value\": \"" + default_value + "\"\n");
source.push_back(String("\t\t\t\t\t}") + ((i < e->get().argument_names.size() - 1) ? "," : "") + "\n");
}
source.push_back("\t\t\t\t]\n");
@@ -708,7 +715,12 @@ static List<String> generate_c_api_json(const List<ClassAPI> &p_api) {
source.push_back("\t\t\t\t\t\t\"name\": \"" + e->get().argument_names[i] + "\",\n");
source.push_back("\t\t\t\t\t\t\"type\": \"" + e->get().argument_types[i] + "\",\n");
source.push_back(String("\t\t\t\t\t\t\"has_default_value\": ") + (e->get().default_arguments.has(i) ? "true" : "false") + ",\n");
- source.push_back("\t\t\t\t\t\t\"default_value\": \"" + (e->get().default_arguments.has(i) ? (String)e->get().default_arguments[i] : "") + "\"\n");
+ String default_value;
+ if (e->get().default_arguments.has(i)) {
+ writer.write_to_string(e->get().default_arguments[i], default_value);
+ default_value = default_value.replace("\n", "").json_escape();
+ }
+ source.push_back("\t\t\t\t\t\t\"default_value\": \"" + default_value + "\"\n");
source.push_back(String("\t\t\t\t\t}") + ((i < e->get().argument_names.size() - 1) ? "," : "") + "\n");
}
source.push_back("\t\t\t\t]\n");
@@ -756,6 +768,8 @@ static void append_indented(StringBuilder &p_source, const char *p_text) {
}
static void write_builtin_method(StringBuilder &p_source, const MethodAPI &p_method) {
+ VariantWriter writer;
+
append_indented(p_source, vformat(R"("name": "%s",)", p_method.method_name));
append_indented(p_source, vformat(R"("return_type": "%s",)", p_method.return_type));
append_indented(p_source, vformat(R"("is_const": %s,)", p_method.is_const ? "true" : "false"));
@@ -771,7 +785,12 @@ static void write_builtin_method(StringBuilder &p_source, const MethodAPI &p_met
append_indented(p_source, vformat(R"("name": "%s",)", p_method.argument_names[i]));
append_indented(p_source, vformat(R"("type": "%s",)", p_method.argument_types[i]));
append_indented(p_source, vformat(R"("has_default_value": %s,)", p_method.default_arguments.has(i) ? "true" : "false"));
- append_indented(p_source, vformat(R"("default_value": "%s")", p_method.default_arguments.has(i) ? p_method.default_arguments[i].operator String() : ""));
+ String default_value;
+ if (p_method.default_arguments.has(i)) {
+ writer.write_to_string(p_method.default_arguments[i], default_value);
+ default_value = default_value.replace("\n", "").json_escape();
+ }
+ append_indented(p_source, vformat(R"("default_value": "%s")", default_value));
indent_level--;
append_indented(p_source, i < p_method.argument_count - 1 ? "}," : "}");
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 1aafaf9351..1567576009 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -360,7 +360,7 @@ PlaceHolderScriptInstance *GDScript::placeholder_instance_create(Object *p_this)
#ifdef TOOLS_ENABLED
PlaceHolderScriptInstance *si = memnew(PlaceHolderScriptInstance(GDScriptLanguage::get_singleton(), Ref<Script>(this), p_this));
placeholders.insert(si);
- _update_exports();
+ _update_exports(nullptr, false, si);
return si;
#else
return nullptr;
@@ -584,7 +584,7 @@ void GDScript::_update_doc() {
}
#endif
-bool GDScript::_update_exports(bool *r_err, bool p_recursive_call) {
+bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderScriptInstance *p_instance_to_update) {
#ifdef TOOLS_ENABLED
static Vector<GDScript *> base_caches;
@@ -721,15 +721,19 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call) {
}
}
- if (placeholders.size()) { //hm :(
+ if ((changed || p_instance_to_update) && placeholders.size()) { //hm :(
// update placeholders if any
Map<StringName, Variant> values;
List<PropertyInfo> propnames;
_update_exports_values(values, propnames);
- for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
- E->get()->update(propnames, values);
+ if (changed) {
+ for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
+ E->get()->update(propnames, values);
+ }
+ } else {
+ p_instance_to_update->update(propnames, values);
}
}
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 0584ced35e..602553bb1a 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -148,7 +148,7 @@ class GDScript : public Script {
#endif
- bool _update_exports(bool *r_err = nullptr, bool p_recursive_call = false);
+ bool _update_exports(bool *r_err = nullptr, bool p_recursive_call = false, PlaceHolderScriptInstance *p_instance_to_update = nullptr);
void _save_orphaned_subclasses();
void _init_rpc_methods_properties();
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index 318ec966ae..8a261a88e3 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -1989,7 +1989,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
ip += instr_arg_count;
- int self_fun = _code_ptr[ip + 1];
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+
+ int self_fun = _code_ptr[ip + 2];
#ifdef DEBUG_ENABLED
if (self_fun < 0 || self_fun >= _global_names_count) {
err_text = "compiler bug, function name not found";
@@ -1998,9 +2001,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#endif
const StringName *methodname = &_global_names_ptr[self_fun];
- int argc = _code_ptr[ip + 2];
- GD_ERR_BREAK(argc < 0);
-
Variant **argptrs = instruction_args;
GET_INSTRUCTION_ARG(dst, argc);
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 67344b7719..576256b6ec 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -2370,7 +2370,7 @@ void CSharpScript::_update_member_info_no_exports() {
}
#endif
-bool CSharpScript::_update_exports() {
+bool CSharpScript::_update_exports(PlaceHolderScriptInstance *p_instance_to_update) {
#ifdef TOOLS_ENABLED
bool is_editor = Engine::get_singleton()->is_editor_hint();
if (is_editor) {
@@ -2542,14 +2542,18 @@ bool CSharpScript::_update_exports() {
if (is_editor) {
placeholder_fallback_enabled = false;
- if (placeholders.size()) {
+ if ((changed || p_instance_to_update) && placeholders.size()) {
// Update placeholders if any
Map<StringName, Variant> values;
List<PropertyInfo> propnames;
_update_exports_values(values, propnames);
- for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
- E->get()->update(propnames, values);
+ if (changed) {
+ for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
+ E->get()->update(propnames, values);
+ }
+ } else {
+ p_instance_to_update->update(propnames, values);
}
}
}
@@ -3230,7 +3234,7 @@ PlaceHolderScriptInstance *CSharpScript::placeholder_instance_create(Object *p_t
#ifdef TOOLS_ENABLED
PlaceHolderScriptInstance *si = memnew(PlaceHolderScriptInstance(CSharpLanguage::get_singleton(), Ref<Script>(this), p_this));
placeholders.insert(si);
- _update_exports();
+ _update_exports(si);
return si;
#else
return nullptr;
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index c347a902ed..965e882c5b 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -163,7 +163,7 @@ private:
void load_script_signals(GDMonoClass *p_class, GDMonoClass *p_native_class);
bool _get_signal(GDMonoClass *p_class, GDMonoMethod *p_delegate_invoke, Vector<SignalParameter> &params);
- bool _update_exports();
+ bool _update_exports(PlaceHolderScriptInstance *p_instance_to_update = nullptr);
bool _get_member_export(IMonoClassMember *p_member, bool p_inspect_export, PropertyInfo &r_prop_info, bool &r_exported);
#ifdef TOOLS_ENABLED
diff --git a/modules/tga/image_loader_tga.cpp b/modules/tga/image_loader_tga.cpp
index 3cfd4ff36a..6eaa2d24a8 100644
--- a/modules/tga/image_loader_tga.cpp
+++ b/modules/tga/image_loader_tga.cpp
@@ -35,7 +35,7 @@
#include "core/os/os.h"
#include "core/string/print_string.h"
-Error ImageLoaderTGA::decode_tga_rle(const uint8_t *p_compressed_buffer, size_t p_pixel_size, uint8_t *p_uncompressed_buffer, size_t p_output_size) {
+Error ImageLoaderTGA::decode_tga_rle(const uint8_t *p_compressed_buffer, size_t p_pixel_size, uint8_t *p_uncompressed_buffer, size_t p_output_size, size_t p_input_size) {
Error error;
Vector<uint8_t> pixels;
@@ -56,11 +56,14 @@ Error ImageLoaderTGA::decode_tga_rle(const uint8_t *p_compressed_buffer, size_t
compressed_pos += 1;
count = (c & 0x7f) + 1;
- if (output_pos + count * p_pixel_size > output_pos) {
+ if (output_pos + count * p_pixel_size > p_output_size) {
return ERR_PARSE_ERROR;
}
if (c & 0x80) {
+ if (compressed_pos + p_pixel_size > p_input_size) {
+ return ERR_PARSE_ERROR;
+ }
for (size_t i = 0; i < p_pixel_size; i++) {
pixels_w[i] = p_compressed_buffer[compressed_pos];
compressed_pos += 1;
@@ -72,6 +75,9 @@ Error ImageLoaderTGA::decode_tga_rle(const uint8_t *p_compressed_buffer, size_t
output_pos += p_pixel_size;
}
} else {
+ if (compressed_pos + count * p_pixel_size > p_input_size) {
+ return ERR_PARSE_ERROR;
+ }
count *= p_pixel_size;
for (size_t i = 0; i < count; i++) {
p_uncompressed_buffer[output_pos] = p_compressed_buffer[compressed_pos];
@@ -83,7 +89,7 @@ Error ImageLoaderTGA::decode_tga_rle(const uint8_t *p_compressed_buffer, size_t
return OK;
}
-Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buffer, const tga_header_s &p_header, const uint8_t *p_palette, const bool p_is_monochrome, size_t p_output_size) {
+Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buffer, const tga_header_s &p_header, const uint8_t *p_palette, const bool p_is_monochrome, size_t p_input_size) {
#define TGA_PUT_PIXEL(r, g, b, a) \
int image_data_ofs = ((y * width) + x); \
image_data_w[image_data_ofs * 4 + 0] = r; \
@@ -134,7 +140,7 @@ Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buff
if (p_is_monochrome) {
while (y != y_end) {
while (x != x_end) {
- if (i > p_output_size) {
+ if (i >= p_input_size) {
return ERR_PARSE_ERROR;
}
uint8_t shade = p_buffer[i];
@@ -150,7 +156,7 @@ Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buff
} else {
while (y != y_end) {
while (x != x_end) {
- if (i > p_output_size) {
+ if (i >= p_input_size) {
return ERR_PARSE_ERROR;
}
uint8_t index = p_buffer[i];
@@ -181,7 +187,7 @@ Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buff
} else if (p_header.pixel_depth == 24) {
while (y != y_end) {
while (x != x_end) {
- if (i + 2 > p_output_size) {
+ if (i + 2 >= p_input_size) {
return ERR_PARSE_ERROR;
}
@@ -200,7 +206,7 @@ Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buff
} else if (p_header.pixel_depth == 32) {
while (y != y_end) {
while (x != x_end) {
- if (i + 3 > p_output_size) {
+ if (i + 3 >= p_input_size) {
return ERR_PARSE_ERROR;
}
@@ -307,7 +313,7 @@ Error ImageLoaderTGA::load_image(Ref<Image> p_image, FileAccess *f, bool p_force
const uint8_t *buffer = nullptr;
if (is_encoded) {
- err = decode_tga_rle(src_image_r, pixel_size, uncompressed_buffer_w, buffer_size);
+ err = decode_tga_rle(src_image_r, pixel_size, uncompressed_buffer_w, buffer_size, src_image_len);
if (err == OK) {
uncompressed_buffer_r = uncompressed_buffer.ptr();
diff --git a/modules/tga/image_loader_tga.h b/modules/tga/image_loader_tga.h
index cb2ce07edd..e4463a322f 100644
--- a/modules/tga/image_loader_tga.h
+++ b/modules/tga/image_loader_tga.h
@@ -72,8 +72,8 @@ class ImageLoaderTGA : public ImageFormatLoader {
uint8_t pixel_depth = 0;
uint8_t image_descriptor = 0;
};
- static Error decode_tga_rle(const uint8_t *p_compressed_buffer, size_t p_pixel_size, uint8_t *p_uncompressed_buffer, size_t p_output_size);
- static Error convert_to_image(Ref<Image> p_image, const uint8_t *p_buffer, const tga_header_s &p_header, const uint8_t *p_palette, const bool p_is_monochrome, size_t p_output_size);
+ static Error decode_tga_rle(const uint8_t *p_compressed_buffer, size_t p_pixel_size, uint8_t *p_uncompressed_buffer, size_t p_output_size, size_t p_input_size);
+ static Error convert_to_image(Ref<Image> p_image, const uint8_t *p_buffer, const tga_header_s &p_header, const uint8_t *p_palette, const bool p_is_monochrome, size_t p_input_size);
public:
virtual Error load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale);
diff --git a/modules/visual_script/doc_classes/VisualScriptFunctionState.xml b/modules/visual_script/doc_classes/VisualScriptFunctionState.xml
index f5bf195bbf..9ed71bf10e 100644
--- a/modules/visual_script/doc_classes/VisualScriptFunctionState.xml
+++ b/modules/visual_script/doc_classes/VisualScriptFunctionState.xml
@@ -28,7 +28,7 @@
<method name="resume">
<return type="Variant">
</return>
- <argument index="0" name="args" type="Array" default="null">
+ <argument index="0" name="args" type="Array" default="[ ]">
</argument>
<description>
</description>
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
index c9ed912ac1..7badb1b717 100644
--- a/modules/visual_script/visual_script.cpp
+++ b/modules/visual_script/visual_script.cpp
@@ -2204,7 +2204,7 @@ Variant VisualScriptFunctionState::resume(Array p_args) {
void VisualScriptFunctionState::_bind_methods() {
ClassDB::bind_method(D_METHOD("connect_to_signal", "obj", "signals", "args"), &VisualScriptFunctionState::connect_to_signal);
- ClassDB::bind_method(D_METHOD("resume", "args"), &VisualScriptFunctionState::resume, DEFVAL(Variant()));
+ ClassDB::bind_method(D_METHOD("resume", "args"), &VisualScriptFunctionState::resume, DEFVAL(Array()));
ClassDB::bind_method(D_METHOD("is_valid"), &VisualScriptFunctionState::is_valid);
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "_signal_callback", &VisualScriptFunctionState::_signal_callback, MethodInfo("_signal_callback"));
}
diff --git a/platform/javascript/api/javascript_tools_editor_plugin.cpp b/platform/javascript/api/javascript_tools_editor_plugin.cpp
index 83a0fcc68f..54f541f607 100644
--- a/platform/javascript/api/javascript_tools_editor_plugin.cpp
+++ b/platform/javascript/api/javascript_tools_editor_plugin.cpp
@@ -41,7 +41,7 @@
// JavaScript functions defined in library_godot_editor_tools.js
extern "C" {
-extern int godot_js_os_download_buffer(const uint8_t *p_buf, int p_buf_size, const char *p_name, const char *p_mime);
+extern void godot_js_os_download_buffer(const uint8_t *p_buf, int p_buf_size, const char *p_name, const char *p_mime);
}
static void _javascript_editor_init_callback() {
diff --git a/platform/javascript/godot_js.h b/platform/javascript/godot_js.h
index 8927a83cb3..d332af2c31 100644
--- a/platform/javascript/godot_js.h
+++ b/platform/javascript/godot_js.h
@@ -84,7 +84,7 @@ extern void godot_js_display_cursor_set_custom_shape(const char *p_shape, const
extern void godot_js_display_cursor_set_visible(int p_visible);
// Display gamepad
-extern char *godot_js_display_gamepad_cb(void (*p_on_change)(int p_index, int p_connected, const char *p_id, const char *p_guid));
+extern void godot_js_display_gamepad_cb(void (*p_on_change)(int p_index, int p_connected, const char *p_id, const char *p_guid));
extern int godot_js_display_gamepad_sample();
extern int godot_js_display_gamepad_sample_count();
extern int godot_js_display_gamepad_sample_get(int p_idx, float r_btns[16], int32_t *r_btns_num, float r_axes[10], int32_t *r_axes_num, int32_t *r_standard);
diff --git a/platform/javascript/javascript_singleton.cpp b/platform/javascript/javascript_singleton.cpp
index 5ef67c0cdd..c441ed0517 100644
--- a/platform/javascript/javascript_singleton.cpp
+++ b/platform/javascript/javascript_singleton.cpp
@@ -28,12 +28,16 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifdef JAVASCRIPT_EVAL_ENABLED
-
#include "api/javascript_singleton.h"
#include "emscripten.h"
extern "C" {
+extern void godot_js_os_download_buffer(const uint8_t *p_buf, int p_buf_size, const char *p_name, const char *p_mime);
+}
+
+#ifdef JAVASCRIPT_EVAL_ENABLED
+
+extern "C" {
typedef union {
int64_t i;
double r;
@@ -301,7 +305,6 @@ union js_eval_ret {
};
extern int godot_js_eval(const char *p_js, int p_use_global_ctx, union js_eval_ret *p_union_ptr, void *p_byte_arr, void *p_byte_arr_write, void *(*p_callback)(void *p_ptr, void *p_ptr2, int p_len));
-extern int godot_js_os_download_buffer(const uint8_t *p_buf, int p_buf_size, const char *p_name, const char *p_mime);
}
void *resize_PackedByteArray_and_open_write(void *p_arr, void *r_write, int p_len) {
diff --git a/platform/javascript/js/libs/library_godot_display.js b/platform/javascript/js/libs/library_godot_display.js
index 91cab5eacc..affae90370 100644
--- a/platform/javascript/js/libs/library_godot_display.js
+++ b/platform/javascript/js/libs/library_godot_display.js
@@ -683,7 +683,7 @@ const GodotDisplay = {
return GodotDisplayScreen.exitFullscreen();
},
- godot_js_display_desired_size_set__sig: 'v',
+ godot_js_display_desired_size_set__sig: 'vii',
godot_js_display_desired_size_set: function (width, height) {
GodotDisplayScreen.desired_size = [width, height];
GodotDisplayScreen.updateSize();
diff --git a/platform/javascript/js/libs/library_godot_fetch.js b/platform/javascript/js/libs/library_godot_fetch.js
index 04b90aea9c..615f9de8b0 100644
--- a/platform/javascript/js/libs/library_godot_fetch.js
+++ b/platform/javascript/js/libs/library_godot_fetch.js
@@ -126,7 +126,7 @@ const GodotFetch = {
},
},
- godot_js_fetch_create__sig: 'iii',
+ godot_js_fetch_create__sig: 'iiiiiii',
godot_js_fetch_create: function (p_method, p_url, p_headers, p_headers_size, p_body, p_body_size) {
const method = GodotRuntime.parseString(p_method);
const url = GodotRuntime.parseString(p_url);
@@ -176,7 +176,7 @@ const GodotFetch = {
return obj.status;
},
- godot_js_fetch_read_headers__sig: 'iii',
+ godot_js_fetch_read_headers__sig: 'iiii',
godot_js_fetch_read_headers: function (p_id, p_parse_cb, p_ref) {
const obj = IDHandler.get(p_id);
if (!obj || !obj.response) {
@@ -193,7 +193,7 @@ const GodotFetch = {
return 0;
},
- godot_js_fetch_read_chunk__sig: 'ii',
+ godot_js_fetch_read_chunk__sig: 'iiii',
godot_js_fetch_read_chunk: function (p_id, p_buf, p_buf_size) {
const obj = IDHandler.get(p_id);
if (!obj || !obj.response) {
diff --git a/platform/javascript/js/libs/library_godot_javascript_singleton.js b/platform/javascript/js/libs/library_godot_javascript_singleton.js
index 09ef4a1a5d..cb80273ca8 100644
--- a/platform/javascript/js/libs/library_godot_javascript_singleton.js
+++ b/platform/javascript/js/libs/library_godot_javascript_singleton.js
@@ -196,7 +196,7 @@ const GodotJSWrapper = {
}
},
- godot_js_wrapper_create_cb__sig: 'vii',
+ godot_js_wrapper_create_cb__sig: 'iii',
godot_js_wrapper_create_cb: function (p_ref, p_func) {
const func = GodotRuntime.get_func(p_func);
let id = 0;
diff --git a/platform/javascript/package-lock.json b/platform/javascript/package-lock.json
index 8bf5c52ff6..8003619576 100644
--- a/platform/javascript/package-lock.json
+++ b/platform/javascript/package-lock.json
@@ -5,27 +5,27 @@
"requires": true,
"dependencies": {
"@babel/code-frame": {
- "version": "7.10.4",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
- "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
+ "version": "7.12.11",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz",
+ "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==",
"dev": true,
"requires": {
"@babel/highlight": "^7.10.4"
}
},
"@babel/helper-validator-identifier": {
- "version": "7.10.4",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
- "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz",
+ "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==",
"dev": true
},
"@babel/highlight": {
- "version": "7.10.4",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
- "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz",
+ "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==",
"dev": true,
"requires": {
- "@babel/helper-validator-identifier": "^7.10.4",
+ "@babel/helper-validator-identifier": "^7.14.5",
"chalk": "^2.0.0",
"js-tokens": "^4.0.0"
},
@@ -40,39 +40,38 @@
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
}
}
},
"@babel/parser": {
- "version": "7.14.1",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.1.tgz",
- "integrity": "sha512-muUGEKu8E/ftMTPlNp+mc6zL3E9zKWmF5sDHZ5MSsoTP9Wyz64AhEf9kD08xYJ7w6Hdcu8H550ircnPyWSIF0Q==",
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.5.tgz",
+ "integrity": "sha512-TM8C+xtH/9n1qzX+JNHi7AN2zHMTiPUtspO0ZdHflW8KaskkALhMmuMHb4bCmNdv9VAPzJX3/bXqkVLnAvsPfg==",
"dev": true
},
"@eslint/eslintrc": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.1.3.tgz",
- "integrity": "sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA==",
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz",
+ "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==",
"dev": true,
"requires": {
"ajv": "^6.12.4",
"debug": "^4.1.1",
"espree": "^7.3.0",
- "globals": "^12.1.0",
+ "globals": "^13.9.0",
"ignore": "^4.0.6",
"import-fresh": "^3.2.1",
"js-yaml": "^3.13.1",
- "lodash": "^4.17.19",
"minimatch": "^3.0.4",
"strip-json-comments": "^3.1.1"
}
},
- "@types/color-name": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
- "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
- "dev": true
- },
"@types/json5": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
@@ -80,9 +79,9 @@
"dev": true
},
"acorn": {
- "version": "7.4.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz",
- "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==",
+ "version": "7.4.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
+ "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
"dev": true
},
"acorn-jsx": {
@@ -92,9 +91,9 @@
"dev": true
},
"ajv": {
- "version": "6.12.5",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz",
- "integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==",
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
@@ -134,78 +133,39 @@
}
},
"array-includes": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz",
- "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==",
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz",
+ "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==",
"dev": true,
"requires": {
+ "call-bind": "^1.0.2",
"define-properties": "^1.1.3",
- "es-abstract": "^1.17.0",
+ "es-abstract": "^1.18.0-next.2",
+ "get-intrinsic": "^1.1.1",
"is-string": "^1.0.5"
- },
- "dependencies": {
- "es-abstract": {
- "version": "1.17.6",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
- "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.2.0",
- "is-regex": "^1.1.0",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimend": "^1.0.1",
- "string.prototype.trimstart": "^1.0.1"
- }
- }
}
},
"array.prototype.flat": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz",
- "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz",
+ "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==",
"dev": true,
"requires": {
+ "call-bind": "^1.0.0",
"define-properties": "^1.1.3",
- "es-abstract": "^1.17.0-next.1"
- },
- "dependencies": {
- "es-abstract": {
- "version": "1.17.6",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
- "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.2.0",
- "is-regex": "^1.1.0",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimend": "^1.0.1",
- "string.prototype.trimstart": "^1.0.1"
- }
- }
+ "es-abstract": "^1.18.0-next.1"
}
},
"astral-regex": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
- "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
+ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
"dev": true
},
"balanced-match": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
- "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
"bluebird": {
@@ -224,6 +184,16 @@
"concat-map": "0.0.1"
}
},
+ "call-bind": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+ "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1",
+ "get-intrinsic": "^1.0.2"
+ }
+ },
"callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -231,18 +201,18 @@
"dev": true
},
"catharsis": {
- "version": "0.8.11",
- "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.11.tgz",
- "integrity": "sha512-a+xUyMV7hD1BrDQA/3iPV7oc+6W26BgVJO05PGEoatMyIuPScQKsde6i3YorWX1qs+AZjnJ18NqdKoCtKiNh1g==",
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz",
+ "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==",
"dev": true,
"requires": {
- "lodash": "^4.17.14"
+ "lodash": "^4.17.15"
}
},
"chalk": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
- "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
+ "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
"dev": true,
"requires": {
"ansi-styles": "^4.1.0",
@@ -250,12 +220,11 @@
},
"dependencies": {
"ansi-styles": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
- "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"requires": {
- "@types/color-name": "^1.1.1",
"color-convert": "^2.0.1"
}
},
@@ -313,15 +282,9 @@
"dev": true
},
"confusing-browser-globals": {
- "version": "1.0.9",
- "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz",
- "integrity": "sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw==",
- "dev": true
- },
- "contains-path": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
- "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=",
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz",
+ "integrity": "sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==",
"dev": true
},
"cross-spawn": {
@@ -336,12 +299,12 @@
}
},
"debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
+ "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
"dev": true,
"requires": {
- "ms": "^2.1.1"
+ "ms": "2.1.2"
}
},
"deep-is": {
@@ -369,9 +332,9 @@
}
},
"emoji-regex": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
- "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
},
"enquirer": {
@@ -399,23 +362,27 @@
}
},
"es-abstract": {
- "version": "1.18.0-next.0",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.0.tgz",
- "integrity": "sha512-elZXTZXKn51hUBdJjSZGYRujuzilgXo8vSPQzjGYXLvSlGiCo8VO8ZGV3kjo9a0WNJJ57hENagwbtlRuHuzkcQ==",
+ "version": "1.18.3",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.3.tgz",
+ "integrity": "sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw==",
"dev": true,
"requires": {
+ "call-bind": "^1.0.2",
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
+ "get-intrinsic": "^1.1.1",
"has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.2.0",
- "is-negative-zero": "^2.0.0",
- "is-regex": "^1.1.1",
- "object-inspect": "^1.8.0",
+ "has-symbols": "^1.0.2",
+ "is-callable": "^1.2.3",
+ "is-negative-zero": "^2.0.1",
+ "is-regex": "^1.1.3",
+ "is-string": "^1.0.6",
+ "object-inspect": "^1.10.3",
"object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimend": "^1.0.1",
- "string.prototype.trimstart": "^1.0.1"
+ "object.assign": "^4.1.2",
+ "string.prototype.trimend": "^1.0.4",
+ "string.prototype.trimstart": "^1.0.4",
+ "unbox-primitive": "^1.0.1"
}
},
"es-to-primitive": {
@@ -430,35 +397,37 @@
}
},
"escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true
},
"eslint": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.9.0.tgz",
- "integrity": "sha512-V6QyhX21+uXp4T+3nrNfI3hQNBDa/P8ga7LoQOenwrlEFXrEnUEE+ok1dMtaS3b6rmLXhT1TkTIsG75HMLbknA==",
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.28.0.tgz",
+ "integrity": "sha512-UMfH0VSjP0G4p3EWirscJEQ/cHqnT/iuH6oNZOB94nBjWbMnhGEPxsZm1eyIW0C/9jLI0Fow4W5DXLjEI7mn1g==",
"dev": true,
"requires": {
- "@babel/code-frame": "^7.0.0",
- "@eslint/eslintrc": "^0.1.3",
+ "@babel/code-frame": "7.12.11",
+ "@eslint/eslintrc": "^0.4.2",
"ajv": "^6.10.0",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
"debug": "^4.0.1",
"doctrine": "^3.0.0",
"enquirer": "^2.3.5",
- "eslint-scope": "^5.1.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^5.1.1",
"eslint-utils": "^2.1.0",
- "eslint-visitor-keys": "^1.3.0",
- "espree": "^7.3.0",
- "esquery": "^1.2.0",
+ "eslint-visitor-keys": "^2.0.0",
+ "espree": "^7.3.1",
+ "esquery": "^1.4.0",
"esutils": "^2.0.2",
- "file-entry-cache": "^5.0.1",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
"functional-red-black-tree": "^1.0.1",
- "glob-parent": "^5.0.0",
- "globals": "^12.1.0",
+ "glob-parent": "^5.1.2",
+ "globals": "^13.6.0",
"ignore": "^4.0.6",
"import-fresh": "^3.0.0",
"imurmurhash": "^0.1.4",
@@ -466,7 +435,7 @@
"js-yaml": "^3.13.1",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.4.1",
- "lodash": "^4.17.19",
+ "lodash.merge": "^4.6.2",
"minimatch": "^3.0.4",
"natural-compare": "^1.4.0",
"optionator": "^0.9.1",
@@ -475,19 +444,19 @@
"semver": "^7.2.1",
"strip-ansi": "^6.0.0",
"strip-json-comments": "^3.1.0",
- "table": "^5.2.3",
+ "table": "^6.0.9",
"text-table": "^0.2.0",
"v8-compile-cache": "^2.0.3"
}
},
"eslint-config-airbnb-base": {
- "version": "14.2.0",
- "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.0.tgz",
- "integrity": "sha512-Snswd5oC6nJaevs3nZoLSTvGJBvzTfnBqOIArkf3cbyTyq9UD79wOk8s+RiL6bhca0p/eRO6veczhf6A/7Jy8Q==",
+ "version": "14.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz",
+ "integrity": "sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA==",
"dev": true,
"requires": {
- "confusing-browser-globals": "^1.0.9",
- "object.assign": "^4.1.0",
+ "confusing-browser-globals": "^1.0.10",
+ "object.assign": "^4.1.2",
"object.entries": "^1.1.2"
}
},
@@ -519,50 +488,46 @@
}
},
"eslint-module-utils": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz",
- "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==",
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz",
+ "integrity": "sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A==",
"dev": true,
"requires": {
- "debug": "^2.6.9",
+ "debug": "^3.2.7",
"pkg-dir": "^2.0.0"
},
"dependencies": {
"debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
"dev": true,
"requires": {
- "ms": "2.0.0"
+ "ms": "^2.1.1"
}
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
- "dev": true
}
}
},
"eslint-plugin-import": {
- "version": "2.22.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.0.tgz",
- "integrity": "sha512-66Fpf1Ln6aIS5Gr/55ts19eUuoDhAbZgnr6UxK5hbDx6l/QgQgx61AePq+BV4PP2uXQFClgMVzep5zZ94qqsxg==",
+ "version": "2.23.4",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.23.4.tgz",
+ "integrity": "sha512-6/wP8zZRsnQFiR3iaPFgh5ImVRM1WN5NUWfTIRqwOdeiGJlBcSk82o1FEVq8yXmy4lkIzTo7YhHCIxlU/2HyEQ==",
"dev": true,
"requires": {
- "array-includes": "^3.1.1",
- "array.prototype.flat": "^1.2.3",
- "contains-path": "^0.1.0",
+ "array-includes": "^3.1.3",
+ "array.prototype.flat": "^1.2.4",
"debug": "^2.6.9",
- "doctrine": "1.5.0",
- "eslint-import-resolver-node": "^0.3.3",
- "eslint-module-utils": "^2.6.0",
+ "doctrine": "^2.1.0",
+ "eslint-import-resolver-node": "^0.3.4",
+ "eslint-module-utils": "^2.6.1",
+ "find-up": "^2.0.0",
"has": "^1.0.3",
+ "is-core-module": "^2.4.0",
"minimatch": "^3.0.4",
- "object.values": "^1.1.1",
- "read-pkg-up": "^2.0.0",
- "resolve": "^1.17.0",
+ "object.values": "^1.1.3",
+ "pkg-up": "^2.0.0",
+ "read-pkg-up": "^3.0.0",
+ "resolve": "^1.20.0",
"tsconfig-paths": "^3.9.0"
},
"dependencies": {
@@ -576,13 +541,12 @@
}
},
"doctrine": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
- "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
"dev": true,
"requires": {
- "esutils": "^2.0.2",
- "isarray": "^1.0.0"
+ "esutils": "^2.0.2"
}
},
"ms": {
@@ -610,23 +574,39 @@
"dev": true,
"requires": {
"eslint-visitor-keys": "^1.1.0"
+ },
+ "dependencies": {
+ "eslint-visitor-keys": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+ "dev": true
+ }
}
},
"eslint-visitor-keys": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
- "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+ "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
"dev": true
},
"espree": {
- "version": "7.3.0",
- "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz",
- "integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==",
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz",
+ "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==",
"dev": true,
"requires": {
"acorn": "^7.4.0",
- "acorn-jsx": "^5.2.0",
+ "acorn-jsx": "^5.3.1",
"eslint-visitor-keys": "^1.3.0"
+ },
+ "dependencies": {
+ "eslint-visitor-keys": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+ "dev": true
+ }
}
},
"esprima": {
@@ -636,9 +616,9 @@
"dev": true
},
"esquery": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz",
- "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==",
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
+ "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
"dev": true,
"requires": {
"estraverse": "^5.1.0"
@@ -700,12 +680,12 @@
"dev": true
},
"file-entry-cache": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
- "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
"dev": true,
"requires": {
- "flat-cache": "^2.0.1"
+ "flat-cache": "^3.0.4"
}
},
"find-up": {
@@ -718,20 +698,19 @@
}
},
"flat-cache": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
- "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
+ "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
"dev": true,
"requires": {
- "flatted": "^2.0.0",
- "rimraf": "2.6.3",
- "write": "1.0.3"
+ "flatted": "^3.1.0",
+ "rimraf": "^3.0.2"
}
},
"flatted": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz",
- "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz",
+ "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==",
"dev": true
},
"fs.realpath": {
@@ -752,10 +731,21 @@
"integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
"dev": true
},
+ "get-intrinsic": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
+ "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1"
+ }
+ },
"glob": {
- "version": "7.1.6",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
- "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+ "version": "7.1.7",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
+ "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
@@ -767,27 +757,27 @@
}
},
"glob-parent": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
- "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"requires": {
"is-glob": "^4.0.1"
}
},
"globals": {
- "version": "12.4.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz",
- "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==",
+ "version": "13.9.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz",
+ "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==",
"dev": true,
"requires": {
- "type-fest": "^0.8.1"
+ "type-fest": "^0.20.2"
}
},
"graceful-fs": {
- "version": "4.2.4",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
- "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
+ "version": "4.2.6",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
+ "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==",
"dev": true
},
"has": {
@@ -799,6 +789,12 @@
"function-bind": "^1.1.1"
}
},
+ "has-bigints": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz",
+ "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==",
+ "dev": true
+ },
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@@ -806,9 +802,9 @@
"dev": true
},
"has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
+ "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
"dev": true
},
"hosted-git-info": {
@@ -824,9 +820,9 @@
"dev": true
},
"import-fresh": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
- "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
"dev": true,
"requires": {
"parent-module": "^1.0.0",
@@ -861,16 +857,40 @@
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
"dev": true
},
+ "is-bigint": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz",
+ "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==",
+ "dev": true
+ },
+ "is-boolean-object": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz",
+ "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2"
+ }
+ },
"is-callable": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.1.tgz",
- "integrity": "sha512-wliAfSzx6V+6WfMOmus1xy0XvSgf/dlStkvTfq7F0g4bOIW0PSUbnyse3NhDwdyYS1ozfUtAAySqTws3z9Eqgg==",
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz",
+ "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==",
"dev": true
},
+ "is-core-module": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz",
+ "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.3"
+ }
+ },
"is-date-object": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
- "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==",
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz",
+ "integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==",
"dev": true
},
"is-extglob": {
@@ -880,9 +900,9 @@
"dev": true
},
"is-fullwidth-code-point": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
- "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true
},
"is-glob": {
@@ -895,41 +915,42 @@
}
},
"is-negative-zero": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz",
- "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz",
+ "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==",
+ "dev": true
+ },
+ "is-number-object": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz",
+ "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==",
"dev": true
},
"is-regex": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
- "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==",
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz",
+ "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==",
"dev": true,
"requires": {
- "has-symbols": "^1.0.1"
+ "call-bind": "^1.0.2",
+ "has-symbols": "^1.0.2"
}
},
"is-string": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz",
- "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==",
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz",
+ "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==",
"dev": true
},
"is-symbol": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
- "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
+ "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
"dev": true,
"requires": {
- "has-symbols": "^1.0.1"
+ "has-symbols": "^1.0.2"
}
},
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "dev": true
- },
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -943,9 +964,9 @@
"dev": true
},
"js-yaml": {
- "version": "3.14.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
- "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
"dev": true,
"requires": {
"argparse": "^1.0.7",
@@ -962,13 +983,14 @@
}
},
"jsdoc": {
- "version": "github:jsdoc/jsdoc#544a992824631fc652183d8b0b4b1281dc961560",
- "from": "github:jsdoc/jsdoc#544a992824631fc652183d8b0b4b1281dc961560",
+ "version": "3.6.7",
+ "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.7.tgz",
+ "integrity": "sha512-sxKt7h0vzCd+3Y81Ey2qinupL6DpRSZJclS04ugHDNmRUXGzqicMJ6iwayhSA0S0DwwX30c5ozyUthr1QKF6uw==",
"dev": true,
"requires": {
"@babel/parser": "^7.9.4",
"bluebird": "^3.7.2",
- "catharsis": "^0.8.11",
+ "catharsis": "^0.9.0",
"escape-string-regexp": "^2.0.0",
"js2xmlparser": "^4.0.1",
"klaw": "^3.0.0",
@@ -979,7 +1001,7 @@
"requizzle": "^0.2.3",
"strip-json-comments": "^3.1.0",
"taffydb": "2.6.2",
- "underscore": "~1.12.1"
+ "underscore": "~1.13.1"
},
"dependencies": {
"escape-string-regexp": {
@@ -987,15 +1009,15 @@
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
"integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
"dev": true
- },
- "mkdirp": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
- "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
- "dev": true
}
}
},
+ "json-parse-better-errors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
+ "dev": true
+ },
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
@@ -1046,14 +1068,14 @@
}
},
"load-json-file": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
- "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
+ "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
"dev": true,
"requires": {
"graceful-fs": "^4.1.2",
- "parse-json": "^2.2.0",
- "pify": "^2.0.0",
+ "parse-json": "^4.0.0",
+ "pify": "^3.0.0",
"strip-bom": "^3.0.0"
}
},
@@ -1073,6 +1095,33 @@
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
+ "lodash.clonedeep": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
+ "dev": true
+ },
+ "lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
+ "lodash.truncate": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
+ "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=",
+ "dev": true
+ },
+ "lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ },
"markdown-it": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz",
@@ -1093,9 +1142,9 @@
"dev": true
},
"marked": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.3.tgz",
- "integrity": "sha512-5otztIIcJfPc2qGTN8cVtOJEjNJZ0jwa46INMagrYfk0EvqtRuEHLsEe0LrFS0/q+ZRKT0+kXK7P2T1AN5lWRA==",
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.7.tgz",
+ "integrity": "sha512-BJXxkuIfJchcXOJWTT2DOL+yFWifFv2yGYOUzvXg8Qz610QKw+sHCvTMYwA+qWGhlA2uivBezChZ/pBy1tWdkQ==",
"dev": true
},
"mdurl": {
@@ -1120,13 +1169,10 @@
"dev": true
},
"mkdirp": {
- "version": "0.5.5",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
- "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
- "dev": true,
- "requires": {
- "minimist": "^1.2.5"
- }
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "dev": true
},
"ms": {
"version": "2.1.2",
@@ -1161,9 +1207,9 @@
}
},
"object-inspect": {
- "version": "1.8.0",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
- "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==",
+ "version": "1.10.3",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz",
+ "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==",
"dev": true
},
"object-keys": {
@@ -1173,80 +1219,37 @@
"dev": true
},
"object.assign": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz",
- "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==",
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+ "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
"dev": true,
"requires": {
+ "call-bind": "^1.0.0",
"define-properties": "^1.1.3",
- "es-abstract": "^1.18.0-next.0",
"has-symbols": "^1.0.1",
"object-keys": "^1.1.1"
}
},
"object.entries": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.2.tgz",
- "integrity": "sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA==",
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.4.tgz",
+ "integrity": "sha512-h4LWKWE+wKQGhtMjZEBud7uLGhqyLwj8fpHOarZhD2uY3C9cRtk57VQ89ke3moByLXMedqs3XCHzyb4AmA2DjA==",
"dev": true,
"requires": {
+ "call-bind": "^1.0.2",
"define-properties": "^1.1.3",
- "es-abstract": "^1.17.5",
- "has": "^1.0.3"
- },
- "dependencies": {
- "es-abstract": {
- "version": "1.17.6",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
- "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.2.0",
- "is-regex": "^1.1.0",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimend": "^1.0.1",
- "string.prototype.trimstart": "^1.0.1"
- }
- }
+ "es-abstract": "^1.18.2"
}
},
"object.values": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz",
- "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==",
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.4.tgz",
+ "integrity": "sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==",
"dev": true,
"requires": {
+ "call-bind": "^1.0.2",
"define-properties": "^1.1.3",
- "es-abstract": "^1.17.0-next.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3"
- },
- "dependencies": {
- "es-abstract": {
- "version": "1.17.6",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
- "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.2.0",
- "is-regex": "^1.1.0",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimend": "^1.0.1",
- "string.prototype.trimstart": "^1.0.1"
- }
- }
+ "es-abstract": "^1.18.2"
}
},
"once": {
@@ -1306,12 +1309,13 @@
}
},
"parse-json": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
- "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
"dev": true,
"requires": {
- "error-ex": "^1.2.0"
+ "error-ex": "^1.3.1",
+ "json-parse-better-errors": "^1.0.1"
}
},
"path-exists": {
@@ -1333,24 +1337,24 @@
"dev": true
},
"path-parse": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
- "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
},
"path-type": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
- "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
+ "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
"dev": true,
"requires": {
- "pify": "^2.0.0"
+ "pify": "^3.0.0"
}
},
"pify": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
"dev": true
},
"pkg-dir": {
@@ -1362,6 +1366,15 @@
"find-up": "^2.1.0"
}
},
+ "pkg-up": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz",
+ "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=",
+ "dev": true,
+ "requires": {
+ "find-up": "^2.1.0"
+ }
+ },
"prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -1381,30 +1394,36 @@
"dev": true
},
"read-pkg": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
- "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
+ "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
"dev": true,
"requires": {
- "load-json-file": "^2.0.0",
+ "load-json-file": "^4.0.0",
"normalize-package-data": "^2.3.2",
- "path-type": "^2.0.0"
+ "path-type": "^3.0.0"
}
},
"read-pkg-up": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
- "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz",
+ "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=",
"dev": true,
"requires": {
"find-up": "^2.0.0",
- "read-pkg": "^2.0.0"
+ "read-pkg": "^3.0.0"
}
},
"regexpp": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz",
- "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==",
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
+ "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
+ "dev": true
+ },
+ "require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
"dev": true
},
"requizzle": {
@@ -1417,11 +1436,12 @@
}
},
"resolve": {
- "version": "1.17.0",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
- "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
+ "version": "1.20.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
+ "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
"dev": true,
"requires": {
+ "is-core-module": "^2.2.0",
"path-parse": "^1.0.6"
}
},
@@ -1432,19 +1452,22 @@
"dev": true
},
"rimraf": {
- "version": "2.6.3",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
- "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
"dev": true,
"requires": {
"glob": "^7.1.3"
}
},
"semver": {
- "version": "7.3.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
- "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
- "dev": true
+ "version": "7.3.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
+ "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^6.0.0"
+ }
},
"shebang-command": {
"version": "2.0.0",
@@ -1462,14 +1485,40 @@
"dev": true
},
"slice-ansi": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
- "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
+ "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
"dev": true,
"requires": {
- "ansi-styles": "^3.2.0",
- "astral-regex": "^1.0.0",
- "is-fullwidth-code-point": "^2.0.0"
+ "ansi-styles": "^4.0.0",
+ "astral-regex": "^2.0.0",
+ "is-fullwidth-code-point": "^3.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ }
}
},
"spdx-correct": {
@@ -1499,9 +1548,9 @@
}
},
"spdx-license-ids": {
- "version": "3.0.6",
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz",
- "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==",
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz",
+ "integrity": "sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ==",
"dev": true
},
"sprintf-js": {
@@ -1511,93 +1560,34 @@
"dev": true
},
"string-width": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
- "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
+ "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
"dev": true,
"requires": {
- "emoji-regex": "^7.0.1",
- "is-fullwidth-code-point": "^2.0.0",
- "strip-ansi": "^5.1.0"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
- "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
- "dev": true
- },
- "strip-ansi": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
- "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
- "dev": true,
- "requires": {
- "ansi-regex": "^4.1.0"
- }
- }
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.0"
}
},
"string.prototype.trimend": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
- "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz",
+ "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==",
"dev": true,
"requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.5"
- },
- "dependencies": {
- "es-abstract": {
- "version": "1.17.6",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
- "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.2.0",
- "is-regex": "^1.1.0",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimend": "^1.0.1",
- "string.prototype.trimstart": "^1.0.1"
- }
- }
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3"
}
},
"string.prototype.trimstart": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
- "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz",
+ "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==",
"dev": true,
"requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.5"
- },
- "dependencies": {
- "es-abstract": {
- "version": "1.17.6",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
- "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.2.0",
- "is-regex": "^1.1.0",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimend": "^1.0.1",
- "string.prototype.trimstart": "^1.0.1"
- }
- }
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3"
}
},
"strip-ansi": {
@@ -1631,15 +1621,37 @@
}
},
"table": {
- "version": "5.4.6",
- "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz",
- "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==",
+ "version": "6.7.1",
+ "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz",
+ "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==",
"dev": true,
"requires": {
- "ajv": "^6.10.2",
- "lodash": "^4.17.14",
- "slice-ansi": "^2.1.0",
- "string-width": "^3.0.0"
+ "ajv": "^8.0.1",
+ "lodash.clonedeep": "^4.5.0",
+ "lodash.truncate": "^4.4.2",
+ "slice-ansi": "^4.0.0",
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "8.6.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.0.tgz",
+ "integrity": "sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true
+ }
}
},
"taffydb": {
@@ -1676,9 +1688,9 @@
}
},
"type-fest": {
- "version": "0.8.1",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
- "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
"dev": true
},
"uc.micro": {
@@ -1687,25 +1699,37 @@
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==",
"dev": true
},
+ "unbox-primitive": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
+ "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1",
+ "has-bigints": "^1.0.1",
+ "has-symbols": "^1.0.2",
+ "which-boxed-primitive": "^1.0.2"
+ }
+ },
"underscore": {
- "version": "1.12.1",
- "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz",
- "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==",
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz",
+ "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==",
"dev": true
},
"uri-js": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz",
- "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==",
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"dev": true,
"requires": {
"punycode": "^2.1.0"
}
},
"v8-compile-cache": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz",
- "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
+ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
"dev": true
},
"validate-npm-package-license": {
@@ -1727,6 +1751,19 @@
"isexe": "^2.0.0"
}
},
+ "which-boxed-primitive": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
+ "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
+ "dev": true,
+ "requires": {
+ "is-bigint": "^1.0.1",
+ "is-boolean-object": "^1.1.0",
+ "is-number-object": "^1.0.4",
+ "is-string": "^1.0.5",
+ "is-symbol": "^1.0.3"
+ }
+ },
"word-wrap": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
@@ -1739,20 +1776,17 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true
},
- "write": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz",
- "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==",
- "dev": true,
- "requires": {
- "mkdirp": "^0.5.1"
- }
- },
"xmlcreate": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.3.tgz",
"integrity": "sha512-HgS+X6zAztGa9zIK3Y3LXuJes33Lz9x+YyTxgrkIdabu2vqcGOWwdfCpf1hWLRrd553wd4QCDf6BBO6FfdsRiQ==",
"dev": true
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
}
}
}
diff --git a/platform/javascript/package.json b/platform/javascript/package.json
index 53748503f9..9dafae30c5 100644
--- a/platform/javascript/package.json
+++ b/platform/javascript/package.json
@@ -20,9 +20,9 @@
"author": "Godot Engine contributors",
"license": "MIT",
"devDependencies": {
- "eslint": "^7.9.0",
- "eslint-config-airbnb-base": "^14.2.0",
- "eslint-plugin-import": "^2.22.0",
- "jsdoc": "github:jsdoc/jsdoc#544a992824631fc652183d8b0b4b1281dc961560"
+ "eslint": "^7.28.0",
+ "eslint-config-airbnb-base": "^14.2.1",
+ "eslint-plugin-import": "^2.23.4",
+ "jsdoc": "^3.6.7"
}
}
diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp
index 8cd7afe5a7..f52853ca9e 100644
--- a/platform/osx/export/export.cpp
+++ b/platform/osx/export/export.cpp
@@ -147,6 +147,7 @@ void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.png,*.icns"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/bundle_identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/app_category", PROPERTY_HINT_ENUM, "Business,Developer-tools,Education,Entertainment,Finance,Games,Action-games,Adventure-games,Arcade-games,Board-games,Card-games,Casino-games,Dice-games,Educational-games,Family-games,Kids-games,Music-games,Puzzle-games,Racing-games,Role-playing-games,Simulation-games,Sports-games,Strategy-games,Trivia-games,Word-games,Graphics-design,Healthcare-fitness,Lifestyle,Medical,Music,News,Photography,Productivity,Reference,Social-networking,Sports,Travel,Utilities,Video,Weather"), "Games"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), ""));
@@ -386,6 +387,9 @@ void EditorExportPlatformOSX::_fix_plist(const Ref<EditorExportPreset> &p_preset
strnew += lines[i].replace("$version", p_preset->get("application/version")) + "\n";
} else if (lines[i].find("$signature") != -1) {
strnew += lines[i].replace("$signature", p_preset->get("application/signature")) + "\n";
+ } else if (lines[i].find("$app_category") != -1) {
+ String cat = p_preset->get("application/app_category");
+ strnew += lines[i].replace("$app_category", cat.to_lower()) + "\n";
} else if (lines[i].find("$copyright") != -1) {
strnew += lines[i].replace("$copyright", p_preset->get("application/copyright")) + "\n";
} else if (lines[i].find("$highres") != -1) {
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index e79dfb019c..e39c8841cd 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -711,12 +711,17 @@ Map<Vector2i, TileMapQuadrant> &TileMap::get_quadrant_map() {
void TileMap::fix_invalid_tiles() {
ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot fix invalid tiles if Tileset is not open.");
+
+ Set<Vector2i> coords;
for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) {
TileSetSource *source = *tile_set->get_source(E->get().source_id);
if (!source || !source->has_tile(E->get().get_atlas_coords()) || !source->has_alternative_tile(E->get().get_atlas_coords(), E->get().alternative_tile)) {
- set_cell(E->key(), -1, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
+ coords.insert(E->key());
}
}
+ for (Set<Vector2i>::Element *E = coords.front(); E; E = E->next()) {
+ set_cell(E->get(), -1, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
+ }
}
void TileMap::_recreate_quadrants() {
diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp
index 1c9418ae83..6b8851b4f8 100644
--- a/scene/3d/camera_3d.cpp
+++ b/scene/3d/camera_3d.cpp
@@ -500,6 +500,7 @@ void Camera3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_doppler_tracking", "mode"), &Camera3D::set_doppler_tracking);
ClassDB::bind_method(D_METHOD("get_doppler_tracking"), &Camera3D::get_doppler_tracking);
ClassDB::bind_method(D_METHOD("get_frustum"), &Camera3D::get_frustum);
+ ClassDB::bind_method(D_METHOD("is_position_in_frustum", "world_point"), &Camera3D::is_position_in_frustum);
ClassDB::bind_method(D_METHOD("get_camera_rid"), &Camera3D::get_camera);
ClassDB::bind_method(D_METHOD("set_cull_mask_bit", "layer", "enable"), &Camera3D::set_cull_mask_bit);
@@ -623,6 +624,16 @@ Vector<Plane> Camera3D::get_frustum() const {
return cm.get_projection_planes(get_camera_transform());
}
+bool Camera3D::is_position_in_frustum(const Vector3 &p_position) const {
+ Vector<Plane> frustum = get_frustum();
+ for (int i = 0; i < frustum.size(); i++) {
+ if (frustum[i].is_point_over(p_position)) {
+ return false;
+ }
+ }
+ return true;
+}
+
void Camera3D::set_v_offset(float p_offset) {
v_offset = p_offset;
_update_camera();
diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h
index d9ebe78f1a..c6efc8f9a9 100644
--- a/scene/3d/camera_3d.h
+++ b/scene/3d/camera_3d.h
@@ -153,6 +153,7 @@ public:
bool get_cull_mask_bit(int p_layer) const;
virtual Vector<Plane> get_frustum() const;
+ bool is_position_in_frustum(const Vector3 &p_position) const;
void set_environment(const Ref<Environment> &p_environment);
Ref<Environment> get_environment() const;
diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp
index e96e4df55c..e96b8ee1f9 100644
--- a/scene/3d/node_3d.cpp
+++ b/scene/3d/node_3d.cpp
@@ -32,6 +32,7 @@
#include "core/config/engine.h"
#include "core/object/message_queue.h"
+#include "scene/3d/visual_instance_3d.h"
#include "scene/main/scene_tree.h"
#include "scene/main/window.h"
#include "scene/scene_string_names.h"
@@ -148,6 +149,7 @@ void Node3D::_notification(int p_what) {
_notify_dirty();
notification(NOTIFICATION_ENTER_WORLD);
+ _update_visibility_parent(true);
} break;
case NOTIFICATION_EXIT_TREE: {
@@ -161,6 +163,7 @@ void Node3D::_notification(int p_what) {
data.parent = nullptr;
data.C = nullptr;
data.top_level_active = false;
+ _update_visibility_parent(true);
} break;
case NOTIFICATION_ENTER_WORLD: {
data.inside_world = true;
@@ -282,8 +285,12 @@ Transform3D Node3D::get_local_gizmo_transform() const {
}
#endif
-Node3D *Node3D::get_parent_spatial() const {
- return data.parent;
+Node3D *Node3D::get_parent_node_3d() const {
+ if (data.top_level) {
+ return nullptr;
+ }
+
+ return Object::cast_to<Node3D>(get_parent());
}
Transform3D Node3D::get_relative_transform(const Node *p_parent) const {
@@ -690,6 +697,51 @@ void Node3D::force_update_transform() {
notification(NOTIFICATION_TRANSFORM_CHANGED);
}
+void Node3D::_update_visibility_parent(bool p_update_root) {
+ RID new_parent;
+
+ if (!visibility_parent_path.is_empty()) {
+ if (!p_update_root) {
+ return;
+ }
+ Node *parent = get_node_or_null(visibility_parent_path);
+ ERR_FAIL_COND_MSG(!parent, "Can't find visibility parent node at path: " + visibility_parent_path);
+ ERR_FAIL_COND_MSG(parent == this, "The visibility parent can't be the same node.");
+ GeometryInstance3D *gi = Object::cast_to<GeometryInstance3D>(parent);
+ ERR_FAIL_COND_MSG(!gi, "The visibility parent node must be a GeometryInstance3D, at path: " + visibility_parent_path);
+ new_parent = gi ? gi->get_instance() : RID();
+ } else if (data.parent) {
+ new_parent = data.parent->data.visibility_parent;
+ }
+
+ if (new_parent == data.visibility_parent) {
+ return;
+ }
+
+ data.visibility_parent = new_parent;
+
+ VisualInstance3D *vi = Object::cast_to<VisualInstance3D>(this);
+ if (vi) {
+ RS::get_singleton()->instance_set_visibility_parent(vi->get_instance(), data.visibility_parent);
+ }
+
+ for (List<Node3D *>::Element *E = data.children.front(); E; E = E->next()) {
+ Node3D *c = E->get();
+ c->_update_visibility_parent(false);
+ }
+}
+
+void Node3D::set_visibility_parent(const NodePath &p_path) {
+ visibility_parent_path = p_path;
+ if (is_inside_tree()) {
+ _update_visibility_parent(true);
+ }
+}
+
+NodePath Node3D::get_visibility_parent() const {
+ return visibility_parent_path;
+}
+
void Node3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_transform", "local"), &Node3D::set_transform);
ClassDB::bind_method(D_METHOD("get_transform"), &Node3D::get_transform);
@@ -703,7 +755,7 @@ void Node3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_scale"), &Node3D::get_scale);
ClassDB::bind_method(D_METHOD("set_global_transform", "global"), &Node3D::set_global_transform);
ClassDB::bind_method(D_METHOD("get_global_transform"), &Node3D::get_global_transform);
- ClassDB::bind_method(D_METHOD("get_parent_spatial"), &Node3D::get_parent_spatial);
+ ClassDB::bind_method(D_METHOD("get_parent_node_3d"), &Node3D::get_parent_node_3d);
ClassDB::bind_method(D_METHOD("set_ignore_transform_notification", "enabled"), &Node3D::set_ignore_transform_notification);
ClassDB::bind_method(D_METHOD("set_as_top_level", "enable"), &Node3D::set_as_top_level);
ClassDB::bind_method(D_METHOD("is_set_as_top_level"), &Node3D::is_set_as_top_level);
@@ -713,6 +765,9 @@ void Node3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("force_update_transform"), &Node3D::force_update_transform);
+ ClassDB::bind_method(D_METHOD("set_visibility_parent", "path"), &Node3D::set_visibility_parent);
+ ClassDB::bind_method(D_METHOD("get_visibility_parent"), &Node3D::get_visibility_parent);
+
ClassDB::bind_method(D_METHOD("_update_gizmo"), &Node3D::_update_gizmo);
ClassDB::bind_method(D_METHOD("update_gizmo"), &Node3D::update_gizmo);
@@ -768,6 +823,7 @@ void Node3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "transform", PROPERTY_HINT_NONE, ""), "set_transform", "get_transform");
ADD_GROUP("Visibility", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "visibility_parent", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "GeometryInstance3D"), "set_visibility_parent", "get_visibility_parent");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gizmo", PROPERTY_HINT_RESOURCE_TYPE, "Node3DGizmo", 0), "set_gizmo", "get_gizmo");
ADD_SIGNAL(MethodInfo("visibility_changed"));
diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h
index 5f076ceb12..c7e36cf2ec 100644
--- a/scene/3d/node_3d.h
+++ b/scene/3d/node_3d.h
@@ -75,6 +75,8 @@ class Node3D : public Node {
bool top_level = false;
bool inside_world = false;
+ RID visibility_parent;
+
int children_lock = 0;
Node3D *parent = nullptr;
List<Node3D *> children;
@@ -95,12 +97,17 @@ class Node3D : public Node {
} data;
+ NodePath visibility_parent_path;
+
void _update_gizmo();
void _notify_dirty();
void _propagate_transform_changed(Node3D *p_origin);
void _propagate_visibility_changed();
+ void _propagate_visibility_parent();
+ void _update_visibility_parent(bool p_update_root);
+
protected:
_FORCE_INLINE_ void set_ignore_transform_notification(bool p_ignore) { data.ignore_notification = p_ignore; }
@@ -118,7 +125,7 @@ public:
NOTIFICATION_LOCAL_TRANSFORM_CHANGED = 44,
};
- Node3D *get_parent_spatial() const;
+ Node3D *get_parent_node_3d() const;
Ref<World3D> get_world_3d() const;
@@ -196,6 +203,9 @@ public:
void force_update_transform();
+ void set_visibility_parent(const NodePath &p_path);
+ NodePath get_visibility_parent() const;
+
Node3D();
};
diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp
index 6971c1ce2a..c16e3c2d78 100644
--- a/scene/3d/visual_instance_3d.cpp
+++ b/scene/3d/visual_instance_3d.cpp
@@ -150,40 +150,40 @@ Ref<Material> GeometryInstance3D::get_material_override() const {
return material_override;
}
-void GeometryInstance3D::set_lod_min_distance(float p_dist) {
- lod_min_distance = p_dist;
- RS::get_singleton()->instance_geometry_set_draw_range(get_instance(), lod_min_distance, lod_max_distance, lod_min_hysteresis, lod_max_hysteresis);
+void GeometryInstance3D::set_visibility_range_begin(float p_dist) {
+ visibility_range_begin = p_dist;
+ RS::get_singleton()->instance_geometry_set_visibility_range(get_instance(), visibility_range_begin, visibility_range_end, visibility_range_begin_margin, visibility_range_end_margin);
}
-float GeometryInstance3D::get_lod_min_distance() const {
- return lod_min_distance;
+float GeometryInstance3D::get_visibility_range_begin() const {
+ return visibility_range_begin;
}
-void GeometryInstance3D::set_lod_max_distance(float p_dist) {
- lod_max_distance = p_dist;
- RS::get_singleton()->instance_geometry_set_draw_range(get_instance(), lod_min_distance, lod_max_distance, lod_min_hysteresis, lod_max_hysteresis);
+void GeometryInstance3D::set_visibility_range_end(float p_dist) {
+ visibility_range_end = p_dist;
+ RS::get_singleton()->instance_geometry_set_visibility_range(get_instance(), visibility_range_begin, visibility_range_end, visibility_range_begin_margin, visibility_range_end_margin);
}
-float GeometryInstance3D::get_lod_max_distance() const {
- return lod_max_distance;
+float GeometryInstance3D::get_visibility_range_end() const {
+ return visibility_range_end;
}
-void GeometryInstance3D::set_lod_min_hysteresis(float p_dist) {
- lod_min_hysteresis = p_dist;
- RS::get_singleton()->instance_geometry_set_draw_range(get_instance(), lod_min_distance, lod_max_distance, lod_min_hysteresis, lod_max_hysteresis);
+void GeometryInstance3D::set_visibility_range_begin_margin(float p_dist) {
+ visibility_range_begin_margin = p_dist;
+ RS::get_singleton()->instance_geometry_set_visibility_range(get_instance(), visibility_range_begin, visibility_range_end, visibility_range_begin_margin, visibility_range_end_margin);
}
-float GeometryInstance3D::get_lod_min_hysteresis() const {
- return lod_min_hysteresis;
+float GeometryInstance3D::get_visibility_range_begin_margin() const {
+ return visibility_range_begin_margin;
}
-void GeometryInstance3D::set_lod_max_hysteresis(float p_dist) {
- lod_max_hysteresis = p_dist;
- RS::get_singleton()->instance_geometry_set_draw_range(get_instance(), lod_min_distance, lod_max_distance, lod_min_hysteresis, lod_max_hysteresis);
+void GeometryInstance3D::set_visibility_range_end_margin(float p_dist) {
+ visibility_range_end_margin = p_dist;
+ RS::get_singleton()->instance_geometry_set_visibility_range(get_instance(), visibility_range_begin, visibility_range_end, visibility_range_begin_margin, visibility_range_end_margin);
}
-float GeometryInstance3D::get_lod_max_hysteresis() const {
- return lod_max_hysteresis;
+float GeometryInstance3D::get_visibility_range_end_margin() const {
+ return visibility_range_end_margin;
}
void GeometryInstance3D::_notification(int p_what) {
@@ -357,17 +357,17 @@ void GeometryInstance3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_lod_bias", "bias"), &GeometryInstance3D::set_lod_bias);
ClassDB::bind_method(D_METHOD("get_lod_bias"), &GeometryInstance3D::get_lod_bias);
- ClassDB::bind_method(D_METHOD("set_lod_max_hysteresis", "mode"), &GeometryInstance3D::set_lod_max_hysteresis);
- ClassDB::bind_method(D_METHOD("get_lod_max_hysteresis"), &GeometryInstance3D::get_lod_max_hysteresis);
+ ClassDB::bind_method(D_METHOD("set_visibility_range_end_margin", "distance"), &GeometryInstance3D::set_visibility_range_end_margin);
+ ClassDB::bind_method(D_METHOD("get_visibility_range_end_margin"), &GeometryInstance3D::get_visibility_range_end_margin);
- ClassDB::bind_method(D_METHOD("set_lod_max_distance", "mode"), &GeometryInstance3D::set_lod_max_distance);
- ClassDB::bind_method(D_METHOD("get_lod_max_distance"), &GeometryInstance3D::get_lod_max_distance);
+ ClassDB::bind_method(D_METHOD("set_visibility_range_end", "distance"), &GeometryInstance3D::set_visibility_range_end);
+ ClassDB::bind_method(D_METHOD("get_visibility_range_end"), &GeometryInstance3D::get_visibility_range_end);
- ClassDB::bind_method(D_METHOD("set_lod_min_hysteresis", "mode"), &GeometryInstance3D::set_lod_min_hysteresis);
- ClassDB::bind_method(D_METHOD("get_lod_min_hysteresis"), &GeometryInstance3D::get_lod_min_hysteresis);
+ ClassDB::bind_method(D_METHOD("set_visibility_range_begin_margin", "distance"), &GeometryInstance3D::set_visibility_range_begin_margin);
+ ClassDB::bind_method(D_METHOD("get_visibility_range_begin_margin"), &GeometryInstance3D::get_visibility_range_begin_margin);
- ClassDB::bind_method(D_METHOD("set_lod_min_distance", "mode"), &GeometryInstance3D::set_lod_min_distance);
- ClassDB::bind_method(D_METHOD("get_lod_min_distance"), &GeometryInstance3D::get_lod_min_distance);
+ ClassDB::bind_method(D_METHOD("set_visibility_range_begin", "distance"), &GeometryInstance3D::set_visibility_range_begin);
+ ClassDB::bind_method(D_METHOD("get_visibility_range_begin"), &GeometryInstance3D::get_visibility_range_begin);
ClassDB::bind_method(D_METHOD("set_shader_instance_uniform", "uniform", "value"), &GeometryInstance3D::set_shader_instance_uniform);
ClassDB::bind_method(D_METHOD("get_shader_instance_uniform", "uniform"), &GeometryInstance3D::get_shader_instance_uniform);
@@ -398,11 +398,11 @@ void GeometryInstance3D::_bind_methods() {
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_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_hysteresis", PROPERTY_HINT_RANGE, "0,32768,0.01"), "set_lod_min_hysteresis", "get_lod_min_hysteresis");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_max_distance", PROPERTY_HINT_RANGE, "0,32768,0.01"), "set_lod_max_distance", "get_lod_max_distance");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_max_hysteresis", PROPERTY_HINT_RANGE, "0,32768,0.01"), "set_lod_max_hysteresis", "get_lod_max_hysteresis");
+ ADD_GROUP("Visibility Range", "visibility_range_");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_begin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01"), "set_visibility_range_begin", "get_visibility_range_begin");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_begin_margin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01"), "set_visibility_range_begin_margin", "get_visibility_range_begin_margin");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_end", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01"), "set_visibility_range_end", "get_visibility_range_end");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_end_margin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01"), "set_visibility_range_end_margin", "get_visibility_range_end_margin");
//ADD_SIGNAL( MethodInfo("visibility_changed"));
diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h
index 68d29ef81e..2d5699859b 100644
--- a/scene/3d/visual_instance_3d.h
+++ b/scene/3d/visual_instance_3d.h
@@ -107,10 +107,13 @@ public:
private:
ShadowCastingSetting shadow_casting_setting = SHADOW_CASTING_SETTING_ON;
Ref<Material> material_override;
- float lod_min_distance = 0.0;
- float lod_max_distance = 0.0;
- float lod_min_hysteresis = 0.0;
- float lod_max_hysteresis = 0.0;
+
+ float visibility_range_begin = 0.0;
+ float visibility_range_end = 0.0;
+ float visibility_range_begin_margin = 0.0;
+ float visibility_range_end_margin = 0.0;
+
+ Vector<NodePath> visibility_range_children;
float lod_bias = 1.0;
@@ -136,17 +139,20 @@ public:
void set_cast_shadows_setting(ShadowCastingSetting p_shadow_casting_setting);
ShadowCastingSetting get_cast_shadows_setting() const;
- void set_lod_min_distance(float p_dist);
- float get_lod_min_distance() const;
+ void set_visibility_range_begin(float p_dist);
+ float get_visibility_range_begin() const;
+
+ void set_visibility_range_end(float p_dist);
+ float get_visibility_range_end() const;
- void set_lod_max_distance(float p_dist);
- float get_lod_max_distance() const;
+ void set_visibility_range_begin_margin(float p_dist);
+ float get_visibility_range_begin_margin() const;
- void set_lod_min_hysteresis(float p_dist);
- float get_lod_min_hysteresis() const;
+ void set_visibility_range_end_margin(float p_dist);
+ float get_visibility_range_end_margin() const;
- void set_lod_max_hysteresis(float p_dist);
- float get_lod_max_hysteresis() const;
+ void set_visibility_range_parent(const Node *p_parent);
+ void clear_visibility_range_parent();
void set_material_override(const Ref<Material> &p_material);
Ref<Material> get_material_override() const;
diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp
index 5a4d9b5d7e..4f2c816934 100644
--- a/scene/3d/xr_nodes.cpp
+++ b/scene/3d/xr_nodes.cpp
@@ -520,9 +520,9 @@ TypedArray<String> XROrigin3D::get_configuration_warnings() const {
}
}
- bool vr_enabled = GLOBAL_GET("rendering/vr/enabled");
- if (!vr_enabled) {
- warnings.push_back(TTR("VR is not enabled in rendering project settings. Stereoscopic output is not supported unless this is enabled."));
+ bool xr_enabled = GLOBAL_GET("rendering/xr/enabled");
+ if (!xr_enabled) {
+ warnings.push_back(TTR("XR is not enabled in rendering project settings. Stereoscopic output is not supported unless this is enabled."));
}
return warnings;
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index d5000e88d7..48e327ce78 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -1526,6 +1526,9 @@ void CodeEdit::_clear_delimiters(DelimiterType p_type) {
}
}
delimiter_cache.clear();
+ if (!setting_delimiters) {
+ _update_delimiter_cache();
+ }
}
TypedArray<String> CodeEdit::_get_delimiters(DelimiterType p_type) const {
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index 78ab30699a..1e444e439d 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -42,8 +42,13 @@
#define ZOOM_SCALE 1.2
-#define MIN_ZOOM (((1 / ZOOM_SCALE) / ZOOM_SCALE) / ZOOM_SCALE)
-#define MAX_ZOOM (1 * ZOOM_SCALE * ZOOM_SCALE * ZOOM_SCALE)
+// Allow dezooming 8 times from the default zoom level.
+// At low zoom levels, text is unreadable due to its small size and poor filtering,
+// but this is still useful for previewing purposes.
+#define MIN_ZOOM (1 / Math::pow(ZOOM_SCALE, 8))
+
+// Allow zooming 4 times from the default zoom level.
+#define MAX_ZOOM (1 * Math::pow(ZOOM_SCALE, 4))
#define MINIMAP_OFFSET 12
#define MINIMAP_PADDING 5
diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp
index a407ef21cb..5947f3b99e 100644
--- a/scene/gui/slider.cpp
+++ b/scene/gui/slider.cpp
@@ -172,7 +172,7 @@ void Slider::_notification(int p_what) {
int widget_width = style->get_minimum_size().width + style->get_center_size().width;
float areasize = size.height - grabber->get_size().height;
style->draw(ci, Rect2i(Point2i(size.width / 2 - widget_width / 2, 0), Size2i(widget_width, size.height)));
- grabber_area->draw(ci, Rect2i(Point2i((size.width - widget_width) / 2, size.height - areasize * ratio - grabber->get_size().height / 2), Size2i(widget_width, areasize * ratio + grabber->get_size().width / 2)));
+ grabber_area->draw(ci, Rect2i(Point2i((size.width - widget_width) / 2, size.height - areasize * ratio - grabber->get_size().height / 2), Size2i(widget_width, areasize * ratio + grabber->get_size().height / 2)));
if (ticks > 1) {
int grabber_offset = (grabber->get_size().height / 2 - tick->get_height() / 2);
diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp
index f43e3d1a9d..8659ea06a2 100644
--- a/scene/gui/texture_button.cpp
+++ b/scene/gui/texture_button.cpp
@@ -295,11 +295,13 @@ void TextureButton::set_normal_texture(const Ref<Texture2D> &p_normal) {
void TextureButton::set_pressed_texture(const Ref<Texture2D> &p_pressed) {
pressed = p_pressed;
update();
+ minimum_size_changed();
}
void TextureButton::set_hover_texture(const Ref<Texture2D> &p_hover) {
hover = p_hover;
update();
+ minimum_size_changed();
}
void TextureButton::set_disabled_texture(const Ref<Texture2D> &p_disabled) {
@@ -310,6 +312,7 @@ void TextureButton::set_disabled_texture(const Ref<Texture2D> &p_disabled) {
void TextureButton::set_click_mask(const Ref<BitMap> &p_click_mask) {
click_mask = p_click_mask;
update();
+ minimum_size_changed();
}
Ref<Texture2D> TextureButton::get_normal_texture() const {
diff --git a/scene/resources/theme.h b/scene/resources/theme.h
index cc4b716aa2..fe64fd7290 100644
--- a/scene/resources/theme.h
+++ b/scene/resources/theme.h
@@ -44,6 +44,7 @@ class Theme : public Resource {
#ifdef TOOLS_ENABLED
friend class ThemeItemImportTree;
friend class ThemeItemEditorDialog;
+ friend class ThemeTypeEditor;
#endif
public:
diff --git a/servers/rendering/renderer_rd/effects_rd.cpp b/servers/rendering/renderer_rd/effects_rd.cpp
index 8c6f76b2ab..4fd5520e56 100644
--- a/servers/rendering/renderer_rd/effects_rd.cpp
+++ b/servers/rendering/renderer_rd/effects_rd.cpp
@@ -940,10 +940,10 @@ void EffectsRD::bokeh_dof(RID p_base_texture, RID p_depth_texture, const Size2i
RD::get_singleton()->compute_list_end();
}
-void EffectsRD::gather_ssao(RD::ComputeListID p_compute_list, const Vector<RID> p_ao_slices, const SSAOSettings &p_settings, bool p_adaptive_base_pass) {
- RD::get_singleton()->compute_list_bind_uniform_set(p_compute_list, ssao.gather_uniform_set, 0);
+void EffectsRD::gather_ssao(RD::ComputeListID p_compute_list, const Vector<RID> p_ao_slices, const SSAOSettings &p_settings, bool p_adaptive_base_pass, RID p_gather_uniform_set, RID p_importance_map_uniform_set) {
+ RD::get_singleton()->compute_list_bind_uniform_set(p_compute_list, p_gather_uniform_set, 0);
if ((p_settings.quality == RS::ENV_SSAO_QUALITY_ULTRA) && !p_adaptive_base_pass) {
- RD::get_singleton()->compute_list_bind_uniform_set(p_compute_list, ssao.importance_map_uniform_set, 1);
+ RD::get_singleton()->compute_list_bind_uniform_set(p_compute_list, p_importance_map_uniform_set, 1);
}
for (int i = 0; i < 4; i++) {
@@ -966,7 +966,7 @@ void EffectsRD::gather_ssao(RD::ComputeListID p_compute_list, const Vector<RID>
RD::get_singleton()->compute_list_add_barrier(p_compute_list);
}
-void EffectsRD::generate_ssao(RID p_depth_buffer, RID p_normal_buffer, RID p_depth_mipmaps_texture, const Vector<RID> &p_depth_mipmaps, RID p_ao, const Vector<RID> p_ao_slices, RID p_ao_pong, const Vector<RID> p_ao_pong_slices, RID p_upscale_buffer, RID p_importance_map, RID p_importance_map_pong, const CameraMatrix &p_projection, const SSAOSettings &p_settings, bool p_invalidate_uniform_sets) {
+void EffectsRD::generate_ssao(RID p_depth_buffer, RID p_normal_buffer, RID p_depth_mipmaps_texture, const Vector<RID> &p_depth_mipmaps, RID p_ao, const Vector<RID> p_ao_slices, RID p_ao_pong, const Vector<RID> p_ao_pong_slices, RID p_upscale_buffer, RID p_importance_map, RID p_importance_map_pong, const CameraMatrix &p_projection, const SSAOSettings &p_settings, bool p_invalidate_uniform_sets, RID &r_downsample_uniform_set, RID &r_gather_uniform_set, RID &r_importance_map_uniform_set) {
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
RD::get_singleton()->draw_command_begin_label("SSAO");
/* FIRST PASS */
@@ -996,7 +996,7 @@ void EffectsRD::generate_ssao(RID p_depth_buffer, RID p_normal_buffer, RID p_dep
u.ids.push_back(p_depth_mipmaps[3]);
uniforms.push_back(u);
}
- ssao.downsample_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, ssao.downsample_shader.version_get_shader(ssao.downsample_shader_version, 2), 2);
+ r_downsample_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, ssao.downsample_shader.version_get_shader(ssao.downsample_shader_version, 2), 2);
}
float depth_linearize_mul = -p_projection.matrix[3][2];
@@ -1031,7 +1031,7 @@ void EffectsRD::generate_ssao(RID p_depth_buffer, RID p_normal_buffer, RID p_dep
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_depth_buffer), 0);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_depth_mipmaps[0]), 1);
if (p_settings.quality > RS::ENV_SSAO_QUALITY_MEDIUM) {
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, ssao.downsample_uniform_set, 2);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, r_downsample_uniform_set, 2);
}
RD::get_singleton()->compute_list_set_push_constant(compute_list, &ssao.downsample_push_constant, sizeof(SSAODownsamplePushConstant));
@@ -1115,7 +1115,7 @@ void EffectsRD::generate_ssao(RID p_depth_buffer, RID p_normal_buffer, RID p_dep
u.ids.push_back(ssao.gather_constants_buffer);
uniforms.push_back(u);
}
- ssao.gather_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, ssao.gather_shader.version_get_shader(ssao.gather_shader_version, 0), 0);
+ r_gather_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, ssao.gather_shader.version_get_shader(ssao.gather_shader_version, 0), 0);
}
if (p_invalidate_uniform_sets) {
@@ -1142,7 +1142,7 @@ void EffectsRD::generate_ssao(RID p_depth_buffer, RID p_normal_buffer, RID p_dep
u.ids.push_back(ssao.importance_map_load_counter);
uniforms.push_back(u);
}
- ssao.importance_map_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, ssao.gather_shader.version_get_shader(ssao.gather_shader_version, 2), 1);
+ r_importance_map_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, ssao.gather_shader.version_get_shader(ssao.gather_shader_version, 2), 1);
}
if (p_settings.quality == RS::ENV_SSAO_QUALITY_ULTRA) {
@@ -1153,7 +1153,7 @@ void EffectsRD::generate_ssao(RID p_depth_buffer, RID p_normal_buffer, RID p_dep
ssao.importance_map_push_constant.power = p_settings.power;
//base pass
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssao.pipelines[SSAO_GATHER_BASE]);
- gather_ssao(compute_list, p_ao_pong_slices, p_settings, true);
+ gather_ssao(compute_list, p_ao_pong_slices, p_settings, true, r_gather_uniform_set, RID());
//generate importance map
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssao.pipelines[SSAO_GENERATE_IMPORTANCE_MAP]);
@@ -1184,7 +1184,7 @@ void EffectsRD::generate_ssao(RID p_depth_buffer, RID p_normal_buffer, RID p_dep
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssao.pipelines[SSAO_GATHER]);
}
- gather_ssao(compute_list, p_ao_slices, p_settings, false);
+ gather_ssao(compute_list, p_ao_slices, p_settings, false, r_gather_uniform_set, r_importance_map_uniform_set);
RD::get_singleton()->draw_command_end_label(); // Gather SSAO
}
@@ -1580,7 +1580,11 @@ EffectsRD::EffectsRD() {
tonemap.shader_version = tonemap.shader.version_create();
for (int i = 0; i < TONEMAP_MODE_MAX; i++) {
- tonemap.pipelines[i].setup(tonemap.shader.version_get_shader(tonemap.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
+ if (tonemap.shader.is_variant_enabled(i)) {
+ tonemap.pipelines[i].setup(tonemap.shader.version_get_shader(tonemap.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
+ } else {
+ tonemap.pipelines[i].clear();
+ }
}
}
diff --git a/servers/rendering/renderer_rd/effects_rd.h b/servers/rendering/renderer_rd/effects_rd.h
index cd106e6630..8b31ffbbd0 100644
--- a/servers/rendering/renderer_rd/effects_rd.h
+++ b/servers/rendering/renderer_rd/effects_rd.h
@@ -385,12 +385,10 @@ class EffectsRD {
SSAODownsamplePushConstant downsample_push_constant;
SsaoDownsampleShaderRD downsample_shader;
RID downsample_shader_version;
- RID downsample_uniform_set;
SSAOGatherPushConstant gather_push_constant;
SsaoShaderRD gather_shader;
RID gather_shader_version;
- RID gather_uniform_set;
RID gather_constants_buffer;
bool gather_initialized = false;
@@ -398,7 +396,6 @@ class EffectsRD {
SsaoImportanceMapShaderRD importance_map_shader;
RID importance_map_shader_version;
RID importance_map_load_counter;
- RID importance_map_uniform_set;
RID counter_uniform_set;
SSAOBlurPushConstant blur_push_constant;
@@ -746,8 +743,8 @@ public:
void tonemapper(RID p_source_color, RID p_dst_framebuffer, const TonemapSettings &p_settings);
- void gather_ssao(RD::ComputeListID p_compute_list, const Vector<RID> p_ao_slices, const SSAOSettings &p_settings, bool p_adaptive_base_pass);
- void generate_ssao(RID p_depth_buffer, RID p_normal_buffer, RID p_depth_mipmaps_texture, const Vector<RID> &depth_mipmaps, RID p_ao, const Vector<RID> p_ao_slices, RID p_ao_pong, const Vector<RID> p_ao_pong_slices, RID p_upscale_buffer, RID p_importance_map, RID p_importance_map_pong, const CameraMatrix &p_projection, const SSAOSettings &p_settings, bool p_invalidate_uniform_sets);
+ void gather_ssao(RD::ComputeListID p_compute_list, const Vector<RID> p_ao_slices, const SSAOSettings &p_settings, bool p_adaptive_base_pass, RID p_gather_uniform_set, RID p_importance_map_uniform_set);
+ void generate_ssao(RID p_depth_buffer, RID p_normal_buffer, RID p_depth_mipmaps_texture, const Vector<RID> &depth_mipmaps, RID p_ao, const Vector<RID> p_ao_slices, RID p_ao_pong, const Vector<RID> p_ao_pong_slices, RID p_upscale_buffer, RID p_importance_map, RID p_importance_map_pong, const CameraMatrix &p_projection, const SSAOSettings &p_settings, bool p_invalidate_uniform_sets, RID &r_downsample_uniform_set, RID &r_gather_uniform_set, RID &r_importance_map_uniform_set);
void roughness_limit(RID p_source_normal, RID p_roughness, const Size2i &p_size, float p_curve);
void cubemap_downsample(RID p_source_cubemap, RID p_dest_cubemap, const Size2i &p_size);
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
index 6e755bac4e..be98fb42c0 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
@@ -1728,7 +1728,7 @@ void RendererSceneRenderRD::_process_ssao(RID p_render_buffers, RID p_environmen
settings.half_screen_size = Size2i(buffer_width, buffer_height);
settings.quarter_screen_size = Size2i(half_width, half_height);
- storage->get_effects()->generate_ssao(rb->depth_texture, p_normal_buffer, rb->ssao.depth, rb->ssao.depth_slices, rb->ssao.ao_deinterleaved, rb->ssao.ao_deinterleaved_slices, rb->ssao.ao_pong, rb->ssao.ao_pong_slices, rb->ssao.ao_final, rb->ssao.importance_map[0], rb->ssao.importance_map[1], p_projection, settings, uniform_sets_are_invalid);
+ storage->get_effects()->generate_ssao(rb->depth_texture, p_normal_buffer, rb->ssao.depth, rb->ssao.depth_slices, rb->ssao.ao_deinterleaved, rb->ssao.ao_deinterleaved_slices, rb->ssao.ao_pong, rb->ssao.ao_pong_slices, rb->ssao.ao_final, rb->ssao.importance_map[0], rb->ssao.importance_map[1], p_projection, settings, uniform_sets_are_invalid, rb->ssao.downsample_uniform_set, rb->ssao.gather_uniform_set, rb->ssao.importance_map_uniform_set);
}
void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const RenderDataRD *p_render_data) {
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h
index 3096881afa..9a793e42c5 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h
@@ -459,6 +459,10 @@ private:
Vector<RID> ao_pong_slices;
RID ao_final;
RID importance_map[2];
+
+ RID downsample_uniform_set;
+ RID gather_uniform_set;
+ RID importance_map_uniform_set;
} ssao;
struct SSR {
diff --git a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp
index 0a8f174ec6..1aa01dd16e 100644
--- a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp
@@ -127,8 +127,12 @@ void RendererSceneSkyRD::SkyShaderData::set_code(const String &p_code) {
depth_stencil_state.enable_depth_test = true;
depth_stencil_state.depth_compare_operator = RD::COMPARE_OP_LESS_OR_EQUAL;
- RID shader_variant = scene_singleton->sky.sky_shader.shader.version_get_shader(version, i);
- pipelines[i].setup(shader_variant, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), depth_stencil_state, RD::PipelineColorBlendState::create_disabled(), 0);
+ if (scene_singleton->sky.sky_shader.shader.is_variant_enabled(i)) {
+ RID shader_variant = scene_singleton->sky.sky_shader.shader.version_get_shader(version, i);
+ pipelines[i].setup(shader_variant, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), depth_stencil_state, RD::PipelineColorBlendState::create_disabled(), 0);
+ } else {
+ pipelines[i].clear();
+ }
}
valid = true;
diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.cpp b/servers/rendering/renderer_rd/renderer_storage_rd.cpp
index eeca283c13..64be54115f 100644
--- a/servers/rendering/renderer_rd/renderer_storage_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_storage_rd.cpp
@@ -1440,8 +1440,10 @@ void RendererStorageRD::shader_set_code(RID p_shader, const String &p_code) {
material->shader_type = new_type;
}
- for (Map<StringName, RID>::Element *E = shader->default_texture_parameter.front(); E; E = E->next()) {
- shader->data->set_default_texture_param(E->key(), E->get());
+ if (shader->data) {
+ for (Map<StringName, RID>::Element *E = shader->default_texture_parameter.front(); E; E = E->next()) {
+ shader->data->set_default_texture_param(E->key(), E->get());
+ }
}
}
@@ -3823,7 +3825,7 @@ void RendererStorageRD::_update_dirty_multimeshes() {
if (multimesh->data_cache_used_dirty_regions) {
uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1;
- uint32_t visible_region_count = (visible_instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1;
+ uint32_t visible_region_count = visible_instances == 0 ? 0 : (visible_instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1;
uint32_t region_size = multimesh->stride_cache * MULTIMESH_DIRTY_REGION_SIZE * sizeof(float);
diff --git a/servers/rendering/renderer_scene.h b/servers/rendering/renderer_scene.h
index 4d1ea2e382..3b25498ed8 100644
--- a/servers/rendering/renderer_scene.h
+++ b/servers/rendering/renderer_scene.h
@@ -63,6 +63,8 @@ public:
virtual void scenario_set_reflection_atlas_size(RID p_scenario, int p_reflection_size, int p_reflection_count) = 0;
virtual bool is_scenario(RID p_scenario) const = 0;
virtual RID scenario_get_environment(RID p_scenario) = 0;
+ virtual void scenario_add_viewport_visibility_mask(RID p_scenario, RID p_viewport) = 0;
+ virtual void scenario_remove_viewport_visibility_mask(RID p_scenario, RID p_viewport) = 0;
virtual RID instance_allocate() = 0;
virtual void instance_initialize(RID p_rid) = 0;
@@ -82,6 +84,7 @@ public:
virtual void instance_set_exterior(RID p_instance, bool p_enabled) = 0;
virtual void instance_set_extra_visibility_margin(RID p_instance, real_t p_margin) = 0;
+ virtual void instance_set_visibility_parent(RID p_instance, RID p_parent_instance) = 0;
// don't use these in a game!
virtual Vector<ObjectID> instances_cull_aabb(const AABB &p_aabb, RID p_scenario = RID()) const = 0;
@@ -92,8 +95,7 @@ public:
virtual void instance_geometry_set_cast_shadows_setting(RID p_instance, RS::ShadowCastingSetting p_shadow_casting_setting) = 0;
virtual void instance_geometry_set_material_override(RID p_instance, RID p_material) = 0;
- virtual void instance_geometry_set_draw_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin) = 0;
- virtual void instance_geometry_set_as_instance_lod(RID p_instance, RID p_as_lod_of_instance) = 0;
+ virtual void instance_geometry_set_visibility_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin) = 0;
virtual void instance_geometry_set_lightmap(RID p_instance, RID p_lightmap, const Rect2 &p_lightmap_uv_scale, int p_slice_index) = 0;
virtual void instance_geometry_set_lod_bias(RID p_instance, float p_lod_bias) = 0;
diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp
index 290f058fc7..2c66b22089 100644
--- a/servers/rendering/renderer_scene_cull.cpp
+++ b/servers/rendering/renderer_scene_cull.cpp
@@ -323,6 +323,7 @@ void RendererSceneCull::scenario_initialize(RID p_rid) {
scenario->instance_aabbs.set_page_pool(&instance_aabb_page_pool);
scenario->instance_data.set_page_pool(&instance_data_page_pool);
+ scenario->instance_visibility.set_page_pool(&instance_visibility_data_page_pool);
RendererSceneOcclusionCull::get_singleton()->add_scenario(p_rid);
@@ -369,6 +370,37 @@ RID RendererSceneCull::scenario_get_environment(RID p_scenario) {
return scenario->environment;
}
+void RendererSceneCull::scenario_remove_viewport_visibility_mask(RID p_scenario, RID p_viewport) {
+ Scenario *scenario = scenario_owner.getornull(p_scenario);
+ ERR_FAIL_COND(!scenario);
+ if (!scenario->viewport_visibility_masks.has(p_viewport)) {
+ return;
+ }
+
+ uint64_t mask = scenario->viewport_visibility_masks[p_viewport];
+ scenario->used_viewport_visibility_bits &= ~mask;
+ scenario->viewport_visibility_masks.erase(p_viewport);
+}
+
+void RendererSceneCull::scenario_add_viewport_visibility_mask(RID p_scenario, RID p_viewport) {
+ Scenario *scenario = scenario_owner.getornull(p_scenario);
+ ERR_FAIL_COND(!scenario);
+ ERR_FAIL_COND(scenario->viewport_visibility_masks.has(p_viewport));
+
+ uint64_t new_mask = 1;
+ while (new_mask & scenario->used_viewport_visibility_bits) {
+ new_mask <<= 1;
+ }
+
+ if (new_mask == 0) {
+ ERR_PRINT("Only 64 viewports per scenario allowed when using visibility ranges.");
+ new_mask = ((uint64_t)1) << 63;
+ }
+
+ scenario->viewport_visibility_masks[p_viewport] = new_mask;
+ scenario->used_viewport_visibility_bits |= new_mask;
+}
+
/* INSTANCING API */
void RendererSceneCull::_instance_queue_update(Instance *p_instance, bool p_update_aabb, bool p_update_dependencies) {
@@ -1103,10 +1135,142 @@ void RendererSceneCull::instance_geometry_set_material_override(RID p_instance,
}
}
-void RendererSceneCull::instance_geometry_set_draw_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin) {
+void RendererSceneCull::instance_geometry_set_visibility_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin) {
+ Instance *instance = instance_owner.getornull(p_instance);
+ ERR_FAIL_COND(!instance);
+
+ instance->visibility_range_begin = p_min;
+ instance->visibility_range_end = p_max;
+ instance->visibility_range_begin_margin = p_min_margin;
+ instance->visibility_range_end_margin = p_max_margin;
+
+ _update_instance_visibility_dependencies(instance);
+
+ if (instance->scenario && instance->visibility_index != -1) {
+ InstanceVisibilityData &vd = instance->scenario->instance_visibility[instance->visibility_index];
+ vd.range_begin = instance->visibility_range_begin;
+ vd.range_end = instance->visibility_range_end;
+ vd.range_begin_margin = instance->visibility_range_begin_margin;
+ vd.range_end_margin = instance->visibility_range_end_margin;
+ }
+}
+
+void RendererSceneCull::instance_set_visibility_parent(RID p_instance, RID p_parent_instance) {
+ Instance *instance = instance_owner.getornull(p_instance);
+ ERR_FAIL_COND(!instance);
+
+ Instance *old_parent = instance->visibility_parent;
+ if (old_parent) {
+ if ((1 << old_parent->base_type) & RS::INSTANCE_GEOMETRY_MASK && old_parent->base_data) {
+ InstanceGeometryData *old_parent_geom = static_cast<InstanceGeometryData *>(old_parent->base_data);
+ old_parent_geom->visibility_dependencies.erase(instance);
+ _update_instance_visibility_depth(old_parent);
+ }
+ instance->visibility_parent = nullptr;
+ }
+
+ Instance *parent = instance_owner.getornull(p_parent_instance);
+ ERR_FAIL_COND(p_parent_instance.is_valid() && !parent);
+
+ if (parent) {
+ if ((1 << parent->base_type) & RS::INSTANCE_GEOMETRY_MASK && parent->base_data) {
+ InstanceGeometryData *parent_geom = static_cast<InstanceGeometryData *>(parent->base_data);
+ parent_geom->visibility_dependencies.insert(instance);
+ _update_instance_visibility_depth(parent);
+ }
+ instance->visibility_parent = parent;
+ }
+
+ _update_instance_visibility_dependencies(instance);
+}
+
+void RendererSceneCull::_update_instance_visibility_depth(Instance *p_instance) {
+ bool cycle_detected = false;
+ Set<Instance *> traversed_nodes;
+
+ {
+ Instance *instance = p_instance;
+ while (instance && ((1 << instance->base_type) & RS::INSTANCE_GEOMETRY_MASK) && instance->base_data) {
+ InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(instance->base_data);
+ if (!geom->visibility_dependencies.is_empty()) {
+ uint32_t depth = 0;
+ for (Set<Instance *>::Element *E = geom->visibility_dependencies.front(); E; E = E->next()) {
+ if (((1 << E->get()->base_type) & RS::INSTANCE_GEOMETRY_MASK) == 0 || !E->get()->base_data) {
+ continue;
+ }
+ InstanceGeometryData *child_geom = static_cast<InstanceGeometryData *>(E->get()->base_data);
+ depth = MAX(depth, child_geom->visibility_dependencies_depth);
+ }
+ geom->visibility_dependencies_depth = depth + 1;
+ } else {
+ geom->visibility_dependencies_depth = 0;
+ }
+
+ if (instance->scenario && instance->visibility_index != -1) {
+ instance->scenario->instance_visibility.move(instance->visibility_index, geom->visibility_dependencies_depth);
+ }
+
+ traversed_nodes.insert(instance);
+
+ instance = instance->visibility_parent;
+ if (traversed_nodes.has(instance)) {
+ cycle_detected = true;
+ break;
+ }
+ }
+ }
+
+ if (cycle_detected) {
+ ERR_PRINT("Cycle detected in the visibility dependecies tree.");
+ for (Set<Instance *>::Element *E = traversed_nodes.front(); E; E = E->next()) {
+ Instance *instance = E->get();
+ InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(instance->base_data);
+ geom->visibility_dependencies_depth = 0;
+ if (instance->scenario && instance->visibility_index != -1) {
+ instance->scenario->instance_visibility.move(instance->visibility_index, geom->visibility_dependencies_depth);
+ }
+ }
+ }
}
-void RendererSceneCull::instance_geometry_set_as_instance_lod(RID p_instance, RID p_as_lod_of_instance) {
+void RendererSceneCull::_update_instance_visibility_dependencies(Instance *p_instance) {
+ bool is_geometry_instance = ((1 << p_instance->base_type) & RS::INSTANCE_GEOMETRY_MASK) && p_instance->base_data;
+ bool has_visibility_range = p_instance->visibility_range_begin > 0.0 || p_instance->visibility_range_end > 0.0;
+ bool needs_visibility_cull = has_visibility_range && is_geometry_instance && p_instance->array_index != -1;
+
+ if (!needs_visibility_cull && p_instance->visibility_index != -1) {
+ p_instance->scenario->instance_visibility.remove(p_instance->visibility_index);
+ p_instance->visibility_index = -1;
+ } else if (needs_visibility_cull && p_instance->visibility_index == -1) {
+ InstanceVisibilityData vd;
+ vd.instance = p_instance;
+ vd.range_begin = p_instance->visibility_range_begin;
+ vd.range_end = p_instance->visibility_range_end;
+ vd.range_begin_margin = p_instance->visibility_range_begin_margin;
+ vd.range_end_margin = p_instance->visibility_range_end_margin;
+ vd.position = p_instance->transformed_aabb.get_position() + p_instance->transformed_aabb.get_size() / 2.0f;
+ vd.array_index = p_instance->array_index;
+
+ InstanceGeometryData *geom_data = static_cast<InstanceGeometryData *>(p_instance->base_data);
+ p_instance->scenario->instance_visibility.insert(vd, geom_data->visibility_dependencies_depth);
+ }
+
+ if (p_instance->scenario && p_instance->array_index != -1) {
+ p_instance->scenario->instance_data[p_instance->array_index].visibility_index = p_instance->visibility_index;
+
+ InstanceGeometryData *geom_data = static_cast<InstanceGeometryData *>(p_instance->base_data);
+ if ((has_visibility_range || p_instance->visibility_parent) && (p_instance->visibility_index == -1 || (geom_data && geom_data->visibility_dependencies_depth == 0))) {
+ p_instance->scenario->instance_data[p_instance->array_index].flags |= InstanceData::FLAG_VISIBILITY_DEPENDENCY_NEEDS_CHECK;
+ } else {
+ p_instance->scenario->instance_data[p_instance->array_index].flags &= ~InstanceData::FLAG_VISIBILITY_DEPENDENCY_NEEDS_CHECK;
+ }
+
+ if (p_instance->visibility_parent) {
+ p_instance->scenario->instance_data[p_instance->array_index].parent_array_index = p_instance->visibility_parent->array_index;
+ } else {
+ p_instance->scenario->instance_data[p_instance->array_index].parent_array_index = -1;
+ }
+ }
}
void RendererSceneCull::instance_geometry_set_lightmap(RID p_instance, RID p_lightmap, const Rect2 &p_lightmap_uv_scale, int p_slice_index) {
@@ -1352,12 +1516,23 @@ void RendererSceneCull::_update_instance(Instance *p_instance) {
idata.layer_mask = p_instance->layer_mask;
idata.flags = p_instance->base_type; //changing it means de-indexing, so this never needs to be changed later
idata.base_rid = p_instance->base;
+ idata.parent_array_index = p_instance->visibility_parent ? p_instance->visibility_parent->array_index : -1;
+ idata.visibility_index = p_instance->visibility_index;
+
switch (p_instance->base_type) {
case RS::INSTANCE_MESH:
case RS::INSTANCE_MULTIMESH:
case RS::INSTANCE_IMMEDIATE:
case RS::INSTANCE_PARTICLES: {
- idata.instance_geometry = static_cast<InstanceGeometryData *>(p_instance->base_data)->geometry_instance;
+ InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(p_instance->base_data);
+ idata.instance_geometry = geom->geometry_instance;
+
+ for (Set<Instance *>::Element *E = geom->visibility_dependencies.front(); E; E = E->next()) {
+ Instance *dep_instance = E->get();
+ if (dep_instance->array_index != -1) {
+ dep_instance->scenario->instance_data[dep_instance->array_index].parent_array_index = p_instance->array_index;
+ }
+ }
} break;
case RS::INSTANCE_LIGHT: {
idata.instance_data_rid = static_cast<InstanceLightData *>(p_instance->base_data)->instance.get_id();
@@ -1404,6 +1579,7 @@ void RendererSceneCull::_update_instance(Instance *p_instance) {
p_instance->scenario->instance_data.push_back(idata);
p_instance->scenario->instance_aabbs.push_back(InstanceBounds(p_instance->transformed_aabb));
+ _update_instance_visibility_dependencies(p_instance);
} else {
if ((1 << p_instance->base_type) & RS::INSTANCE_GEOMETRY_MASK) {
p_instance->scenario->indexers[Scenario::INDEXER_GEOMETRY].update(p_instance->indexer_id, bvh_aabb);
@@ -1413,6 +1589,10 @@ void RendererSceneCull::_update_instance(Instance *p_instance) {
p_instance->scenario->instance_aabbs[p_instance->array_index] = InstanceBounds(p_instance->transformed_aabb);
}
+ if (p_instance->visibility_index != -1) {
+ p_instance->scenario->instance_visibility[p_instance->visibility_index].position = p_instance->transformed_aabb.get_position() + p_instance->transformed_aabb.get_size() / 2.0f;
+ }
+
//move instance and repair
pair_pass++;
@@ -1486,9 +1666,24 @@ void RendererSceneCull::_unpair_instance(Instance *p_instance) {
//replace this by last
int32_t swap_with_index = p_instance->scenario->instance_data.size() - 1;
if (swap_with_index != p_instance->array_index) {
- p_instance->scenario->instance_data[swap_with_index].instance->array_index = p_instance->array_index; //swap
+ Instance *swapped_instance = p_instance->scenario->instance_data[swap_with_index].instance;
+ swapped_instance->array_index = p_instance->array_index; //swap
p_instance->scenario->instance_data[p_instance->array_index] = p_instance->scenario->instance_data[swap_with_index];
p_instance->scenario->instance_aabbs[p_instance->array_index] = p_instance->scenario->instance_aabbs[swap_with_index];
+
+ if (swapped_instance->visibility_index != -1) {
+ swapped_instance->scenario->instance_visibility[swapped_instance->visibility_index].array_index = swapped_instance->array_index;
+ }
+
+ if ((1 << swapped_instance->base_type) & RS::INSTANCE_GEOMETRY_MASK) {
+ InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(swapped_instance->base_data);
+ for (Set<Instance *>::Element *E = geom->visibility_dependencies.front(); E; E = E->next()) {
+ Instance *dep_instance = E->get();
+ if (dep_instance != p_instance && dep_instance->array_index != -1) {
+ dep_instance->scenario->instance_data[dep_instance->array_index].parent_array_index = swapped_instance->array_index;
+ }
+ }
+ }
}
// pop last
@@ -1505,7 +1700,16 @@ void RendererSceneCull::_unpair_instance(Instance *p_instance) {
scene_render->geometry_instance_pair_reflection_probe_instances(geom->geometry_instance, nullptr, 0);
scene_render->geometry_instance_pair_decal_instances(geom->geometry_instance, nullptr, 0);
scene_render->geometry_instance_pair_voxel_gi_instances(geom->geometry_instance, nullptr, 0);
+
+ for (Set<Instance *>::Element *E = geom->visibility_dependencies.front(); E; E = E->next()) {
+ Instance *dep_instance = E->get();
+ if (dep_instance->array_index != -1) {
+ dep_instance->scenario->instance_data[dep_instance->array_index].parent_array_index = -1;
+ }
+ }
}
+
+ _update_instance_visibility_dependencies(p_instance);
}
void RendererSceneCull::_update_instance_aabb(Instance *p_instance) {
@@ -2272,16 +2476,73 @@ void RendererSceneCull::render_camera(RID p_render_buffers, RID p_camera, RID p_
#endif
}
-void RendererSceneCull::_frustum_cull_threaded(uint32_t p_thread, CullData *cull_data) {
+void RendererSceneCull::_visibility_cull_threaded(uint32_t p_thread, VisibilityCullData *cull_data) {
+ uint32_t total_threads = RendererThreadPool::singleton->thread_work_pool.get_thread_count();
+ uint32_t bin_from = p_thread * cull_data->cull_count / total_threads;
+ uint32_t bin_to = (p_thread + 1 == total_threads) ? cull_data->cull_count : ((p_thread + 1) * cull_data->cull_count / total_threads);
+
+ _visibility_cull(*cull_data, cull_data->cull_offset + bin_from, cull_data->cull_offset + bin_to);
+}
+
+void RendererSceneCull::_visibility_cull(const VisibilityCullData &cull_data, uint64_t p_from, uint64_t p_to) {
+ Scenario *scenario = cull_data.scenario;
+ for (unsigned int i = p_from; i < p_to; i++) {
+ InstanceVisibilityData &vd = scenario->instance_visibility[i];
+ InstanceData &idata = scenario->instance_data[vd.array_index];
+
+ if (idata.parent_array_index >= 0) {
+ uint32_t parent_flags = scenario->instance_data[idata.parent_array_index].flags;
+ if ((parent_flags & InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN) || (parent_flags & InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN_CLOSE_RANGE) == 0) {
+ idata.flags |= InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN;
+ idata.flags &= ~InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN_CLOSE_RANGE;
+ continue;
+ }
+ }
+
+ int range_check = _visibility_range_check(vd, cull_data.camera_position, cull_data.viewport_mask);
+
+ if (range_check == -1) {
+ idata.flags |= InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN;
+ idata.flags &= ~InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN_CLOSE_RANGE;
+ } else if (range_check == 1) {
+ idata.flags &= ~InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN;
+ idata.flags |= InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN_CLOSE_RANGE;
+ } else {
+ idata.flags &= ~InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN;
+ idata.flags &= ~InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN_CLOSE_RANGE;
+ }
+ }
+}
+
+int RendererSceneCull::_visibility_range_check(InstanceVisibilityData &r_vis_data, const Vector3 &p_camera_pos, uint64_t p_viewport_mask) {
+ float dist = p_camera_pos.distance_to(r_vis_data.position);
+
+ bool in_range_last_frame = p_viewport_mask & r_vis_data.viewport_state;
+ float begin_offset = in_range_last_frame ? -r_vis_data.range_begin_margin : r_vis_data.range_begin_margin;
+ float end_offset = in_range_last_frame ? r_vis_data.range_end_margin : -r_vis_data.range_end_margin;
+
+ if (r_vis_data.range_end > 0.0f && dist > r_vis_data.range_end + end_offset) {
+ r_vis_data.viewport_state &= ~p_viewport_mask;
+ return -1;
+ } else if (r_vis_data.range_begin > 0.0f && dist < r_vis_data.range_begin + begin_offset) {
+ r_vis_data.viewport_state &= ~p_viewport_mask;
+ return 1;
+ } else {
+ r_vis_data.viewport_state |= p_viewport_mask;
+ return 0;
+ }
+}
+
+void RendererSceneCull::_scene_cull_threaded(uint32_t p_thread, CullData *cull_data) {
uint32_t cull_total = cull_data->scenario->instance_data.size();
uint32_t total_threads = RendererThreadPool::singleton->thread_work_pool.get_thread_count();
uint32_t cull_from = p_thread * cull_total / total_threads;
uint32_t cull_to = (p_thread + 1 == total_threads) ? cull_total : ((p_thread + 1) * cull_total / total_threads);
- _frustum_cull(*cull_data, frustum_cull_result_threads[p_thread], cull_from, cull_to);
+ _scene_cull(*cull_data, scene_cull_result_threads[p_thread], cull_from, cull_to);
}
-void RendererSceneCull::_frustum_cull(CullData &cull_data, FrustumCullResult &cull_result, uint64_t p_from, uint64_t p_to) {
+void RendererSceneCull::_scene_cull(CullData &cull_data, InstanceCullResult &cull_result, uint64_t p_from, uint64_t p_to) {
uint64_t frame_number = RSG::rasterizer->get_frame_number();
float lightmap_probe_update_speed = RSG::storage->lightmap_get_probe_capture_update_speed() * RSG::rasterizer->get_frame_delta_time();
@@ -2296,177 +2557,192 @@ void RendererSceneCull::_frustum_cull(CullData &cull_data, FrustumCullResult &cu
for (uint64_t i = p_from; i < p_to; i++) {
bool mesh_visible = false;
- if (cull_data.scenario->instance_aabbs[i].in_frustum(cull_data.cull->frustum) && (cull_data.occlusion_buffer == nullptr || cull_data.scenario->instance_data[i].flags & InstanceData::FLAG_IGNORE_OCCLUSION_CULLING ||
- !cull_data.occlusion_buffer->is_occluded(cull_data.scenario->instance_aabbs[i].bounds, cull_data.cam_transform.origin, inv_cam_transform, *cull_data.camera_matrix, z_near))) {
- InstanceData &idata = cull_data.scenario->instance_data[i];
- uint32_t base_type = idata.flags & InstanceData::FLAG_BASE_TYPE_MASK;
-
- if ((cull_data.visible_layers & idata.layer_mask) == 0) {
- //failure
- } else if (base_type == RS::INSTANCE_LIGHT) {
- cull_result.lights.push_back(idata.instance);
- cull_result.light_instances.push_back(RID::from_uint64(idata.instance_data_rid));
- if (cull_data.shadow_atlas.is_valid() && RSG::storage->light_has_shadow(idata.base_rid)) {
- scene_render->light_instance_mark_visible(RID::from_uint64(idata.instance_data_rid)); //mark it visible for shadow allocation later
- }
-
- } else if (base_type == RS::INSTANCE_REFLECTION_PROBE) {
- if (cull_data.render_reflection_probe != idata.instance) {
- //avoid entering The Matrix
+ InstanceData &idata = cull_data.scenario->instance_data[i];
+ uint32_t visibility_flags = idata.flags & (InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN_CLOSE_RANGE | InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN);
+ int32_t visibility_check = -1;
- if ((idata.flags & InstanceData::FLAG_REFLECTION_PROBE_DIRTY) || scene_render->reflection_probe_instance_needs_redraw(RID::from_uint64(idata.instance_data_rid))) {
- InstanceReflectionProbeData *reflection_probe = static_cast<InstanceReflectionProbeData *>(idata.instance->base_data);
- cull_data.cull->lock.lock();
- if (!reflection_probe->update_list.in_list()) {
- reflection_probe->render_step = 0;
- reflection_probe_render_list.add_last(&reflection_probe->update_list);
- }
- cull_data.cull->lock.unlock();
+#define HIDDEN_BY_VISIBILITY_CHECKS (visibility_flags == InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN_CLOSE_RANGE || visibility_flags == InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN)
+#define LAYER_CHECK (cull_data.visible_layers & idata.layer_mask)
+#define IN_FRUSTUM(f) (cull_data.scenario->instance_aabbs[i].in_frustum(f))
+#define VIS_RANGE_CHECK ((idata.visibility_index == -1) || _visibility_range_check(cull_data.scenario->instance_visibility[idata.visibility_index], cull_data.cam_transform.origin, cull_data.visibility_cull_data->viewport_mask) == 0)
+#define VIS_PARENT_CHECK ((idata.parent_array_index == -1) || ((cull_data.scenario->instance_data[idata.parent_array_index].flags & InstanceData::FLAG_VISIBILITY_DEPENDENCY_NEEDS_CHECK) == InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN_CLOSE_RANGE))
+#define VIS_CHECK (visibility_check < 0 ? (visibility_check = (visibility_flags != InstanceData::FLAG_VISIBILITY_DEPENDENCY_NEEDS_CHECK || (VIS_RANGE_CHECK && VIS_PARENT_CHECK))) : visibility_check)
+#define OCCLUSION_CULLED (cull_data.occlusion_buffer != nullptr && cull_data.scenario->instance_data[i].flags & InstanceData::FLAG_IGNORE_OCCLUSION_CULLING && cull_data.occlusion_buffer->is_occluded(cull_data.scenario->instance_aabbs[i].bounds, cull_data.cam_transform.origin, inv_cam_transform, *cull_data.camera_matrix, z_near))
- idata.flags &= ~uint32_t(InstanceData::FLAG_REFLECTION_PROBE_DIRTY);
+ if (!HIDDEN_BY_VISIBILITY_CHECKS) {
+ if (LAYER_CHECK && IN_FRUSTUM(cull_data.cull->frustum) && VIS_CHECK && !OCCLUSION_CULLED) {
+ uint32_t base_type = idata.flags & InstanceData::FLAG_BASE_TYPE_MASK;
+ if (base_type == RS::INSTANCE_LIGHT) {
+ cull_result.lights.push_back(idata.instance);
+ cull_result.light_instances.push_back(RID::from_uint64(idata.instance_data_rid));
+ if (cull_data.shadow_atlas.is_valid() && RSG::storage->light_has_shadow(idata.base_rid)) {
+ scene_render->light_instance_mark_visible(RID::from_uint64(idata.instance_data_rid)); //mark it visible for shadow allocation later
}
- if (scene_render->reflection_probe_instance_has_reflection(RID::from_uint64(idata.instance_data_rid))) {
- cull_result.reflections.push_back(RID::from_uint64(idata.instance_data_rid));
- }
- }
- } else if (base_type == RS::INSTANCE_DECAL) {
- cull_result.decals.push_back(RID::from_uint64(idata.instance_data_rid));
+ } else if (base_type == RS::INSTANCE_REFLECTION_PROBE) {
+ if (cull_data.render_reflection_probe != idata.instance) {
+ //avoid entering The Matrix
- } else if (base_type == RS::INSTANCE_VOXEL_GI) {
- InstanceVoxelGIData *voxel_gi = static_cast<InstanceVoxelGIData *>(idata.instance->base_data);
- cull_data.cull->lock.lock();
- if (!voxel_gi->update_element.in_list()) {
- voxel_gi_update_list.add(&voxel_gi->update_element);
- }
- cull_data.cull->lock.unlock();
- cull_result.voxel_gi_instances.push_back(RID::from_uint64(idata.instance_data_rid));
+ if ((idata.flags & InstanceData::FLAG_REFLECTION_PROBE_DIRTY) || scene_render->reflection_probe_instance_needs_redraw(RID::from_uint64(idata.instance_data_rid))) {
+ InstanceReflectionProbeData *reflection_probe = static_cast<InstanceReflectionProbeData *>(idata.instance->base_data);
+ cull_data.cull->lock.lock();
+ if (!reflection_probe->update_list.in_list()) {
+ reflection_probe->render_step = 0;
+ reflection_probe_render_list.add_last(&reflection_probe->update_list);
+ }
+ cull_data.cull->lock.unlock();
+
+ idata.flags &= ~uint32_t(InstanceData::FLAG_REFLECTION_PROBE_DIRTY);
+ }
- } else if (base_type == RS::INSTANCE_LIGHTMAP) {
- cull_result.lightmaps.push_back(RID::from_uint64(idata.instance_data_rid));
- } else if (((1 << base_type) & RS::INSTANCE_GEOMETRY_MASK) && !(idata.flags & InstanceData::FLAG_CAST_SHADOWS_ONLY)) {
- bool keep = true;
+ if (scene_render->reflection_probe_instance_has_reflection(RID::from_uint64(idata.instance_data_rid))) {
+ cull_result.reflections.push_back(RID::from_uint64(idata.instance_data_rid));
+ }
+ }
+ } else if (base_type == RS::INSTANCE_DECAL) {
+ cull_result.decals.push_back(RID::from_uint64(idata.instance_data_rid));
+
+ } else if (base_type == RS::INSTANCE_VOXEL_GI) {
+ InstanceVoxelGIData *voxel_gi = static_cast<InstanceVoxelGIData *>(idata.instance->base_data);
+ cull_data.cull->lock.lock();
+ if (!voxel_gi->update_element.in_list()) {
+ voxel_gi_update_list.add(&voxel_gi->update_element);
+ }
+ cull_data.cull->lock.unlock();
+ cull_result.voxel_gi_instances.push_back(RID::from_uint64(idata.instance_data_rid));
- if (idata.flags & InstanceData::FLAG_REDRAW_IF_VISIBLE) {
- RenderingServerDefault::redraw_request();
- }
+ } else if (base_type == RS::INSTANCE_LIGHTMAP) {
+ cull_result.lightmaps.push_back(RID::from_uint64(idata.instance_data_rid));
+ } else if (((1 << base_type) & RS::INSTANCE_GEOMETRY_MASK) && !(idata.flags & InstanceData::FLAG_CAST_SHADOWS_ONLY)) {
+ bool keep = true;
- if (base_type == RS::INSTANCE_MESH) {
- mesh_visible = true;
- } else if (base_type == RS::INSTANCE_PARTICLES) {
- //particles visible? process them
- if (RSG::storage->particles_is_inactive(idata.base_rid)) {
- //but if nothing is going on, don't do it.
- keep = false;
- } else {
- cull_data.cull->lock.lock();
- RSG::storage->particles_request_process(idata.base_rid);
- cull_data.cull->lock.unlock();
- RSG::storage->particles_set_view_axis(idata.base_rid, -cull_data.cam_transform.basis.get_axis(2).normalized(), cull_data.cam_transform.basis.get_axis(1).normalized());
- //particles visible? request redraw
+ if (idata.flags & InstanceData::FLAG_REDRAW_IF_VISIBLE) {
RenderingServerDefault::redraw_request();
}
- }
-
- if (geometry_instance_pair_mask & (1 << RS::INSTANCE_LIGHT) && (idata.flags & InstanceData::FLAG_GEOM_LIGHTING_DIRTY)) {
- InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(idata.instance->base_data);
- uint32_t idx = 0;
- for (Set<Instance *>::Element *E = geom->lights.front(); E; E = E->next()) {
- InstanceLightData *light = static_cast<InstanceLightData *>(E->get()->base_data);
- instance_pair_buffer[idx++] = light->instance;
- if (idx == MAX_INSTANCE_PAIRS) {
- break;
+ if (base_type == RS::INSTANCE_MESH) {
+ mesh_visible = true;
+ } else if (base_type == RS::INSTANCE_PARTICLES) {
+ //particles visible? process them
+ if (RSG::storage->particles_is_inactive(idata.base_rid)) {
+ //but if nothing is going on, don't do it.
+ keep = false;
+ } else {
+ cull_data.cull->lock.lock();
+ RSG::storage->particles_request_process(idata.base_rid);
+ cull_data.cull->lock.unlock();
+ RSG::storage->particles_set_view_axis(idata.base_rid, -cull_data.cam_transform.basis.get_axis(2).normalized(), cull_data.cam_transform.basis.get_axis(1).normalized());
+ //particles visible? request redraw
+ RenderingServerDefault::redraw_request();
}
}
- scene_render->geometry_instance_pair_light_instances(geom->geometry_instance, instance_pair_buffer, idx);
- idata.flags &= ~uint32_t(InstanceData::FLAG_GEOM_LIGHTING_DIRTY);
- }
+ if (geometry_instance_pair_mask & (1 << RS::INSTANCE_LIGHT) && (idata.flags & InstanceData::FLAG_GEOM_LIGHTING_DIRTY)) {
+ InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(idata.instance->base_data);
+ uint32_t idx = 0;
+
+ for (Set<Instance *>::Element *E = geom->lights.front(); E; E = E->next()) {
+ InstanceLightData *light = static_cast<InstanceLightData *>(E->get()->base_data);
+ instance_pair_buffer[idx++] = light->instance;
+ if (idx == MAX_INSTANCE_PAIRS) {
+ break;
+ }
+ }
+
+ scene_render->geometry_instance_pair_light_instances(geom->geometry_instance, instance_pair_buffer, idx);
+ idata.flags &= ~uint32_t(InstanceData::FLAG_GEOM_LIGHTING_DIRTY);
+ }
- if (geometry_instance_pair_mask & (1 << RS::INSTANCE_REFLECTION_PROBE) && (idata.flags & InstanceData::FLAG_GEOM_REFLECTION_DIRTY)) {
- InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(idata.instance->base_data);
- uint32_t idx = 0;
+ if (geometry_instance_pair_mask & (1 << RS::INSTANCE_REFLECTION_PROBE) && (idata.flags & InstanceData::FLAG_GEOM_REFLECTION_DIRTY)) {
+ InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(idata.instance->base_data);
+ uint32_t idx = 0;
- for (Set<Instance *>::Element *E = geom->reflection_probes.front(); E; E = E->next()) {
- InstanceReflectionProbeData *reflection_probe = static_cast<InstanceReflectionProbeData *>(E->get()->base_data);
+ for (Set<Instance *>::Element *E = geom->reflection_probes.front(); E; E = E->next()) {
+ InstanceReflectionProbeData *reflection_probe = static_cast<InstanceReflectionProbeData *>(E->get()->base_data);
- instance_pair_buffer[idx++] = reflection_probe->instance;
- if (idx == MAX_INSTANCE_PAIRS) {
- break;
+ instance_pair_buffer[idx++] = reflection_probe->instance;
+ if (idx == MAX_INSTANCE_PAIRS) {
+ break;
+ }
}
- }
- scene_render->geometry_instance_pair_reflection_probe_instances(geom->geometry_instance, instance_pair_buffer, idx);
- idata.flags &= ~uint32_t(InstanceData::FLAG_GEOM_REFLECTION_DIRTY);
- }
+ scene_render->geometry_instance_pair_reflection_probe_instances(geom->geometry_instance, instance_pair_buffer, idx);
+ idata.flags &= ~uint32_t(InstanceData::FLAG_GEOM_REFLECTION_DIRTY);
+ }
- if (geometry_instance_pair_mask & (1 << RS::INSTANCE_DECAL) && (idata.flags & InstanceData::FLAG_GEOM_DECAL_DIRTY)) {
- InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(idata.instance->base_data);
- uint32_t idx = 0;
+ if (geometry_instance_pair_mask & (1 << RS::INSTANCE_DECAL) && (idata.flags & InstanceData::FLAG_GEOM_DECAL_DIRTY)) {
+ InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(idata.instance->base_data);
+ uint32_t idx = 0;
- for (Set<Instance *>::Element *E = geom->decals.front(); E; E = E->next()) {
- InstanceDecalData *decal = static_cast<InstanceDecalData *>(E->get()->base_data);
+ for (Set<Instance *>::Element *E = geom->decals.front(); E; E = E->next()) {
+ InstanceDecalData *decal = static_cast<InstanceDecalData *>(E->get()->base_data);
- instance_pair_buffer[idx++] = decal->instance;
- if (idx == MAX_INSTANCE_PAIRS) {
- break;
+ instance_pair_buffer[idx++] = decal->instance;
+ if (idx == MAX_INSTANCE_PAIRS) {
+ break;
+ }
}
+ scene_render->geometry_instance_pair_decal_instances(geom->geometry_instance, instance_pair_buffer, idx);
+ idata.flags &= ~uint32_t(InstanceData::FLAG_GEOM_DECAL_DIRTY);
}
- scene_render->geometry_instance_pair_decal_instances(geom->geometry_instance, instance_pair_buffer, idx);
- idata.flags &= ~uint32_t(InstanceData::FLAG_GEOM_DECAL_DIRTY);
- }
- if (idata.flags & InstanceData::FLAG_GEOM_VOXEL_GI_DIRTY) {
- InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(idata.instance->base_data);
- uint32_t idx = 0;
- for (Set<Instance *>::Element *E = geom->voxel_gi_instances.front(); E; E = E->next()) {
- InstanceVoxelGIData *voxel_gi = static_cast<InstanceVoxelGIData *>(E->get()->base_data);
+ if (idata.flags & InstanceData::FLAG_GEOM_VOXEL_GI_DIRTY) {
+ InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(idata.instance->base_data);
+ uint32_t idx = 0;
+ for (Set<Instance *>::Element *E = geom->voxel_gi_instances.front(); E; E = E->next()) {
+ InstanceVoxelGIData *voxel_gi = static_cast<InstanceVoxelGIData *>(E->get()->base_data);
- instance_pair_buffer[idx++] = voxel_gi->probe_instance;
- if (idx == MAX_INSTANCE_PAIRS) {
- break;
+ instance_pair_buffer[idx++] = voxel_gi->probe_instance;
+ if (idx == MAX_INSTANCE_PAIRS) {
+ break;
+ }
}
- }
- scene_render->geometry_instance_pair_voxel_gi_instances(geom->geometry_instance, instance_pair_buffer, idx);
- idata.flags &= ~uint32_t(InstanceData::FLAG_GEOM_VOXEL_GI_DIRTY);
- }
+ scene_render->geometry_instance_pair_voxel_gi_instances(geom->geometry_instance, instance_pair_buffer, idx);
+ idata.flags &= ~uint32_t(InstanceData::FLAG_GEOM_VOXEL_GI_DIRTY);
+ }
- if ((idata.flags & InstanceData::FLAG_LIGHTMAP_CAPTURE) && idata.instance->last_frame_pass != frame_number && !idata.instance->lightmap_target_sh.is_empty() && !idata.instance->lightmap_sh.is_empty()) {
- InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(idata.instance->base_data);
- Color *sh = idata.instance->lightmap_sh.ptrw();
- const Color *target_sh = idata.instance->lightmap_target_sh.ptr();
- for (uint32_t j = 0; j < 9; j++) {
- sh[j] = sh[j].lerp(target_sh[j], MIN(1.0, lightmap_probe_update_speed));
+ if ((idata.flags & InstanceData::FLAG_LIGHTMAP_CAPTURE) && idata.instance->last_frame_pass != frame_number && !idata.instance->lightmap_target_sh.is_empty() && !idata.instance->lightmap_sh.is_empty()) {
+ InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(idata.instance->base_data);
+ Color *sh = idata.instance->lightmap_sh.ptrw();
+ const Color *target_sh = idata.instance->lightmap_target_sh.ptr();
+ for (uint32_t j = 0; j < 9; j++) {
+ sh[j] = sh[j].lerp(target_sh[j], MIN(1.0, lightmap_probe_update_speed));
+ }
+ scene_render->geometry_instance_set_lightmap_capture(geom->geometry_instance, sh);
+ idata.instance->last_frame_pass = frame_number;
}
- scene_render->geometry_instance_set_lightmap_capture(geom->geometry_instance, sh);
- idata.instance->last_frame_pass = frame_number;
- }
- if (keep) {
- cull_result.geometry_instances.push_back(idata.instance_geometry);
+ if (keep) {
+ cull_result.geometry_instances.push_back(idata.instance_geometry);
+ }
}
}
- }
- for (uint32_t j = 0; j < cull_data.cull->shadow_count; j++) {
- for (uint32_t k = 0; k < cull_data.cull->shadows[j].cascade_count; k++) {
- if (cull_data.scenario->instance_aabbs[i].in_frustum(cull_data.cull->shadows[j].cascades[k].frustum)) {
- InstanceData &idata = cull_data.scenario->instance_data[i];
- uint32_t base_type = idata.flags & InstanceData::FLAG_BASE_TYPE_MASK;
+ for (uint32_t j = 0; j < cull_data.cull->shadow_count; j++) {
+ for (uint32_t k = 0; k < cull_data.cull->shadows[j].cascade_count; k++) {
+ if (IN_FRUSTUM(cull_data.cull->shadows[j].cascades[k].frustum) && VIS_CHECK) {
+ uint32_t base_type = idata.flags & InstanceData::FLAG_BASE_TYPE_MASK;
- if (((1 << base_type) & RS::INSTANCE_GEOMETRY_MASK) && idata.flags & InstanceData::FLAG_CAST_SHADOWS) {
- cull_result.directional_shadows[j].cascade_geometry_instances[k].push_back(idata.instance_geometry);
- mesh_visible = true;
+ if (((1 << base_type) & RS::INSTANCE_GEOMETRY_MASK) && idata.flags & InstanceData::FLAG_CAST_SHADOWS) {
+ cull_result.directional_shadows[j].cascade_geometry_instances[k].push_back(idata.instance_geometry);
+ mesh_visible = true;
+ }
}
}
}
}
+#undef HIDDEN_BY_VISIBILITY_CHECKS
+#undef LAYER_CHECK
+#undef IN_FRUSTUM
+#undef VIS_RANGE_CHECK
+#undef VIS_PARENT_CHECK
+#undef VIS_CHECK
+#undef OCCLUSION_CULLED
+
for (uint32_t j = 0; j < cull_data.cull->sdfgi.region_count; j++) {
if (cull_data.scenario->instance_aabbs[i].in_aabb(cull_data.cull->sdfgi.region_aabb[j])) {
- InstanceData &idata = cull_data.scenario->instance_data[i];
uint32_t base_type = idata.flags & InstanceData::FLAG_BASE_TYPE_MASK;
if (base_type == RS::INSTANCE_LIGHT) {
@@ -2507,7 +2783,35 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c
scene_render->sdfgi_update(p_render_buffers, p_environment, p_camera_data->main_transform.origin); //update conditions for SDFGI (whether its used or not)
}
- RENDER_TIMESTAMP("Frustum Culling");
+ RENDER_TIMESTAMP("Visibility Dependencies");
+
+ VisibilityCullData visibility_cull_data;
+ if (scenario->instance_visibility.get_bin_count() > 0) {
+ if (!scenario->viewport_visibility_masks.has(p_viewport)) {
+ scenario_add_viewport_visibility_mask(scenario->self, p_viewport);
+ }
+
+ visibility_cull_data.scenario = scenario;
+ visibility_cull_data.viewport_mask = scenario->viewport_visibility_masks[p_viewport];
+ visibility_cull_data.camera_position = p_camera_data->main_transform.origin;
+
+ for (int i = scenario->instance_visibility.get_bin_count() - 1; i > 0; i--) { // We skip bin 0
+ visibility_cull_data.cull_offset = scenario->instance_visibility.get_bin_start(i);
+ visibility_cull_data.cull_count = scenario->instance_visibility.get_bin_size(i);
+
+ if (visibility_cull_data.cull_count == 0) {
+ continue;
+ }
+
+ if (visibility_cull_data.cull_count > thread_cull_threshold) {
+ RendererThreadPool::singleton->thread_work_pool.do_work(RendererThreadPool::singleton->thread_work_pool.get_thread_count(), this, &RendererSceneCull::_visibility_cull_threaded, &visibility_cull_data);
+ } else {
+ _visibility_cull(visibility_cull_data, visibility_cull_data.cull_offset, visibility_cull_data.cull_offset + visibility_cull_data.cull_count);
+ }
+ }
+ }
+
+ RENDER_TIMESTAMP("Culling");
//rasterizer->set_camera(p_camera_data->main_transform, p_camera_data.main_projection, p_camera_data.is_ortogonal);
@@ -2580,7 +2884,7 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c
}
}
- frustum_cull_result.clear();
+ scene_cull_result.clear();
{
uint64_t cull_from = 0;
@@ -2603,19 +2907,19 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c
#endif
if (cull_to > thread_cull_threshold) {
//multiple threads
- for (uint32_t i = 0; i < frustum_cull_result_threads.size(); i++) {
- frustum_cull_result_threads[i].clear();
+ for (uint32_t i = 0; i < scene_cull_result_threads.size(); i++) {
+ scene_cull_result_threads[i].clear();
}
- RendererThreadPool::singleton->thread_work_pool.do_work(frustum_cull_result_threads.size(), this, &RendererSceneCull::_frustum_cull_threaded, &cull_data);
+ RendererThreadPool::singleton->thread_work_pool.do_work(scene_cull_result_threads.size(), this, &RendererSceneCull::_scene_cull_threaded, &cull_data);
- for (uint32_t i = 0; i < frustum_cull_result_threads.size(); i++) {
- frustum_cull_result.append_from(frustum_cull_result_threads[i]);
+ for (uint32_t i = 0; i < scene_cull_result_threads.size(); i++) {
+ scene_cull_result.append_from(scene_cull_result_threads[i]);
}
} else {
//single threaded
- _frustum_cull(cull_data, frustum_cull_result, cull_from, cull_to);
+ _scene_cull(cull_data, scene_cull_result, cull_from, cull_to);
}
#ifdef DEBUG_CULL_TIME
@@ -2626,9 +2930,9 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c
print_line("time taken: " + rtos(time_avg / time_count));
#endif
- if (frustum_cull_result.mesh_instances.size()) {
- for (uint64_t i = 0; i < frustum_cull_result.mesh_instances.size(); i++) {
- RSG::storage->mesh_instance_check_for_update(frustum_cull_result.mesh_instances[i]);
+ if (scene_cull_result.mesh_instances.size()) {
+ for (uint64_t i = 0; i < scene_cull_result.mesh_instances.size(); i++) {
+ RSG::storage->mesh_instance_check_for_update(scene_cull_result.mesh_instances[i]);
}
RSG::storage->update_mesh_instances();
}
@@ -2652,14 +2956,14 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c
}
render_shadow_data[max_shadows_used].light = cull.shadows[i].light_instance;
render_shadow_data[max_shadows_used].pass = j;
- render_shadow_data[max_shadows_used].instances.merge_unordered(frustum_cull_result.directional_shadows[i].cascade_geometry_instances[j]);
+ render_shadow_data[max_shadows_used].instances.merge_unordered(scene_cull_result.directional_shadows[i].cascade_geometry_instances[j]);
max_shadows_used++;
}
}
// Positional Shadowss
- for (uint32_t i = 0; i < (uint32_t)frustum_cull_result.lights.size(); i++) {
- Instance *ins = frustum_cull_result.lights[i];
+ for (uint32_t i = 0; i < (uint32_t)scene_cull_result.lights.size(); i++) {
+ Instance *ins = scene_cull_result.lights[i];
if (!p_shadow_atlas.is_valid() || !RSG::storage->light_has_shadow(ins->base)) {
continue;
@@ -2763,13 +3067,13 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c
if (cull.sdfgi.region_count > 0) {
//update regions
for (uint32_t i = 0; i < cull.sdfgi.region_count; i++) {
- render_sdfgi_data[i].instances.merge_unordered(frustum_cull_result.sdfgi_region_geometry_instances[i]);
+ render_sdfgi_data[i].instances.merge_unordered(scene_cull_result.sdfgi_region_geometry_instances[i]);
render_sdfgi_data[i].region = i;
}
//check if static lights were culled
bool static_lights_culled = false;
for (uint32_t i = 0; i < cull.sdfgi.cascade_light_count; i++) {
- if (frustum_cull_result.sdfgi_cascade_lights[i].size()) {
+ if (scene_cull_result.sdfgi_cascade_lights[i].size()) {
static_lights_culled = true;
break;
}
@@ -2778,7 +3082,7 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c
if (static_lights_culled) {
sdfgi_update_data.static_cascade_count = cull.sdfgi.cascade_light_count;
sdfgi_update_data.static_cascade_indices = cull.sdfgi.cascade_light_index;
- sdfgi_update_data.static_positional_lights = frustum_cull_result.sdfgi_cascade_lights;
+ sdfgi_update_data.static_positional_lights = scene_cull_result.sdfgi_cascade_lights;
sdfgi_update_data.update_static = true;
}
}
@@ -2792,7 +3096,7 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c
//append the directional lights to the lights culled
for (int i = 0; i < directional_lights.size(); i++) {
- frustum_cull_result.light_instances.push_back(directional_lights[i]);
+ scene_cull_result.light_instances.push_back(directional_lights[i]);
}
RID camera_effects;
@@ -2809,7 +3113,7 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c
}
RENDER_TIMESTAMP("Render Scene ");
- scene_render->render_scene(p_render_buffers, p_camera_data, frustum_cull_result.geometry_instances, frustum_cull_result.light_instances, frustum_cull_result.reflections, frustum_cull_result.voxel_gi_instances, frustum_cull_result.decals, frustum_cull_result.lightmaps, p_environment, camera_effects, p_shadow_atlas, occluders_tex, p_reflection_probe.is_valid() ? RID() : scenario->reflection_atlas, p_reflection_probe, p_reflection_probe_pass, p_screen_lod_threshold, render_shadow_data, max_shadows_used, render_sdfgi_data, cull.sdfgi.region_count, &sdfgi_update_data);
+ scene_render->render_scene(p_render_buffers, p_camera_data, scene_cull_result.geometry_instances, scene_cull_result.light_instances, scene_cull_result.reflections, scene_cull_result.voxel_gi_instances, scene_cull_result.decals, scene_cull_result.lightmaps, p_environment, camera_effects, p_shadow_atlas, occluders_tex, p_reflection_probe.is_valid() ? RID() : scenario->reflection_atlas, p_reflection_probe, p_reflection_probe_pass, p_screen_lod_threshold, render_shadow_data, max_shadows_used, render_sdfgi_data, cull.sdfgi.region_count, &sdfgi_update_data);
for (uint32_t i = 0; i < max_shadows_used; i++) {
render_shadow_data[i].instances.clear();
@@ -3144,7 +3448,7 @@ void RendererSceneCull::render_probes() {
update_lights = true;
}
- frustum_cull_result.geometry_instances.clear();
+ scene_cull_result.geometry_instances.clear();
RID instance_pair_buffer[MAX_INSTANCE_PAIRS];
@@ -3171,10 +3475,10 @@ void RendererSceneCull::render_probes() {
ins->scenario->instance_data[ins->array_index].flags &= ~uint32_t(InstanceData::FLAG_GEOM_VOXEL_GI_DIRTY);
}
- frustum_cull_result.geometry_instances.push_back(geom->geometry_instance);
+ scene_cull_result.geometry_instances.push_back(geom->geometry_instance);
}
- scene_render->voxel_gi_update(probe->probe_instance, update_lights, probe->light_instances, frustum_cull_result.geometry_instances);
+ scene_render->voxel_gi_update(probe->probe_instance, update_lights, probe->light_instances, scene_cull_result.geometry_instances);
voxel_gi_update_list.remove(voxel_gi);
@@ -3189,7 +3493,7 @@ void RendererSceneCull::render_particle_colliders() {
if (hfpc->scenario && hfpc->base_type == RS::INSTANCE_PARTICLES_COLLISION && RSG::storage->particles_collision_is_heightfield(hfpc->base)) {
//update heightfield
instance_cull_result.clear();
- frustum_cull_result.geometry_instances.clear();
+ scene_cull_result.geometry_instances.clear();
struct CullAABB {
PagedArray<Instance *> *result;
@@ -3211,10 +3515,10 @@ void RendererSceneCull::render_particle_colliders() {
continue;
}
InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(instance->base_data);
- frustum_cull_result.geometry_instances.push_back(geom->geometry_instance);
+ scene_cull_result.geometry_instances.push_back(geom->geometry_instance);
}
- scene_render->render_particle_collider_heightfield(hfpc->base, hfpc->transform, frustum_cull_result.geometry_instances);
+ scene_render->render_particle_collider_heightfield(hfpc->base, hfpc->transform, scene_cull_result.geometry_instances);
}
heightfield_particle_colliders_update_list.erase(heightfield_particle_colliders_update_list.front());
}
@@ -3503,6 +3807,7 @@ bool RendererSceneCull::free(RID p_rid) {
}
scenario->instance_aabbs.reset();
scenario->instance_data.reset();
+ scenario->instance_visibility.reset();
scene_render->free(scenario->reflection_probe_shadow_atlas);
scene_render->free(scenario->reflection_atlas);
@@ -3571,10 +3876,10 @@ RendererSceneCull::RendererSceneCull() {
render_sdfgi_data[i].instances.set_page_pool(&geometry_instance_cull_page_pool);
}
- frustum_cull_result.init(&rid_cull_page_pool, &geometry_instance_cull_page_pool, &instance_cull_page_pool);
- frustum_cull_result_threads.resize(RendererThreadPool::singleton->thread_work_pool.get_thread_count());
- for (uint32_t i = 0; i < frustum_cull_result_threads.size(); i++) {
- frustum_cull_result_threads[i].init(&rid_cull_page_pool, &geometry_instance_cull_page_pool, &instance_cull_page_pool);
+ scene_cull_result.init(&rid_cull_page_pool, &geometry_instance_cull_page_pool, &instance_cull_page_pool);
+ scene_cull_result_threads.resize(RendererThreadPool::singleton->thread_work_pool.get_thread_count());
+ for (uint32_t i = 0; i < scene_cull_result_threads.size(); i++) {
+ scene_cull_result_threads[i].init(&rid_cull_page_pool, &geometry_instance_cull_page_pool, &instance_cull_page_pool);
}
indexer_update_iterations = GLOBAL_GET("rendering/limits/spatial_indexer/update_iterations_per_frame");
@@ -3595,11 +3900,11 @@ RendererSceneCull::~RendererSceneCull() {
render_sdfgi_data[i].instances.reset();
}
- frustum_cull_result.reset();
- for (uint32_t i = 0; i < frustum_cull_result_threads.size(); i++) {
- frustum_cull_result_threads[i].reset();
+ scene_cull_result.reset();
+ for (uint32_t i = 0; i < scene_cull_result_threads.size(); i++) {
+ scene_cull_result_threads[i].reset();
}
- frustum_cull_result_threads.clear();
+ scene_cull_result_threads.clear();
if (dummy_occlusion_culling) {
memdelete(dummy_occlusion_culling);
diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h
index e42d4bb46c..bdfbea95a2 100644
--- a/servers/rendering/renderer_scene_cull.h
+++ b/servers/rendering/renderer_scene_cull.h
@@ -31,6 +31,7 @@
#ifndef RENDERING_SERVER_SCENE_CULL_H
#define RENDERING_SERVER_SCENE_CULL_H
+#include "core/templates/bin_sorted_array.h"
#include "core/templates/pass_func.h"
#include "servers/rendering/renderer_compositor.h"
@@ -259,6 +260,9 @@ public:
FLAG_USES_MESH_INSTANCE = (1 << 17),
FLAG_REFLECTION_PROBE_DIRTY = (1 << 18),
FLAG_IGNORE_OCCLUSION_CULLING = (1 << 19),
+ FLAG_VISIBILITY_DEPENDENCY_NEEDS_CHECK = (3 << 20), // 2 bits, overlaps with the other vis. dependency flags
+ FLAG_VISIBILITY_DEPENDENCY_HIDDEN_CLOSE_RANGE = (1 << 20),
+ FLAG_VISIBILITY_DEPENDENCY_HIDDEN = (1 << 21),
};
uint32_t flags = 0;
@@ -269,10 +273,33 @@ public:
RendererSceneRender::GeometryInstance *instance_geometry;
};
Instance *instance = nullptr;
+ int32_t parent_array_index = -1;
+ int32_t visibility_index = -1;
+ };
+
+ struct InstanceVisibilityData {
+ uint64_t viewport_state = 0;
+ int32_t array_index = -1;
+ Vector3 position;
+ Instance *instance = nullptr;
+ float range_begin = 0.0f;
+ float range_end = 0.0f;
+ float range_begin_margin = 0.0f;
+ float range_end_margin = 0.0f;
+ };
+
+ class VisibilityArray : public BinSortedArray<InstanceVisibilityData> {
+ _FORCE_INLINE_ virtual void _update_idx(InstanceVisibilityData &r_element, uint64_t p_idx) {
+ r_element.instance->visibility_index = p_idx;
+ if (r_element.instance->scenario && r_element.instance->array_index != -1) {
+ r_element.instance->scenario->instance_data[r_element.instance->array_index].visibility_index = p_idx;
+ }
+ }
};
PagedArrayPool<InstanceBounds> instance_aabb_page_pool;
PagedArrayPool<InstanceData> instance_data_page_pool;
+ PagedArrayPool<InstanceVisibilityData> instance_visibility_data_page_pool;
struct Scenario {
enum IndexerType {
@@ -292,6 +319,8 @@ public:
RID camera_effects;
RID reflection_probe_shadow_atlas;
RID reflection_atlas;
+ uint64_t used_viewport_visibility_bits;
+ Map<RID, uint64_t> viewport_visibility_masks;
SelfList<Instance>::List instances;
@@ -299,11 +328,13 @@ public:
PagedArray<InstanceBounds> instance_aabbs;
PagedArray<InstanceData> instance_data;
+ VisibilityArray instance_visibility;
Scenario() {
indexers[INDEXER_GEOMETRY].set_index(INDEXER_GEOMETRY);
indexers[INDEXER_VOLUMES].set_index(INDEXER_VOLUMES);
debug = RS::SCENARIO_DEBUG_DISABLED;
+ used_viewport_visibility_bits = 0;
}
};
@@ -326,6 +357,8 @@ public:
virtual void scenario_set_reflection_atlas_size(RID p_scenario, int p_reflection_size, int p_reflection_count);
virtual bool is_scenario(RID p_scenario) const;
virtual RID scenario_get_environment(RID p_scenario);
+ virtual void scenario_add_viewport_visibility_mask(RID p_scenario, RID p_viewport);
+ virtual void scenario_remove_viewport_visibility_mask(RID p_scenario, RID p_viewport);
/* INSTANCING API */
@@ -399,6 +432,12 @@ public:
//scenario stuff
DynamicBVH::ID indexer_id;
int32_t array_index;
+ int32_t visibility_index = -1;
+ float visibility_range_begin;
+ float visibility_range_end;
+ float visibility_range_begin_margin;
+ float visibility_range_end_margin;
+ Instance *visibility_parent = nullptr;
Scenario *scenario;
SelfList<Instance> scenario_item;
@@ -412,12 +451,6 @@ public:
float extra_margin;
ObjectID object_id;
- float lod_begin;
- float lod_end;
- float lod_begin_hysteresis;
- float lod_end_hysteresis;
- RID lod_instance;
-
Vector<Color> lightmap_target_sh; //target is used for incrementally changing the SH over time, this avoids pops in some corner cases and when going interior <-> exterior
uint64_t last_frame_pass;
@@ -495,10 +528,10 @@ public:
visible = true;
- lod_begin = 0;
- lod_end = 0;
- lod_begin_hysteresis = 0;
- lod_end_hysteresis = 0;
+ visibility_range_begin = 0;
+ visibility_range_end = 0;
+ visibility_range_begin_margin = 0;
+ visibility_range_end_margin = 0;
last_frame_pass = 0;
version = 1;
@@ -537,6 +570,8 @@ public:
Set<Instance *> reflection_probes;
Set<Instance *> voxel_gi_instances;
Set<Instance *> lightmap_captures;
+ Set<Instance *> visibility_dependencies;
+ uint32_t visibility_dependencies_depth = 0;
InstanceGeometryData() {
can_cast_shadows = true;
@@ -717,7 +752,7 @@ public:
PagedArray<Instance *> instance_cull_result;
PagedArray<Instance *> instance_shadow_cull_result;
- struct FrustumCullResult {
+ struct InstanceCullResult {
PagedArray<RendererSceneRender::GeometryInstance *> geometry_instances;
PagedArray<Instance *> lights;
PagedArray<RID> light_instances;
@@ -782,7 +817,7 @@ public:
}
}
- void append_from(FrustumCullResult &p_cull_result) {
+ void append_from(InstanceCullResult &p_cull_result) {
geometry_instances.merge_unordered(p_cull_result.geometry_instances);
lights.merge_unordered(p_cull_result.lights);
light_instances.merge_unordered(p_cull_result.light_instances);
@@ -832,8 +867,8 @@ public:
}
};
- FrustumCullResult frustum_cull_result;
- LocalVector<FrustumCullResult> frustum_cull_result_threads;
+ InstanceCullResult scene_cull_result;
+ LocalVector<InstanceCullResult> scene_cull_result_threads;
RendererSceneRender::RenderShadowData render_shadow_data[MAX_UPDATE_SHADOWS];
uint32_t max_shadows_used = 0;
@@ -866,6 +901,11 @@ public:
virtual void instance_set_extra_visibility_margin(RID p_instance, real_t p_margin);
+ virtual void instance_set_visibility_parent(RID p_instance, RID p_parent_instance);
+
+ void _update_instance_visibility_depth(Instance *p_instance);
+ void _update_instance_visibility_dependencies(Instance *p_instance);
+
// don't use these in a game!
virtual Vector<ObjectID> instances_cull_aabb(const AABB &p_aabb, RID p_scenario = RID()) const;
virtual Vector<ObjectID> instances_cull_ray(const Vector3 &p_from, const Vector3 &p_to, RID p_scenario = RID()) const;
@@ -875,8 +915,8 @@ public:
virtual void instance_geometry_set_cast_shadows_setting(RID p_instance, RS::ShadowCastingSetting p_shadow_casting_setting);
virtual void instance_geometry_set_material_override(RID p_instance, RID p_material);
- virtual void instance_geometry_set_draw_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin);
- virtual void instance_geometry_set_as_instance_lod(RID p_instance, RID p_as_lod_of_instance);
+ virtual void instance_geometry_set_visibility_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin);
+
virtual void instance_geometry_set_lightmap(RID p_instance, RID p_lightmap, const Rect2 &p_lightmap_uv_scale, int p_slice_index);
virtual void instance_geometry_set_lod_bias(RID p_instance, float p_lod_bias);
@@ -937,6 +977,19 @@ public:
Frustum frustum;
} cull;
+ struct VisibilityCullData {
+ uint64_t viewport_mask;
+ Scenario *scenario;
+ Vector3 camera_position;
+ uint32_t cull_offset;
+ uint32_t cull_count;
+ };
+
+ void _visibility_cull_threaded(uint32_t p_thread, VisibilityCullData *cull_data);
+ void _visibility_cull(const VisibilityCullData &cull_data, uint64_t p_from, uint64_t p_to);
+ _FORCE_INLINE_ void _visibility_cull(const VisibilityCullData &cull_data, uint64_t p_idx);
+ _FORCE_INLINE_ int _visibility_range_check(InstanceVisibilityData &r_vis_data, const Vector3 &p_camera_pos, uint64_t p_viewport_mask);
+
struct CullData {
Cull *cull;
Scenario *scenario;
@@ -946,10 +999,11 @@ public:
Instance *render_reflection_probe;
const RendererSceneOcclusionCull::HZBuffer *occlusion_buffer;
const CameraMatrix *camera_matrix;
+ const VisibilityCullData *visibility_cull_data;
};
- void _frustum_cull_threaded(uint32_t p_thread, CullData *cull_data);
- void _frustum_cull(CullData &cull_data, FrustumCullResult &cull_result, uint64_t p_from, uint64_t p_to);
+ void _scene_cull_threaded(uint32_t p_thread, CullData *cull_data);
+ void _scene_cull(CullData &cull_data, InstanceCullResult &cull_result, uint64_t p_from, uint64_t p_to);
bool _render_reflection_probe_step(Instance *p_instance, int p_step);
void _render_scene(const RendererSceneRender::CameraData *p_camera_data, RID p_render_buffers, RID p_environment, RID p_force_camera_effects, uint32_t p_visible_layers, RID p_scenario, RID p_viewport, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, bool p_using_shadows = true);
diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp
index 21f849e822..34bdb15c62 100644
--- a/servers/rendering/renderer_viewport.cpp
+++ b/servers/rendering/renderer_viewport.cpp
@@ -824,6 +824,10 @@ void RendererViewport::viewport_set_scenario(RID p_viewport, RID p_scenario) {
Viewport *viewport = viewport_owner.getornull(p_viewport);
ERR_FAIL_COND(!viewport);
+ if (viewport->scenario.is_valid()) {
+ RSG::scene->scenario_remove_viewport_visibility_mask(viewport->scenario, p_viewport);
+ }
+
viewport->scenario = p_scenario;
if (viewport->use_occlusion_culling) {
RendererSceneOcclusionCull::get_singleton()->buffer_set_scenario(p_viewport, p_scenario);
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index 6476d7c75d..e4d319ed6c 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -726,6 +726,7 @@ public:
FUNC2(instance_set_exterior, RID, bool)
FUNC2(instance_set_extra_visibility_margin, RID, real_t)
+ FUNC2(instance_set_visibility_parent, RID, RID)
// don't use these in a game!
FUNC2RC(Vector<ObjectID>, instances_cull_aabb, const AABB &, RID)
@@ -736,8 +737,7 @@ public:
FUNC2(instance_geometry_set_cast_shadows_setting, RID, ShadowCastingSetting)
FUNC2(instance_geometry_set_material_override, RID, RID)
- FUNC5(instance_geometry_set_draw_range, RID, float, float, float, float)
- FUNC2(instance_geometry_set_as_instance_lod, RID, RID)
+ FUNC5(instance_geometry_set_visibility_range, RID, float, float, float, float)
FUNC4(instance_geometry_set_lightmap, RID, RID, const Rect2 &, int)
FUNC2(instance_geometry_set_lod_bias, RID, float)
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 7b535a0bda..f25f255321 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -1725,11 +1725,11 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("instance_attach_skeleton", "instance", "skeleton"), &RenderingServer::instance_attach_skeleton);
ClassDB::bind_method(D_METHOD("instance_set_exterior", "instance", "enabled"), &RenderingServer::instance_set_exterior);
ClassDB::bind_method(D_METHOD("instance_set_extra_visibility_margin", "instance", "margin"), &RenderingServer::instance_set_extra_visibility_margin);
+ ClassDB::bind_method(D_METHOD("instance_set_visibility_parent", "instance", "parent"), &RenderingServer::instance_set_visibility_parent);
ClassDB::bind_method(D_METHOD("instance_geometry_set_flag", "instance", "flag", "enabled"), &RenderingServer::instance_geometry_set_flag);
ClassDB::bind_method(D_METHOD("instance_geometry_set_cast_shadows_setting", "instance", "shadow_casting_setting"), &RenderingServer::instance_geometry_set_cast_shadows_setting);
ClassDB::bind_method(D_METHOD("instance_geometry_set_material_override", "instance", "material"), &RenderingServer::instance_geometry_set_material_override);
- ClassDB::bind_method(D_METHOD("instance_geometry_set_draw_range", "instance", "min", "max", "min_margin", "max_margin"), &RenderingServer::instance_geometry_set_draw_range);
- ClassDB::bind_method(D_METHOD("instance_geometry_set_as_instance_lod", "instance", "as_lod_of_instance"), &RenderingServer::instance_geometry_set_as_instance_lod);
+ ClassDB::bind_method(D_METHOD("instance_geometry_set_visibility_range", "instance", "min", "max", "min_margin", "max_margin"), &RenderingServer::instance_geometry_set_visibility_range);
ClassDB::bind_method(D_METHOD("instances_cull_aabb", "aabb", "scenario"), &RenderingServer::_instances_cull_aabb_bind, DEFVAL(RID()));
ClassDB::bind_method(D_METHOD("instances_cull_ray", "from", "to", "scenario"), &RenderingServer::_instances_cull_ray_bind, DEFVAL(RID()));
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index 3247717e98..1806f1da18 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -1172,6 +1172,7 @@ public:
virtual void instance_set_exterior(RID p_instance, bool p_enabled) = 0;
virtual void instance_set_extra_visibility_margin(RID p_instance, real_t p_margin) = 0;
+ virtual void instance_set_visibility_parent(RID p_instance, RID p_parent_instance) = 0;
// don't use these in a game!
virtual Vector<ObjectID> instances_cull_aabb(const AABB &p_aabb, RID p_scenario = RID()) const = 0;
@@ -1201,8 +1202,7 @@ public:
virtual void instance_geometry_set_cast_shadows_setting(RID p_instance, ShadowCastingSetting p_shadow_casting_setting) = 0;
virtual void instance_geometry_set_material_override(RID p_instance, RID p_material) = 0;
- virtual void instance_geometry_set_draw_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin) = 0;
- virtual void instance_geometry_set_as_instance_lod(RID p_instance, RID p_as_lod_of_instance) = 0;
+ virtual void instance_geometry_set_visibility_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin) = 0;
virtual void instance_geometry_set_lightmap(RID p_instance, RID p_lightmap, const Rect2 &p_lightmap_uv_scale, int p_lightmap_slice) = 0;
virtual void instance_geometry_set_lod_bias(RID p_instance, float p_lod_bias) = 0;