summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/doc_data.h8
-rw-r--r--core/math/a_star_grid_2d.cpp12
-rw-r--r--doc/Makefile2
-rw-r--r--doc/classes/AnimatedSprite3D.xml6
-rw-r--r--doc/classes/EditorPlugin.xml39
-rw-r--r--doc/classes/GraphEdit.xml130
-rw-r--r--doc/classes/GraphNode.xml157
-rw-r--r--doc/classes/NavigationObstacle2D.xml13
-rw-r--r--doc/classes/NavigationObstacle3D.xml13
-rw-r--r--doc/classes/Node.xml8
-rw-r--r--doc/classes/ProjectSettings.xml12
-rw-r--r--doc/classes/Tween.xml9
-rwxr-xr-xdoc/tools/make_rst.py2
-rw-r--r--drivers/gles3/rasterizer_gles3.cpp8
-rw-r--r--drivers/gles3/shaders/scene.glsl7
-rw-r--r--drivers/gles3/storage/texture_storage.cpp2
-rw-r--r--editor/create_dialog.cpp9
-rw-r--r--editor/doc_tools.cpp88
-rw-r--r--editor/editor_help.cpp94
-rw-r--r--editor/editor_help_search.cpp39
-rw-r--r--editor/editor_help_search.h2
-rw-r--r--editor/editor_node.cpp4
-rw-r--r--editor/editor_plugin.cpp4
-rw-r--r--editor/editor_plugin.h3
-rw-r--r--editor/editor_themes.cpp22
-rw-r--r--editor/icons/GuiResizerTopLeft.svg1
-rw-r--r--editor/icons/PreviewEnvironment.svg1
-rw-r--r--editor/icons/PreviewSun.svg1
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.cpp4
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp2
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp36
-rw-r--r--editor/plugins/shader_editor_plugin.h4
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.cpp2
-rw-r--r--editor/plugins/tiles/tile_map_editor.cpp1
-rw-r--r--editor/plugins/version_control_editor_plugin.cpp10
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp6
-rw-r--r--editor/project_manager.cpp62
-rw-r--r--editor/project_manager.h3
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml4
-rw-r--r--modules/gdscript/gdscript.cpp6
-rw-r--r--modules/gdscript/gdscript_cache.cpp4
-rw-r--r--modules/gltf/gltf_document.cpp2
-rw-r--r--modules/gltf/register_types.cpp5
-rw-r--r--modules/multiplayer/multiplayer_spawner.cpp1
-rw-r--r--platform/android/java/app/config.gradle26
-rw-r--r--platform/android/java/editor/build.gradle23
-rw-r--r--platform/android/java/editor/src/main/AndroidManifest.xml2
-rw-r--r--platform/linuxbsd/gl_manager_x11.cpp24
-rw-r--r--platform/macos/gl_manager_macos_legacy.mm5
-rw-r--r--platform/web/detect.py4
-rw-r--r--platform/web/display_server_web.cpp6
-rw-r--r--platform/web/js/engine/config.js3
-rw-r--r--platform/web/js/libs/audio.worklet.js2
-rw-r--r--platform/web/js/libs/library_godot_audio.js9
-rw-r--r--platform/web/js/libs/library_godot_os.js14
-rw-r--r--platform/web/web_main.cpp26
-rw-r--r--platform/windows/display_server_windows.cpp26
-rw-r--r--platform/windows/display_server_windows.h2
-rw-r--r--platform/windows/gl_manager_windows.cpp7
-rw-r--r--scene/2d/animated_sprite_2d.cpp66
-rw-r--r--scene/2d/navigation_obstacle_2d.cpp52
-rw-r--r--scene/2d/navigation_obstacle_2d.h7
-rw-r--r--scene/3d/navigation_obstacle_3d.cpp52
-rw-r--r--scene/3d/navigation_obstacle_3d.h7
-rw-r--r--scene/3d/sprite_3d.cpp188
-rw-r--r--scene/3d/sprite_3d.h11
-rw-r--r--scene/gui/graph_edit.cpp62
-rw-r--r--scene/gui/graph_edit.h4
-rw-r--r--scene/gui/graph_node.cpp146
-rw-r--r--scene/gui/graph_node.h30
-rw-r--r--scene/main/canvas_item.cpp2
-rw-r--r--scene/main/node.cpp47
-rw-r--r--scene/main/node.h5
-rw-r--r--servers/rendering/renderer_rd/effects/ss_effects.cpp12
-rw-r--r--servers/rendering/renderer_rd/effects/ss_effects.h7
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp4
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.cpp4
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.h2
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl21
-rw-r--r--servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl7
-rw-r--r--servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp9
-rw-r--r--servers/rendering/renderer_viewport.cpp9
-rw-r--r--servers/rendering/rendering_server_default.cpp5
-rw-r--r--servers/rendering/shader_language.cpp48
-rw-r--r--servers/rendering/shader_language.h4
85 files changed, 1231 insertions, 607 deletions
diff --git a/core/doc_data.h b/core/doc_data.h
index 1d8d2483e0..3b7bf149b4 100644
--- a/core/doc_data.h
+++ b/core/doc_data.h
@@ -66,6 +66,8 @@ public:
String return_enum;
String qualifiers;
String description;
+ bool is_deprecated = false;
+ bool is_experimental = false;
Vector<ArgumentDoc> arguments;
Vector<int> errors_returned;
bool operator<(const MethodDoc &p_method) const {
@@ -105,6 +107,8 @@ public:
String enumeration;
bool is_bitfield = false;
String description;
+ bool is_deprecated = false;
+ bool is_experimental = false;
bool operator<(const ConstantDoc &p_const) const {
return name < p_const.name;
}
@@ -126,6 +130,8 @@ public:
String default_value;
bool overridden = false;
String overrides;
+ bool is_deprecated = false;
+ bool is_experimental = false;
bool operator<(const PropertyDoc &p_prop) const {
return name < p_prop.name;
}
@@ -167,6 +173,8 @@ public:
Vector<PropertyDoc> properties;
Vector<MethodDoc> annotations;
Vector<ThemeItemDoc> theme_properties;
+ bool is_deprecated = false;
+ bool is_experimental = false;
bool is_script_doc = false;
String script_path;
bool operator<(const ClassDoc &p_class) const {
diff --git a/core/math/a_star_grid_2d.cpp b/core/math/a_star_grid_2d.cpp
index 23d7e379ee..ad67cfa852 100644
--- a/core/math/a_star_grid_2d.cpp
+++ b/core/math/a_star_grid_2d.cpp
@@ -30,16 +30,16 @@
#include "a_star_grid_2d.h"
-static real_t heuristic_manhattan(const Vector2i &p_from, const Vector2i &p_to) {
+static real_t heuristic_euclidian(const Vector2i &p_from, const Vector2i &p_to) {
real_t dx = (real_t)ABS(p_to.x - p_from.x);
real_t dy = (real_t)ABS(p_to.y - p_from.y);
- return dx + dy;
+ return (real_t)Math::sqrt(dx * dx + dy * dy);
}
-static real_t heuristic_euclidian(const Vector2i &p_from, const Vector2i &p_to) {
+static real_t heuristic_manhattan(const Vector2i &p_from, const Vector2i &p_to) {
real_t dx = (real_t)ABS(p_to.x - p_from.x);
real_t dy = (real_t)ABS(p_to.y - p_from.y);
- return (real_t)Math::sqrt(dx * dx + dy * dy);
+ return dx + dy;
}
static real_t heuristic_octile(const Vector2i &p_from, const Vector2i &p_to) {
@@ -55,7 +55,7 @@ static real_t heuristic_chebyshev(const Vector2i &p_from, const Vector2i &p_to)
return MAX(dx, dy);
}
-static real_t (*heuristics[AStarGrid2D::HEURISTIC_MAX])(const Vector2i &, const Vector2i &) = { heuristic_manhattan, heuristic_euclidian, heuristic_octile, heuristic_chebyshev };
+static real_t (*heuristics[AStarGrid2D::HEURISTIC_MAX])(const Vector2i &, const Vector2i &) = { heuristic_euclidian, heuristic_manhattan, heuristic_octile, heuristic_chebyshev };
void AStarGrid2D::set_size(const Size2i &p_size) {
ERR_FAIL_COND(p_size.x < 0 || p_size.y < 0);
@@ -572,7 +572,7 @@ void AStarGrid2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "cell_size"), "set_cell_size", "get_cell_size");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "jumping_enabled"), "set_jumping_enabled", "is_jumping_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "default_heuristic", PROPERTY_HINT_ENUM, "Manhattan,Euclidean,Octile,Chebyshev,Max"), "set_default_heuristic", "get_default_heuristic");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "default_heuristic", PROPERTY_HINT_ENUM, "Euclidean,Manhattan,Octile,Chebyshev,Max"), "set_default_heuristic", "get_default_heuristic");
ADD_PROPERTY(PropertyInfo(Variant::INT, "diagonal_mode", PROPERTY_HINT_ENUM, "Never,Always,At Least One Walkable,Only If No Obstacles,Max"), "set_diagonal_mode", "get_diagonal_mode");
BIND_ENUM_CONSTANT(HEURISTIC_EUCLIDEAN);
diff --git a/doc/Makefile b/doc/Makefile
index ecc5e51dd6..c8bf32d6e2 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -1,4 +1,4 @@
-BASEDIR = $(CURDIR)
+BASEDIR = .
CLASSES = $(BASEDIR)/classes/ $(BASEDIR)/../modules/
OUTPUTDIR = $(BASEDIR)/_build
TOOLSDIR = $(BASEDIR)/tools
diff --git a/doc/classes/AnimatedSprite3D.xml b/doc/classes/AnimatedSprite3D.xml
index 0bc5484e3a..58d3ca6ad3 100644
--- a/doc/classes/AnimatedSprite3D.xml
+++ b/doc/classes/AnimatedSprite3D.xml
@@ -13,8 +13,9 @@
<method name="play">
<return type="void" />
<param index="0" name="anim" type="StringName" default="&amp;&quot;&quot;" />
+ <param index="1" name="backwards" type="bool" default="false" />
<description>
- Plays the animation named [param anim]. If no [param anim] is provided, the current animation is played.
+ Plays the animation named [param anim]. If no [param anim] is provided, the current animation is played. If [param backwards] is [code]true[/code], the animation will be played in reverse.
</description>
</method>
<method name="stop">
@@ -37,6 +38,9 @@
<member name="playing" type="bool" setter="set_playing" getter="is_playing" default="false">
If [code]true[/code], the [member animation] is currently playing.
</member>
+ <member name="speed_scale" type="float" setter="set_speed_scale" getter="get_speed_scale" default="1.0">
+ The animation speed is multiplied by this value.
+ </member>
</members>
<signals>
<signal name="animation_finished">
diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml
index 3c0d3ec6be..8f4c848041 100644
--- a/doc/classes/EditorPlugin.xml
+++ b/doc/classes/EditorPlugin.xml
@@ -64,8 +64,8 @@
if event is InputEventMouseMotion:
# Redraw viewport when cursor is moved.
update_overlays()
- return true
- return false
+ return EditorPlugin.AFTER_GUI_INPUT_STOP
+ return EditorPlugin.AFTER_GUI_INPUT_PASS
[/gdscript]
[csharp]
public override void _Forward3dDrawOverViewport(Godot.Control overlay)
@@ -74,15 +74,15 @@
overlay.DrawCircle(overlay.GetLocalMousePosition(), 64, Colors.White);
}
- public override bool _Forward3dGuiInput(Godot.Camera3D camera, InputEvent @event)
+ public override EditorPlugin.AfterGUIInput _Forward3dGuiInput(Godot.Camera3D camera, InputEvent @event)
{
if (@event is InputEventMouseMotion)
{
// Redraw viewport when cursor is moved.
UpdateOverlays();
- return true;
+ return EditorPlugin.AFTER_GUI_INPUT_STOP;
}
- return false;
+ return EditorPlugin.AFTER_GUI_INPUT_PASS;
[/csharp]
[/codeblocks]
</description>
@@ -100,33 +100,33 @@
<param index="0" name="viewport_camera" type="Camera3D" />
<param index="1" name="event" type="InputEvent" />
<description>
- Called when there is a root node in the current edited scene, [method _handles] is implemented and an [InputEvent] happens in the 3D viewport. Intercepts the [InputEvent], if [code]return true[/code] [EditorPlugin] consumes the [param event], otherwise forwards [param event] to other Editor classes. Example:
+ Called when there is a root node in the current edited scene, [method _handles] is implemented, and an [InputEvent] happens in the 3D viewport. The return value decides whether the [InputEvent] is consumed or forwarded to other [EditorPlugin]s. See [enum AfterGUIInput] for options. Example:
[codeblocks]
[gdscript]
- # Prevents the InputEvent to reach other Editor classes.
+ # Prevents the InputEvent from reaching other Editor classes.
func _forward_3d_gui_input(camera, event):
return EditorPlugin.AFTER_GUI_INPUT_STOP
[/gdscript]
[csharp]
- // Prevents the InputEvent to reach other Editor classes.
- public override bool _Forward3dGuiInput(Camera3D camera, InputEvent @event)
+ // Prevents the InputEvent from reaching other Editor classes.
+ public override EditorPlugin.AfterGUIInput _Forward3dGuiInput(Camera3D camera, InputEvent @event)
{
return EditorPlugin.AFTER_GUI_INPUT_STOP;
}
[/csharp]
[/codeblocks]
- Must [code]return false[/code] in order to forward the [InputEvent] to other Editor classes. Example:
+ Must [code]return EditorPlugin.AFTER_GUI_INPUT_PASS[/code] in order to forward the [InputEvent] to other Editor classes. Example:
[codeblocks]
[gdscript]
# Consumes InputEventMouseMotion and forwards other InputEvent types.
func _forward_3d_gui_input(camera, event):
- return event is InputEventMouseMotion
+ return EditorPlugin.AFTER_GUI_INPUT_STOP if event is InputEventMouseMotion else EditorPlugin.AFTER_GUI_INPUT_PASS
[/gdscript]
[csharp]
// Consumes InputEventMouseMotion and forwards other InputEvent types.
- public override bool _Forward3dGuiInput(Camera3D camera, InputEvent @event)
+ public override EditorPlugin.AfterGUIInput _Forward3dGuiInput(Camera3D camera, InputEvent @event)
{
- return @event is InputEventMouseMotion;
+ return @event is InputEventMouseMotion ? EditorPlugin.AFTER_GUI_INPUT_STOP : EditorPlugin.AFTER_GUI_INPUT_PASS;
}
[/csharp]
[/codeblocks]
@@ -185,12 +185,12 @@
Called when there is a root node in the current edited scene, [method _handles] is implemented and an [InputEvent] happens in the 2D viewport. Intercepts the [InputEvent], if [code]return true[/code] [EditorPlugin] consumes the [param event], otherwise forwards [param event] to other Editor classes. Example:
[codeblocks]
[gdscript]
- # Prevents the InputEvent to reach other Editor classes.
+ # Prevents the InputEvent from reaching other Editor classes.
func _forward_canvas_gui_input(event):
return true
[/gdscript]
[csharp]
- // Prevents the InputEvent to reach other Editor classes.
+ // Prevents the InputEvent from reaching other Editor classes.
public override bool ForwardCanvasGuiInput(InputEvent @event)
{
return true;
@@ -755,5 +755,14 @@
<constant name="DOCK_SLOT_MAX" value="8" enum="DockSlot">
Represents the size of the [enum DockSlot] enum.
</constant>
+ <constant name="AFTER_GUI_INPUT_PASS" value="0" enum="AfterGUIInput">
+ Forwards the [InputEvent] to other EditorPlugins.
+ </constant>
+ <constant name="AFTER_GUI_INPUT_STOP" value="1" enum="AfterGUIInput">
+ Prevents the [InputEvent] from reaching other Editor classes.
+ </constant>
+ <constant name="AFTER_GUI_INPUT_CUSTOM" value="2" enum="AfterGUIInput">
+ Pass the [InputEvent] to other editor plugins except the main [Node3D] one. This can be used to prevent node selection changes and work with sub-gizmos instead.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/GraphEdit.xml b/doc/classes/GraphEdit.xml
index dc093acdcd..5050ce7715 100644
--- a/doc/classes/GraphEdit.xml
+++ b/doc/classes/GraphEdit.xml
@@ -1,37 +1,38 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GraphEdit" inherits="Control" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- GraphEdit is an area capable of showing various GraphNodes. It manages connection events between them.
+ GraphEdit is a control responsible for displaying and manipulating graph-like data using [GraphNode]s. It provides access to creation, removal, connection, and disconnection of nodes.
</brief_description>
<description>
- GraphEdit manages the showing of GraphNodes it contains, as well as connections and disconnections between them. Signals are sent for each of these two events. Disconnection between GraphNode slots is disabled by default.
- It is greatly advised to enable low-processor usage mode (see [member OS.low_processor_usage_mode]) when using GraphEdits.
+ GraphEdit provides tools for creation, manipulation, and display of various graphs. Its main purpose in the engine is to power the visual programming systems, such as visual shaders, but it is also available for use in user projects.
+ GraphEdit by itself is only an empty container, representing an infinite grid where [GraphNode]s can be placed. Each [GraphNode] represent a node in the graph, a single unit of data in the connected scheme. GraphEdit, in turn, helps to control various interactions with nodes and between nodes. When the user attempts to connect, disconnect, or close a [GraphNode], a signal is emitted in the GraphEdit, but no action is taken by default. It is the responsibility of the programmer utilizing this control to implement the necessary logic to determine how each request should be handled.
+ [b]Performance:[/b] It is greatly advised to enable low-processor usage mode (see [member OS.low_processor_usage_mode]) when using GraphEdits.
</description>
<tutorials>
</tutorials>
<methods>
<method name="_get_connection_line" qualifiers="virtual const">
<return type="PackedVector2Array" />
- <param index="0" name="from" type="Vector2" />
- <param index="1" name="to" type="Vector2" />
+ <param index="0" name="from_position" type="Vector2" />
+ <param index="1" name="to_position" type="Vector2" />
<description>
Virtual method which can be overridden to customize how connections are drawn.
</description>
</method>
<method name="_is_in_input_hotzone" qualifiers="virtual">
<return type="bool" />
- <param index="0" name="graph_node" type="Object" />
- <param index="1" name="slot_index" type="int" />
+ <param index="0" name="in_node" type="Object" />
+ <param index="1" name="in_port" type="int" />
<param index="2" name="mouse_position" type="Vector2" />
<description>
Returns whether the [param mouse_position] is in the input hot zone.
- By default, a hot zone is a [Rect2] positioned such that its center is at [param graph_node].[method GraphNode.get_connection_input_position]([param slot_index]) (For output's case, call [method GraphNode.get_connection_output_position] instead). The hot zone's width is twice the Theme Property [code]port_grab_distance_horizontal[/code], and its height is twice the [code]port_grab_distance_vertical[/code].
+ By default, a hot zone is a [Rect2] positioned such that its center is at [param in_node].[method GraphNode.get_connection_input_position]([param in_port]) (For output's case, call [method GraphNode.get_connection_output_position] instead). The hot zone's width is twice the Theme Property [code]port_grab_distance_horizontal[/code], and its height is twice the [code]port_grab_distance_vertical[/code].
Below is a sample code to help get started:
[codeblock]
- func _is_in_input_hotzone(graph_node, slot_index, mouse_position):
- var slot_size : Vector2 = Vector2(get_theme_constant("port_grab_distance_horizontal"), get_theme_constant("port_grab_distance_vertical"))
- var slot_pos : Vector2 = graph_node.get_position() + graph_node.get_connection_input_position(slot_index) - slot_size / 2
- var rect = Rect2(slot_pos, slot_size)
+ func _is_in_input_hotzone(in_node, in_port, mouse_position):
+ var port_size : Vector2 = Vector2(get_theme_constant("port_grab_distance_horizontal"), get_theme_constant("port_grab_distance_vertical"))
+ var port_pos : Vector2 = in_node.get_position() + in_node.get_connection_input_position(in_port) - port_size / 2
+ var rect = Rect2(port_pos, port_size)
return rect.has_point(mouse_position)
[/codeblock]
@@ -39,17 +40,17 @@
</method>
<method name="_is_in_output_hotzone" qualifiers="virtual">
<return type="bool" />
- <param index="0" name="graph_node" type="Object" />
- <param index="1" name="slot_index" type="int" />
+ <param index="0" name="in_node" type="Object" />
+ <param index="1" name="in_port" type="int" />
<param index="2" name="mouse_position" type="Vector2" />
<description>
Returns whether the [param mouse_position] is in the output hot zone. For more information on hot zones, see [method _is_in_input_hotzone].
Below is a sample code to help get started:
[codeblock]
- func _is_in_output_hotzone(graph_node, slot_index, mouse_position):
- var slot_size : Vector2 = Vector2(get_theme_constant("port_grab_distance_horizontal"), get_theme_constant("port_grab_distance_vertical"))
- var slot_pos : Vector2 = graph_node.get_position() + graph_node.get_connection_output_position(slot_index) - slot_size / 2
- var rect = Rect2(slot_pos, slot_size)
+ func _is_in_output_hotzone(in_node, in_port, mouse_position):
+ var port_size : Vector2 = Vector2(get_theme_constant("port_grab_distance_horizontal"), get_theme_constant("port_grab_distance_vertical"))
+ var port_pos : Vector2 = in_node.get_position() + in_node.get_connection_output_position(in_port) - port_size / 2
+ var rect = Rect2(port_pos, port_size)
return rect.has_point(mouse_position)
[/codeblock]
@@ -57,17 +58,17 @@
</method>
<method name="_is_node_hover_valid" qualifiers="virtual">
<return type="bool" />
- <param index="0" name="from" type="StringName" />
- <param index="1" name="from_slot" type="int" />
- <param index="2" name="to" type="StringName" />
- <param index="3" name="to_slot" type="int" />
+ <param index="0" name="from_node" type="StringName" />
+ <param index="1" name="from_port" type="int" />
+ <param index="2" name="to_node" type="StringName" />
+ <param index="3" name="to_port" type="int" />
<description>
This virtual method can be used to insert additional error detection while the user is dragging a connection over a valid port.
Return [code]true[/code] if the connection is indeed valid or return [code]false[/code] if the connection is impossible. If the connection is impossible, no snapping to the port and thus no connection request to that port will happen.
In this example a connection to same node is suppressed:
[codeblocks]
[gdscript]
- func _is_node_hover_valid(from, from_slot, to, to_slot):
+ func _is_node_hover_valid(from, from_port, to, to_port):
return from != to
[/gdscript]
[csharp]
@@ -83,21 +84,22 @@
<param index="0" name="from_type" type="int" />
<param index="1" name="to_type" type="int" />
<description>
- Makes possible the connection between two different slot types. The type is defined with the [method GraphNode.set_slot] method.
+ Allows the connection between two different port types. The port type is defined individually for the left and the right port of each slot with the [method GraphNode.set_slot] method.
+ See also [method is_valid_connection_type] and [method remove_valid_connection_type].
</description>
</method>
<method name="add_valid_left_disconnect_type">
<return type="void" />
<param index="0" name="type" type="int" />
<description>
- Makes possible to disconnect nodes when dragging from the slot at the left if it has the specified type.
+ Allows to disconnect nodes when dragging from the left port of the [GraphNode]'s slot if it has the specified type. See also [method remove_valid_left_disconnect_type].
</description>
</method>
<method name="add_valid_right_disconnect_type">
<return type="void" />
<param index="0" name="type" type="int" />
<description>
- Makes possible to disconnect nodes when dragging from the slot at the right if it has the specified type.
+ Allows to disconnect nodes when dragging from the right port of the [GraphNode]'s slot if it has the specified type. See also [method remove_valid_right_disconnect_type].
</description>
</method>
<method name="arrange_nodes">
@@ -114,22 +116,22 @@
</method>
<method name="connect_node">
<return type="int" enum="Error" />
- <param index="0" name="from" type="StringName" />
+ <param index="0" name="from_node" type="StringName" />
<param index="1" name="from_port" type="int" />
- <param index="2" name="to" type="StringName" />
+ <param index="2" name="to_node" type="StringName" />
<param index="3" name="to_port" type="int" />
<description>
- Create a connection between the [param from_port] slot of the [param from] GraphNode and the [param to_port] slot of the [param to] GraphNode. If the connection already exists, no connection is created.
+ Create a connection between the [param from_port] of the [param from_node] [GraphNode] and the [param to_port] of the [param to_node] [GraphNode]. If the connection already exists, no connection is created.
</description>
</method>
<method name="disconnect_node">
<return type="void" />
- <param index="0" name="from" type="StringName" />
+ <param index="0" name="from_node" type="StringName" />
<param index="1" name="from_port" type="int" />
- <param index="2" name="to" type="StringName" />
+ <param index="2" name="to_node" type="StringName" />
<param index="3" name="to_port" type="int" />
<description>
- Removes the connection between the [param from_port] slot of the [param from] GraphNode and the [param to_port] slot of the [param to] GraphNode. If the connection does not exist, no connection is removed.
+ Removes the connection between the [param from_port] of the [param from_node] [GraphNode] and the [param to_port] of the [param to_node] [GraphNode]. If the connection does not exist, no connection is removed.
</description>
</method>
<method name="force_connection_drag_end">
@@ -142,10 +144,10 @@
</method>
<method name="get_connection_line">
<return type="PackedVector2Array" />
- <param index="0" name="from" type="Vector2" />
- <param index="1" name="to" type="Vector2" />
+ <param index="0" name="from_node" type="Vector2" />
+ <param index="1" name="to_node" type="Vector2" />
<description>
- Returns the points which would make up a connection between [param from] and [param to].
+ Returns the points which would make up a connection between [param from_node] and [param to_node].
</description>
</method>
<method name="get_connection_list" qualifiers="const">
@@ -163,12 +165,12 @@
</method>
<method name="is_node_connected">
<return type="bool" />
- <param index="0" name="from" type="StringName" />
+ <param index="0" name="from_node" type="StringName" />
<param index="1" name="from_port" type="int" />
- <param index="2" name="to" type="StringName" />
+ <param index="2" name="to_node" type="StringName" />
<param index="3" name="to_port" type="int" />
<description>
- Returns [code]true[/code] if the [param from_port] slot of the [param from] GraphNode is connected to the [param to_port] slot of the [param to] GraphNode.
+ Returns [code]true[/code] if the [param from_port] of the [param from_node] [GraphNode] is connected to the [param to_port] of the [param to_node] [GraphNode].
</description>
</method>
<method name="is_valid_connection_type" qualifiers="const">
@@ -176,7 +178,8 @@
<param index="0" name="from_type" type="int" />
<param index="1" name="to_type" type="int" />
<description>
- Returns whether it's possible to connect slots of the specified types.
+ Returns whether it's possible to make a connection between two different port types. The port type is defined individually for the left and the right port of each slot with the [method GraphNode.set_slot] method.
+ See also [method add_valid_connection_type] and [method remove_valid_connection_type].
</description>
</method>
<method name="remove_valid_connection_type">
@@ -184,32 +187,33 @@
<param index="0" name="from_type" type="int" />
<param index="1" name="to_type" type="int" />
<description>
- Makes it not possible to connect between two different slot types. The type is defined with the [method GraphNode.set_slot] method.
+ Disallows the connection between two different port types previously allowed by [method add_valid_connection_type]. The port type is defined individually for the left and the right port of each slot with the [method GraphNode.set_slot] method.
+ See also [method is_valid_connection_type].
</description>
</method>
<method name="remove_valid_left_disconnect_type">
<return type="void" />
<param index="0" name="type" type="int" />
<description>
- Removes the possibility to disconnect nodes when dragging from the slot at the left if it has the specified type.
+ Disallows to disconnect nodes when dragging from the left port of the [GraphNode]'s slot if it has the specified type. Use this to disable disconnection previously allowed with [method add_valid_left_disconnect_type].
</description>
</method>
<method name="remove_valid_right_disconnect_type">
<return type="void" />
<param index="0" name="type" type="int" />
<description>
- Removes the possibility to disconnect nodes when dragging from the slot at the right if it has the specified type.
+ Disallows to disconnect nodes when dragging from the right port of the [GraphNode]'s slot if it has the specified type. Use this to disable disconnection previously allowed with [method add_valid_right_disconnect_type].
</description>
</method>
<method name="set_connection_activity">
<return type="void" />
- <param index="0" name="from" type="StringName" />
+ <param index="0" name="from_node" type="StringName" />
<param index="1" name="from_port" type="int" />
- <param index="2" name="to" type="StringName" />
+ <param index="2" name="to_node" type="StringName" />
<param index="3" name="to_port" type="int" />
<param index="4" name="amount" type="float" />
<description>
- Sets the coloration of the connection between [param from]'s [param from_port] and [param to]'s [param to_port] with the color provided in the [theme_item activity] theme property.
+ Sets the coloration of the connection between [param from_node]'s [param from_port] and [param to_node]'s [param to_port] with the color provided in the [theme_item activity] theme property.
</description>
</method>
<method name="set_selected">
@@ -287,36 +291,36 @@
</description>
</signal>
<signal name="connection_drag_started">
- <param index="0" name="from" type="String" />
- <param index="1" name="slot" type="int" />
+ <param index="0" name="from_node" type="String" />
+ <param index="1" name="from_port" type="int" />
<param index="2" name="is_output" type="bool" />
<description>
Emitted at the beginning of a connection drag.
</description>
</signal>
<signal name="connection_from_empty">
- <param index="0" name="to" type="StringName" />
- <param index="1" name="to_slot" type="int" />
+ <param index="0" name="to_node" type="StringName" />
+ <param index="1" name="to_port" type="int" />
<param index="2" name="release_position" type="Vector2" />
<description>
- Emitted when user dragging connection from input port into empty space of the graph.
+ Emitted when user drags a connection from an input port into the empty space of the graph.
</description>
</signal>
<signal name="connection_request">
- <param index="0" name="from" type="StringName" />
- <param index="1" name="from_slot" type="int" />
- <param index="2" name="to" type="StringName" />
- <param index="3" name="to_slot" type="int" />
+ <param index="0" name="from_node" type="StringName" />
+ <param index="1" name="from_port" type="int" />
+ <param index="2" name="to_node" type="StringName" />
+ <param index="3" name="to_port" type="int" />
<description>
- Emitted to the GraphEdit when the connection between the [param from_slot] slot of the [param from] GraphNode and the [param to_slot] slot of the [param to] GraphNode is attempted to be created.
+ Emitted to the GraphEdit when the connection between the [param from_port] of the [param from_node] [GraphNode] and the [param to_port] of the [param to_node] [GraphNode] is attempted to be created.
</description>
</signal>
<signal name="connection_to_empty">
- <param index="0" name="from" type="StringName" />
- <param index="1" name="from_slot" type="int" />
+ <param index="0" name="from_node" type="StringName" />
+ <param index="1" name="from_port" type="int" />
<param index="2" name="release_position" type="Vector2" />
<description>
- Emitted when user dragging connection from output port into empty space of the graph.
+ Emitted when user drags a connection from an output port into the empty space of the graph.
</description>
</signal>
<signal name="copy_nodes_request">
@@ -331,12 +335,12 @@
</description>
</signal>
<signal name="disconnection_request">
- <param index="0" name="from" type="StringName" />
- <param index="1" name="from_slot" type="int" />
- <param index="2" name="to" type="StringName" />
- <param index="3" name="to_slot" type="int" />
+ <param index="0" name="from_node" type="StringName" />
+ <param index="1" name="from_port" type="int" />
+ <param index="2" name="to_node" type="StringName" />
+ <param index="3" name="to_port" type="int" />
<description>
- Emitted to the GraphEdit when the connection between [param from_slot] slot of [param from] GraphNode and [param to_slot] slot of [param to] GraphNode is attempted to be removed.
+ Emitted to the GraphEdit when the connection between [param from_port] of [param from_node] [GraphNode] and [param to_port] of [param to_node] [GraphNode] is attempted to be removed.
</description>
</signal>
<signal name="duplicate_nodes_request">
diff --git a/doc/classes/GraphNode.xml b/doc/classes/GraphNode.xml
index a80dd0d47f..16386ff81b 100644
--- a/doc/classes/GraphNode.xml
+++ b/doc/classes/GraphNode.xml
@@ -1,12 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GraphNode" inherits="Container" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A GraphNode is a container with potentially several input and output slots allowing connections between GraphNodes. Slots can have different, incompatible types.
+ GraphNode is a [Container] control that represents a single data unit in a [GraphEdit] graph. You can customize the number, type, and color of left- and right-side connection ports.
</brief_description>
<description>
- A GraphNode is a container. Each GraphNode can have several input and output slots, sometimes referred to as ports, allowing connections between GraphNodes. To add a slot to GraphNode, add any [Control]-derived child node to it.
- After adding at least one child to GraphNode new sections will be automatically created in the Inspector called 'Slot'. When 'Slot' is expanded you will see list with index number for each slot. You can click on each of them to expand further.
- In the Inspector you can enable (show) or disable (hide) slots. By default, all slots are disabled so you may not see any slots on your GraphNode initially. You can assign a type to each slot. Only slots of the same type will be able to connect to each other. You can also assign colors to slots. A tuple of input and output slots is defined for each GUI element included in the GraphNode. Input connections are on the left and output connections are on the right side of GraphNode. Only enabled slots are counted as connections.
+ GraphNode allows to create nodes for a [GraphEdit] graph with customizable content based on its child [Control]s. GraphNode is a [Container] and is responsible for placing its children on screen. This works similar to [VBoxContainer]. Children, in turn, provide GraphNode with so-called slots, each of which can have a connection port on either side. This is similar to how [TabContainer] uses children to create the tabs.
+ Each GraphNode slot is defined by its index and can provide the node with up to two ports: one on the left, and one on the right. By convention the left port is also referred to as the input port and the right port is referred to as the output port. Each port can be enabled and configured individually, using different type and color. The type is an arbitrary value that you can define using your own considerations. The parent [GraphEdit] will receive this information on each connect and disconnect request.
+ Slots can be configured in the Inspector dock once you add at least one child [Control]. The properties are grouped by each slot's index in the "Slot" section.
+ [b]Note:[/b] While GraphNode is set up using slots and slot indices, connections are made between the ports which are enabled. Because of that [GraphEdit] uses port's index and not slot's index. You can use [method get_connection_input_slot] and [method get_connection_output_slot] to get the slot index from the port index.
</description>
<tutorials>
</tutorials>
@@ -19,16 +20,16 @@
</method>
<method name="clear_slot">
<return type="void" />
- <param index="0" name="idx" type="int" />
+ <param index="0" name="slot_index" type="int" />
<description>
- Disables input and output slot whose index is [param idx].
+ Disables input and output slot whose index is [param slot_index].
</description>
</method>
<method name="get_connection_input_color">
<return type="Color" />
- <param index="0" name="idx" type="int" />
+ <param index="0" name="port" type="int" />
<description>
- Returns the [Color] of the input connection [param idx].
+ Returns the [Color] of the input connection [param port].
</description>
</method>
<method name="get_connection_input_count">
@@ -39,30 +40,37 @@
</method>
<method name="get_connection_input_height">
<return type="int" />
- <param index="0" name="idx" type="int" />
+ <param index="0" name="port" type="int" />
<description>
- Returns the height of the input connection [param idx].
+ Returns the height of the input connection [param port].
</description>
</method>
<method name="get_connection_input_position">
<return type="Vector2" />
- <param index="0" name="idx" type="int" />
+ <param index="0" name="port" type="int" />
+ <description>
+ Returns the position of the input connection [param port].
+ </description>
+ </method>
+ <method name="get_connection_input_slot">
+ <return type="int" />
+ <param index="0" name="port" type="int" />
<description>
- Returns the position of the input connection [param idx].
+ Returns the corresponding slot index of the input connection [param port].
</description>
</method>
<method name="get_connection_input_type">
<return type="int" />
- <param index="0" name="idx" type="int" />
+ <param index="0" name="port" type="int" />
<description>
- Returns the type of the input connection [param idx].
+ Returns the type of the input connection [param port].
</description>
</method>
<method name="get_connection_output_color">
<return type="Color" />
- <param index="0" name="idx" type="int" />
+ <param index="0" name="port" type="int" />
<description>
- Returns the [Color] of the output connection [param idx].
+ Returns the [Color] of the output connection [param port].
</description>
</method>
<method name="get_connection_output_count">
@@ -73,150 +81,157 @@
</method>
<method name="get_connection_output_height">
<return type="int" />
- <param index="0" name="idx" type="int" />
+ <param index="0" name="port" type="int" />
<description>
- Returns the height of the output connection [param idx].
+ Returns the height of the output connection [param port].
</description>
</method>
<method name="get_connection_output_position">
<return type="Vector2" />
- <param index="0" name="idx" type="int" />
+ <param index="0" name="port" type="int" />
+ <description>
+ Returns the position of the output connection [param port].
+ </description>
+ </method>
+ <method name="get_connection_output_slot">
+ <return type="int" />
+ <param index="0" name="port" type="int" />
<description>
- Returns the position of the output connection [param idx].
+ Returns the corresponding slot index of the output connection [param port].
</description>
</method>
<method name="get_connection_output_type">
<return type="int" />
- <param index="0" name="idx" type="int" />
+ <param index="0" name="port" type="int" />
<description>
- Returns the type of the output connection [param idx].
+ Returns the type of the output connection [param port].
</description>
</method>
<method name="get_slot_color_left" qualifiers="const">
<return type="Color" />
- <param index="0" name="idx" type="int" />
+ <param index="0" name="slot_index" type="int" />
<description>
- Returns the left (input) [Color] of the slot [param idx].
+ Returns the left (input) [Color] of the slot [param slot_index].
</description>
</method>
<method name="get_slot_color_right" qualifiers="const">
<return type="Color" />
- <param index="0" name="idx" type="int" />
+ <param index="0" name="slot_index" type="int" />
<description>
- Returns the right (output) [Color] of the slot [param idx].
+ Returns the right (output) [Color] of the slot [param slot_index].
</description>
</method>
<method name="get_slot_type_left" qualifiers="const">
<return type="int" />
- <param index="0" name="idx" type="int" />
+ <param index="0" name="slot_index" type="int" />
<description>
- Returns the left (input) type of the slot [param idx].
+ Returns the left (input) type of the slot [param slot_index].
</description>
</method>
<method name="get_slot_type_right" qualifiers="const">
<return type="int" />
- <param index="0" name="idx" type="int" />
+ <param index="0" name="slot_index" type="int" />
<description>
- Returns the right (output) type of the slot [param idx].
+ Returns the right (output) type of the slot [param slot_index].
</description>
</method>
<method name="is_slot_draw_stylebox" qualifiers="const">
<return type="bool" />
- <param index="0" name="idx" type="int" />
+ <param index="0" name="slot_index" type="int" />
<description>
- Returns true if the background [StyleBox] of the slot [param idx] is drawn.
+ Returns true if the background [StyleBox] of the slot [param slot_index] is drawn.
</description>
</method>
<method name="is_slot_enabled_left" qualifiers="const">
<return type="bool" />
- <param index="0" name="idx" type="int" />
+ <param index="0" name="slot_index" type="int" />
<description>
- Returns [code]true[/code] if left (input) side of the slot [param idx] is enabled.
+ Returns [code]true[/code] if left (input) side of the slot [param slot_index] is enabled.
</description>
</method>
<method name="is_slot_enabled_right" qualifiers="const">
<return type="bool" />
- <param index="0" name="idx" type="int" />
+ <param index="0" name="slot_index" type="int" />
<description>
- Returns [code]true[/code] if right (output) side of the slot [param idx] is enabled.
+ Returns [code]true[/code] if right (output) side of the slot [param slot_index] is enabled.
</description>
</method>
<method name="set_slot">
<return type="void" />
- <param index="0" name="idx" type="int" />
- <param index="1" name="enable_left" type="bool" />
+ <param index="0" name="slot_index" type="int" />
+ <param index="1" name="enable_left_port" type="bool" />
<param index="2" name="type_left" type="int" />
<param index="3" name="color_left" type="Color" />
- <param index="4" name="enable_right" type="bool" />
+ <param index="4" name="enable_right_port" type="bool" />
<param index="5" name="type_right" type="int" />
<param index="6" name="color_right" type="Color" />
- <param index="7" name="custom_left" type="Texture2D" default="null" />
- <param index="8" name="custom_right" type="Texture2D" default="null" />
- <param index="9" name="enable" type="bool" default="true" />
- <description>
- Sets properties of the slot with ID [param idx].
- If [param enable_left]/[param enable_right], a port will appear and the slot will be able to be connected from this side.
- [param type_left]/[param type_right] is an arbitrary type of the port. Only ports with the same type values can be connected and negative values will disallow all connections to be made via user inputs.
- [param color_left]/[param color_right] is the tint of the port's icon on this side.
- [param custom_left]/[param custom_right] is a custom texture for this side's port.
- [b]Note:[/b] This method only sets properties of the slot. To create the slot, add a [Control]-derived child to the GraphNode.
- Individual properties can be set using one of the [code]set_slot_*[/code] methods. You must enable at least one side of the slot to do so.
+ <param index="7" name="custom_icon_left" type="Texture2D" default="null" />
+ <param index="8" name="custom_icon_right" type="Texture2D" default="null" />
+ <param index="9" name="draw_stylebox" type="bool" default="true" />
+ <description>
+ Sets properties of the slot with the [param slot_index] index.
+ If [param enable_left_port]/[param enable_right_port] is [code]true[/code], a port will appear and the slot will be able to be connected from this side.
+ With [param type_left]/[param type_right] an arbitrary type can be assigned to each port. Two ports can be connected if they share the same type, or if the connection between their types is allowed in the parent [GraphEdit] (see [method GraphEdit.add_valid_connection_type]). Keep in mind that the [GraphEdit] has the final say in accepting the connection. Type compatibility simply allows the [signal GraphEdit.connection_request] signal to be emitted.
+ Ports can be further customized using [param color_left]/[param color_right] and [param custom_icon_left]/[param custom_icon_right]. The color parameter adds a tint to the icon. The custom icon can be used to override the default port dot.
+ Additionally, [param draw_stylebox] can be used to enable or disable drawing of the background stylebox for each slot. See [theme_item slot].
+ Individual properties can also be set using one of the [code]set_slot_*[/code] methods.
+ [b]Note:[/b] This method only sets properties of the slot. To create the slot itself, add a [Control]-derived child to the GraphNode.
</description>
</method>
<method name="set_slot_color_left">
<return type="void" />
- <param index="0" name="idx" type="int" />
- <param index="1" name="color_left" type="Color" />
+ <param index="0" name="slot_index" type="int" />
+ <param index="1" name="color" type="Color" />
<description>
- Sets the [Color] of the left (input) side of the slot [param idx] to [param color_left].
+ Sets the [Color] of the left (input) side of the slot [param slot_index] to [param color].
</description>
</method>
<method name="set_slot_color_right">
<return type="void" />
- <param index="0" name="idx" type="int" />
- <param index="1" name="color_right" type="Color" />
+ <param index="0" name="slot_index" type="int" />
+ <param index="1" name="color" type="Color" />
<description>
- Sets the [Color] of the right (output) side of the slot [param idx] to [param color_right].
+ Sets the [Color] of the right (output) side of the slot [param slot_index] to [param color].
</description>
</method>
<method name="set_slot_draw_stylebox">
<return type="void" />
- <param index="0" name="idx" type="int" />
- <param index="1" name="draw_stylebox" type="bool" />
+ <param index="0" name="slot_index" type="int" />
+ <param index="1" name="enable" type="bool" />
<description>
- Toggles the background [StyleBox] of the slot [param idx].
+ Toggles the background [StyleBox] of the slot [param slot_index].
</description>
</method>
<method name="set_slot_enabled_left">
<return type="void" />
- <param index="0" name="idx" type="int" />
- <param index="1" name="enable_left" type="bool" />
+ <param index="0" name="slot_index" type="int" />
+ <param index="1" name="enable" type="bool" />
<description>
- Toggles the left (input) side of the slot [param idx]. If [param enable_left] is [code]true[/code], a port will appear on the left side and the slot will be able to be connected from this side.
+ Toggles the left (input) side of the slot [param slot_index]. If [param enable] is [code]true[/code], a port will appear on the left side and the slot will be able to be connected from this side.
</description>
</method>
<method name="set_slot_enabled_right">
<return type="void" />
- <param index="0" name="idx" type="int" />
- <param index="1" name="enable_right" type="bool" />
+ <param index="0" name="slot_index" type="int" />
+ <param index="1" name="enable" type="bool" />
<description>
- Toggles the right (output) side of the slot [param idx]. If [param enable_right] is [code]true[/code], a port will appear on the right side and the slot will be able to be connected from this side.
+ Toggles the right (output) side of the slot [param slot_index]. If [param enable] is [code]true[/code], a port will appear on the right side and the slot will be able to be connected from this side.
</description>
</method>
<method name="set_slot_type_left">
<return type="void" />
- <param index="0" name="idx" type="int" />
- <param index="1" name="type_left" type="int" />
+ <param index="0" name="slot_index" type="int" />
+ <param index="1" name="type" type="int" />
<description>
- Sets the left (input) type of the slot [param idx] to [param type_left]. If the value is negative, all connections will be disallowed to be created via user inputs.
+ Sets the left (input) type of the slot [param slot_index] to [param type]. If the value is negative, all connections will be disallowed to be created via user inputs.
</description>
</method>
<method name="set_slot_type_right">
<return type="void" />
- <param index="0" name="idx" type="int" />
- <param index="1" name="type_right" type="int" />
+ <param index="0" name="slot_index" type="int" />
+ <param index="1" name="type" type="int" />
<description>
- Sets the right (output) type of the slot [param idx] to [param type_right]. If the value is negative, all connections will be disallowed to be created via user inputs.
+ Sets the right (output) type of the slot [param slot_index] to [param type]. If the value is negative, all connections will be disallowed to be created via user inputs.
</description>
</method>
</methods>
diff --git a/doc/classes/NavigationObstacle2D.xml b/doc/classes/NavigationObstacle2D.xml
index 4ecdc06645..6d05e220e3 100644
--- a/doc/classes/NavigationObstacle2D.xml
+++ b/doc/classes/NavigationObstacle2D.xml
@@ -10,12 +10,25 @@
<tutorials>
</tutorials>
<methods>
+ <method name="get_navigation_map" qualifiers="const">
+ <return type="RID" />
+ <description>
+ Returns the [RID] of the navigation map for this NavigationObstacle node. This function returns always the map set on the NavigationObstacle node and not the map of the abstract agent on the NavigationServer. If the agent map is changed directly with the NavigationServer API the NavigationObstacle node will not be aware of the map change. Use [method set_navigation_map] to change the navigation map for the NavigationObstacle and also update the agent on the NavigationServer.
+ </description>
+ </method>
<method name="get_rid" qualifiers="const">
<return type="RID" />
<description>
Returns the [RID] of this obstacle on the [NavigationServer2D].
</description>
</method>
+ <method name="set_navigation_map">
+ <return type="void" />
+ <param index="0" name="navigation_map" type="RID" />
+ <description>
+ Sets the [RID] of the navigation map this NavigationObstacle node should use and also updates the [code]agent[/code] on the NavigationServer.
+ </description>
+ </method>
</methods>
<members>
<member name="estimate_radius" type="bool" setter="set_estimate_radius" getter="is_radius_estimated" default="true">
diff --git a/doc/classes/NavigationObstacle3D.xml b/doc/classes/NavigationObstacle3D.xml
index ed8af3883c..f5a0bde089 100644
--- a/doc/classes/NavigationObstacle3D.xml
+++ b/doc/classes/NavigationObstacle3D.xml
@@ -10,12 +10,25 @@
<tutorials>
</tutorials>
<methods>
+ <method name="get_navigation_map" qualifiers="const">
+ <return type="RID" />
+ <description>
+ Returns the [RID] of the navigation map for this NavigationObstacle node. This function returns always the map set on the NavigationObstacle node and not the map of the abstract agent on the NavigationServer. If the agent map is changed directly with the NavigationServer API the NavigationObstacle node will not be aware of the map change. Use [method set_navigation_map] to change the navigation map for the NavigationObstacle and also update the agent on the NavigationServer.
+ </description>
+ </method>
<method name="get_rid" qualifiers="const">
<return type="RID" />
<description>
Returns the [RID] of this obstacle on the [NavigationServer3D].
</description>
</method>
+ <method name="set_navigation_map">
+ <return type="void" />
+ <param index="0" name="navigation_map" type="RID" />
+ <description>
+ Sets the [RID] of the navigation map this NavigationObstacle node should use and also updates the [code]agent[/code] on the NavigationServer.
+ </description>
+ </method>
</methods>
<members>
<member name="estimate_radius" type="bool" setter="set_estimate_radius" getter="is_radius_estimated" default="true">
diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml
index 92beefa5fc..d8ad65082f 100644
--- a/doc/classes/Node.xml
+++ b/doc/classes/Node.xml
@@ -514,7 +514,7 @@
<param index="0" name="child_node" type="Node" />
<param index="1" name="to_position" type="int" />
<description>
- Moves a child node to a different position (order) among the other children. Since calls, signals, etc are performed by tree order, changing the order of children nodes may be useful.
+ Moves a child node to a different position (order) among the other children. Since calls, signals, etc are performed by tree order, changing the order of children nodes may be useful. If [param to_position] is negative, the index will be counted from the end.
[b]Note:[/b] Internal children can only be moved within their expected "internal range" (see [code]internal[/code] parameter in [method add_child]).
</description>
</method>
@@ -577,12 +577,6 @@
Queues a node for deletion at the end of the current frame. When deleted, all of its child nodes will be deleted as well. This method ensures it's safe to delete the node, contrary to [method Object.free]. Use [method Object.is_queued_for_deletion] to check whether a node will be deleted at the end of the frame.
</description>
</method>
- <method name="remove_and_skip">
- <return type="void" />
- <description>
- Removes a node and sets all its children as children of the parent node (if it exists). All event subscriptions that pass by the removed node will be unsubscribed.
- </description>
- </method>
<method name="remove_child">
<return type="void" />
<param index="0" name="node" type="Node" />
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 3dbf7c75e5..1f07a13c94 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -640,10 +640,22 @@
If [code]true[/code], Blender 3D scene files with the [code].blend[/code] extension will be imported by converting them to glTF 2.0.
This requires configuring a path to a Blender executable in the editor settings at [code]filesystem/import/blender/blender3_path[/code]. Blender 3.0 or later is required.
</member>
+ <member name="filesystem/import/blender/enabled.android" type="bool" setter="" getter="" default="false">
+ Override for [member filesystem/import/blender/enabled] on Android where Blender can't easily be accessed from Godot.
+ </member>
+ <member name="filesystem/import/blender/enabled.web" type="bool" setter="" getter="" default="false">
+ Override for [member filesystem/import/blender/enabled] on the Web where Blender can't easily be accessed from Godot.
+ </member>
<member name="filesystem/import/fbx/enabled" type="bool" setter="" getter="" default="true">
If [code]true[/code], Autodesk FBX 3D scene files with the [code].fbx[/code] extension will be imported by converting them to glTF 2.0.
This requires configuring a path to a FBX2glTF executable in the editor settings at [code]filesystem/import/fbx/fbx2gltf_path[/code].
</member>
+ <member name="filesystem/import/fbx/enabled.android" type="bool" setter="" getter="" default="false">
+ Override for [member filesystem/import/fbx/enabled] on Android where FBX2glTF can't easily be accessed from Godot.
+ </member>
+ <member name="filesystem/import/fbx/enabled.web" type="bool" setter="" getter="" default="false">
+ Override for [member filesystem/import/fbx/enabled] on the Web where FBX2glTF can't easily be accessed from Godot.
+ </member>
<member name="gui/common/default_scroll_deadzone" type="int" setter="" getter="" default="0">
Default value for [member ScrollContainer.scroll_deadzone], which will be used for all [ScrollContainer]s unless overridden.
</member>
diff --git a/doc/classes/Tween.xml b/doc/classes/Tween.xml
index c7fc78c1d3..5186972477 100644
--- a/doc/classes/Tween.xml
+++ b/doc/classes/Tween.xml
@@ -36,9 +36,18 @@
tween.tween_property(sprite, "position", Vector2(0, 0), 1)
[/codeblock]
In the example above, all children of a node are moved one after another to position (0, 0).
+ You should avoid using more than one [Tween] per object's property. If two or more tweens animate one property at the same time, the last one created will take priority and assign the final value. If you want to interrupt and restart an animation, consider assigning the [Tween] to a variable:
+ [codeblock]
+ var tween
+ func animate():
+ if tween:
+ tween.kill() # Abort the previous animation.
+ tween = create_tween()
+ [/codeblock]
Some [Tweener]s use transitions and eases. The first accepts a [enum TransitionType] constant, and refers to the way the timing of the animation is handled (see [url=https://easings.net/]easings.net[/url] for some examples). The second accepts an [enum EaseType] constant, and controls where the [code]trans_type[/code] is applied to the interpolation (in the beginning, the end, or both). If you don't know which transition and easing to pick, you can try different [enum TransitionType] constants with [constant EASE_IN_OUT], and use the one that looks best.
[url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/tween_cheatsheet.png]Tween easing and transition types cheatsheet[/url]
[b]Note:[/b] All [Tween]s will automatically start by default. To prevent a [Tween] from autostarting, you can call [method stop] immediately after it is created.
+ [b]Note:[/b] [Tween]s are processing after all of nodes in the current frame, i.e. after [method Node._process] or [method Node._physics_process] (depending on [enum TweenProcessMode]).
</description>
<tutorials>
</tutorials>
diff --git a/doc/tools/make_rst.py b/doc/tools/make_rst.py
index cd7de085d8..a8569413ec 100755
--- a/doc/tools/make_rst.py
+++ b/doc/tools/make_rst.py
@@ -1480,6 +1480,8 @@ def format_text_block(
)
tag_text = f"``{link_target}``"
+ escape_pre = True
+ escape_post = True
# Formatting directives.
diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp
index cc96294ca5..3575837794 100644
--- a/drivers/gles3/rasterizer_gles3.cpp
+++ b/drivers/gles3/rasterizer_gles3.cpp
@@ -280,11 +280,6 @@ void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, Display
GLES3::RenderTarget *rt = texture_storage->get_render_target(p_render_target);
ERR_FAIL_COND(!rt);
- // TODO: do we need a keep 3d linear option?
-
- // Make sure we are drawing to the right context.
- DisplayServer::get_singleton()->gl_window_make_current(p_screen);
-
if (rt->external.fbo != 0) {
glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->external.fbo);
} else {
@@ -298,9 +293,6 @@ void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, Display
// is this p_screen useless in a multi window environment?
void RasterizerGLES3::blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount) {
- // All blits are going to the system framebuffer, so just bind once.
- glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
-
for (int i = 0; i < p_amount; i++) {
const BlitToScreen &blit = p_render_targets[i];
diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl
index c7fdd6ebd8..efd6036ba9 100644
--- a/drivers/gles3/shaders/scene.glsl
+++ b/drivers/gles3/shaders/scene.glsl
@@ -629,11 +629,12 @@ void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, float atte
float diffuse_brdf_NL; // BRDF times N.L for calculating diffuse radiance
#if defined(DIFFUSE_LAMBERT_WRAP)
- // energy conserving lambert wrap shader
- diffuse_brdf_NL = max(0.0, (NdotL + roughness) / ((1.0 + roughness) * (1.0 + roughness)));
+ // Energy conserving lambert wrap shader.
+ // https://web.archive.org/web/20210228210901/http://blog.stevemcauley.com/2011/12/03/energy-conserving-wrapped-diffuse/
+ diffuse_brdf_NL = max(0.0, (NdotL + roughness) / ((1.0 + roughness) * (1.0 + roughness))) * (1.0 / M_PI);
#elif defined(DIFFUSE_TOON)
- diffuse_brdf_NL = smoothstep(-roughness, max(roughness, 0.01), NdotL);
+ diffuse_brdf_NL = smoothstep(-roughness, max(roughness, 0.01), NdotL) * (1.0 / M_PI);
#elif defined(DIFFUSE_BURLEY)
diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp
index b8ab4d6839..a801b3285a 100644
--- a/drivers/gles3/storage/texture_storage.cpp
+++ b/drivers/gles3/storage/texture_storage.cpp
@@ -1199,7 +1199,7 @@ void TextureStorage::_update_render_target(RenderTarget *rt) {
rt->color_internal_format = rt->is_transparent ? GL_RGBA8 : GL_RGB10_A2;
rt->color_format = GL_RGBA;
- rt->color_type = rt->is_transparent ? GL_BYTE : GL_UNSIGNED_INT_2_10_10_10_REV;
+ rt->color_type = rt->is_transparent ? GL_UNSIGNED_BYTE : GL_UNSIGNED_INT_2_10_10_10_REV;
rt->image_format = Image::FORMAT_RGBA8;
glDisable(GL_SCISSOR_TEST);
diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp
index 4ac32d7317..3e72c6211d 100644
--- a/editor/create_dialog.cpp
+++ b/editor/create_dialog.cpp
@@ -296,6 +296,15 @@ void CreateDialog::_configure_search_option_item(TreeItem *r_item, const String
r_item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_type, icon_fallback));
}
+ bool is_deprecated = EditorHelp::get_doc_data()->class_list[p_type].is_deprecated;
+ bool is_experimental = EditorHelp::get_doc_data()->class_list[p_type].is_experimental;
+
+ if (is_deprecated) {
+ r_item->add_button(0, get_theme_icon("StatusError", SNAME("EditorIcons")), 0, false, TTR("This class is marked as deprecated."));
+ } else if (is_experimental) {
+ r_item->add_button(0, get_theme_icon("NodeWarning", SNAME("EditorIcons")), 0, false, TTR("This class is marked as experimental."));
+ }
+
if (!search_box->get_text().is_empty()) {
r_item->set_collapsed(false);
} else {
diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp
index ec9a744e57..9f655ab00a 100644
--- a/editor/doc_tools.cpp
+++ b/editor/doc_tools.cpp
@@ -85,6 +85,9 @@ void DocTools::merge_from(const DocTools &p_data) {
const DocData::ClassDoc &cf = p_data.class_list[c.name];
+ c.is_deprecated = cf.is_deprecated;
+ c.is_experimental = cf.is_experimental;
+
c.description = cf.description;
c.brief_description = cf.brief_description;
c.tutorials = cf.tutorials;
@@ -133,6 +136,8 @@ void DocTools::merge_from(const DocTools &p_data) {
const DocData::MethodDoc &mf = cf.constructors[j];
m.description = mf.description;
+ m.is_deprecated = mf.is_deprecated;
+ m.is_experimental = mf.is_experimental;
break;
}
}
@@ -148,6 +153,8 @@ void DocTools::merge_from(const DocTools &p_data) {
const DocData::MethodDoc &mf = cf.methods[j];
m.description = mf.description;
+ m.is_deprecated = mf.is_deprecated;
+ m.is_experimental = mf.is_experimental;
break;
}
}
@@ -162,6 +169,8 @@ void DocTools::merge_from(const DocTools &p_data) {
const DocData::MethodDoc &mf = cf.signals[j];
m.description = mf.description;
+ m.is_deprecated = mf.is_deprecated;
+ m.is_experimental = mf.is_experimental;
break;
}
}
@@ -176,6 +185,8 @@ void DocTools::merge_from(const DocTools &p_data) {
const DocData::ConstantDoc &mf = cf.constants[j];
m.description = mf.description;
+ m.is_deprecated = mf.is_deprecated;
+ m.is_experimental = mf.is_experimental;
break;
}
}
@@ -190,6 +201,8 @@ void DocTools::merge_from(const DocTools &p_data) {
const DocData::MethodDoc &mf = cf.annotations[j];
m.description = mf.description;
+ m.is_deprecated = mf.is_deprecated;
+ m.is_experimental = mf.is_experimental;
break;
}
}
@@ -204,6 +217,8 @@ void DocTools::merge_from(const DocTools &p_data) {
const DocData::PropertyDoc &pf = cf.properties[j];
p.description = pf.description;
+ p.is_deprecated = pf.is_deprecated;
+ p.is_experimental = pf.is_experimental;
break;
}
}
@@ -266,6 +281,8 @@ void DocTools::merge_from(const DocTools &p_data) {
const DocData::MethodDoc &mf = cf.operators[j];
m.description = mf.description;
+ m.is_deprecated = mf.is_deprecated;
+ m.is_experimental = mf.is_experimental;
break;
}
}
@@ -1007,6 +1024,12 @@ static Error _parse_methods(Ref<XMLParser> &parser, Vector<DocData::MethodDoc> &
if (parser->has_attribute("qualifiers")) {
method.qualifiers = parser->get_attribute_value("qualifiers");
}
+ if (parser->has_attribute("is_deprecated")) {
+ method.is_deprecated = parser->get_attribute_value("is_deprecated").to_lower() == "true";
+ }
+ if (parser->has_attribute("is_experimental")) {
+ method.is_experimental = parser->get_attribute_value("is_experimental").to_lower() == "true";
+ }
while (parser->read() == OK) {
if (parser->get_node_type() == XMLParser::NODE_ELEMENT) {
@@ -1138,6 +1161,16 @@ Error DocTools::_load(Ref<XMLParser> parser) {
c.inherits = parser->get_attribute_value("inherits");
}
+ if (parser->has_attribute("is_deprecated")) {
+ String result = parser->get_attribute_value("is_deprecated");
+ c.is_deprecated = (result == "true" || result == "True" || result == "TRUE" || result == "1");
+ }
+
+ if (parser->has_attribute("is_experimental")) {
+ String result = parser->get_attribute_value("is_experimental");
+ c.is_experimental = (result == "true" || result == "True" || result == "TRUE" || result == "1");
+ }
+
while (parser->read() == OK) {
if (parser->get_node_type() == XMLParser::NODE_ELEMENT) {
String name2 = parser->get_node_name();
@@ -1211,6 +1244,12 @@ Error DocTools::_load(Ref<XMLParser> parser) {
if (parser->has_attribute("enum")) {
prop2.enumeration = parser->get_attribute_value("enum");
}
+ if (parser->has_attribute("is_deprecated")) {
+ prop2.is_deprecated = parser->get_attribute_value("is_deprecated").to_lower() == "true";
+ }
+ if (parser->has_attribute("is_experimental")) {
+ prop2.is_experimental = parser->get_attribute_value("is_experimental").to_lower() == "true";
+ }
if (!parser->is_empty()) {
parser->read();
if (parser->get_node_type() == XMLParser::NODE_TEXT) {
@@ -1275,6 +1314,14 @@ Error DocTools::_load(Ref<XMLParser> parser) {
if (parser->has_attribute("is_bitfield")) {
constant2.is_bitfield = parser->get_attribute_value("is_bitfield").to_lower() == "true";
}
+ if (parser->has_attribute("is_deprecated")) {
+ String result = parser->get_attribute_value("is_deprecated");
+ constant2.is_deprecated = (result == "true" || result == "True" || result == "TRUE" || result == "1");
+ }
+ if (parser->has_attribute("is_experimental")) {
+ String result = parser->get_attribute_value("is_experimental");
+ constant2.is_experimental = (result == "true" || result == "True" || result == "TRUE" || result == "1");
+ }
if (!parser->is_empty()) {
parser->read();
if (parser->get_node_type() == XMLParser::NODE_TEXT) {
@@ -1327,7 +1374,15 @@ static void _write_method_doc(Ref<FileAccess> f, const String &p_name, Vector<Do
qualifiers += " qualifiers=\"" + m.qualifiers.xml_escape() + "\"";
}
- _write_string(f, 2, "<" + p_name + " name=\"" + m.name.xml_escape() + "\"" + qualifiers + ">");
+ String additional_attributes;
+ if (m.is_deprecated) {
+ additional_attributes += " is_deprecated=\"True\"";
+ }
+ if (m.is_experimental) {
+ additional_attributes += " is_experimental=\"True\"";
+ }
+
+ _write_string(f, 2, "<" + p_name + " name=\"" + m.name.xml_escape() + "\"" + qualifiers + additional_attributes + ">");
if (!m.return_type.is_empty()) {
String enum_text;
@@ -1390,6 +1445,12 @@ Error DocTools::save_classes(const String &p_default_path, const HashMap<String,
String header = "<class name=\"" + c.name + "\"";
if (!c.inherits.is_empty()) {
header += " inherits=\"" + c.inherits + "\"";
+ if (c.is_deprecated) {
+ header += " is_deprecated=\"True\"";
+ }
+ if (c.is_experimental) {
+ header += " is_experimental=\"True\"";
+ }
}
header += String(" version=\"") + VERSION_BRANCH + "\"";
// Reference the XML schema so editors can provide error checking.
@@ -1433,6 +1494,12 @@ Error DocTools::save_classes(const String &p_default_path, const HashMap<String,
if (!c.properties[i].default_value.is_empty()) {
additional_attributes += " default=\"" + c.properties[i].default_value.xml_escape(true) + "\"";
}
+ if (c.properties[i].is_deprecated) {
+ additional_attributes += " is_deprecated=\"True\"";
+ }
+ if (c.properties[i].is_experimental) {
+ additional_attributes += " is_experimental=\"True\"";
+ }
const DocData::PropertyDoc &p = c.properties[i];
@@ -1453,21 +1520,30 @@ Error DocTools::save_classes(const String &p_default_path, const HashMap<String,
_write_string(f, 1, "<constants>");
for (int i = 0; i < c.constants.size(); i++) {
const DocData::ConstantDoc &k = c.constants[i];
+
+ String additional_attributes;
+ if (c.constants[i].is_deprecated) {
+ additional_attributes += " is_deprecated=\"True\"";
+ }
+ if (c.constants[i].is_experimental) {
+ additional_attributes += " is_experimental=\"True\"";
+ }
+
if (k.is_value_valid) {
if (!k.enumeration.is_empty()) {
if (k.is_bitfield) {
- _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value + "\" enum=\"" + k.enumeration + "\" is_bitfield=\"true\">");
+ _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value + "\" enum=\"" + k.enumeration + "\" is_bitfield=\"true\"" + additional_attributes + ">");
} else {
- _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value + "\" enum=\"" + k.enumeration + "\">");
+ _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value + "\" enum=\"" + k.enumeration + "\"" + additional_attributes + ">");
}
} else {
- _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value + "\">");
+ _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value + "\"" + additional_attributes + ">");
}
} else {
if (!k.enumeration.is_empty()) {
- _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"platform-dependent\" enum=\"" + k.enumeration + "\">");
+ _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"platform-dependent\" enum=\"" + k.enumeration + "\"" + additional_attributes + ">");
} else {
- _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"platform-dependent\">");
+ _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"platform-dependent\"" + additional_attributes + ">");
}
}
_write_string(f, 3, _translate_doc_string(k.description).strip_edges().xml_escape());
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index b8f115e82e..745dcdd04c 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -276,6 +276,23 @@ String EditorHelp::_fix_constant(const String &p_constant) const {
return p_constant;
}
+// Macros for assigning the deprecation/experimental information to class members
+#define DEPRECATED_DOC_TAG \
+ class_desc->push_color(get_theme_color(SNAME("error_color"), SNAME("Editor"))); \
+ Ref<Texture2D> error_icon = get_theme_icon(SNAME("StatusError"), SNAME("EditorIcons")); \
+ class_desc->add_text(" "); \
+ class_desc->add_image(error_icon, error_icon->get_width(), error_icon->get_height()); \
+ class_desc->add_text(" (" + TTR("Deprecated") + ")"); \
+ class_desc->pop();
+
+#define EXPERIMENTAL_DOC_TAG \
+ class_desc->push_color(get_theme_color(SNAME("warning_color"), SNAME("Editor"))); \
+ Ref<Texture2D> warning_icon = get_theme_icon(SNAME("NodeWarning"), SNAME("EditorIcons")); \
+ class_desc->add_text(" "); \
+ class_desc->add_image(warning_icon, warning_icon->get_width(), warning_icon->get_height()); \
+ class_desc->add_text(" (" + TTR("Experimental") + ")"); \
+ class_desc->pop();
+
void EditorHelp::_add_method(const DocData::MethodDoc &p_method, bool p_overview) {
method_line[p_method.name] = class_desc->get_paragraph_count() - 2; //gets overridden if description
@@ -356,6 +373,14 @@ void EditorHelp::_add_method(const DocData::MethodDoc &p_method, bool p_overview
class_desc->pop();
}
+ if (p_method.is_deprecated) {
+ DEPRECATED_DOC_TAG;
+ }
+
+ if (p_method.is_experimental) {
+ EXPERIMENTAL_DOC_TAG;
+ }
+
if (p_overview) {
class_desc->pop(); //cell
}
@@ -565,6 +590,17 @@ void EditorHelp::_update_doc() {
class_desc->pop(); // color
class_desc->pop(); // font size
class_desc->pop(); // font
+
+ if (cd.is_deprecated) {
+ class_desc->add_text(" ");
+ Ref<Texture2D> error_icon = get_theme_icon(SNAME("StatusError"), SNAME("EditorIcons"));
+ class_desc->add_image(error_icon, error_icon->get_width(), error_icon->get_height());
+ }
+ if (cd.is_experimental) {
+ class_desc->add_text(" ");
+ Ref<Texture2D> warning_icon = get_theme_icon(SNAME("NodeWarning"), SNAME("EditorIcons"));
+ class_desc->add_image(warning_icon, warning_icon->get_width(), warning_icon->get_height());
+ }
class_desc->add_newline();
const String non_breaking_space = String::chr(160);
@@ -627,6 +663,26 @@ void EditorHelp::_update_doc() {
}
}
+ // Note if deprecated.
+ if (cd.is_deprecated) {
+ Ref<Texture2D> error_icon = get_theme_icon(SNAME("StatusError"), SNAME("EditorIcons"));
+ class_desc->push_color(get_theme_color(SNAME("error_color"), SNAME("Editor")));
+ class_desc->add_image(error_icon, error_icon->get_width(), error_icon->get_height());
+ class_desc->add_text(String(" ") + TTR("This class is marked as deprecated. It will be removed in future versions."));
+ class_desc->pop();
+ class_desc->add_newline();
+ }
+
+ // Note if experimental.
+ if (cd.is_experimental) {
+ Ref<Texture2D> warning_icon = get_theme_icon(SNAME("NodeWarning"), SNAME("EditorIcons"));
+ class_desc->push_color(get_theme_color(SNAME("warning_color"), SNAME("Editor")));
+ class_desc->add_image(warning_icon, warning_icon->get_width(), warning_icon->get_height());
+ class_desc->add_text(String(" ") + TTR("This class is marked as experimental. It is subject to likely change or possible removal in future versions. Use at your own discretion."));
+ class_desc->pop();
+ class_desc->add_newline();
+ }
+
class_desc->add_newline();
class_desc->add_newline();
@@ -817,6 +873,13 @@ void EditorHelp::_update_doc() {
class_desc->pop();
}
+ if (cd.properties[i].is_deprecated) {
+ DEPRECATED_DOC_TAG;
+ }
+ if (cd.properties[i].is_experimental) {
+ EXPERIMENTAL_DOC_TAG;
+ }
+
class_desc->pop();
class_desc->pop(); // cell
@@ -1066,6 +1129,14 @@ void EditorHelp::_update_doc() {
class_desc->push_color(symbol_color);
class_desc->add_text(")");
+
+ if (cd.signals[i].is_deprecated) {
+ DEPRECATED_DOC_TAG;
+ }
+ if (cd.signals[i].is_experimental) {
+ EXPERIMENTAL_DOC_TAG;
+ }
+
class_desc->pop();
class_desc->pop(); // end monofont
if (!cd.signals[i].description.strip_edges().is_empty()) {
@@ -1187,6 +1258,14 @@ void EditorHelp::_update_doc() {
class_desc->pop();
class_desc->pop();
+ if (enum_list[i].is_deprecated) {
+ DEPRECATED_DOC_TAG;
+ }
+
+ if (enum_list[i].is_experimental) {
+ EXPERIMENTAL_DOC_TAG;
+ }
+
class_desc->add_newline();
if (!enum_list[i].description.strip_edges().is_empty()) {
@@ -1258,6 +1337,14 @@ void EditorHelp::_update_doc() {
class_desc->pop();
+ if (constants[i].is_deprecated) {
+ DEPRECATED_DOC_TAG;
+ }
+
+ if (constants[i].is_experimental) {
+ EXPERIMENTAL_DOC_TAG;
+ }
+
class_desc->add_newline();
if (!constants[i].description.strip_edges().is_empty()) {
@@ -1438,6 +1525,13 @@ void EditorHelp::_update_doc() {
class_desc->pop(); // color
}
+ if (cd.properties[i].is_deprecated) {
+ DEPRECATED_DOC_TAG;
+ }
+ if (cd.properties[i].is_experimental) {
+ EXPERIMENTAL_DOC_TAG;
+ }
+
if (cd.is_script_doc && (!cd.properties[i].setter.is_empty() || !cd.properties[i].getter.is_empty())) {
class_desc->push_color(symbol_color);
class_desc->add_text(" [" + TTR("property:") + " ");
diff --git a/editor/editor_help_search.cpp b/editor/editor_help_search.cpp
index af0cff9ad6..7e7d7ca418 100644
--- a/editor/editor_help_search.cpp
+++ b/editor/editor_help_search.cpp
@@ -326,6 +326,7 @@ bool EditorHelpSearch::Runner::_phase_match_classes_init() {
bool EditorHelpSearch::Runner::_phase_match_classes() {
DocData::ClassDoc &class_doc = iterator_doc->value;
if (class_doc.name.is_empty()) {
+ ++iterator_doc;
return false;
}
if (!_is_class_disabled_by_feature_profile(class_doc.name)) {
@@ -432,7 +433,7 @@ bool EditorHelpSearch::Runner::_phase_class_items_init() {
bool EditorHelpSearch::Runner::_phase_class_items() {
if (!iterator_match) {
- return false;
+ return true;
}
ClassMatch &match = iterator_match->value;
@@ -459,10 +460,8 @@ bool EditorHelpSearch::Runner::_phase_member_items_init() {
bool EditorHelpSearch::Runner::_phase_member_items() {
ClassMatch &match = iterator_match->value;
- if (!match.doc) {
- return false;
- }
- if (match.doc->name.is_empty()) {
+ if (!match.doc || match.doc->name.is_empty()) {
+ ++iterator_match;
return false;
}
@@ -599,6 +598,14 @@ TreeItem *EditorHelpSearch::Runner::_create_class_item(TreeItem *p_parent, const
item->set_custom_color(1, disabled_color);
}
+ if (p_doc->is_deprecated) {
+ Ref<Texture2D> error_icon = ui_service->get_theme_icon("StatusError", SNAME("EditorIcons"));
+ item->add_button(0, error_icon, 0, false, TTR("This class is marked as deprecated."));
+ } else if (p_doc->is_experimental) {
+ Ref<Texture2D> warning_icon = ui_service->get_theme_icon("NodeWarning", SNAME("EditorIcons"));
+ item->add_button(0, warning_icon, 0, false, TTR("This class is marked as experimental."));
+ }
+
_match_item(item, p_doc->name);
return item;
@@ -606,37 +613,37 @@ TreeItem *EditorHelpSearch::Runner::_create_class_item(TreeItem *p_parent, const
TreeItem *EditorHelpSearch::Runner::_create_method_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const String &p_text, const DocData::MethodDoc *p_doc) {
String tooltip = _build_method_tooltip(p_class_doc, p_doc);
- return _create_member_item(p_parent, p_class_doc->name, "MemberMethod", p_doc->name, p_text, TTRC("Method"), "method", tooltip);
+ return _create_member_item(p_parent, p_class_doc->name, "MemberMethod", p_doc->name, p_text, TTRC("Method"), "method", tooltip, p_doc->is_deprecated, p_doc->is_experimental);
}
TreeItem *EditorHelpSearch::Runner::_create_signal_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::MethodDoc *p_doc) {
String tooltip = _build_method_tooltip(p_class_doc, p_doc);
- return _create_member_item(p_parent, p_class_doc->name, "MemberSignal", p_doc->name, p_doc->name, TTRC("Signal"), "signal", tooltip);
+ return _create_member_item(p_parent, p_class_doc->name, "MemberSignal", p_doc->name, p_doc->name, TTRC("Signal"), "signal", tooltip, p_doc->is_deprecated, p_doc->is_experimental);
}
TreeItem *EditorHelpSearch::Runner::_create_annotation_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const String &p_text, const DocData::MethodDoc *p_doc) {
String tooltip = _build_method_tooltip(p_class_doc, p_doc);
- return _create_member_item(p_parent, p_class_doc->name, "MemberAnnotation", p_doc->name, p_text, TTRC("Annotation"), "annotation", tooltip);
+ return _create_member_item(p_parent, p_class_doc->name, "MemberAnnotation", p_doc->name, p_text, TTRC("Annotation"), "annotation", tooltip, p_doc->is_deprecated, p_doc->is_experimental);
}
TreeItem *EditorHelpSearch::Runner::_create_constant_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ConstantDoc *p_doc) {
String tooltip = p_class_doc->name + "." + p_doc->name;
- return _create_member_item(p_parent, p_class_doc->name, "MemberConstant", p_doc->name, p_doc->name, TTRC("Constant"), "constant", tooltip);
+ return _create_member_item(p_parent, p_class_doc->name, "MemberConstant", p_doc->name, p_doc->name, TTRC("Constant"), "constant", tooltip, p_doc->is_deprecated, p_doc->is_experimental);
}
TreeItem *EditorHelpSearch::Runner::_create_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::PropertyDoc *p_doc) {
String tooltip = p_doc->type + " " + p_class_doc->name + "." + p_doc->name;
tooltip += "\n " + p_class_doc->name + "." + p_doc->setter + "(value) setter";
tooltip += "\n " + p_class_doc->name + "." + p_doc->getter + "() getter";
- return _create_member_item(p_parent, p_class_doc->name, "MemberProperty", p_doc->name, p_doc->name, TTRC("Property"), "property", tooltip);
+ return _create_member_item(p_parent, p_class_doc->name, "MemberProperty", p_doc->name, p_doc->name, TTRC("Property"), "property", tooltip, p_doc->is_deprecated, p_doc->is_experimental);
}
TreeItem *EditorHelpSearch::Runner::_create_theme_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ThemeItemDoc *p_doc) {
String tooltip = p_doc->type + " " + p_class_doc->name + "." + p_doc->name;
- return _create_member_item(p_parent, p_class_doc->name, "MemberTheme", p_doc->name, p_doc->name, TTRC("Theme Property"), "theme_item", tooltip);
+ return _create_member_item(p_parent, p_class_doc->name, "MemberTheme", p_doc->name, p_doc->name, TTRC("Theme Property"), "theme_item", tooltip, false, false);
}
-TreeItem *EditorHelpSearch::Runner::_create_member_item(TreeItem *p_parent, const String &p_class_name, const String &p_icon, const String &p_name, const String &p_text, const String &p_type, const String &p_metatype, const String &p_tooltip) {
+TreeItem *EditorHelpSearch::Runner::_create_member_item(TreeItem *p_parent, const String &p_class_name, const String &p_icon, const String &p_name, const String &p_text, const String &p_type, const String &p_metatype, const String &p_tooltip, bool is_deprecated, bool is_experimental) {
Ref<Texture2D> icon;
String text;
if (search_flags & SEARCH_SHOW_HIERARCHY) {
@@ -655,6 +662,14 @@ TreeItem *EditorHelpSearch::Runner::_create_member_item(TreeItem *p_parent, cons
item->set_tooltip_text(1, p_tooltip);
item->set_metadata(0, "class_" + p_metatype + ":" + p_class_name + ":" + p_name);
+ if (is_deprecated) {
+ Ref<Texture2D> error_icon = ui_service->get_theme_icon("StatusError", SNAME("EditorIcons"));
+ item->add_button(0, error_icon, 0, false, TTR("This member is marked as deprecated."));
+ } else if (is_experimental) {
+ Ref<Texture2D> warning_icon = ui_service->get_theme_icon("NodeWarning", SNAME("EditorIcons"));
+ item->add_button(0, warning_icon, 0, false, TTR("This member is marked as experimental."));
+ }
+
_match_item(item, p_name);
return item;
diff --git a/editor/editor_help_search.h b/editor/editor_help_search.h
index 26abaec6e6..efd8645cd7 100644
--- a/editor/editor_help_search.h
+++ b/editor/editor_help_search.h
@@ -155,7 +155,7 @@ class EditorHelpSearch::Runner : public RefCounted {
TreeItem *_create_constant_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ConstantDoc *p_doc);
TreeItem *_create_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::PropertyDoc *p_doc);
TreeItem *_create_theme_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ThemeItemDoc *p_doc);
- TreeItem *_create_member_item(TreeItem *p_parent, const String &p_class_name, const String &p_icon, const String &p_name, const String &p_text, const String &p_type, const String &p_metatype, const String &p_tooltip);
+ TreeItem *_create_member_item(TreeItem *p_parent, const String &p_class_name, const String &p_icon, const String &p_name, const String &p_text, const String &p_type, const String &p_metatype, const String &p_tooltip, bool is_deprecated, bool is_experimental);
public:
bool work(uint64_t slot = 100000);
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index b00127f5b1..9dc4c2c953 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -7601,8 +7601,8 @@ EditorPlugin::AfterGUIInput EditorPluginList::forward_spatial_gui_input(Camera3D
if (current_after == EditorPlugin::AFTER_GUI_INPUT_STOP) {
after = EditorPlugin::AFTER_GUI_INPUT_STOP;
}
- if (after != EditorPlugin::AFTER_GUI_INPUT_STOP && current_after == EditorPlugin::AFTER_GUI_INPUT_DESELECT) {
- after = EditorPlugin::AFTER_GUI_INPUT_DESELECT;
+ if (after != EditorPlugin::AFTER_GUI_INPUT_STOP && current_after == EditorPlugin::AFTER_GUI_INPUT_CUSTOM) {
+ after = EditorPlugin::AFTER_GUI_INPUT_CUSTOM;
}
}
diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp
index f3ac8f4ba0..981dad2d2a 100644
--- a/editor/editor_plugin.cpp
+++ b/editor/editor_plugin.cpp
@@ -972,6 +972,10 @@ void EditorPlugin::_bind_methods() {
BIND_ENUM_CONSTANT(DOCK_SLOT_RIGHT_UR);
BIND_ENUM_CONSTANT(DOCK_SLOT_RIGHT_BR);
BIND_ENUM_CONSTANT(DOCK_SLOT_MAX);
+
+ BIND_ENUM_CONSTANT(AFTER_GUI_INPUT_PASS);
+ BIND_ENUM_CONSTANT(AFTER_GUI_INPUT_STOP);
+ BIND_ENUM_CONSTANT(AFTER_GUI_INPUT_CUSTOM);
}
Ref<EditorUndoRedoManager> EditorPlugin::get_undo_redo() {
diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h
index fe01524bea..a048b174e4 100644
--- a/editor/editor_plugin.h
+++ b/editor/editor_plugin.h
@@ -204,7 +204,7 @@ public:
enum AfterGUIInput {
AFTER_GUI_INPUT_PASS,
AFTER_GUI_INPUT_STOP,
- AFTER_GUI_INPUT_DESELECT
+ AFTER_GUI_INPUT_CUSTOM
};
//TODO: send a resource for editing to the editor node?
@@ -312,6 +312,7 @@ public:
VARIANT_ENUM_CAST(EditorPlugin::CustomControlContainer);
VARIANT_ENUM_CAST(EditorPlugin::DockSlot);
+VARIANT_ENUM_CAST(EditorPlugin::AfterGUIInput);
typedef EditorPlugin *(*EditorPluginCreateFunc)();
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 64ddecc588..edbd2dd62f 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -191,25 +191,6 @@ static Ref<StyleBoxLine> make_line_stylebox(Color p_color, int p_thickness = 1,
return style;
}
-static Ref<Texture2D> flip_icon(Ref<Texture2D> p_texture, bool p_flip_y = false, bool p_flip_x = false) {
- if (!p_flip_y && !p_flip_x) {
- return p_texture;
- }
-
- Ref<Image> img = p_texture->get_image();
- ERR_FAIL_NULL_V(img, Ref<Texture2D>());
- img = img->duplicate();
-
- if (p_flip_y) {
- img->flip_y();
- }
- if (p_flip_x) {
- img->flip_x();
- }
-
- return ImageTexture::create_from_image(img);
-}
-
#ifdef MODULE_SVG_ENABLED
// See also `generate_icon()` in `scene/resources/default_theme.cpp`.
static Ref<ImageTexture> editor_generate_icon(int p_index, float p_scale, float p_saturation, const HashMap<Color, Color> &p_convert_colors = HashMap<Color, Color>()) {
@@ -1567,14 +1548,13 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("camera", "GraphEditMinimap", style_minimap_camera);
theme->set_stylebox("node", "GraphEditMinimap", style_minimap_node);
- Ref<Texture2D> minimap_resizer_icon = theme->get_icon(SNAME("GuiResizer"), SNAME("EditorIcons"));
Color minimap_resizer_color;
if (dark_theme) {
minimap_resizer_color = Color(1, 1, 1, 0.65);
} else {
minimap_resizer_color = Color(0, 0, 0, 0.65);
}
- theme->set_icon("resizer", "GraphEditMinimap", flip_icon(minimap_resizer_icon, true, true));
+ theme->set_icon("resizer", "GraphEditMinimap", theme->get_icon(SNAME("GuiResizerTopLeft"), SNAME("EditorIcons")));
theme->set_color("resizer_color", "GraphEditMinimap", minimap_resizer_color);
// GraphNode
diff --git a/editor/icons/GuiResizerTopLeft.svg b/editor/icons/GuiResizerTopLeft.svg
new file mode 100644
index 0000000000..a67c2c0722
--- /dev/null
+++ b/editor/icons/GuiResizerTopLeft.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m4 12c.55228 0 1-.44772 1-1v-6h6c.55228 0 1-.44772 1-1s-.44772-1-1-1h-7c-.55226.000055-.99994.44774-1 1v7c0 .55228.44772 1 1 1z" fill="#fff" fill-opacity=".58824"/></svg>
diff --git a/editor/icons/PreviewEnvironment.svg b/editor/icons/PreviewEnvironment.svg
new file mode 100644
index 0000000000..e0b0257daf
--- /dev/null
+++ b/editor/icons/PreviewEnvironment.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="m8 1a7 7 0 0 0 -7 7 7 7 0 0 0 7 7 7 7 0 0 0 7-7 7 7 0 0 0 -7-7zm-1.7305 2.3125c-.83125 1.5372-1.2685 3.1037-1.2695 4.6816-.64057-.11251-1.3005-.27158-1.9766-.47266a5 5 0 0 1 3.2461-4.209zm3.4629.00391a5 5 0 0 1 3.2383 4.1875c-.65187.17448-1.3077.32867-1.9727.44922-.0084-1.5627-.44294-3.1141-1.2656-4.6367zm-1.7324.0078088c1.0126 1.593 1.5 3.1425 1.5 4.6758 0 .054042-.00662.10803-.00781.16211-.96392.096801-1.9566.1103-2.9844.027344-.00163-.063192-.00781-.12632-.00781-.18945 0-1.5333.48744-3.0828 1.5-4.6758zm4.8789 5.7578a5 5 0 0 1 -3.1484 3.6055002c.57106-1.0564.95277-2.1268 1.1367-3.2051002.68204-.10905 1.3556-.23789 2.0117-.40039zm-9.7461.033203c.68377.18153 1.3555.33345 2.0098.43164.18781 1.0551002.56647 2.1026002 1.125 3.1367002a5 5 0 0 1 -3.1348-3.5684002zm6.168.55469c-.22615.9886602-.65424 1.9884002-1.3008 3.0059002-.63811-1.0042-1.0645-1.9908-1.293-2.9668002.89027.054126 1.7517.029377 2.5938-.039062z"/><path d="m8 1v2.3242c1.0126 1.593 1.5 3.1425 1.5 4.6758 0 .054042-.00662.10803-.00781.16211-.4894.049148-.98713.077552-1.4922.082031v1.4922c.43915-.0076.87287-.031628 1.3008-.066406-.22615.98866-.65424 1.9884-1.3008 3.0059v2.3242a7 7 0 0 0 7.000001-7 7 7 0 0 0 -7.000001-7zm1.7324 2.3164a5 5 0 0 1 3.2383 4.1875c-.65187.17448-1.3077.32867-1.9727.44922-.00845-1.5627-.44294-3.1141-1.2656-4.6367zm3.1465 5.7656a5 5 0 0 1 -3.1484 3.6055c.57106-1.0564.95277-2.1268 1.1367-3.2051.68204-.10905 1.3556-.23789 2.0117-.40039z"/></g></svg>
diff --git a/editor/icons/PreviewSun.svg b/editor/icons/PreviewSun.svg
new file mode 100644
index 0000000000..a8c652be65
--- /dev/null
+++ b/editor/icons/PreviewSun.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1v3h2v-3zm-2.5352 2.0508-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm7.0703 0-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm-3.5352 1.9492c-1.6569 0-3 1.3432-3 3s1.3431 3 3 3 3-1.3432 3-3-1.3431-3-3-3zm-7 2v2h3v-2zm11 0v2h3v-2zm-7.5352 3.1211-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm7.0703 0-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm-4.5352 1.8789v3h2v-3z" fill="#e0e0e0"/></svg>
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp
index f1e6c70549..f4b8646e18 100644
--- a/editor/plugins/animation_blend_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp
@@ -907,6 +907,10 @@ void AnimationNodeBlendTreeEditor::_bind_methods() {
AnimationNodeBlendTreeEditor *AnimationNodeBlendTreeEditor::singleton = nullptr;
void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<AnimationNode> p_node) {
+ if (blend_tree.is_null()) {
+ return;
+ }
+
String prev_name = blend_tree->get_node_name(p_node);
ERR_FAIL_COND(prev_name.is_empty());
GraphNode *gn = Object::cast_to<GraphNode>(graph->get_node(prev_name));
diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index 436113093f..3c9486cdaa 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -648,7 +648,7 @@ void EditorAssetLibrary::shortcut_input(const Ref<InputEvent> &p_event) {
const Ref<InputEventKey> key = p_event;
if (key.is_valid() && key->is_pressed()) {
- if (key->is_match(InputEventKey::create_reference(KeyModifierMask::CMD_OR_CTRL | Key::F))) {
+ if (key->is_match(InputEventKey::create_reference(KeyModifierMask::CMD_OR_CTRL | Key::F)) && is_visible_in_tree()) {
filter->grab_focus();
filter->select_all();
accept_event();
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 6c3ce20d7a..c8b49678d2 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -1360,8 +1360,8 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
if (discard == EditorPlugin::AFTER_GUI_INPUT_STOP) {
return;
}
- if (discard == EditorPlugin::AFTER_GUI_INPUT_DESELECT) {
- after = EditorPlugin::AFTER_GUI_INPUT_DESELECT;
+ if (discard == EditorPlugin::AFTER_GUI_INPUT_CUSTOM) {
+ after = EditorPlugin::AFTER_GUI_INPUT_CUSTOM;
}
}
}
@@ -1373,8 +1373,8 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
if (discard == EditorPlugin::AFTER_GUI_INPUT_STOP) {
return;
}
- if (discard == EditorPlugin::AFTER_GUI_INPUT_DESELECT) {
- after = EditorPlugin::AFTER_GUI_INPUT_DESELECT;
+ if (discard == EditorPlugin::AFTER_GUI_INPUT_CUSTOM) {
+ after = EditorPlugin::AFTER_GUI_INPUT_CUSTOM;
}
}
}
@@ -1601,7 +1601,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
break;
}
- if (after != EditorPlugin::AFTER_GUI_INPUT_DESELECT) {
+ if (after != EditorPlugin::AFTER_GUI_INPUT_CUSTOM) {
//clicking is always deferred to either move or release
clicked = _select_ray(b->get_position());
selection_in_progress = true;
@@ -1622,7 +1622,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
break;
}
- if (after != EditorPlugin::AFTER_GUI_INPUT_DESELECT) {
+ if (after != EditorPlugin::AFTER_GUI_INPUT_CUSTOM) {
selection_in_progress = false;
if (clicked.is_valid()) {
@@ -5516,8 +5516,8 @@ Dictionary Node3DEditor::get_state() const {
pd["sun_color"] = sun_color->get_pick_color();
pd["sun_energy"] = sun_energy->get_value();
- pd["sun_disabled"] = sun_button->is_pressed();
- pd["environ_disabled"] = environ_button->is_pressed();
+ pd["sun_enabled"] = sun_button->is_pressed();
+ pd["environ_enabled"] = environ_button->is_pressed();
d["preview_sun_env"] = pd;
}
@@ -5648,8 +5648,8 @@ void Node3DEditor::set_state(const Dictionary &p_state) {
sun_color->set_pick_color(pd["sun_color"]);
sun_energy->set_value(pd["sun_energy"]);
- sun_button->set_pressed(pd["sun_disabled"]);
- environ_button->set_pressed(pd["environ_disabled"]);
+ sun_button->set_pressed(pd["sun_enabled"]);
+ environ_button->set_pressed(pd["environ_enabled"]);
sun_environ_updating = false;
@@ -5657,8 +5657,8 @@ void Node3DEditor::set_state(const Dictionary &p_state) {
_update_preview_environment();
} else {
_load_default_preview_settings();
- sun_button->set_pressed(false);
- environ_button->set_pressed(false);
+ sun_button->set_pressed(true);
+ environ_button->set_pressed(true);
_preview_settings_changed();
_update_preview_environment();
}
@@ -7159,8 +7159,8 @@ void Node3DEditor::_update_theme() {
view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), get_theme_icon(SNAME("Panels3Alt"), SNAME("EditorIcons")));
view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), get_theme_icon(SNAME("Panels4"), SNAME("EditorIcons")));
- sun_button->set_icon(get_theme_icon(SNAME("DirectionalLight3D"), SNAME("EditorIcons")));
- environ_button->set_icon(get_theme_icon(SNAME("WorldEnvironment"), SNAME("EditorIcons")));
+ sun_button->set_icon(get_theme_icon(SNAME("PreviewSun"), SNAME("EditorIcons")));
+ environ_button->set_icon(get_theme_icon(SNAME("PreviewEnvironment"), SNAME("EditorIcons")));
sun_environ_settings->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons")));
sun_title->add_theme_font_override("font", get_theme_font(SNAME("title_font"), SNAME("Window")));
@@ -7636,7 +7636,7 @@ void Node3DEditor::_load_default_preview_settings() {
}
void Node3DEditor::_update_preview_environment() {
- bool disable_light = directional_light_count > 0 || sun_button->is_pressed();
+ bool disable_light = directional_light_count > 0 || !sun_button->is_pressed();
sun_button->set_disabled(directional_light_count > 0);
@@ -7664,7 +7664,7 @@ void Node3DEditor::_update_preview_environment() {
sun_angle_altitude->set_value(-Math::rad_to_deg(sun_rotation.x));
sun_angle_azimuth->set_value(180.0 - Math::rad_to_deg(sun_rotation.y));
- bool disable_env = world_env_count > 0 || environ_button->is_pressed();
+ bool disable_env = world_env_count > 0 || !environ_button->is_pressed();
environ_button->set_disabled(world_env_count > 0);
@@ -7855,6 +7855,8 @@ Node3DEditor::Node3DEditor() {
sun_button->set_flat(true);
sun_button->connect("pressed", callable_mp(this, &Node3DEditor::_update_preview_environment), CONNECT_DEFERRED);
sun_button->set_disabled(true);
+ // Preview is enabled by default - ensure this applies on editor startup when there is no state yet.
+ sun_button->set_pressed(true);
main_menu_hbox->add_child(sun_button);
@@ -7864,6 +7866,8 @@ Node3DEditor::Node3DEditor() {
environ_button->set_flat(true);
environ_button->connect("pressed", callable_mp(this, &Node3DEditor::_update_preview_environment), CONNECT_DEFERRED);
environ_button->set_disabled(true);
+ // Preview is enabled by default - ensure this applies on editor startup when there is no state yet.
+ environ_button->set_pressed(true);
main_menu_hbox->add_child(environ_button);
diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h
index afd38ef71a..f48b2fc70e 100644
--- a/editor/plugins/shader_editor_plugin.h
+++ b/editor/plugins/shader_editor_plugin.h
@@ -115,8 +115,8 @@ public:
ShaderTextEditor();
};
-class ShaderEditor : public PanelContainer {
- GDCLASS(ShaderEditor, PanelContainer);
+class ShaderEditor : public MarginContainer {
+ GDCLASS(ShaderEditor, MarginContainer);
enum {
EDIT_UNDO,
diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp
index 3c75ac6ca6..2478ac9514 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_3d_editor_plugin.cpp
@@ -1143,7 +1143,7 @@ EditorPlugin::AfterGUIInput Skeleton3DEditorPlugin::forward_spatial_gui_input(Ca
se->update_bone_original();
}
}
- return EditorPlugin::AFTER_GUI_INPUT_DESELECT;
+ return EditorPlugin::AFTER_GUI_INPUT_CUSTOM;
}
return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp
index a91f22ccd0..79230891f1 100644
--- a/editor/plugins/tiles/tile_map_editor.cpp
+++ b/editor/plugins/tiles/tile_map_editor.cpp
@@ -3677,6 +3677,7 @@ void TileMapEditor::_update_layers_selection() {
tile_map_layer = -1;
}
tile_map->set_selected_layer(toggle_highlight_selected_layer_button->is_pressed() ? tile_map_layer : -1);
+ tileset_changed_needs_update = false; // Update is not needed here and actually causes problems.
layers_selection_button->clear();
if (tile_map->get_layers_count() > 0) {
diff --git a/editor/plugins/version_control_editor_plugin.cpp b/editor/plugins/version_control_editor_plugin.cpp
index 761140b2d5..336ce9e4c8 100644
--- a/editor/plugins/version_control_editor_plugin.cpp
+++ b/editor/plugins/version_control_editor_plugin.cpp
@@ -1124,6 +1124,8 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() {
set_up_password->connect(SNAME("text_changed"), callable_mp(this, &VersionControlEditorPlugin::_update_set_up_warning));
set_up_password_input->add_child(set_up_password);
+ const String home_dir = OS::get_singleton()->has_environment("HOME") ? OS::get_singleton()->get_environment("HOME") : OS::get_singleton()->get_system_dir(OS::SYSTEM_DIR_DOCUMENTS);
+
HBoxContainer *set_up_ssh_public_key_input = memnew(HBoxContainer);
set_up_ssh_public_key_input->set_h_size_flags(Control::SIZE_EXPAND_FILL);
set_up_settings_vbc->add_child(set_up_ssh_public_key_input);
@@ -1147,10 +1149,7 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() {
set_up_ssh_public_key_file_dialog->set_access(FileDialog::ACCESS_FILESYSTEM);
set_up_ssh_public_key_file_dialog->set_file_mode(FileDialog::FILE_MODE_OPEN_FILE);
set_up_ssh_public_key_file_dialog->set_show_hidden_files(true);
- // TODO: Make this start at the user's home folder
- Ref<DirAccess> d = DirAccess::open(OS::get_singleton()->get_system_dir(OS::SYSTEM_DIR_DOCUMENTS));
- d->change_dir("../");
- set_up_ssh_public_key_file_dialog->set_current_dir(d->get_current_dir());
+ set_up_ssh_public_key_file_dialog->set_current_dir(home_dir);
set_up_ssh_public_key_file_dialog->connect(SNAME("file_selected"), callable_mp(this, &VersionControlEditorPlugin::_ssh_public_key_selected));
set_up_ssh_public_key_input_hbc->add_child(set_up_ssh_public_key_file_dialog);
@@ -1183,8 +1182,7 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() {
set_up_ssh_private_key_file_dialog->set_access(FileDialog::ACCESS_FILESYSTEM);
set_up_ssh_private_key_file_dialog->set_file_mode(FileDialog::FILE_MODE_OPEN_FILE);
set_up_ssh_private_key_file_dialog->set_show_hidden_files(true);
- // TODO: Make this start at the user's home folder
- set_up_ssh_private_key_file_dialog->set_current_dir(d->get_current_dir());
+ set_up_ssh_private_key_file_dialog->set_current_dir(home_dir);
set_up_ssh_private_key_file_dialog->connect("file_selected", callable_mp(this, &VersionControlEditorPlugin::_ssh_private_key_selected));
set_up_ssh_private_key_input_hbc->add_child(set_up_ssh_private_key_file_dialog);
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 2af8da02a3..ee8148f00a 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -5198,9 +5198,9 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("Tangent", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "tangent", "TANGENT"), { "tangent" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("Vertex", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "vertex", "VERTEX"), { "vertex" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("View", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "view", "VIEW"), { "view" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("ViewIndex", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "view_index", "VIEW_INDEX"), { "view_index" }, VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("ViewMonoLeft", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "view_mono_left", "VIEW_MONO_LEFT"), { "view_mono_left" }, VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("ViewRight", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "view_right", "VIEW_RIGHT"), { "view_right" }, VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("ViewIndex", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "view_index", "VIEW_INDEX"), { "view_index" }, VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("ViewMonoLeft", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "view_mono_left", "VIEW_MONO_LEFT"), { "view_mono_left" }, VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("ViewRight", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "view_right", "VIEW_RIGHT"), { "view_right" }, VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("NodePositionWorld", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "node_position_world", "NODE_POSITION_WORLD"), { "node_position_world" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("CameraPositionWorld", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "camera_position_world", "CAMERA_POSITION_WORLD"), { "camera_position_world" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("CameraDirectionWorld", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "camera_direction_world", "CAMERA_DIRECTION_WORLD"), { "camera_direction_world" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index 270e04050e..f11874a994 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -59,6 +59,8 @@
#include "servers/navigation_server_3d.h"
#include "servers/physics_server_2d.h"
+constexpr int GODOT4_CONFIG_VERSION = 5;
+
class ProjectDialog : public ConfirmationDialog {
GDCLASS(ProjectDialog, ConfirmationDialog);
@@ -1065,6 +1067,7 @@ public:
int refresh_project(const String &dir_path);
void add_project(const String &dir_path, bool favorite);
void save_config();
+ void set_project_version(const String &p_project_path, int version);
private:
static void _bind_methods();
@@ -1673,6 +1676,15 @@ void ProjectList::save_config() {
_config.save(_config_path);
}
+void ProjectList::set_project_version(const String &p_project_path, int p_version) {
+ for (ProjectList::Item &E : _projects) {
+ if (E.path == p_project_path) {
+ E.version = p_version;
+ break;
+ }
+ }
+}
+
int ProjectList::get_project_count() const {
return _projects.size();
}
@@ -2170,6 +2182,9 @@ void ProjectManager::_open_selected_projects_ask() {
Label *ask_update_label = ask_update_settings->get_label();
ask_update_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_LEFT); // Reset in case of previous center align.
+ full_convert_button->hide();
+
+ ask_update_settings->get_ok_button()->set_text("OK");
// Check if the config_version property was empty or 0
if (config_version == 0) {
@@ -2179,7 +2194,11 @@ void ProjectManager::_open_selected_projects_ask() {
}
// Check if we need to convert project settings from an earlier engine version
if (config_version < ProjectSettings::CONFIG_VERSION) {
- ask_update_settings->set_text(vformat(TTR("The following project settings file was generated by an older engine version, and needs to be converted for this version:\n\n%s\n\nDo you want to convert it?\nWarning: You won't be able to open the project with previous versions of the engine anymore."), conf));
+ if (config_version == GODOT4_CONFIG_VERSION - 1 && ProjectSettings::CONFIG_VERSION == GODOT4_CONFIG_VERSION) { // Conversion from Godot 3 to 4.
+ full_convert_button->show();
+ }
+ ask_update_settings->set_text(vformat(TTR("The following project settings file was generated by an older engine version, and needs to be converted for this version:\n\n%s\n\nDo you want to convert it? You can also convert the entire project (recommended if you are upgrading).\nWarning: You won't be able to open the project with previous versions of the engine anymore."), conf));
+ ask_update_settings->get_ok_button()->set_text(TTR("Convert project.godot"));
ask_update_settings->popup_centered();
return;
}
@@ -2223,6 +2242,32 @@ void ProjectManager::_open_selected_projects_ask() {
_open_selected_projects();
}
+void ProjectManager::_perform_full_project_conversion() {
+ Vector<ProjectList::Item> selected_list = _project_list->get_selected_projects();
+ if (selected_list.is_empty()) {
+ return;
+ }
+
+ const String &path = selected_list[0].path;
+
+ print_line("Converting project: " + path);
+
+ Ref<ConfigFile> cf;
+ cf.instantiate();
+ cf->load(path.path_join("project.godot"));
+ cf->set_value("", "config_version", GODOT4_CONFIG_VERSION);
+ cf->save(path.path_join("project.godot"));
+ _project_list->set_project_version(path, GODOT4_CONFIG_VERSION);
+
+ List<String> args;
+ args.push_back("--path");
+ args.push_back(path);
+ args.push_back("--convert-3to4");
+
+ Error err = OS::get_singleton()->create_instance(args);
+ ERR_FAIL_COND(err);
+}
+
void ProjectManager::_run_project_confirm() {
Vector<ProjectList::Item> selected_list = _project_list->get_selected_projects();
@@ -2724,9 +2769,10 @@ ProjectManager::ProjectManager() {
settings_hb->add_child(h_spacer);
language_btn = memnew(OptionButton);
- language_btn->set_flat(true);
language_btn->set_icon(get_theme_icon(SNAME("Environment"), SNAME("EditorIcons")));
language_btn->set_focus_mode(Control::FOCUS_NONE);
+ language_btn->set_fit_to_longest_item(false);
+ language_btn->set_flat(true);
language_btn->connect("item_selected", callable_mp(this, &ProjectManager::_language_selected));
#ifdef ANDROID_ENABLED
// The language selection dropdown doesn't work on Android (as the setting isn't saved), see GH-60353.
@@ -2824,8 +2870,20 @@ ProjectManager::ProjectManager() {
ask_update_settings = memnew(ConfirmationDialog);
ask_update_settings->get_ok_button()->connect("pressed", callable_mp(this, &ProjectManager::_confirm_update_settings));
+ full_convert_button = ask_update_settings->add_button("Perform Full Project Conversion", false);
add_child(ask_update_settings);
+ ask_full_convert_dialog = memnew(ConfirmationDialog);
+ ask_full_convert_dialog->set_autowrap(true);
+ ask_full_convert_dialog->set_text(TTR(R"(This option will perform full project conversion, updating scenes and scripts from Godot 3.x to work in Godot 4.0. Note that this is a best-effort conversion, i.e. it makes upgrading the project easier, but it will not open out-of-the-box and will still require manual adjustments.
+
+IMPORTANT: Make sure to backup your project before converting, as this operation makes it impossible to open in older versions of Godot.)"));
+ ask_full_convert_dialog->connect("confirmed", callable_mp(this, &ProjectManager::_perform_full_project_conversion));
+ add_child(ask_full_convert_dialog);
+
+ full_convert_button->connect("pressed", callable_mp((Window *)ask_update_settings, &ConfirmationDialog::hide));
+ full_convert_button->connect("pressed", callable_mp((Window *)ask_full_convert_dialog, &ConfirmationDialog::popup_centered_ratio).bind(0.2));
+
npdialog = memnew(ProjectDialog);
npdialog->connect("projects_updated", callable_mp(this, &ProjectManager::_on_projects_updated));
npdialog->connect("project_created", callable_mp(this, &ProjectManager::_on_project_created));
diff --git a/editor/project_manager.h b/editor/project_manager.h
index 10bf25c048..0831a63e68 100644
--- a/editor/project_manager.h
+++ b/editor/project_manager.h
@@ -87,6 +87,7 @@ class ProjectManager : public Control {
ConfirmationDialog *multi_open_ask = nullptr;
ConfirmationDialog *multi_run_ask = nullptr;
ConfirmationDialog *multi_scan_ask = nullptr;
+ ConfirmationDialog *ask_full_convert_dialog = nullptr;
ConfirmationDialog *ask_update_settings = nullptr;
ConfirmationDialog *open_templates = nullptr;
EditorAbout *about = nullptr;
@@ -97,6 +98,7 @@ class ProjectManager : public Control {
AcceptDialog *dialog_error = nullptr;
ProjectDialog *npdialog = nullptr;
+ Button *full_convert_button = nullptr;
OptionButton *language_btn = nullptr;
LinkButton *version_btn = nullptr;
@@ -106,6 +108,7 @@ class ProjectManager : public Control {
void _run_project_confirm();
void _open_selected_projects();
void _open_selected_projects_ask();
+ void _perform_full_project_conversion();
void _import_project();
void _new_project();
void _rename_project();
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index 4a38caea52..4f325fcf52 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -35,14 +35,14 @@
<description>
Asserts that the [code]condition[/code] is [code]true[/code]. If the [code]condition[/code] is [code]false[/code], an error is generated. When running from the editor, the running project will also be paused until you resume it. This can be used as a stronger form of [method @GlobalScope.push_error] for reporting errors to project developers or add-on users.
[b]Note:[/b] For performance reasons, the code inside [method assert] is only executed in debug builds or when running the project from the editor. Don't include code that has side effects in an [method assert] call. Otherwise, the project will behave differently when exported in release mode.
- The optional [code]message[/code] argument, if given, is shown in addition to the generic "Assertion failed" message. You can use this to provide additional details about why the assertion failed.
+ The optional [code]message[/code] argument, if given, is shown in addition to the generic "Assertion failed" message. It must be a static string, so format strings can't be used. You can use this to provide additional details about why the assertion failed.
[codeblock]
# Imagine we always want speed to be between 0 and 20.
var speed = -10
assert(speed &lt; 20) # True, the program will continue
assert(speed &gt;= 0) # False, the program will stop
assert(speed &gt;= 0 and speed &lt; 20) # You can also combine the two conditional statements in one check
- assert(speed &lt; 20, "speed = %f, but the speed limit is 20" % speed) # Show a message with clarifying details
+ assert(speed &lt; 20, "the speed limit is 20") # Show a message
[/codeblock]
</description>
</method>
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 10babad378..1cff2181af 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -1077,10 +1077,12 @@ Error GDScript::load_source_code(const String &p_path) {
}
source = s;
+ path = p_path;
#ifdef TOOLS_ENABLED
source_changed_cache = true;
-#endif
- path = p_path;
+ set_edited(false);
+ set_last_modified_time(FileAccess::get_modified_time(path));
+#endif // TOOLS_ENABLED
return OK;
}
diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp
index 48d5fbc569..c25f5b58d5 100644
--- a/modules/gdscript/gdscript_cache.cpp
+++ b/modules/gdscript/gdscript_cache.cpp
@@ -146,9 +146,7 @@ String GDScriptCache::get_source_code(const String &p_path) {
Vector<uint8_t> source_file;
Error err;
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err);
- if (err) {
- ERR_FAIL_COND_V(err, "");
- }
+ ERR_FAIL_COND_V(err, "");
uint64_t len = f->get_length();
source_file.resize(len + 1);
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index 6cb398b5f8..f5730e7137 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -2673,7 +2673,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
} else if (a.has("JOINTS_0") && a.has("JOINTS_1")) {
PackedInt32Array joints_0 = _decode_accessor_as_ints(state, a["JOINTS_0"], true);
PackedInt32Array joints_1 = _decode_accessor_as_ints(state, a["JOINTS_1"], true);
- ERR_FAIL_COND_V(joints_0.size() != joints_0.size(), ERR_INVALID_DATA);
+ ERR_FAIL_COND_V(joints_0.size() != joints_1.size(), ERR_INVALID_DATA);
int32_t weight_8_count = JOINT_GROUP_SIZE * 2;
Vector<int> joints;
joints.resize(vertex_num * weight_8_count);
diff --git a/modules/gltf/register_types.cpp b/modules/gltf/register_types.cpp
index dbbccc9bcc..6e7f7d6fed 100644
--- a/modules/gltf/register_types.cpp
+++ b/modules/gltf/register_types.cpp
@@ -142,6 +142,11 @@ void initialize_gltf_module(ModuleInitializationLevel p_level) {
GLOBAL_DEF_RST("filesystem/import/fbx/enabled", true);
GDREGISTER_CLASS(EditorSceneFormatImporterBlend);
GDREGISTER_CLASS(EditorSceneFormatImporterFBX);
+ // Can't (a priori) run external app on these platforms.
+ GLOBAL_DEF_RST("filesystem/import/blender/enabled.android", false);
+ GLOBAL_DEF_RST("filesystem/import/blender/enabled.web", false);
+ GLOBAL_DEF_RST("filesystem/import/fbx/enabled.android", false);
+ GLOBAL_DEF_RST("filesystem/import/fbx/enabled.web", false);
ClassDB::set_current_api(prev_api);
EditorNode::add_init_callback(_editor_init);
diff --git a/modules/multiplayer/multiplayer_spawner.cpp b/modules/multiplayer/multiplayer_spawner.cpp
index 6f60318b3b..9d0864e4d5 100644
--- a/modules/multiplayer/multiplayer_spawner.cpp
+++ b/modules/multiplayer/multiplayer_spawner.cpp
@@ -101,6 +101,7 @@ int MultiplayerSpawner::get_spawnable_scene_count() const {
return spawnable_scenes.size();
}
String MultiplayerSpawner::get_spawnable_scene(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, (int)spawnable_scenes.size(), "");
return spawnable_scenes[p_idx].path;
}
void MultiplayerSpawner::clear_spawnable_scenes() {
diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle
index fbd97fae0b..0346625e4b 100644
--- a/platform/android/java/app/config.gradle
+++ b/platform/android/java/app/config.gradle
@@ -127,16 +127,36 @@ ext.generateGodotLibraryVersion = { List<String> requiredKeys ->
if (requiredKeys.empty) {
libraryVersionName = map.values().join(".")
try {
+ if (map.containsKey("status")) {
+ int statusCode = 0
+ String statusValue = map["status"]
+ if (statusValue == null) {
+ statusCode = 0
+ } else if (statusValue.startsWith("alpha")) {
+ statusCode = 1
+ } else if (statusValue.startsWith("beta")) {
+ statusCode = 2
+ } else if (statusValue.startsWith("rc")) {
+ statusCode = 3
+ } else if (statusValue.startsWith("stable")) {
+ statusCode = 4
+ } else {
+ statusCode = 0
+ }
+
+ libraryVersionCode = statusCode
+ }
+
if (map.containsKey("patch")) {
- libraryVersionCode = Integer.parseInt(map["patch"])
+ libraryVersionCode += Integer.parseInt(map["patch"]) * 10
}
if (map.containsKey("minor")) {
- libraryVersionCode += (Integer.parseInt(map["minor"]) * 100)
+ libraryVersionCode += (Integer.parseInt(map["minor"]) * 1000)
}
if (map.containsKey("major")) {
- libraryVersionCode += (Integer.parseInt(map["major"]) * 10000)
+ libraryVersionCode += (Integer.parseInt(map["major"]) * 100000)
}
} catch (NumberFormatException ignore) {
libraryVersionCode = 1
diff --git a/platform/android/java/editor/build.gradle b/platform/android/java/editor/build.gradle
index 729966ee69..9152492e9d 100644
--- a/platform/android/java/editor/build.gradle
+++ b/platform/android/java/editor/build.gradle
@@ -12,6 +12,25 @@ dependencies {
implementation "androidx.window:window:1.0.0"
}
+ext {
+ // Build number added as a suffix to the version code, and incremented for each build/upload to
+ // the Google Play store.
+ // This should be reset on each stable release of Godot.
+ editorBuildNumber = 0
+ // Value by which the Godot version code should be offset by to make room for the build number
+ editorBuildNumberOffset = 100
+}
+
+def generateVersionCode() {
+ int libraryVersionCode = getGodotLibraryVersionCode()
+ return (libraryVersionCode * editorBuildNumberOffset) + editorBuildNumber
+}
+
+def generateVersionName() {
+ String libraryVersionName = getGodotLibraryVersionName()
+ return libraryVersionName + ".$editorBuildNumber"
+}
+
android {
compileSdkVersion versions.compileSdk
buildToolsVersion versions.buildTools
@@ -20,8 +39,8 @@ android {
defaultConfig {
// The 'applicationId' suffix allows to install Godot 3.x(v3) and 4.x(v4) on the same device
applicationId "org.godotengine.editor.v4"
- versionCode getGodotLibraryVersionCode()
- versionName getGodotLibraryVersionName()
+ versionCode generateVersionCode()
+ versionName generateVersionName()
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
diff --git a/platform/android/java/editor/src/main/AndroidManifest.xml b/platform/android/java/editor/src/main/AndroidManifest.xml
index abf506a83c..6aa5f06f31 100644
--- a/platform/android/java/editor/src/main/AndroidManifest.xml
+++ b/platform/android/java/editor/src/main/AndroidManifest.xml
@@ -7,7 +7,7 @@
<supports-screens
android:largeScreens="true"
android:normalScreens="true"
- android:smallScreens="true"
+ android:smallScreens="false"
android:xlargeScreens="true" />
<uses-feature
diff --git a/platform/linuxbsd/gl_manager_x11.cpp b/platform/linuxbsd/gl_manager_x11.cpp
index 04c1df71fb..838be2c042 100644
--- a/platform/linuxbsd/gl_manager_x11.cpp
+++ b/platform/linuxbsd/gl_manager_x11.cpp
@@ -256,7 +256,11 @@ void GLManager_X11::release_current() {
if (!_current_window) {
return;
}
- glXMakeCurrent(_x_windisp.x11_display, None, nullptr);
+
+ if (!glXMakeCurrent(_x_windisp.x11_display, None, nullptr)) {
+ ERR_PRINT("glXMakeCurrent failed");
+ }
+ _current_window = nullptr;
}
void GLManager_X11::window_make_current(DisplayServer::WindowID p_window_id) {
@@ -276,7 +280,9 @@ void GLManager_X11::window_make_current(DisplayServer::WindowID p_window_id) {
const GLDisplay &disp = get_display(win.gldisplay_id);
- glXMakeCurrent(disp.x11_display, win.x11_window, disp.context->glx_context);
+ if (!glXMakeCurrent(disp.x11_display, win.x11_window, disp.context->glx_context)) {
+ ERR_PRINT("glXMakeCurrent failed");
+ }
_internal_set_current_window(&win);
}
@@ -290,13 +296,12 @@ void GLManager_X11::make_current() {
return;
}
const GLDisplay &disp = get_current_display();
- glXMakeCurrent(_x_windisp.x11_display, _x_windisp.x11_window, disp.context->glx_context);
+ if (!glXMakeCurrent(_x_windisp.x11_display, _x_windisp.x11_window, disp.context->glx_context)) {
+ ERR_PRINT("glXMakeCurrent failed");
+ }
}
void GLManager_X11::swap_buffers() {
- // NO NEED TO CALL SWAP BUFFERS for each window...
- // see https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glXSwapBuffers.xml
-
if (!_current_window) {
return;
}
@@ -315,13 +320,6 @@ void GLManager_X11::swap_buffers() {
}
}
- // print_line("\tswap_buffers");
-
- // only for debugging without drawing anything
- // glClearColor(Math::randf(), 0, 1, 1);
- //glClear(GL_COLOR_BUFFER_BIT);
-
- //const GLDisplay &disp = get_current_display();
glXSwapBuffers(_x_windisp.x11_display, _x_windisp.x11_window);
}
diff --git a/platform/macos/gl_manager_macos_legacy.mm b/platform/macos/gl_manager_macos_legacy.mm
index e6bb7aaa85..dec4821b86 100644
--- a/platform/macos/gl_manager_macos_legacy.mm
+++ b/platform/macos/gl_manager_macos_legacy.mm
@@ -167,9 +167,8 @@ void GLManager_MacOS::make_current() {
}
void GLManager_MacOS::swap_buffers() {
- for (const KeyValue<DisplayServer::WindowID, GLWindow> &E : windows) {
- [E.value.context flushBuffer];
- }
+ GLWindow &win = windows[current_window];
+ [win.context flushBuffer];
}
void GLManager_MacOS::window_update(DisplayServer::WindowID p_window_id) {
diff --git a/platform/web/detect.py b/platform/web/detect.py
index e055af8400..08f964db92 100644
--- a/platform/web/detect.py
+++ b/platform/web/detect.py
@@ -227,3 +227,7 @@ def configure(env):
# Add code that allow exiting runtime.
env.Append(LINKFLAGS=["-s", "EXIT_RUNTIME=1"])
+
+ # This workaround creates a closure that prevents the garbage collector from freeing the WebGL context.
+ # We also only use WebGL2, and changing context version is not widely supported anyway.
+ env.Append(LINKFLAGS=["-s", "GL_WORKAROUND_SAFARI_GETCONTEXT_BUG=0"])
diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp
index b36f9d14a4..f6a61b18e4 100644
--- a/platform/web/display_server_web.cpp
+++ b/platform/web/display_server_web.cpp
@@ -764,10 +764,10 @@ DisplayServerWeb::DisplayServerWeb(const String &p_rendering_driver, WindowMode
if (wants_webgl2 && !webgl2_init_failed) {
EmscriptenWebGLContextAttributes attributes;
emscripten_webgl_init_context_attributes(&attributes);
- //attributes.alpha = GLOBAL_GET("display/window/per_pixel_transparency/allowed");
- attributes.alpha = true;
+ attributes.alpha = OS::get_singleton()->is_layered_allowed();
attributes.antialias = false;
attributes.majorVersion = 2;
+ attributes.explicitSwapControl = true;
webgl_ctx = emscripten_webgl_create_context(canvas_id, &attributes);
if (emscripten_webgl_make_context_current(webgl_ctx) != EMSCRIPTEN_RESULT_SUCCESS) {
@@ -997,7 +997,7 @@ void DisplayServerWeb::window_set_mode(WindowMode p_mode, WindowID p_window) {
} break;
case WINDOW_MODE_MAXIMIZED:
case WINDOW_MODE_MINIMIZED:
- WARN_PRINT("WindowMode MAXIMIZED and MINIMIZED are not supported in Web platform.");
+ // WindowMode MAXIMIZED and MINIMIZED are not supported in Web platform.
break;
default:
break;
diff --git a/platform/web/js/engine/config.js b/platform/web/js/engine/config.js
index 9c4b6c2012..41be7b2512 100644
--- a/platform/web/js/engine/config.js
+++ b/platform/web/js/engine/config.js
@@ -317,7 +317,8 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
if (!(this.canvas instanceof HTMLCanvasElement)) {
const nodes = document.getElementsByTagName('canvas');
if (nodes.length && nodes[0] instanceof HTMLCanvasElement) {
- this.canvas = nodes[0];
+ const first = nodes[0];
+ this.canvas = /** @type {!HTMLCanvasElement} */ (first);
}
if (!this.canvas) {
throw new Error('No canvas found in page');
diff --git a/platform/web/js/libs/audio.worklet.js b/platform/web/js/libs/audio.worklet.js
index ea4d8cb221..daf5c9ef12 100644
--- a/platform/web/js/libs/audio.worklet.js
+++ b/platform/web/js/libs/audio.worklet.js
@@ -133,6 +133,8 @@ class GodotProcessor extends AudioWorkletProcessor {
this.running = false;
this.output = null;
this.input = null;
+ this.lock = null;
+ this.notifier = null;
} else if (p_cmd === 'start_nothreads') {
this.output = new RingBuffer(p_data[0], p_data[0].length, false);
} else if (p_cmd === 'chunk') {
diff --git a/platform/web/js/libs/library_godot_audio.js b/platform/web/js/libs/library_godot_audio.js
index 756c1ac595..68e100cca0 100644
--- a/platform/web/js/libs/library_godot_audio.js
+++ b/platform/web/js/libs/library_godot_audio.js
@@ -339,16 +339,21 @@ const GodotAudioWorklet = {
if (GodotAudioWorklet.promise === null) {
return;
}
- GodotAudioWorklet.promise.then(function () {
+ const p = GodotAudioWorklet.promise;
+ p.then(function () {
GodotAudioWorklet.worklet.port.postMessage({
'cmd': 'stop',
'data': null,
});
GodotAudioWorklet.worklet.disconnect();
+ GodotAudioWorklet.worklet.port.onmessage = null;
GodotAudioWorklet.worklet = null;
GodotAudioWorklet.promise = null;
resolve();
- }).catch(function (err) { /* aborted? */ });
+ }).catch(function (err) {
+ // Aborted?
+ GodotRuntime.error(err);
+ });
});
},
},
diff --git a/platform/web/js/libs/library_godot_os.js b/platform/web/js/libs/library_godot_os.js
index 377eec3234..ce64fb98c0 100644
--- a/platform/web/js/libs/library_godot_os.js
+++ b/platform/web/js/libs/library_godot_os.js
@@ -106,12 +106,14 @@ autoAddDeps(GodotConfig, '$GodotConfig');
mergeInto(LibraryManager.library, GodotConfig);
const GodotFS = {
- $GodotFS__deps: ['$ERRNO_CODES', '$FS', '$IDBFS', '$GodotRuntime'],
+ $GodotFS__deps: ['$FS', '$IDBFS', '$GodotRuntime'],
$GodotFS__postset: [
'Module["initFS"] = GodotFS.init;',
'Module["copyToFS"] = GodotFS.copy_to_fs;',
].join(''),
$GodotFS: {
+ // ERRNO_CODES works every odd version of emscripten, but this will break too eventually.
+ ENOENT: 44,
_idbfs: false,
_syncing: false,
_mount_points: [],
@@ -138,8 +140,9 @@ const GodotFS = {
try {
FS.stat(dir);
} catch (e) {
- if (e.errno !== ERRNO_CODES.ENOENT) {
- throw e;
+ if (e.errno !== GodotFS.ENOENT) {
+ // Let mkdirTree throw in case, we cannot trust the above check.
+ GodotRuntime.error(e);
}
FS.mkdirTree(dir);
}
@@ -208,8 +211,9 @@ const GodotFS = {
try {
FS.stat(dir);
} catch (e) {
- if (e.errno !== ERRNO_CODES.ENOENT) {
- throw e;
+ if (e.errno !== GodotFS.ENOENT) {
+ // Let mkdirTree throw in case, we cannot trust the above check.
+ GodotRuntime.error(e);
}
FS.mkdirTree(dir);
}
diff --git a/platform/web/web_main.cpp b/platform/web/web_main.cpp
index 0f4411727a..287fe48c4d 100644
--- a/platform/web/web_main.cpp
+++ b/platform/web/web_main.cpp
@@ -55,6 +55,18 @@ void cleanup_after_sync() {
emscripten_set_main_loop(exit_callback, -1, false);
}
+void early_cleanup() {
+ emscripten_cancel_main_loop(); // After this, we can exit!
+ int exit_code = OS_Web::get_singleton()->get_exit_code();
+ memdelete(os);
+ os = nullptr;
+ emscripten_force_exit(exit_code); // No matter that we call cancel_main_loop, regular "exit" will not work, forcing.
+}
+
+void early_cleanup_sync() {
+ emscripten_set_main_loop(early_cleanup, -1, false);
+}
+
void main_loop_callback() {
uint64_t current_ticks = os->get_ticks_usec();
@@ -87,7 +99,19 @@ extern EMSCRIPTEN_KEEPALIVE int godot_web_main(int argc, char *argv[]) {
// We must override main when testing is enabled
TEST_MAIN_OVERRIDE
- Main::setup(argv[0], argc - 1, &argv[1]);
+ Error err = Main::setup(argv[0], argc - 1, &argv[1]);
+
+ // Proper shutdown in case of setup failure.
+ if (err != OK) {
+ int exit_code = (int)err;
+ if (err == ERR_HELP) {
+ exit_code = 0; // Called with --help.
+ }
+ os->set_exit_code(exit_code);
+ // Will only exit after sync.
+ godot_js_os_finish_async(early_cleanup_sync);
+ return exit_code;
+ }
// Ease up compatibility.
ResourceLoader::set_abort_on_missing_resources(false);
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index b4949de3f7..a14170525d 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -2419,14 +2419,14 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
} break;
case WM_SETTINGCHANGE: {
if (lParam && CompareStringOrdinal(reinterpret_cast<LPCWCH>(lParam), -1, L"ImmersiveColorSet", -1, true) == CSTR_EQUAL) {
- if (is_dark_mode_supported()) {
+ if (is_dark_mode_supported() && dark_title_available) {
BOOL value = is_dark_mode();
::DwmSetWindowAttribute(windows[window_id].hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));
}
}
} break;
case WM_THEMECHANGED: {
- if (is_dark_mode_supported()) {
+ if (is_dark_mode_supported() && dark_title_available) {
BOOL value = is_dark_mode();
::DwmSetWindowAttribute(windows[window_id].hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));
}
@@ -3541,7 +3541,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
wd.pre_fs_valid = true;
}
- if (is_dark_mode_supported()) {
+ if (is_dark_mode_supported() && dark_title_available) {
BOOL value = is_dark_mode();
::DwmSetWindowAttribute(wd.hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));
}
@@ -3633,6 +3633,7 @@ WTPacketPtr DisplayServerWindows::wintab_WTPacket = nullptr;
WTEnablePtr DisplayServerWindows::wintab_WTEnable = nullptr;
// UXTheme API.
+bool DisplayServerWindows::dark_title_available = false;
bool DisplayServerWindows::ux_theme_available = false;
IsDarkModeAllowedForAppPtr DisplayServerWindows::IsDarkModeAllowedForApp = nullptr;
ShouldAppsUseDarkModePtr DisplayServerWindows::ShouldAppsUseDarkMode = nullptr;
@@ -3725,7 +3726,21 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
// Enforce default keep screen on value.
screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on"));
- // Load UXTheme
+ // Load Windows version info.
+ OSVERSIONINFOW os_ver;
+ ZeroMemory(&os_ver, sizeof(OSVERSIONINFOW));
+ os_ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
+
+ HMODULE nt_lib = LoadLibraryW(L"ntdll.dll");
+ if (nt_lib) {
+ RtlGetVersionPtr RtlGetVersion = (RtlGetVersionPtr)GetProcAddress(nt_lib, "RtlGetVersion");
+ if (RtlGetVersion) {
+ RtlGetVersion(&os_ver);
+ }
+ FreeLibrary(nt_lib);
+ }
+
+ // Load UXTheme.
HMODULE ux_theme_lib = LoadLibraryW(L"uxtheme.dll");
if (ux_theme_lib) {
IsDarkModeAllowedForApp = (IsDarkModeAllowedForAppPtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(136));
@@ -3735,6 +3750,9 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
GetImmersiveUserColorSetPreference = (GetImmersiveUserColorSetPreferencePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(98));
ux_theme_available = IsDarkModeAllowedForApp && ShouldAppsUseDarkMode && GetImmersiveColorFromColorSetEx && GetImmersiveColorTypeFromName && GetImmersiveUserColorSetPreference;
+ if (os_ver.dwBuildNumber >= 22000) {
+ dark_title_available = true;
+ }
}
// Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink.
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index dbc9821970..b0ad7e36c0 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -157,6 +157,7 @@ typedef bool(WINAPI *ShouldAppsUseDarkModePtr)();
typedef DWORD(WINAPI *GetImmersiveColorFromColorSetExPtr)(UINT dwImmersiveColorSet, UINT dwImmersiveColorType, bool bIgnoreHighContrast, UINT dwHighContrastCacheMode);
typedef int(WINAPI *GetImmersiveColorTypeFromNamePtr)(const WCHAR *name);
typedef int(WINAPI *GetImmersiveUserColorSetPreferencePtr)(bool bForceCheckRegistry, bool bSkipCheckOnFail);
+typedef HRESULT(WINAPI *RtlGetVersionPtr)(OSVERSIONINFOW *lpVersionInformation);
// Windows Ink API
#ifndef POINTER_STRUCTURES
@@ -285,6 +286,7 @@ class DisplayServerWindows : public DisplayServer {
_THREAD_SAFE_CLASS_
// UXTheme API
+ static bool dark_title_available;
static bool ux_theme_available;
static IsDarkModeAllowedForAppPtr IsDarkModeAllowedForApp;
static ShouldAppsUseDarkModePtr ShouldAppsUseDarkMode;
diff --git a/platform/windows/gl_manager_windows.cpp b/platform/windows/gl_manager_windows.cpp
index d509ff8c51..7689751f1b 100644
--- a/platform/windows/gl_manager_windows.cpp
+++ b/platform/windows/gl_manager_windows.cpp
@@ -289,12 +289,7 @@ void GLManager_Windows::make_current() {
}
void GLManager_Windows::swap_buffers() {
- // on other platforms, OpenGL swaps buffers for all windows (on all displays, really?)
- // Windows swaps buffers on a per-window basis
- // REVISIT: this could be structurally bad, should we have "dirty" flags then?
- for (KeyValue<DisplayServer::WindowID, GLWindow> &entry : _windows) {
- SwapBuffers(entry.value.hDC);
- }
+ SwapBuffers(_current_window->hDC);
}
Error GLManager_Windows::initialize() {
diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp
index b1b1cb23ed..7fe464d2f4 100644
--- a/scene/2d/animated_sprite_2d.cpp
+++ b/scene/2d/animated_sprite_2d.cpp
@@ -108,6 +108,7 @@ void AnimatedSprite2D::_validate_property(PropertyInfo &p_property) const {
if (!frames.is_valid()) {
return;
}
+
if (p_property.name == "animation") {
p_property.hint = PROPERTY_HINT_ENUM;
List<StringName> names;
@@ -137,9 +138,15 @@ void AnimatedSprite2D::_validate_property(PropertyInfo &p_property) const {
p_property.hint_string = String(animation) + "," + p_property.hint_string;
}
}
+ return;
}
if (p_property.name == "frame") {
+ if (playing) {
+ p_property.usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY;
+ return;
+ }
+
p_property.hint = PROPERTY_HINT_RANGE;
if (frames->has_animation(animation) && frames->get_frame_count(animation) > 0) {
p_property.hint_string = "0," + itos(frames->get_frame_count(animation) - 1) + ",1";
@@ -175,33 +182,38 @@ void AnimatedSprite2D::_notification(int p_what) {
if (timeout <= 0) {
timeout = _get_frame_duration();
- int fc = frames->get_frame_count(animation);
- if ((!backwards && frame >= fc - 1) || (backwards && frame <= 0)) {
- if (frames->get_animation_loop(animation)) {
- if (backwards) {
- frame = fc - 1;
- } else {
- frame = 0;
- }
-
- emit_signal(SceneStringNames::get_singleton()->animation_finished);
- } else {
- if (backwards) {
+ int last_frame = frames->get_frame_count(animation) - 1;
+ if (!backwards) {
+ // Forward.
+ if (frame >= last_frame) {
+ if (frames->get_animation_loop(animation)) {
frame = 0;
- } else {
- frame = fc - 1;
- }
-
- if (!is_over) {
- is_over = true;
emit_signal(SceneStringNames::get_singleton()->animation_finished);
+ } else {
+ frame = last_frame;
+ if (!is_over) {
+ is_over = true;
+ emit_signal(SceneStringNames::get_singleton()->animation_finished);
+ }
}
+ } else {
+ frame++;
}
} else {
- if (backwards) {
- frame--;
+ // Reversed.
+ if (frame <= 0) {
+ if (frames->get_animation_loop(animation)) {
+ frame = last_frame;
+ emit_signal(SceneStringNames::get_singleton()->animation_finished);
+ } else {
+ frame = 0;
+ if (!is_over) {
+ is_over = true;
+ emit_signal(SceneStringNames::get_singleton()->animation_finished);
+ }
+ }
} else {
- frame++;
+ frame--;
}
}
@@ -259,14 +271,15 @@ void AnimatedSprite2D::_notification(int p_what) {
void AnimatedSprite2D::set_sprite_frames(const Ref<SpriteFrames> &p_frames) {
if (frames.is_valid()) {
- frames->disconnect("changed", callable_mp(this, &AnimatedSprite2D::_res_changed));
+ frames->disconnect(SceneStringNames::get_singleton()->changed, callable_mp(this, &AnimatedSprite2D::_res_changed));
}
+
frames = p_frames;
if (frames.is_valid()) {
- frames->connect("changed", callable_mp(this, &AnimatedSprite2D::_res_changed));
+ frames->connect(SceneStringNames::get_singleton()->changed, callable_mp(this, &AnimatedSprite2D::_res_changed));
}
- if (!frames.is_valid()) {
+ if (frames.is_null()) {
frame = 0;
} else {
set_frame(frame);
@@ -283,7 +296,7 @@ Ref<SpriteFrames> AnimatedSprite2D::get_sprite_frames() const {
}
void AnimatedSprite2D::set_frame(int p_frame) {
- if (!frames.is_valid()) {
+ if (frames.is_null()) {
return;
}
@@ -318,7 +331,7 @@ void AnimatedSprite2D::set_speed_scale(double p_speed_scale) {
speed_scale = MAX(p_speed_scale, 0.0f);
- // We adapt the timeout so that the animation speed adapts as soon as the speed scale is changed
+ // We adapt the timeout so that the animation speed adapts as soon as the speed scale is changed.
_reset_timeout();
timeout -= elapsed;
}
@@ -378,6 +391,7 @@ void AnimatedSprite2D::set_playing(bool p_playing) {
playing = p_playing;
_reset_timeout();
set_process_internal(playing);
+ notify_property_list_changed();
}
bool AnimatedSprite2D::is_playing() const {
diff --git a/scene/2d/navigation_obstacle_2d.cpp b/scene/2d/navigation_obstacle_2d.cpp
index a592d20cba..1850e00ecd 100644
--- a/scene/2d/navigation_obstacle_2d.cpp
+++ b/scene/2d/navigation_obstacle_2d.cpp
@@ -38,6 +38,9 @@
void NavigationObstacle2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_rid"), &NavigationObstacle2D::get_rid);
+ ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationObstacle2D::set_navigation_map);
+ ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationObstacle2D::get_navigation_map);
+
ClassDB::bind_method(D_METHOD("set_estimate_radius", "estimate_radius"), &NavigationObstacle2D::set_estimate_radius);
ClassDB::bind_method(D_METHOD("is_radius_estimated"), &NavigationObstacle2D::is_radius_estimated);
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationObstacle2D::set_radius);
@@ -57,28 +60,26 @@ void NavigationObstacle2D::_validate_property(PropertyInfo &p_property) const {
void NavigationObstacle2D::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE: {
- parent_node2d = Object::cast_to<Node2D>(get_parent());
- reevaluate_agent_radius();
- if (parent_node2d != nullptr) {
- // place agent on navigation map first or else the RVO agent callback creation fails silently later
- NavigationServer2D::get_singleton()->agent_set_map(get_rid(), parent_node2d->get_world_2d()->get_navigation_map());
- }
+ case NOTIFICATION_POST_ENTER_TREE: {
+ set_agent_parent(get_parent());
set_physics_process_internal(true);
} break;
case NOTIFICATION_EXIT_TREE: {
- parent_node2d = nullptr;
+ set_agent_parent(nullptr);
set_physics_process_internal(false);
} break;
case NOTIFICATION_PARENTED: {
- parent_node2d = Object::cast_to<Node2D>(get_parent());
- reevaluate_agent_radius();
+ if (is_inside_tree() && (get_parent() != parent_node2d)) {
+ set_agent_parent(get_parent());
+ set_physics_process_internal(true);
+ }
} break;
case NOTIFICATION_UNPARENTED: {
- parent_node2d = nullptr;
+ set_agent_parent(nullptr);
+ set_physics_process_internal(false);
} break;
case NOTIFICATION_PAUSED: {
@@ -182,6 +183,35 @@ real_t NavigationObstacle2D::estimate_agent_radius() const {
return 1.0; // Never a 0 radius
}
+void NavigationObstacle2D::set_agent_parent(Node *p_agent_parent) {
+ if (Object::cast_to<Node2D>(p_agent_parent) != nullptr) {
+ parent_node2d = Object::cast_to<Node2D>(p_agent_parent);
+ if (map_override.is_valid()) {
+ NavigationServer2D::get_singleton()->agent_set_map(get_rid(), map_override);
+ } else {
+ NavigationServer2D::get_singleton()->agent_set_map(get_rid(), parent_node2d->get_world_2d()->get_navigation_map());
+ }
+ reevaluate_agent_radius();
+ } else {
+ parent_node2d = nullptr;
+ NavigationServer2D::get_singleton()->agent_set_map(get_rid(), RID());
+ }
+}
+
+void NavigationObstacle2D::set_navigation_map(RID p_navigation_map) {
+ map_override = p_navigation_map;
+ NavigationServer2D::get_singleton()->agent_set_map(agent, map_override);
+}
+
+RID NavigationObstacle2D::get_navigation_map() const {
+ if (map_override.is_valid()) {
+ return map_override;
+ } else if (parent_node2d != nullptr) {
+ return parent_node2d->get_world_2d()->get_navigation_map();
+ }
+ return RID();
+}
+
void NavigationObstacle2D::set_estimate_radius(bool p_estimate_radius) {
estimate_radius = p_estimate_radius;
notify_property_list_changed();
diff --git a/scene/2d/navigation_obstacle_2d.h b/scene/2d/navigation_obstacle_2d.h
index 5795c6c94f..6eff95adec 100644
--- a/scene/2d/navigation_obstacle_2d.h
+++ b/scene/2d/navigation_obstacle_2d.h
@@ -38,8 +38,10 @@ class NavigationObstacle2D : public Node {
GDCLASS(NavigationObstacle2D, Node);
Node2D *parent_node2d = nullptr;
+
RID agent;
RID map_before_pause;
+ RID map_override;
bool estimate_radius = true;
real_t radius = 1.0;
@@ -57,6 +59,11 @@ public:
return agent;
}
+ void set_agent_parent(Node *p_agent_parent);
+
+ void set_navigation_map(RID p_navigation_map);
+ RID get_navigation_map() const;
+
void set_estimate_radius(bool p_estimate_radius);
bool is_radius_estimated() const {
return estimate_radius;
diff --git a/scene/3d/navigation_obstacle_3d.cpp b/scene/3d/navigation_obstacle_3d.cpp
index 953e52e591..9b49238333 100644
--- a/scene/3d/navigation_obstacle_3d.cpp
+++ b/scene/3d/navigation_obstacle_3d.cpp
@@ -37,6 +37,9 @@
void NavigationObstacle3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_rid"), &NavigationObstacle3D::get_rid);
+ ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationObstacle3D::set_navigation_map);
+ ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationObstacle3D::get_navigation_map);
+
ClassDB::bind_method(D_METHOD("set_estimate_radius", "estimate_radius"), &NavigationObstacle3D::set_estimate_radius);
ClassDB::bind_method(D_METHOD("is_radius_estimated"), &NavigationObstacle3D::is_radius_estimated);
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationObstacle3D::set_radius);
@@ -56,28 +59,26 @@ void NavigationObstacle3D::_validate_property(PropertyInfo &p_property) const {
void NavigationObstacle3D::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE: {
- parent_node3d = Object::cast_to<Node3D>(get_parent());
- reevaluate_agent_radius();
- if (parent_node3d != nullptr) {
- // place agent on navigation map first or else the RVO agent callback creation fails silently later
- NavigationServer3D::get_singleton()->agent_set_map(get_rid(), parent_node3d->get_world_3d()->get_navigation_map());
- }
+ case NOTIFICATION_POST_ENTER_TREE: {
+ set_agent_parent(get_parent());
set_physics_process_internal(true);
} break;
case NOTIFICATION_EXIT_TREE: {
- parent_node3d = nullptr;
+ set_agent_parent(nullptr);
set_physics_process_internal(false);
} break;
case NOTIFICATION_PARENTED: {
- parent_node3d = Object::cast_to<Node3D>(get_parent());
- reevaluate_agent_radius();
+ if (is_inside_tree() && (get_parent() != parent_node3d)) {
+ set_agent_parent(get_parent());
+ set_physics_process_internal(true);
+ }
} break;
case NOTIFICATION_UNPARENTED: {
- parent_node3d = nullptr;
+ set_agent_parent(nullptr);
+ set_physics_process_internal(false);
} break;
case NOTIFICATION_PAUSED: {
@@ -189,6 +190,35 @@ real_t NavigationObstacle3D::estimate_agent_radius() const {
return 1.0; // Never a 0 radius
}
+void NavigationObstacle3D::set_agent_parent(Node *p_agent_parent) {
+ if (Object::cast_to<Node3D>(p_agent_parent) != nullptr) {
+ parent_node3d = Object::cast_to<Node3D>(p_agent_parent);
+ if (map_override.is_valid()) {
+ NavigationServer3D::get_singleton()->agent_set_map(get_rid(), map_override);
+ } else {
+ NavigationServer3D::get_singleton()->agent_set_map(get_rid(), parent_node3d->get_world_3d()->get_navigation_map());
+ }
+ reevaluate_agent_radius();
+ } else {
+ parent_node3d = nullptr;
+ NavigationServer3D::get_singleton()->agent_set_map(get_rid(), RID());
+ }
+}
+
+void NavigationObstacle3D::set_navigation_map(RID p_navigation_map) {
+ map_override = p_navigation_map;
+ NavigationServer3D::get_singleton()->agent_set_map(agent, map_override);
+}
+
+RID NavigationObstacle3D::get_navigation_map() const {
+ if (map_override.is_valid()) {
+ return map_override;
+ } else if (parent_node3d != nullptr) {
+ return parent_node3d->get_world_3d()->get_navigation_map();
+ }
+ return RID();
+}
+
void NavigationObstacle3D::set_estimate_radius(bool p_estimate_radius) {
estimate_radius = p_estimate_radius;
notify_property_list_changed();
diff --git a/scene/3d/navigation_obstacle_3d.h b/scene/3d/navigation_obstacle_3d.h
index 7f6af668b6..24caf50680 100644
--- a/scene/3d/navigation_obstacle_3d.h
+++ b/scene/3d/navigation_obstacle_3d.h
@@ -37,8 +37,10 @@ class NavigationObstacle3D : public Node {
GDCLASS(NavigationObstacle3D, Node);
Node3D *parent_node3d = nullptr;
+
RID agent;
RID map_before_pause;
+ RID map_override;
bool estimate_radius = true;
real_t radius = 1.0;
@@ -56,6 +58,11 @@ public:
return agent;
}
+ void set_agent_parent(Node *p_agent_parent);
+
+ void set_navigation_map(RID p_navigation_map);
+ RID get_navigation_map() const;
+
void set_estimate_radius(bool p_estimate_radius);
bool is_radius_estimated() const {
return estimate_radius;
diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp
index 4515277dc3..7a89bf81bb 100644
--- a/scene/3d/sprite_3d.cpp
+++ b/scene/3d/sprite_3d.cpp
@@ -30,7 +30,6 @@
#include "sprite_3d.h"
-#include "core/core_string_names.h"
#include "scene/scene_string_names.h"
Color SpriteBase3D::_get_color_accum() {
@@ -58,7 +57,7 @@ void SpriteBase3D::_propagate_color_changed() {
}
color_dirty = true;
- _queue_update();
+ _queue_redraw();
for (SpriteBase3D *&E : children) {
E->_propagate_color_changed();
@@ -90,7 +89,7 @@ void SpriteBase3D::_notification(int p_what) {
void SpriteBase3D::set_centered(bool p_center) {
centered = p_center;
- _queue_update();
+ _queue_redraw();
}
bool SpriteBase3D::is_centered() const {
@@ -99,7 +98,7 @@ bool SpriteBase3D::is_centered() const {
void SpriteBase3D::set_offset(const Point2 &p_offset) {
offset = p_offset;
- _queue_update();
+ _queue_redraw();
}
Point2 SpriteBase3D::get_offset() const {
@@ -108,7 +107,7 @@ Point2 SpriteBase3D::get_offset() const {
void SpriteBase3D::set_flip_h(bool p_flip) {
hflip = p_flip;
- _queue_update();
+ _queue_redraw();
}
bool SpriteBase3D::is_flipped_h() const {
@@ -117,7 +116,7 @@ bool SpriteBase3D::is_flipped_h() const {
void SpriteBase3D::set_flip_v(bool p_flip) {
vflip = p_flip;
- _queue_update();
+ _queue_redraw();
}
bool SpriteBase3D::is_flipped_v() const {
@@ -127,7 +126,7 @@ bool SpriteBase3D::is_flipped_v() const {
void SpriteBase3D::set_modulate(const Color &p_color) {
modulate = p_color;
_propagate_color_changed();
- _queue_update();
+ _queue_redraw();
}
Color SpriteBase3D::get_modulate() const {
@@ -137,7 +136,7 @@ Color SpriteBase3D::get_modulate() const {
void SpriteBase3D::set_render_priority(int p_priority) {
ERR_FAIL_COND(p_priority < RS::MATERIAL_RENDER_PRIORITY_MIN || p_priority > RS::MATERIAL_RENDER_PRIORITY_MAX);
render_priority = p_priority;
- _queue_update();
+ _queue_redraw();
}
int SpriteBase3D::get_render_priority() const {
@@ -146,7 +145,7 @@ int SpriteBase3D::get_render_priority() const {
void SpriteBase3D::set_pixel_size(real_t p_amount) {
pixel_size = p_amount;
- _queue_update();
+ _queue_redraw();
}
real_t SpriteBase3D::get_pixel_size() const {
@@ -156,7 +155,7 @@ real_t SpriteBase3D::get_pixel_size() const {
void SpriteBase3D::set_axis(Vector3::Axis p_axis) {
ERR_FAIL_INDEX(p_axis, 3);
axis = p_axis;
- _queue_update();
+ _queue_redraw();
}
Vector3::Axis SpriteBase3D::get_axis() const {
@@ -171,7 +170,8 @@ void SpriteBase3D::_im_update() {
//texture->draw_rect_region(ci,dst_rect,src_rect,modulate);
}
-void SpriteBase3D::_queue_update() {
+void SpriteBase3D::_queue_redraw() {
+ // The 3D equivalent of CanvasItem.queue_redraw().
if (pending_update) {
return;
}
@@ -250,7 +250,7 @@ Ref<TriangleMesh> SpriteBase3D::generate_triangle_mesh() const {
void SpriteBase3D::set_draw_flag(DrawFlags p_flag, bool p_enable) {
ERR_FAIL_INDEX(p_flag, FLAG_MAX);
flags[p_flag] = p_enable;
- _queue_update();
+ _queue_redraw();
}
bool SpriteBase3D::get_draw_flag(DrawFlags p_flag) const {
@@ -261,7 +261,7 @@ bool SpriteBase3D::get_draw_flag(DrawFlags p_flag) const {
void SpriteBase3D::set_alpha_cut_mode(AlphaCutMode p_mode) {
ERR_FAIL_INDEX(p_mode, 3);
alpha_cut = p_mode;
- _queue_update();
+ _queue_redraw();
}
SpriteBase3D::AlphaCutMode SpriteBase3D::get_alpha_cut_mode() const {
@@ -269,9 +269,9 @@ SpriteBase3D::AlphaCutMode SpriteBase3D::get_alpha_cut_mode() const {
}
void SpriteBase3D::set_billboard_mode(StandardMaterial3D::BillboardMode p_mode) {
- ERR_FAIL_INDEX(p_mode, 3);
+ ERR_FAIL_INDEX(p_mode, 3); // Cannot use BILLBOARD_PARTICLES.
billboard_mode = p_mode;
- _queue_update();
+ _queue_redraw();
}
StandardMaterial3D::BillboardMode SpriteBase3D::get_billboard_mode() const {
@@ -281,7 +281,7 @@ StandardMaterial3D::BillboardMode SpriteBase3D::get_billboard_mode() const {
void SpriteBase3D::set_texture_filter(StandardMaterial3D::TextureFilter p_filter) {
if (texture_filter != p_filter) {
texture_filter = p_filter;
- _queue_update();
+ _queue_redraw();
}
}
@@ -329,7 +329,6 @@ void SpriteBase3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_item_rect"), &SpriteBase3D::get_item_rect);
ClassDB::bind_method(D_METHOD("generate_triangle_mesh"), &SpriteBase3D::generate_triangle_mesh);
- ClassDB::bind_method(D_METHOD("_queue_update"), &SpriteBase3D::_queue_update);
ClassDB::bind_method(D_METHOD("_im_update"), &SpriteBase3D::_im_update);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "centered"), "set_centered", "is_centered");
@@ -368,7 +367,7 @@ SpriteBase3D::SpriteBase3D() {
}
material = RenderingServer::get_singleton()->material_create();
- // Set defaults for material, names need to match up those in StandardMaterial3D
+ // Set defaults for material, names need to match up those in StandardMaterial3D.
RS::get_singleton()->material_set_param(material, "albedo", Color(1, 1, 1, 1));
RS::get_singleton()->material_set_param(material, "specular", 0.5);
RS::get_singleton()->material_set_param(material, "metallic", 0.0);
@@ -394,7 +393,7 @@ SpriteBase3D::SpriteBase3D() {
mesh_colors.resize(4);
mesh_uvs.resize(4);
- // create basic mesh and store format information
+ // Create basic mesh and store format information.
for (int i = 0; i < 4; i++) {
mesh_normals.write[i] = Vector3(0.0, 0.0, 0.0);
mesh_tangents.write[i * 4 + 0] = 0.0;
@@ -554,7 +553,7 @@ void Sprite3D::_draw() {
AABB aabb;
- // Everything except position and UV is compressed
+ // Everything except position and UV is compressed.
uint8_t *vertex_write_buffer = vertex_buffer.ptrw();
uint8_t *attribute_write_buffer = attribute_buffer.ptrw();
@@ -637,13 +636,14 @@ void Sprite3D::set_texture(const Ref<Texture2D> &p_texture) {
return;
}
if (texture.is_valid()) {
- texture->disconnect(CoreStringNames::get_singleton()->changed, Callable(this, "_queue_update"));
+ texture->disconnect(SceneStringNames::get_singleton()->changed, callable_mp((SpriteBase3D *)this, &Sprite3D::_queue_redraw));
}
texture = p_texture;
if (texture.is_valid()) {
- texture->connect(CoreStringNames::get_singleton()->changed, Callable(this, "_queue_update"));
+ texture->connect(SceneStringNames::get_singleton()->changed, callable_mp((SpriteBase3D *)this, &Sprite3D::_queue_redraw));
}
- _queue_update();
+
+ _queue_redraw();
emit_signal(SceneStringNames::get_singleton()->texture_changed);
}
@@ -657,7 +657,7 @@ void Sprite3D::set_region_enabled(bool p_region) {
}
region = p_region;
- _queue_update();
+ _queue_redraw();
}
bool Sprite3D::is_region_enabled() const {
@@ -668,7 +668,7 @@ void Sprite3D::set_region_rect(const Rect2 &p_region_rect) {
bool changed = region_rect != p_region_rect;
region_rect = p_region_rect;
if (region && changed) {
- _queue_update();
+ _queue_redraw();
}
}
@@ -681,7 +681,7 @@ void Sprite3D::set_frame(int p_frame) {
frame = p_frame;
- _queue_update();
+ _queue_redraw();
emit_signal(SceneStringNames::get_singleton()->frame_changed);
}
@@ -704,7 +704,7 @@ Vector2i Sprite3D::get_frame_coords() const {
void Sprite3D::set_vframes(int p_amount) {
ERR_FAIL_COND(p_amount < 1);
vframes = p_amount;
- _queue_update();
+ _queue_redraw();
notify_property_list_changed();
}
@@ -715,7 +715,7 @@ int Sprite3D::get_vframes() const {
void Sprite3D::set_hframes(int p_amount) {
ERR_FAIL_COND(p_amount < 1);
hframes = p_amount;
- _queue_update();
+ _queue_redraw();
notify_property_list_changed();
}
@@ -820,9 +820,9 @@ void AnimatedSprite3D::_draw() {
}
Ref<Texture2D> texture = frames->get_frame(animation, frame);
- if (!texture.is_valid()) {
+ if (texture.is_null()) {
set_base(RID());
- return; //no texuture no life
+ return;
}
Size2 tsize = texture->get_size();
if (tsize.x == 0 || tsize.y == 0) {
@@ -917,7 +917,7 @@ void AnimatedSprite3D::_draw() {
AABB aabb;
- // Everything except position and UV is compressed
+ // Everything except position and UV is compressed.
uint8_t *vertex_write_buffer = vertex_buffer.ptrw();
uint8_t *attribute_write_buffer = attribute_buffer.ptrw();
@@ -997,6 +997,7 @@ void AnimatedSprite3D::_validate_property(PropertyInfo &p_property) const {
if (!frames.is_valid()) {
return;
}
+
if (p_property.name == "animation") {
p_property.hint = PROPERTY_HINT_ENUM;
List<StringName> names;
@@ -1026,9 +1027,15 @@ void AnimatedSprite3D::_validate_property(PropertyInfo &p_property) const {
p_property.hint_string = String(animation) + "," + p_property.hint_string;
}
}
+ return;
}
if (p_property.name == "frame") {
+ if (playing) {
+ p_property.usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY;
+ return;
+ }
+
p_property.hint = PROPERTY_HINT_RANGE;
if (frames->has_animation(animation) && frames->get_frame_count(animation) > 0) {
p_property.hint_string = "0," + itos(frames->get_frame_count(animation) - 1) + ",1";
@@ -1056,27 +1063,51 @@ void AnimatedSprite3D::_notification(int p_what) {
double remaining = get_process_delta_time();
while (remaining) {
- double speed = frames->get_animation_speed(animation);
+ double speed = frames->get_animation_speed(animation) * speed_scale;
if (speed == 0) {
return; // Do nothing.
}
if (timeout <= 0) {
- timeout = 1.0 / speed;
-
- int fc = frames->get_frame_count(animation);
- if (frame >= fc - 1) {
- if (frames->get_animation_loop(animation)) {
- frame = 0;
+ timeout = _get_frame_duration();
+
+ int last_frame = frames->get_frame_count(animation) - 1;
+ if (!backwards) {
+ // Forward.
+ if (frame >= last_frame) {
+ if (frames->get_animation_loop(animation)) {
+ frame = 0;
+ emit_signal(SceneStringNames::get_singleton()->animation_finished);
+ } else {
+ frame = last_frame;
+ if (!is_over) {
+ is_over = true;
+ emit_signal(SceneStringNames::get_singleton()->animation_finished);
+ }
+ }
} else {
- frame = fc - 1;
+ frame++;
}
- emit_signal(SceneStringNames::get_singleton()->animation_finished);
} else {
- frame++;
+ // Reversed.
+ if (frame <= 0) {
+ if (frames->get_animation_loop(animation)) {
+ frame = last_frame;
+ emit_signal(SceneStringNames::get_singleton()->animation_finished);
+ } else {
+ frame = 0;
+ if (!is_over) {
+ is_over = true;
+ emit_signal(SceneStringNames::get_singleton()->animation_finished);
+ }
+ }
+ } else {
+ frame--;
+ }
}
- _queue_update();
+ _queue_redraw();
+
emit_signal(SceneStringNames::get_singleton()->frame_changed);
}
@@ -1090,14 +1121,15 @@ void AnimatedSprite3D::_notification(int p_what) {
void AnimatedSprite3D::set_sprite_frames(const Ref<SpriteFrames> &p_frames) {
if (frames.is_valid()) {
- frames->disconnect("changed", Callable(this, "_res_changed"));
+ frames->disconnect(SceneStringNames::get_singleton()->changed, callable_mp(this, &AnimatedSprite3D::_res_changed));
}
+
frames = p_frames;
if (frames.is_valid()) {
- frames->connect("changed", Callable(this, "_res_changed"));
+ frames->connect(SceneStringNames::get_singleton()->changed, callable_mp(this, &AnimatedSprite3D::_res_changed));
}
- if (!frames.is_valid()) {
+ if (frames.is_null()) {
frame = 0;
} else {
set_frame(frame);
@@ -1105,7 +1137,7 @@ void AnimatedSprite3D::set_sprite_frames(const Ref<SpriteFrames> &p_frames) {
notify_property_list_changed();
_reset_timeout();
- _queue_update();
+ _queue_redraw();
update_configuration_warnings();
}
@@ -1114,7 +1146,7 @@ Ref<SpriteFrames> AnimatedSprite3D::get_sprite_frames() const {
}
void AnimatedSprite3D::set_frame(int p_frame) {
- if (!frames.is_valid()) {
+ if (frames.is_null()) {
return;
}
@@ -1135,7 +1167,8 @@ void AnimatedSprite3D::set_frame(int p_frame) {
frame = p_frame;
_reset_timeout();
- _queue_update();
+ _queue_redraw();
+
emit_signal(SceneStringNames::get_singleton()->frame_changed);
}
@@ -1143,6 +1176,20 @@ int AnimatedSprite3D::get_frame() const {
return frame;
}
+void AnimatedSprite3D::set_speed_scale(double p_speed_scale) {
+ double elapsed = _get_frame_duration() - timeout;
+
+ speed_scale = MAX(p_speed_scale, 0.0f);
+
+ // We adapt the timeout so that the animation speed adapts as soon as the speed scale is changed.
+ _reset_timeout();
+ timeout -= elapsed;
+}
+
+double AnimatedSprite3D::get_speed_scale() const {
+ return speed_scale;
+}
+
Rect2 AnimatedSprite3D::get_item_rect() const {
if (!frames.is_valid() || !frames->has_animation(animation) || frame < 0 || frame >= frames->get_frame_count(animation)) {
return Rect2(0, 0, 1, 1);
@@ -1171,7 +1218,8 @@ Rect2 AnimatedSprite3D::get_item_rect() const {
void AnimatedSprite3D::_res_changed() {
set_frame(frame);
- _queue_update();
+
+ _queue_redraw();
}
void AnimatedSprite3D::set_playing(bool p_playing) {
@@ -1181,16 +1229,24 @@ void AnimatedSprite3D::set_playing(bool p_playing) {
playing = p_playing;
_reset_timeout();
set_process_internal(playing);
+ notify_property_list_changed();
}
bool AnimatedSprite3D::is_playing() const {
return playing;
}
-void AnimatedSprite3D::play(const StringName &p_animation) {
+void AnimatedSprite3D::play(const StringName &p_animation, const bool p_backwards) {
+ backwards = p_backwards;
+
if (p_animation) {
set_animation(p_animation);
+ if (frames.is_valid() && backwards && get_frame() == 0) {
+ set_frame(frames->get_frame_count(p_animation) - 1);
+ }
}
+
+ is_over = false;
set_playing(true);
}
@@ -1198,24 +1254,28 @@ void AnimatedSprite3D::stop() {
set_playing(false);
}
+double AnimatedSprite3D::_get_frame_duration() {
+ if (frames.is_valid() && frames->has_animation(animation)) {
+ double speed = frames->get_animation_speed(animation) * speed_scale;
+ if (speed > 0) {
+ return 1.0 / speed;
+ }
+ }
+ return 0.0;
+}
+
void AnimatedSprite3D::_reset_timeout() {
if (!playing) {
return;
}
- if (frames.is_valid() && frames->has_animation(animation)) {
- float speed = frames->get_animation_speed(animation);
- if (speed > 0) {
- timeout = 1.0 / speed;
- } else {
- timeout = 0;
- }
- } else {
- timeout = 0;
- }
+ timeout = _get_frame_duration();
+ is_over = false;
}
void AnimatedSprite3D::set_animation(const StringName &p_animation) {
+ ERR_FAIL_COND_MSG(frames == nullptr, vformat("There is no animation with name '%s'.", p_animation));
+ ERR_FAIL_COND_MSG(!frames->get_animation_names().has(p_animation), vformat("There is no animation with name '%s'.", p_animation));
if (animation == p_animation) {
return;
}
@@ -1224,7 +1284,7 @@ void AnimatedSprite3D::set_animation(const StringName &p_animation) {
_reset_timeout();
set_frame(0);
notify_property_list_changed();
- _queue_update();
+ _queue_redraw();
}
StringName AnimatedSprite3D::get_animation() const {
@@ -1261,12 +1321,15 @@ void AnimatedSprite3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_playing", "playing"), &AnimatedSprite3D::set_playing);
ClassDB::bind_method(D_METHOD("is_playing"), &AnimatedSprite3D::is_playing);
- ClassDB::bind_method(D_METHOD("play", "anim"), &AnimatedSprite3D::play, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("play", "anim", "backwards"), &AnimatedSprite3D::play, DEFVAL(StringName()), DEFVAL(false));
ClassDB::bind_method(D_METHOD("stop"), &AnimatedSprite3D::stop);
ClassDB::bind_method(D_METHOD("set_frame", "frame"), &AnimatedSprite3D::set_frame);
ClassDB::bind_method(D_METHOD("get_frame"), &AnimatedSprite3D::get_frame);
+ ClassDB::bind_method(D_METHOD("set_speed_scale", "speed_scale"), &AnimatedSprite3D::set_speed_scale);
+ ClassDB::bind_method(D_METHOD("get_speed_scale"), &AnimatedSprite3D::get_speed_scale);
+
ClassDB::bind_method(D_METHOD("_res_changed"), &AnimatedSprite3D::_res_changed);
ADD_SIGNAL(MethodInfo("frame_changed"));
@@ -1275,6 +1338,7 @@ void AnimatedSprite3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "frames", PROPERTY_HINT_RESOURCE_TYPE, "SpriteFrames"), "set_sprite_frames", "get_sprite_frames");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "animation"), "set_animation", "get_animation");
ADD_PROPERTY(PropertyInfo(Variant::INT, "frame"), "set_frame", "get_frame");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale"), "set_speed_scale", "get_speed_scale");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing"), "set_playing", "is_playing");
}
diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h
index 84244a2476..e6a546a76d 100644
--- a/scene/3d/sprite_3d.h
+++ b/scene/3d/sprite_3d.h
@@ -106,7 +106,7 @@ protected:
uint32_t skin_stride = 0;
uint32_t mesh_surface_format = 0;
- void _queue_update();
+ void _queue_redraw();
public:
void set_centered(bool p_center);
@@ -209,15 +209,19 @@ class AnimatedSprite3D : public SpriteBase3D {
Ref<SpriteFrames> frames;
bool playing = false;
+ bool backwards = false;
StringName animation = "default";
int frame = 0;
+ float speed_scale = 1.0f;
bool centered = false;
+ bool is_over = false;
double timeout = 0.0;
void _res_changed();
+ double _get_frame_duration();
void _reset_timeout();
RID last_shader;
@@ -233,7 +237,7 @@ public:
void set_sprite_frames(const Ref<SpriteFrames> &p_frames);
Ref<SpriteFrames> get_sprite_frames() const;
- void play(const StringName &p_animation = StringName());
+ void play(const StringName &p_animation = StringName(), const bool p_backwards = false);
void stop();
void set_playing(bool p_playing);
@@ -245,6 +249,9 @@ public:
void set_frame(int p_frame);
int get_frame() const;
+ void set_speed_scale(double p_speed_scale);
+ double get_speed_scale() const;
+
virtual Rect2 get_item_rect() const override;
virtual TypedArray<String> get_configuration_warnings() const override;
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index c8de8789fe..69512903b4 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -773,25 +773,25 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
if (connecting_valid) {
if (connecting && connecting_target) {
String from = connecting_from;
- int from_slot = connecting_index;
+ int from_port = connecting_index;
String to = connecting_target_to;
- int to_slot = connecting_target_index;
+ int to_port = connecting_target_index;
if (!connecting_out) {
SWAP(from, to);
- SWAP(from_slot, to_slot);
+ SWAP(from_port, to_port);
}
- emit_signal(SNAME("connection_request"), from, from_slot, to, to_slot);
+ emit_signal(SNAME("connection_request"), from, from_port, to, to_port);
} else if (!just_disconnected) {
String from = connecting_from;
- int from_slot = connecting_index;
+ int from_port = connecting_index;
Vector2 ofs = mb->get_position();
if (!connecting_out) {
- emit_signal(SNAME("connection_from_empty"), from, from_slot, ofs);
+ emit_signal(SNAME("connection_from_empty"), from, from_port, ofs);
} else {
- emit_signal(SNAME("connection_to_empty"), from, from_slot, ofs);
+ emit_signal(SNAME("connection_to_empty"), from, from_port, ofs);
}
}
}
@@ -830,22 +830,22 @@ bool GraphEdit::_check_clickable_control(Control *p_control, const Vector2 &mpos
}
}
-bool GraphEdit::is_in_input_hotzone(GraphNode *p_graph_node, int p_slot_index, const Vector2 &p_mouse_pos, const Vector2i &p_port_size) {
+bool GraphEdit::is_in_input_hotzone(GraphNode *p_node, int p_port, const Vector2 &p_mouse_pos, const Vector2i &p_port_size) {
bool success;
- if (GDVIRTUAL_CALL(_is_in_input_hotzone, p_graph_node, p_slot_index, p_mouse_pos, success)) {
+ if (GDVIRTUAL_CALL(_is_in_input_hotzone, p_node, p_port, p_mouse_pos, success)) {
return success;
} else {
- Vector2 pos = p_graph_node->get_connection_input_position(p_slot_index) + p_graph_node->get_position();
+ Vector2 pos = p_node->get_connection_input_position(p_port) + p_node->get_position();
return is_in_port_hotzone(pos / zoom, p_mouse_pos, p_port_size, true);
}
}
-bool GraphEdit::is_in_output_hotzone(GraphNode *p_graph_node, int p_slot_index, const Vector2 &p_mouse_pos, const Vector2i &p_port_size) {
+bool GraphEdit::is_in_output_hotzone(GraphNode *p_node, int p_port, const Vector2 &p_mouse_pos, const Vector2i &p_port_size) {
bool success;
- if (GDVIRTUAL_CALL(_is_in_output_hotzone, p_graph_node, p_slot_index, p_mouse_pos, success)) {
+ if (GDVIRTUAL_CALL(_is_in_output_hotzone, p_node, p_port, p_mouse_pos, success)) {
return success;
} else {
- Vector2 pos = p_graph_node->get_connection_output_position(p_slot_index) + p_graph_node->get_position();
+ Vector2 pos = p_node->get_connection_output_position(p_port) + p_node->get_position();
return is_in_port_hotzone(pos / zoom, p_mouse_pos, p_port_size, false);
}
}
@@ -1100,11 +1100,11 @@ void GraphEdit::_minimap_draw() {
continue;
}
- Vector2 from_slot_position = gfrom->get_position_offset() * zoom + gfrom->get_connection_output_position(E.from_port);
- Vector2 from_position = minimap->_convert_from_graph_position(from_slot_position - graph_offset) + minimap_offset;
+ Vector2 from_port_position = gfrom->get_position_offset() * zoom + gfrom->get_connection_output_position(E.from_port);
+ Vector2 from_position = minimap->_convert_from_graph_position(from_port_position - graph_offset) + minimap_offset;
Color from_color = gfrom->get_connection_output_color(E.from_port);
- Vector2 to_slot_position = gto->get_position_offset() * zoom + gto->get_connection_input_position(E.to_port);
- Vector2 to_position = minimap->_convert_from_graph_position(to_slot_position - graph_offset) + minimap_offset;
+ Vector2 to_port_position = gto->get_position_offset() * zoom + gto->get_connection_input_position(E.to_port);
+ Vector2 to_position = minimap->_convert_from_graph_position(to_port_position - graph_offset) + minimap_offset;
Color to_color = gto->get_connection_input_color(E.to_port);
if (E.activity > 0) {
@@ -2303,10 +2303,10 @@ void GraphEdit::arrange_nodes() {
}
void GraphEdit::_bind_methods() {
- ClassDB::bind_method(D_METHOD("connect_node", "from", "from_port", "to", "to_port"), &GraphEdit::connect_node);
- ClassDB::bind_method(D_METHOD("is_node_connected", "from", "from_port", "to", "to_port"), &GraphEdit::is_node_connected);
- ClassDB::bind_method(D_METHOD("disconnect_node", "from", "from_port", "to", "to_port"), &GraphEdit::disconnect_node);
- ClassDB::bind_method(D_METHOD("set_connection_activity", "from", "from_port", "to", "to_port", "amount"), &GraphEdit::set_connection_activity);
+ ClassDB::bind_method(D_METHOD("connect_node", "from_node", "from_port", "to_node", "to_port"), &GraphEdit::connect_node);
+ ClassDB::bind_method(D_METHOD("is_node_connected", "from_node", "from_port", "to_node", "to_port"), &GraphEdit::is_node_connected);
+ ClassDB::bind_method(D_METHOD("disconnect_node", "from_node", "from_port", "to_node", "to_port"), &GraphEdit::disconnect_node);
+ ClassDB::bind_method(D_METHOD("set_connection_activity", "from_node", "from_port", "to_node", "to_port", "amount"), &GraphEdit::set_connection_activity);
ClassDB::bind_method(D_METHOD("get_connection_list"), &GraphEdit::_get_connection_list);
ClassDB::bind_method(D_METHOD("clear_connections"), &GraphEdit::clear_connections);
ClassDB::bind_method(D_METHOD("force_connection_drag_end"), &GraphEdit::force_connection_drag_end);
@@ -2320,7 +2320,7 @@ void GraphEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_valid_connection_type", "from_type", "to_type"), &GraphEdit::add_valid_connection_type);
ClassDB::bind_method(D_METHOD("remove_valid_connection_type", "from_type", "to_type"), &GraphEdit::remove_valid_connection_type);
ClassDB::bind_method(D_METHOD("is_valid_connection_type", "from_type", "to_type"), &GraphEdit::is_valid_connection_type);
- ClassDB::bind_method(D_METHOD("get_connection_line", "from", "to"), &GraphEdit::get_connection_line);
+ ClassDB::bind_method(D_METHOD("get_connection_line", "from_node", "to_node"), &GraphEdit::get_connection_line);
ClassDB::bind_method(D_METHOD("set_panning_scheme", "scheme"), &GraphEdit::set_panning_scheme);
ClassDB::bind_method(D_METHOD("get_panning_scheme"), &GraphEdit::get_panning_scheme);
@@ -2370,8 +2370,8 @@ void GraphEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_right_disconnects_enabled"), &GraphEdit::is_right_disconnects_enabled);
ClassDB::bind_method(D_METHOD("_update_scroll_offset"), &GraphEdit::_update_scroll_offset);
- GDVIRTUAL_BIND(_is_in_input_hotzone, "graph_node", "slot_index", "mouse_position");
- GDVIRTUAL_BIND(_is_in_output_hotzone, "graph_node", "slot_index", "mouse_position");
+ GDVIRTUAL_BIND(_is_in_input_hotzone, "in_node", "in_port", "mouse_position");
+ GDVIRTUAL_BIND(_is_in_output_hotzone, "in_node", "in_port", "mouse_position");
ClassDB::bind_method(D_METHOD("get_zoom_hbox"), &GraphEdit::get_zoom_hbox);
@@ -2379,8 +2379,8 @@ void GraphEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_selected", "node"), &GraphEdit::set_selected);
- GDVIRTUAL_BIND(_get_connection_line, "from", "to")
- GDVIRTUAL_BIND(_is_node_hover_valid, "from", "from_slot", "to", "to_slot");
+ GDVIRTUAL_BIND(_get_connection_line, "from_position", "to_position")
+ GDVIRTUAL_BIND(_is_node_hover_valid, "from_node", "from_port", "to_node", "to_port");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "right_disconnects"), "set_right_disconnects", "is_right_disconnects_enabled");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scroll_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_scroll_ofs", "get_scroll_ofs");
@@ -2408,21 +2408,21 @@ void GraphEdit::_bind_methods() {
ADD_GROUP("UI", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "arrange_nodes_button_hidden"), "set_arrange_nodes_button_hidden", "is_arrange_nodes_button_hidden");
- ADD_SIGNAL(MethodInfo("connection_request", PropertyInfo(Variant::STRING_NAME, "from"), PropertyInfo(Variant::INT, "from_slot"), PropertyInfo(Variant::STRING_NAME, "to"), PropertyInfo(Variant::INT, "to_slot")));
- ADD_SIGNAL(MethodInfo("disconnection_request", PropertyInfo(Variant::STRING_NAME, "from"), PropertyInfo(Variant::INT, "from_slot"), PropertyInfo(Variant::STRING_NAME, "to"), PropertyInfo(Variant::INT, "to_slot")));
+ ADD_SIGNAL(MethodInfo("connection_request", PropertyInfo(Variant::STRING_NAME, "from_node"), PropertyInfo(Variant::INT, "from_port"), PropertyInfo(Variant::STRING_NAME, "to_node"), PropertyInfo(Variant::INT, "to_port")));
+ ADD_SIGNAL(MethodInfo("disconnection_request", PropertyInfo(Variant::STRING_NAME, "from_node"), PropertyInfo(Variant::INT, "from_port"), PropertyInfo(Variant::STRING_NAME, "to_node"), PropertyInfo(Variant::INT, "to_port")));
ADD_SIGNAL(MethodInfo("popup_request", PropertyInfo(Variant::VECTOR2, "position")));
ADD_SIGNAL(MethodInfo("duplicate_nodes_request"));
ADD_SIGNAL(MethodInfo("copy_nodes_request"));
ADD_SIGNAL(MethodInfo("paste_nodes_request"));
ADD_SIGNAL(MethodInfo("node_selected", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
ADD_SIGNAL(MethodInfo("node_deselected", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
- ADD_SIGNAL(MethodInfo("connection_to_empty", PropertyInfo(Variant::STRING_NAME, "from"), PropertyInfo(Variant::INT, "from_slot"), PropertyInfo(Variant::VECTOR2, "release_position")));
- ADD_SIGNAL(MethodInfo("connection_from_empty", PropertyInfo(Variant::STRING_NAME, "to"), PropertyInfo(Variant::INT, "to_slot"), PropertyInfo(Variant::VECTOR2, "release_position")));
+ ADD_SIGNAL(MethodInfo("connection_to_empty", PropertyInfo(Variant::STRING_NAME, "from_node"), PropertyInfo(Variant::INT, "from_port"), PropertyInfo(Variant::VECTOR2, "release_position")));
+ ADD_SIGNAL(MethodInfo("connection_from_empty", PropertyInfo(Variant::STRING_NAME, "to_node"), PropertyInfo(Variant::INT, "to_port"), PropertyInfo(Variant::VECTOR2, "release_position")));
ADD_SIGNAL(MethodInfo("delete_nodes_request", PropertyInfo(Variant::ARRAY, "nodes", PROPERTY_HINT_ARRAY_TYPE, "StringName")));
ADD_SIGNAL(MethodInfo("begin_node_move"));
ADD_SIGNAL(MethodInfo("end_node_move"));
ADD_SIGNAL(MethodInfo("scroll_offset_changed", PropertyInfo(Variant::VECTOR2, "offset")));
- ADD_SIGNAL(MethodInfo("connection_drag_started", PropertyInfo(Variant::STRING, "from"), PropertyInfo(Variant::INT, "slot"), PropertyInfo(Variant::BOOL, "is_output")));
+ ADD_SIGNAL(MethodInfo("connection_drag_started", PropertyInfo(Variant::STRING, "from_node"), PropertyInfo(Variant::INT, "from_port"), PropertyInfo(Variant::BOOL, "is_output")));
ADD_SIGNAL(MethodInfo("connection_drag_ended"));
BIND_ENUM_CONSTANT(SCROLL_ZOOMS);
diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h
index b6ce575009..9371ed3df4 100644
--- a/scene/gui/graph_edit.h
+++ b/scene/gui/graph_edit.h
@@ -201,8 +201,8 @@ private:
GraphEditMinimap *minimap = nullptr;
void _top_layer_input(const Ref<InputEvent> &p_ev);
- bool is_in_input_hotzone(GraphNode *p_graph_node, int p_slot_index, const Vector2 &p_mouse_pos, const Vector2i &p_port_size);
- bool is_in_output_hotzone(GraphNode *p_graph_node, int p_slot_index, const Vector2 &p_mouse_pos, const Vector2i &p_port_size);
+ bool is_in_input_hotzone(GraphNode *p_node, int p_port, const Vector2 &p_mouse_pos, const Vector2i &p_port_size);
+ bool is_in_output_hotzone(GraphNode *p_node, int p_port, const Vector2 &p_mouse_pos, const Vector2i &p_port_size);
bool is_in_port_hotzone(const Vector2 &pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left);
void _top_layer_draw();
diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp
index f441144a8e..21c0b5a842 100644
--- a/scene/gui/graph_node.cpp
+++ b/scene/gui/graph_node.cpp
@@ -779,8 +779,8 @@ void GraphNode::_connpos_update() {
int sep = get_theme_constant(SNAME("separation"));
Ref<StyleBox> sb = get_theme_stylebox(SNAME("frame"));
- conn_input_cache.clear();
- conn_output_cache.clear();
+ left_port_cache.clear();
+ right_port_cache.clear();
int vofs = 0;
int idx = 0;
@@ -801,20 +801,26 @@ void GraphNode::_connpos_update() {
if (slot_info.has(idx)) {
if (slot_info[idx].enable_left) {
- ConnCache cc;
- cc.pos = Point2i(edgeofs, y + h / 2);
+ PortCache cc;
+ cc.position = Point2i(edgeofs, y + h / 2);
+ cc.height = size.height;
+
+ cc.slot_idx = idx;
cc.type = slot_info[idx].type_left;
cc.color = slot_info[idx].color_left;
- cc.height = size.height;
- conn_input_cache.push_back(cc);
+
+ left_port_cache.push_back(cc);
}
if (slot_info[idx].enable_right) {
- ConnCache cc;
- cc.pos = Point2i(get_size().width - edgeofs, y + h / 2);
+ PortCache cc;
+ cc.position = Point2i(get_size().width - edgeofs, y + h / 2);
+ cc.height = size.height;
+
+ cc.slot_idx = idx;
cc.type = slot_info[idx].type_right;
cc.color = slot_info[idx].color_right;
- cc.height = size.height;
- conn_output_cache.push_back(cc);
+
+ right_port_cache.push_back(cc);
}
}
@@ -831,46 +837,55 @@ int GraphNode::get_connection_input_count() {
_connpos_update();
}
- return conn_input_cache.size();
+ return left_port_cache.size();
}
-int GraphNode::get_connection_input_height(int p_idx) {
+int GraphNode::get_connection_input_height(int p_port) {
if (connpos_dirty) {
_connpos_update();
}
- ERR_FAIL_INDEX_V(p_idx, conn_input_cache.size(), 0);
- return conn_input_cache[p_idx].height;
+ ERR_FAIL_INDEX_V(p_port, left_port_cache.size(), 0);
+ return left_port_cache[p_port].height;
}
-Vector2 GraphNode::get_connection_input_position(int p_idx) {
+Vector2 GraphNode::get_connection_input_position(int p_port) {
if (connpos_dirty) {
_connpos_update();
}
- ERR_FAIL_INDEX_V(p_idx, conn_input_cache.size(), Vector2());
- Vector2 pos = conn_input_cache[p_idx].pos;
+ ERR_FAIL_INDEX_V(p_port, left_port_cache.size(), Vector2());
+ Vector2 pos = left_port_cache[p_port].position;
pos.x *= get_scale().x;
pos.y *= get_scale().y;
return pos;
}
-int GraphNode::get_connection_input_type(int p_idx) {
+int GraphNode::get_connection_input_type(int p_port) {
+ if (connpos_dirty) {
+ _connpos_update();
+ }
+
+ ERR_FAIL_INDEX_V(p_port, left_port_cache.size(), 0);
+ return left_port_cache[p_port].type;
+}
+
+Color GraphNode::get_connection_input_color(int p_port) {
if (connpos_dirty) {
_connpos_update();
}
- ERR_FAIL_INDEX_V(p_idx, conn_input_cache.size(), 0);
- return conn_input_cache[p_idx].type;
+ ERR_FAIL_INDEX_V(p_port, left_port_cache.size(), Color());
+ return left_port_cache[p_port].color;
}
-Color GraphNode::get_connection_input_color(int p_idx) {
+int GraphNode::get_connection_input_slot(int p_port) {
if (connpos_dirty) {
_connpos_update();
}
- ERR_FAIL_INDEX_V(p_idx, conn_input_cache.size(), Color());
- return conn_input_cache[p_idx].color;
+ ERR_FAIL_INDEX_V(p_port, left_port_cache.size(), -1);
+ return left_port_cache[p_port].slot_idx;
}
int GraphNode::get_connection_output_count() {
@@ -878,46 +893,55 @@ int GraphNode::get_connection_output_count() {
_connpos_update();
}
- return conn_output_cache.size();
+ return right_port_cache.size();
}
-int GraphNode::get_connection_output_height(int p_idx) {
+int GraphNode::get_connection_output_height(int p_port) {
if (connpos_dirty) {
_connpos_update();
}
- ERR_FAIL_INDEX_V(p_idx, conn_output_cache.size(), 0);
- return conn_output_cache[p_idx].height;
+ ERR_FAIL_INDEX_V(p_port, right_port_cache.size(), 0);
+ return right_port_cache[p_port].height;
}
-Vector2 GraphNode::get_connection_output_position(int p_idx) {
+Vector2 GraphNode::get_connection_output_position(int p_port) {
if (connpos_dirty) {
_connpos_update();
}
- ERR_FAIL_INDEX_V(p_idx, conn_output_cache.size(), Vector2());
- Vector2 pos = conn_output_cache[p_idx].pos;
+ ERR_FAIL_INDEX_V(p_port, right_port_cache.size(), Vector2());
+ Vector2 pos = right_port_cache[p_port].position;
pos.x *= get_scale().x;
pos.y *= get_scale().y;
return pos;
}
-int GraphNode::get_connection_output_type(int p_idx) {
+int GraphNode::get_connection_output_type(int p_port) {
+ if (connpos_dirty) {
+ _connpos_update();
+ }
+
+ ERR_FAIL_INDEX_V(p_port, right_port_cache.size(), 0);
+ return right_port_cache[p_port].type;
+}
+
+Color GraphNode::get_connection_output_color(int p_port) {
if (connpos_dirty) {
_connpos_update();
}
- ERR_FAIL_INDEX_V(p_idx, conn_output_cache.size(), 0);
- return conn_output_cache[p_idx].type;
+ ERR_FAIL_INDEX_V(p_port, right_port_cache.size(), Color());
+ return right_port_cache[p_port].color;
}
-Color GraphNode::get_connection_output_color(int p_idx) {
+int GraphNode::get_connection_output_slot(int p_port) {
if (connpos_dirty) {
_connpos_update();
}
- ERR_FAIL_INDEX_V(p_idx, conn_output_cache.size(), Color());
- return conn_output_cache[p_idx].color;
+ ERR_FAIL_INDEX_V(p_port, right_port_cache.size(), -1);
+ return right_port_cache[p_port].slot_idx;
}
void GraphNode::gui_input(const Ref<InputEvent> &p_ev) {
@@ -1050,30 +1074,30 @@ void GraphNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_language", "language"), &GraphNode::set_language);
ClassDB::bind_method(D_METHOD("get_language"), &GraphNode::get_language);
- ClassDB::bind_method(D_METHOD("set_slot", "idx", "enable_left", "type_left", "color_left", "enable_right", "type_right", "color_right", "custom_left", "custom_right", "enable"), &GraphNode::set_slot, DEFVAL(Ref<Texture2D>()), DEFVAL(Ref<Texture2D>()), DEFVAL(true));
- ClassDB::bind_method(D_METHOD("clear_slot", "idx"), &GraphNode::clear_slot);
+ ClassDB::bind_method(D_METHOD("set_slot", "slot_index", "enable_left_port", "type_left", "color_left", "enable_right_port", "type_right", "color_right", "custom_icon_left", "custom_icon_right", "draw_stylebox"), &GraphNode::set_slot, DEFVAL(Ref<Texture2D>()), DEFVAL(Ref<Texture2D>()), DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("clear_slot", "slot_index"), &GraphNode::clear_slot);
ClassDB::bind_method(D_METHOD("clear_all_slots"), &GraphNode::clear_all_slots);
- ClassDB::bind_method(D_METHOD("is_slot_enabled_left", "idx"), &GraphNode::is_slot_enabled_left);
- ClassDB::bind_method(D_METHOD("set_slot_enabled_left", "idx", "enable_left"), &GraphNode::set_slot_enabled_left);
+ ClassDB::bind_method(D_METHOD("set_slot_enabled_left", "slot_index", "enable"), &GraphNode::set_slot_enabled_left);
+ ClassDB::bind_method(D_METHOD("is_slot_enabled_left", "slot_index"), &GraphNode::is_slot_enabled_left);
- ClassDB::bind_method(D_METHOD("set_slot_type_left", "idx", "type_left"), &GraphNode::set_slot_type_left);
- ClassDB::bind_method(D_METHOD("get_slot_type_left", "idx"), &GraphNode::get_slot_type_left);
+ ClassDB::bind_method(D_METHOD("set_slot_type_left", "slot_index", "type"), &GraphNode::set_slot_type_left);
+ ClassDB::bind_method(D_METHOD("get_slot_type_left", "slot_index"), &GraphNode::get_slot_type_left);
- ClassDB::bind_method(D_METHOD("set_slot_color_left", "idx", "color_left"), &GraphNode::set_slot_color_left);
- ClassDB::bind_method(D_METHOD("get_slot_color_left", "idx"), &GraphNode::get_slot_color_left);
+ ClassDB::bind_method(D_METHOD("set_slot_color_left", "slot_index", "color"), &GraphNode::set_slot_color_left);
+ ClassDB::bind_method(D_METHOD("get_slot_color_left", "slot_index"), &GraphNode::get_slot_color_left);
- ClassDB::bind_method(D_METHOD("is_slot_enabled_right", "idx"), &GraphNode::is_slot_enabled_right);
- ClassDB::bind_method(D_METHOD("set_slot_enabled_right", "idx", "enable_right"), &GraphNode::set_slot_enabled_right);
+ ClassDB::bind_method(D_METHOD("set_slot_enabled_right", "slot_index", "enable"), &GraphNode::set_slot_enabled_right);
+ ClassDB::bind_method(D_METHOD("is_slot_enabled_right", "slot_index"), &GraphNode::is_slot_enabled_right);
- ClassDB::bind_method(D_METHOD("set_slot_type_right", "idx", "type_right"), &GraphNode::set_slot_type_right);
- ClassDB::bind_method(D_METHOD("get_slot_type_right", "idx"), &GraphNode::get_slot_type_right);
+ ClassDB::bind_method(D_METHOD("set_slot_type_right", "slot_index", "type"), &GraphNode::set_slot_type_right);
+ ClassDB::bind_method(D_METHOD("get_slot_type_right", "slot_index"), &GraphNode::get_slot_type_right);
- ClassDB::bind_method(D_METHOD("set_slot_color_right", "idx", "color_right"), &GraphNode::set_slot_color_right);
- ClassDB::bind_method(D_METHOD("get_slot_color_right", "idx"), &GraphNode::get_slot_color_right);
+ ClassDB::bind_method(D_METHOD("set_slot_color_right", "slot_index", "color"), &GraphNode::set_slot_color_right);
+ ClassDB::bind_method(D_METHOD("get_slot_color_right", "slot_index"), &GraphNode::get_slot_color_right);
- ClassDB::bind_method(D_METHOD("is_slot_draw_stylebox", "idx"), &GraphNode::is_slot_draw_stylebox);
- ClassDB::bind_method(D_METHOD("set_slot_draw_stylebox", "idx", "draw_stylebox"), &GraphNode::set_slot_draw_stylebox);
+ ClassDB::bind_method(D_METHOD("is_slot_draw_stylebox", "slot_index"), &GraphNode::is_slot_draw_stylebox);
+ ClassDB::bind_method(D_METHOD("set_slot_draw_stylebox", "slot_index", "enable"), &GraphNode::set_slot_draw_stylebox);
ClassDB::bind_method(D_METHOD("set_position_offset", "offset"), &GraphNode::set_position_offset);
ClassDB::bind_method(D_METHOD("get_position_offset"), &GraphNode::get_position_offset);
@@ -1094,16 +1118,18 @@ void GraphNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_selected"), &GraphNode::is_selected);
ClassDB::bind_method(D_METHOD("get_connection_input_count"), &GraphNode::get_connection_input_count);
- ClassDB::bind_method(D_METHOD("get_connection_input_height", "idx"), &GraphNode::get_connection_input_height);
- ClassDB::bind_method(D_METHOD("get_connection_input_position", "idx"), &GraphNode::get_connection_input_position);
- ClassDB::bind_method(D_METHOD("get_connection_input_type", "idx"), &GraphNode::get_connection_input_type);
- ClassDB::bind_method(D_METHOD("get_connection_input_color", "idx"), &GraphNode::get_connection_input_color);
+ ClassDB::bind_method(D_METHOD("get_connection_input_height", "port"), &GraphNode::get_connection_input_height);
+ ClassDB::bind_method(D_METHOD("get_connection_input_position", "port"), &GraphNode::get_connection_input_position);
+ ClassDB::bind_method(D_METHOD("get_connection_input_type", "port"), &GraphNode::get_connection_input_type);
+ ClassDB::bind_method(D_METHOD("get_connection_input_color", "port"), &GraphNode::get_connection_input_color);
+ ClassDB::bind_method(D_METHOD("get_connection_input_slot", "port"), &GraphNode::get_connection_input_slot);
ClassDB::bind_method(D_METHOD("get_connection_output_count"), &GraphNode::get_connection_output_count);
- ClassDB::bind_method(D_METHOD("get_connection_output_height", "idx"), &GraphNode::get_connection_output_height);
- ClassDB::bind_method(D_METHOD("get_connection_output_position", "idx"), &GraphNode::get_connection_output_position);
- ClassDB::bind_method(D_METHOD("get_connection_output_type", "idx"), &GraphNode::get_connection_output_type);
- ClassDB::bind_method(D_METHOD("get_connection_output_color", "idx"), &GraphNode::get_connection_output_color);
+ ClassDB::bind_method(D_METHOD("get_connection_output_height", "port"), &GraphNode::get_connection_output_height);
+ ClassDB::bind_method(D_METHOD("get_connection_output_position", "port"), &GraphNode::get_connection_output_position);
+ ClassDB::bind_method(D_METHOD("get_connection_output_type", "port"), &GraphNode::get_connection_output_type);
+ ClassDB::bind_method(D_METHOD("get_connection_output_color", "port"), &GraphNode::get_connection_output_color);
+ ClassDB::bind_method(D_METHOD("get_connection_output_slot", "port"), &GraphNode::get_connection_output_slot);
ClassDB::bind_method(D_METHOD("set_show_close_button", "show"), &GraphNode::set_show_close_button);
ClassDB::bind_method(D_METHOD("is_close_button_visible"), &GraphNode::is_close_button_visible);
diff --git a/scene/gui/graph_node.h b/scene/gui/graph_node.h
index 9c8f926403..e66b0cfc20 100644
--- a/scene/gui/graph_node.h
+++ b/scene/gui/graph_node.h
@@ -78,15 +78,17 @@ private:
Vector<int> cache_y;
- struct ConnCache {
- Vector2 pos;
+ struct PortCache {
+ Vector2 position;
+ int height;
+
+ int slot_idx;
int type = 0;
Color color;
- int height;
};
- Vector<ConnCache> conn_input_cache;
- Vector<ConnCache> conn_output_cache;
+ Vector<PortCache> left_port_cache;
+ Vector<PortCache> right_port_cache;
HashMap<int, Slot> slot_info;
@@ -165,16 +167,18 @@ public:
bool is_close_button_visible() const;
int get_connection_input_count();
- int get_connection_input_height(int p_idx);
- Vector2 get_connection_input_position(int p_idx);
- int get_connection_input_type(int p_idx);
- Color get_connection_input_color(int p_idx);
+ int get_connection_input_height(int p_port);
+ Vector2 get_connection_input_position(int p_port);
+ int get_connection_input_type(int p_port);
+ Color get_connection_input_color(int p_port);
+ int get_connection_input_slot(int p_port);
int get_connection_output_count();
- int get_connection_output_height(int p_idx);
- Vector2 get_connection_output_position(int p_idx);
- int get_connection_output_type(int p_idx);
- Color get_connection_output_color(int p_idx);
+ int get_connection_output_height(int p_port);
+ Vector2 get_connection_output_position(int p_port);
+ int get_connection_output_type(int p_port);
+ Color get_connection_output_color(int p_port);
+ int get_connection_output_slot(int p_port);
void set_overlay(Overlay p_overlay);
Overlay get_overlay() const;
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index 093e4a8cd3..05d86f77f2 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -373,7 +373,7 @@ void CanvasItem::move_to_front() {
if (!get_parent()) {
return;
}
- get_parent()->move_child(this, get_parent()->get_child_count() - 1);
+ get_parent()->move_child(this, -1);
}
void CanvasItem::set_modulate(const Color &p_modulate) {
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index f01ec99205..29f4d4fb1c 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -328,12 +328,21 @@ void Node::move_child(Node *p_child, int p_pos) {
// We need to check whether node is internal and move it only in the relevant node range.
if (p_child->_is_internal_front()) {
+ if (p_pos < 0) {
+ p_pos += data.internal_children_front;
+ }
ERR_FAIL_INDEX_MSG(p_pos, data.internal_children_front, vformat("Invalid new child position: %d. Child is internal.", p_pos));
_move_child(p_child, p_pos);
} else if (p_child->_is_internal_back()) {
+ if (p_pos < 0) {
+ p_pos += data.internal_children_back;
+ }
ERR_FAIL_INDEX_MSG(p_pos, data.internal_children_back, vformat("Invalid new child position: %d. Child is internal.", p_pos));
_move_child(p_child, data.children.size() - data.internal_children_back + p_pos);
} else {
+ if (p_pos < 0) {
+ p_pos += get_child_count(false);
+ }
ERR_FAIL_INDEX_MSG(p_pos, data.children.size() + 1 - data.internal_children_front - data.internal_children_back, vformat("Invalid new child position: %d.", p_pos));
_move_child(p_child, p_pos + data.internal_children_front);
}
@@ -1907,43 +1916,6 @@ Ref<Tween> Node::create_tween() {
return tween;
}
-void Node::remove_and_skip() {
- ERR_FAIL_COND(!data.parent);
-
- Node *new_owner = get_owner();
-
- List<Node *> children;
-
- while (true) {
- bool clear = true;
- for (int i = 0; i < data.children.size(); i++) {
- Node *c_node = data.children[i];
- if (!c_node->get_owner()) {
- continue;
- }
-
- remove_child(c_node);
- c_node->_propagate_replace_owner(this, nullptr);
- children.push_back(c_node);
- clear = false;
- break;
- }
-
- if (clear) {
- break;
- }
- }
-
- while (!children.is_empty()) {
- Node *c_node = children.front()->get();
- data.parent->add_child(c_node);
- c_node->_propagate_replace_owner(nullptr, new_owner);
- children.pop_front();
- }
-
- data.parent->remove_child(this);
-}
-
void Node::set_scene_file_path(const String &p_scene_file_path) {
data.scene_file_path = p_scene_file_path;
}
@@ -2803,7 +2775,6 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_groups"), &Node::_get_groups);
ClassDB::bind_method(D_METHOD("set_owner", "owner"), &Node::set_owner);
ClassDB::bind_method(D_METHOD("get_owner"), &Node::get_owner);
- ClassDB::bind_method(D_METHOD("remove_and_skip"), &Node::remove_and_skip);
ClassDB::bind_method(D_METHOD("get_index", "include_internal"), &Node::get_index, DEFVAL(false));
ClassDB::bind_method(D_METHOD("print_tree"), &Node::print_tree);
ClassDB::bind_method(D_METHOD("print_tree_pretty"), &Node::print_tree_pretty);
diff --git a/scene/main/node.h b/scene/main/node.h
index 9f07bc28bc..13a938ef97 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -356,7 +356,6 @@ public:
void set_unique_name_in_owner(bool p_enabled);
bool is_unique_name_in_owner() const;
- void remove_and_skip();
int get_index(bool p_include_internal = true) const;
Ref<Tween> create_tween();
@@ -530,4 +529,8 @@ Error Node::rpc_id(int p_peer_id, const StringName &p_method, VarArgs... p_args)
return rpcp(p_peer_id, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
}
+// Add these macro to your class's 'get_configuration_warnings' function to have warnings show up in the scene tree inspector.
+#define DEPRECATED_NODE_WARNING warnings.push_back(RTR("This node is marked as deprecated and will be removed in future versions.\nPlease check the Godot documentation for information about migration."));
+#define EXPERIMENTAL_NODE_WARNING warnings.push_back(RTR("This node is marked as experimental and may be subject to removal or major changes in future versions."));
+
#endif // NODE_H
diff --git a/servers/rendering/renderer_rd/effects/ss_effects.cpp b/servers/rendering/renderer_rd/effects/ss_effects.cpp
index 315bea2e67..582c5abbdd 100644
--- a/servers/rendering/renderer_rd/effects/ss_effects.cpp
+++ b/servers/rendering/renderer_rd/effects/ss_effects.cpp
@@ -443,6 +443,11 @@ void SSEffects::downsample_depth(RID p_depth_buffer, const Vector<RID> &p_depth_
RD::get_singleton()->draw_command_begin_label("Downsample Depth");
if (p_invalidate_uniform_set || use_full_mips != ss_effects.used_full_mips_last_frame || use_half_size != ss_effects.used_half_size_last_frame || use_mips != ss_effects.used_mips_last_frame) {
+ if (ss_effects.downsample_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(ss_effects.downsample_uniform_set)) {
+ RD::get_singleton()->free(ss_effects.downsample_uniform_set);
+ ss_effects.downsample_uniform_set = RID();
+ }
+
Vector<RD::Uniform> uniforms;
{
RD::Uniform u;
@@ -516,6 +521,7 @@ void SSEffects::downsample_depth(RID p_depth_buffer, const Vector<RID> &p_depth_
ss_effects.used_full_mips_last_frame = use_full_mips;
ss_effects.used_half_size_last_frame = use_half_size;
+ ss_effects.used_mips_last_frame = use_mips;
}
/* SSIL */
@@ -1484,7 +1490,7 @@ void SSEffects::ssr_allocate_buffers(SSRRenderBuffers &p_ssr_buffers, const Rend
}
}
-void SSEffects::screen_space_reflection(SSRRenderBuffers &p_ssr_buffers, const RID *p_diffuse_slices, const RID *p_normal_roughness_slices, RenderingServer::EnvironmentSSRRoughnessQuality p_roughness_quality, const RID *p_metallic_slices, const Color &p_metallic_mask, const RID *p_depth_slices, const Size2i &p_screen_size, int p_max_steps, float p_fade_in, float p_fade_out, float p_tolerance, const uint32_t p_view_count, const Projection *p_projections, const Vector3 *p_eye_offsets) {
+void SSEffects::screen_space_reflection(SSRRenderBuffers &p_ssr_buffers, const RID *p_diffuse_slices, const RID *p_normal_roughness_slices, RenderingServer::EnvironmentSSRRoughnessQuality p_roughness_quality, const RID *p_metallic_slices, const RID *p_depth_slices, const Size2i &p_screen_size, int p_max_steps, float p_fade_in, float p_fade_out, float p_tolerance, const uint32_t p_view_count, const Projection *p_projections, const Vector3 *p_eye_offsets) {
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
MaterialStorage *material_storage = MaterialStorage::get_singleton();
@@ -1579,10 +1585,6 @@ void SSEffects::screen_space_reflection(SSRRenderBuffers &p_ssr_buffers, const R
push_constant.proj_info[1] = -2.0f / (p_screen_size.height * p_projections[v].matrix[1][1]);
push_constant.proj_info[2] = (1.0f - p_projections[v].matrix[0][2]) / p_projections[v].matrix[0][0];
push_constant.proj_info[3] = (1.0f + p_projections[v].matrix[1][2]) / p_projections[v].matrix[1][1];
- push_constant.metallic_mask[0] = CLAMP(p_metallic_mask.r * 255.0, 0, 255);
- push_constant.metallic_mask[1] = CLAMP(p_metallic_mask.g * 255.0, 0, 255);
- push_constant.metallic_mask[2] = CLAMP(p_metallic_mask.b * 255.0, 0, 255);
- push_constant.metallic_mask[3] = CLAMP(p_metallic_mask.a * 255.0, 0, 255);
ScreenSpaceReflectionMode mode = (p_roughness_quality != RS::ENV_SSR_ROUGHNESS_QUALITY_DISABLED) ? SCREEN_SPACE_REFLECTION_ROUGH : SCREEN_SPACE_REFLECTION_NORMAL;
RID shader = ssr.shader.version_get_shader(ssr.shader_version, mode);
diff --git a/servers/rendering/renderer_rd/effects/ss_effects.h b/servers/rendering/renderer_rd/effects/ss_effects.h
index a60f3a48ab..d50319c46f 100644
--- a/servers/rendering/renderer_rd/effects/ss_effects.h
+++ b/servers/rendering/renderer_rd/effects/ss_effects.h
@@ -168,7 +168,7 @@ public:
};
void ssr_allocate_buffers(SSRRenderBuffers &p_ssr_buffers, const RenderingDevice::DataFormat p_color_format, RenderingServer::EnvironmentSSRRoughnessQuality p_roughness_quality, const Size2i &p_screen_size, const uint32_t p_view_count);
- void screen_space_reflection(SSRRenderBuffers &p_ssr_buffers, const RID *p_diffuse_slices, const RID *p_normal_roughness_slices, RS::EnvironmentSSRRoughnessQuality p_roughness_quality, const RID *p_metallic_slices, const Color &p_metallic_mask, const RID *p_depth_slices, const Size2i &p_screen_size, int p_max_steps, float p_fade_in, float p_fade_out, float p_tolerance, const uint32_t p_view_count, const Projection *p_projections, const Vector3 *p_eye_offsets);
+ void screen_space_reflection(SSRRenderBuffers &p_ssr_buffers, const RID *p_diffuse_slices, const RID *p_normal_roughness_slices, RS::EnvironmentSSRRoughnessQuality p_roughness_quality, const RID *p_metallic_slices, const RID *p_depth_slices, const Size2i &p_screen_size, int p_max_steps, float p_fade_in, float p_fade_out, float p_tolerance, const uint32_t p_view_count, const Projection *p_projections, const Vector3 *p_eye_offsets);
void ssr_free(SSRRenderBuffers &p_ssr_buffers);
/* subsurface scattering */
@@ -465,10 +465,7 @@ private:
uint32_t orthogonal; // 4 - 52
float filter_mipmap_levels; // 4 - 56
uint32_t use_half_res; // 4 - 60
- uint8_t metallic_mask[4]; // 4 - 64
-
- uint32_t view_index; // 4 - 68
- uint32_t pad[3]; // 12 - 80
+ uint32_t view_index; // 4 - 64
// float projection[16]; // this is in our ScreenSpaceReflectionSceneData now
};
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
index a0f6e69fd5..dec5e23558 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
@@ -124,7 +124,7 @@ void RenderForwardClustered::RenderBufferDataForwardClustered::configure(RenderS
render_buffers = p_render_buffers;
ERR_FAIL_NULL(render_buffers);
- bool msaa_3d = render_buffers->get_msaa_3d();
+ RS::ViewportMSAA msaa_3d = render_buffers->get_msaa_3d();
if (msaa_3d != RS::VIEWPORT_MSAA_DISABLED) {
RD::DataFormat format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
@@ -1609,7 +1609,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
for (uint32_t v = 0; v < p_render_data->view_count; v++) {
specular_views[v] = rb_data->get_specular(v);
}
- _process_ssr(rb, color_only_framebuffer, normal_roughness_views, rb_data->get_specular(), specular_views, Color(0, 0, 0, 1), p_render_data->environment, p_render_data->view_projection, p_render_data->view_eye_offset, rb->get_msaa_3d() == RS::VIEWPORT_MSAA_DISABLED);
+ _process_ssr(rb, color_only_framebuffer, normal_roughness_views, rb_data->get_specular(), specular_views, p_render_data->environment, p_render_data->view_projection, p_render_data->view_eye_offset, rb->get_msaa_3d() == RS::VIEWPORT_MSAA_DISABLED);
RD::get_singleton()->draw_command_end_label();
} else {
//just mix specular back
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
index 78c83d1fb4..e2b06bc92f 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
@@ -1328,7 +1328,7 @@ void RendererSceneRenderRD::_process_sss(Ref<RenderSceneBuffersRD> p_render_buff
}
}
-void RendererSceneRenderRD::_process_ssr(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_dest_framebuffer, const RID *p_normal_slices, RID p_specular_buffer, const RID *p_metallic_slices, const Color &p_metallic_mask, RID p_environment, const Projection *p_projections, const Vector3 *p_eye_offsets, bool p_use_additive) {
+void RendererSceneRenderRD::_process_ssr(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_dest_framebuffer, const RID *p_normal_slices, RID p_specular_buffer, const RID *p_metallic_slices, RID p_environment, const Projection *p_projections, const Vector3 *p_eye_offsets, bool p_use_additive) {
ERR_FAIL_NULL(ss_effects);
ERR_FAIL_COND(p_render_buffers.is_null());
@@ -1355,7 +1355,7 @@ void RendererSceneRenderRD::_process_ssr(Ref<RenderSceneBuffersRD> p_render_buff
texture_slices[v] = p_render_buffers->get_internal_texture(v);
depth_slices[v] = p_render_buffers->get_depth_texture(v);
}
- ss_effects->screen_space_reflection(p_render_buffers->ssr, texture_slices, p_normal_slices, ssr_roughness_quality, p_metallic_slices, p_metallic_mask, depth_slices, half_size, environment_get_ssr_max_steps(p_environment), environment_get_ssr_fade_in(p_environment), environment_get_ssr_fade_out(p_environment), environment_get_ssr_depth_tolerance(p_environment), view_count, p_projections, p_eye_offsets);
+ ss_effects->screen_space_reflection(p_render_buffers->ssr, texture_slices, p_normal_slices, ssr_roughness_quality, p_metallic_slices, depth_slices, half_size, environment_get_ssr_max_steps(p_environment), environment_get_ssr_fade_in(p_environment), environment_get_ssr_fade_out(p_environment), environment_get_ssr_depth_tolerance(p_environment), view_count, p_projections, p_eye_offsets);
copy_effects->merge_specular(p_dest_framebuffer, p_specular_buffer, p_use_additive ? RID() : p_render_buffers->get_internal_texture(), p_render_buffers->ssr.output, view_count);
}
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h
index 76d2bc68fe..d47394989f 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h
@@ -138,7 +138,7 @@ protected:
virtual RID _render_buffers_get_velocity_texture(Ref<RenderSceneBuffersRD> p_render_buffers) = 0;
void _process_ssao(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_environment, RID p_normal_buffer, const Projection &p_projection);
- void _process_ssr(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_dest_framebuffer, const RID *p_normal_buffer_slices, RID p_specular_buffer, const RID *p_metallic_slices, const Color &p_metallic_mask, RID p_environment, const Projection *p_projections, const Vector3 *p_eye_offsets, bool p_use_additive);
+ void _process_ssr(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_dest_framebuffer, const RID *p_normal_buffer_slices, RID p_specular_buffer, const RID *p_metallic_slices, RID p_environment, const Projection *p_projections, const Vector3 *p_eye_offsets, bool p_use_additive);
void _process_sss(Ref<RenderSceneBuffersRD> p_render_buffers, const Projection &p_camera);
void _process_ssil(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_environment, RID p_normal_buffer, const Projection &p_projection, const Transform3D &p_transform);
diff --git a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl
index 246ef81cb2..9f86643e52 100644
--- a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl
@@ -30,12 +30,7 @@ layout(push_constant, std430) uniform Params {
bool orthogonal;
float filter_mipmap_levels;
bool use_half_res;
- uint metallic_mask;
-
uint view_index;
- uint pad1;
- uint pad2;
- uint pad3;
}
params;
@@ -167,7 +162,7 @@ void main() {
if (depth > z_to) {
// if depth was surpassed
- if (depth <= max(z_to, z_from) + params.depth_tolerance && -depth < params.camera_z_far) {
+ if (depth <= max(z_to, z_from) + params.depth_tolerance && -depth < params.camera_z_far * 0.95) {
// check the depth tolerance and far clip
// check that normal is valid
found = true;
@@ -231,18 +226,20 @@ void main() {
}
}
- // Isn't this going to be overwritten after our endif?
- final_color = imageLoad(source_diffuse, ivec2((final_pos - 0.5) * pixel_size));
-
imageStore(blur_radius_image, ssC, vec4(blur_radius / 255.0)); //stored in r8
#endif // MODE_ROUGH
final_color = vec4(imageLoad(source_diffuse, ivec2(final_pos - 0.5)).rgb, fade * margin_blend);
- //change blend by metallic
- vec4 metallic_mask = unpackUnorm4x8(params.metallic_mask);
- final_color.a *= dot(metallic_mask, texelFetch(source_metallic, ssC << 1, 0));
+ // Schlick term.
+ float metallic = texelFetch(source_metallic, ssC << 1, 0).w;
+ float f0 = mix(0.04, 1.0, metallic); // Assume a "specular" amount of 0.5
+ normal.y = -normal.y;
+ float m = clamp(1.0 - dot(normalize(normal), -view_dir), 0.0, 1.0);
+ float m2 = m * m;
+ m = m2 * m2 * m; // pow(m,5)
+ final_color.a *= f0 + (1.0 - f0) * m; // Fresnel Schlick term.
imageStore(ssr_image, ssC, final_color);
diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
index 4e6e29b315..2fba1351f7 100644
--- a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
@@ -97,11 +97,12 @@ void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, float atte
float diffuse_brdf_NL; // BRDF times N.L for calculating diffuse radiance
#if defined(DIFFUSE_LAMBERT_WRAP)
- // energy conserving lambert wrap shader
- diffuse_brdf_NL = max(0.0, (NdotL + roughness) / ((1.0 + roughness) * (1.0 + roughness)));
+ // Energy conserving lambert wrap shader.
+ // https://web.archive.org/web/20210228210901/http://blog.stevemcauley.com/2011/12/03/energy-conserving-wrapped-diffuse/
+ diffuse_brdf_NL = max(0.0, (NdotL + roughness) / ((1.0 + roughness) * (1.0 + roughness))) * (1.0 / M_PI);
#elif defined(DIFFUSE_TOON)
- diffuse_brdf_NL = smoothstep(-roughness, max(roughness, 0.01), NdotL);
+ diffuse_brdf_NL = smoothstep(-roughness, max(roughness, 0.01), NdotL) * (1.0 / M_PI);
#elif defined(DIFFUSE_BURLEY)
diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp
index 576ec81124..16fdbc07f5 100644
--- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp
@@ -128,6 +128,11 @@ void RenderSceneBuffersRD::cleanup() {
ss_effects.linear_depth_slices.clear();
}
+ if (ss_effects.downsample_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(ss_effects.downsample_uniform_set)) {
+ RD::get_singleton()->free(ss_effects.downsample_uniform_set);
+ ss_effects.downsample_uniform_set = RID();
+ }
+
sse->ssao_free(ss_effects.ssao);
sse->ssil_free(ss_effects.ssil);
sse->ssr_free(ssr);
@@ -535,7 +540,9 @@ void RenderSceneBuffersRD::ensure_velocity() {
RD::TEXTURE_SAMPLES_8,
};
- create_texture(RB_SCOPE_BUFFERS, RB_TEX_VELOCITY_MSAA, RD::DATA_FORMAT_R16G16_SFLOAT, msaa_usage_bits, ts[msaa_3d]);
+ RD::TextureSamples texture_samples = ts[msaa_3d];
+
+ create_texture(RB_SCOPE_BUFFERS, RB_TEX_VELOCITY_MSAA, RD::DATA_FORMAT_R16G16_SFLOAT, msaa_usage_bits, texture_samples);
}
create_texture(RB_SCOPE_BUFFERS, RB_TEX_VELOCITY, RD::DATA_FORMAT_R16G16_SFLOAT, usage_bits);
diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp
index d466f90e79..5771def45f 100644
--- a/servers/rendering/renderer_viewport.cpp
+++ b/servers/rendering/renderer_viewport.cpp
@@ -714,7 +714,14 @@ void RendererViewport::draw_viewports() {
blit_to_screen_list[vp->viewport_to_screen] = Vector<BlitToScreen>();
}
- blit_to_screen_list[vp->viewport_to_screen].push_back(blit);
+ if (OS::get_singleton()->get_current_rendering_driver_name() == "opengl3") {
+ Vector<BlitToScreen> blit_to_screen_vec;
+ blit_to_screen_vec.push_back(blit);
+ RSG::rasterizer->blit_render_targets_to_screen(vp->viewport_to_screen, blit_to_screen_vec.ptr(), 1);
+ RSG::rasterizer->end_frame(true);
+ } else {
+ blit_to_screen_list[vp->viewport_to_screen].push_back(blit);
+ }
}
}
diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp
index 1f686069bd..9103b0cf56 100644
--- a/servers/rendering/rendering_server_default.cpp
+++ b/servers/rendering/rendering_server_default.cpp
@@ -91,7 +91,10 @@ void RenderingServerDefault::_draw(bool p_swap_buffers, double frame_step) {
RSG::viewport->draw_viewports();
RSG::canvas_render->update();
- RSG::rasterizer->end_frame(p_swap_buffers);
+ if (OS::get_singleton()->get_current_rendering_driver_name() != "opengl3") {
+ // Already called for gl_compatibility renderer.
+ RSG::rasterizer->end_frame(p_swap_buffers);
+ }
XRServer *xr_server = XRServer::get_singleton();
if (xr_server != nullptr) {
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index e2519ba8d1..6754d84cd4 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -1069,7 +1069,7 @@ String ShaderLanguage::get_uniform_hint_name(ShaderNode::Uniform::Hint p_hint) {
result = "hint_range";
} break;
case ShaderNode::Uniform::HINT_SOURCE_COLOR: {
- result = "hint_color";
+ result = "source_color";
} break;
case ShaderNode::Uniform::HINT_NORMAL: {
result = "hint_normal";
@@ -1171,6 +1171,10 @@ void ShaderLanguage::clear() {
last_type = IDENTIFIER_MAX;
current_uniform_group_name = "";
current_uniform_subgroup_name = "";
+ current_uniform_hint = ShaderNode::Uniform::HINT_NONE;
+ current_uniform_filter = FILTER_DEFAULT;
+ current_uniform_repeat = REPEAT_DEFAULT;
+ current_uniform_instance_index_defined = false;
completion_type = COMPLETION_NONE;
completion_block = nullptr;
@@ -8617,6 +8621,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
}
custom_instance_index = tk.constant;
+ current_uniform_instance_index_defined = true;
if (custom_instance_index >= MAX_INSTANCE_UNIFORM_INDICES) {
_set_error(vformat(RTR("Allowed instance uniform indices must be within [0..%d] range."), MAX_INSTANCE_UNIFORM_INDICES - 1));
@@ -8682,6 +8687,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
return ERR_PARSE_ERROR;
} else {
uniform.hint = new_hint;
+ current_uniform_hint = new_hint;
}
}
@@ -8695,6 +8701,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
return ERR_PARSE_ERROR;
} else {
uniform.filter = new_filter;
+ current_uniform_filter = new_filter;
}
}
@@ -8708,6 +8715,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
return ERR_PARSE_ERROR;
} else {
uniform.repeat = new_repeat;
+ current_uniform_repeat = new_repeat;
}
}
@@ -8775,6 +8783,11 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
keyword_completion_context = CF_GLOBAL_SPACE;
#endif // DEBUG_ENABLED
completion_type = COMPLETION_NONE;
+
+ current_uniform_hint = ShaderNode::Uniform::HINT_NONE;
+ current_uniform_filter = FILTER_DEFAULT;
+ current_uniform_repeat = REPEAT_DEFAULT;
+ current_uniform_instance_index_defined = false;
} else { // varying
ShaderNode::Varying varying;
varying.type = type;
@@ -10311,28 +10324,33 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_
} break;
case COMPLETION_HINT: {
if (completion_base == DataType::TYPE_VEC3 || completion_base == DataType::TYPE_VEC4) {
- ScriptLanguage::CodeCompletionOption option("source_color", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
- r_options->push_back(option);
+ if (current_uniform_hint == ShaderNode::Uniform::HINT_NONE) {
+ ScriptLanguage::CodeCompletionOption option("source_color", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
+ r_options->push_back(option);
+ }
} else if ((completion_base == DataType::TYPE_INT || completion_base == DataType::TYPE_FLOAT) && !completion_base_array) {
- ScriptLanguage::CodeCompletionOption option("hint_range", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
+ if (current_uniform_hint == ShaderNode::Uniform::HINT_NONE) {
+ ScriptLanguage::CodeCompletionOption option("hint_range", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
- if (completion_base == DataType::TYPE_INT) {
- option.insert_text = "hint_range(0, 100, 1)";
- } else {
- option.insert_text = "hint_range(0.0, 1.0, 0.1)";
- }
+ if (completion_base == DataType::TYPE_INT) {
+ option.insert_text = "hint_range(0, 100, 1)";
+ } else {
+ option.insert_text = "hint_range(0.0, 1.0, 0.1)";
+ }
- r_options->push_back(option);
+ r_options->push_back(option);
+ }
} else if ((int(completion_base) > int(TYPE_MAT4) && int(completion_base) < int(TYPE_STRUCT)) && !completion_base_array) {
- static Vector<String> options;
-
- if (options.is_empty()) {
+ Vector<String> options;
+ if (current_uniform_filter == FILTER_DEFAULT) {
options.push_back("filter_linear");
options.push_back("filter_linear_mipmap");
options.push_back("filter_linear_mipmap_anisotropic");
options.push_back("filter_nearest");
options.push_back("filter_nearest_mipmap");
options.push_back("filter_nearest_mipmap_anisotropic");
+ }
+ if (current_uniform_hint == ShaderNode::Uniform::HINT_NONE) {
options.push_back("hint_anisotropy");
options.push_back("hint_default_black");
options.push_back("hint_default_white");
@@ -10348,6 +10366,8 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_
options.push_back("hint_normal_roughness_texture");
options.push_back("hint_depth_texture");
options.push_back("source_color");
+ }
+ if (current_uniform_repeat == REPEAT_DEFAULT) {
options.push_back("repeat_enable");
options.push_back("repeat_disable");
}
@@ -10357,7 +10377,7 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_
r_options->push_back(option);
}
}
- if (!completion_base_array) {
+ if (!completion_base_array && !current_uniform_instance_index_defined) {
ScriptLanguage::CodeCompletionOption option("instance_index", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
option.insert_text = "instance_index(0)";
r_options->push_back(option);
diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h
index 75b713d167..e9f8c3b289 100644
--- a/servers/rendering/shader_language.h
+++ b/servers/rendering/shader_language.h
@@ -1050,6 +1050,10 @@ private:
};
CompletionType completion_type;
+ ShaderNode::Uniform::Hint current_uniform_hint = ShaderNode::Uniform::HINT_NONE;
+ TextureFilter current_uniform_filter = FILTER_DEFAULT;
+ TextureRepeat current_uniform_repeat = REPEAT_DEFAULT;
+ bool current_uniform_instance_index_defined = false;
int completion_line = 0;
BlockNode *completion_block = nullptr;
DataType completion_base;