summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--SConstruct4
-rw-r--r--doc/classes/Node2D.xml27
-rw-r--r--doc/classes/RDShaderSource.xml1
-rw-r--r--doc/classes/TreeItem.xml10
-rw-r--r--drivers/gles3/storage/light_storage.cpp5
-rw-r--r--drivers/gles3/storage/material_storage.cpp4
-rw-r--r--editor/connections_dialog.cpp5
-rw-r--r--editor/editor_dock_manager.cpp4
-rw-r--r--editor/editor_node.cpp116
-rw-r--r--editor/editor_node.h11
-rw-r--r--editor/editor_properties.cpp6
-rw-r--r--editor/gui/editor_spin_slider.cpp2
-rw-r--r--editor/import/3d/scene_import_settings.cpp66
-rw-r--r--editor/import/3d/scene_import_settings.h4
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp2
-rw-r--r--editor/plugins/path_3d_editor_plugin.cpp73
-rw-r--r--editor/plugins/path_3d_editor_plugin.h4
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.cpp61
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.h11
-rw-r--r--editor/scene_tree_dock.cpp4
-rw-r--r--editor/themes/editor_theme_manager.cpp4
-rw-r--r--modules/minimp3/audio_stream_mp3.cpp3
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.cpp3
-rw-r--r--platform/linuxbsd/wayland/wayland_thread.cpp9
-rw-r--r--platform/macos/export/export_plugin.cpp19
-rw-r--r--scene/2d/canvas_modulate.cpp2
-rw-r--r--scene/2d/light_2d.cpp2
-rw-r--r--scene/2d/light_occluder_2d.cpp2
-rw-r--r--scene/2d/navigation_link_2d.cpp2
-rw-r--r--scene/2d/parallax_layer.cpp2
-rw-r--r--scene/2d/path_2d.cpp2
-rw-r--r--scene/2d/physics/collision_object_2d.cpp2
-rw-r--r--scene/2d/physics/collision_polygon_2d.cpp2
-rw-r--r--scene/2d/physics/collision_shape_2d.cpp2
-rw-r--r--scene/2d/physics/physical_bone_2d.cpp2
-rw-r--r--scene/2d/remote_transform_2d.cpp2
-rw-r--r--scene/2d/skeleton_2d.cpp2
-rw-r--r--scene/2d/tile_map.cpp2
-rw-r--r--scene/3d/decal.cpp2
-rw-r--r--scene/3d/fog_volume.cpp2
-rw-r--r--scene/3d/gpu_particles_collision_3d.cpp2
-rw-r--r--scene/3d/lightmap_gi.cpp2
-rw-r--r--scene/3d/navigation_link_3d.cpp2
-rw-r--r--scene/3d/navigation_region_3d.cpp2
-rw-r--r--scene/3d/occluder_instance_3d.cpp2
-rw-r--r--scene/3d/path_3d.cpp2
-rw-r--r--scene/3d/physics/collision_object_3d.cpp2
-rw-r--r--scene/3d/physics/collision_polygon_3d.cpp2
-rw-r--r--scene/3d/physics/collision_shape_3d.cpp2
-rw-r--r--scene/3d/physics/vehicle_body_3d.cpp2
-rw-r--r--scene/3d/remote_transform_3d.cpp2
-rw-r--r--scene/3d/soft_body_3d.cpp2
-rw-r--r--scene/3d/visual_instance_3d.cpp2
-rw-r--r--scene/3d/voxel_gi.cpp2
-rw-r--r--scene/3d/xr_nodes.cpp6
-rw-r--r--scene/animation/animation_mixer.cpp3
-rw-r--r--scene/animation/animation_tree.cpp2
-rw-r--r--scene/gui/control.cpp27
-rw-r--r--scene/gui/range.cpp2
-rw-r--r--scene/gui/rich_text_label.cpp29
-rw-r--r--scene/gui/subviewport_container.cpp2
-rw-r--r--scene/gui/tree.cpp18
-rw-r--r--scene/resources/audio_stream_polyphonic.cpp8
-rw-r--r--scene/resources/audio_stream_wav.cpp71
-rw-r--r--scene/resources/audio_stream_wav.h4
-rw-r--r--scene/resources/packed_scene.cpp19
-rw-r--r--servers/audio/audio_stream.cpp10
-rw-r--r--servers/audio/audio_stream.h1
-rw-r--r--servers/audio_server.cpp48
-rw-r--r--servers/audio_server.h2
-rw-r--r--servers/rendering/renderer_rd/storage_rd/material_storage.cpp4
-rw-r--r--servers/rendering/shader_compiler.cpp2
-rw-r--r--servers/rendering/shader_language.cpp607
-rw-r--r--servers/rendering/shader_language.h44
74 files changed, 997 insertions, 428 deletions
diff --git a/SConstruct b/SConstruct
index 5c1ccd2449..178f2d86a2 100644
--- a/SConstruct
+++ b/SConstruct
@@ -1053,7 +1053,7 @@ if env["ninja"]:
SetOption("experimental", "ninja")
env["NINJA_FILE_NAME"] = env["ninja_file"]
env["NINJA_DISABLE_AUTO_RUN"] = not env["ninja_auto_run"]
- env.Tool("ninja")
+ env.Tool("ninja", "build.ninja")
# Threads
if env["threads"]:
@@ -1116,7 +1116,7 @@ atexit.register(print_elapsed_time)
def purge_flaky_files():
- paths_to_keep = ["ninja.build"]
+ paths_to_keep = ["build.ninja"]
for build_failure in GetBuildFailures():
path = build_failure.node.path
if os.path.isfile(path) and path not in paths_to_keep:
diff --git a/doc/classes/Node2D.xml b/doc/classes/Node2D.xml
index 851290de7b..0b2dfcea03 100644
--- a/doc/classes/Node2D.xml
+++ b/doc/classes/Node2D.xml
@@ -95,43 +95,44 @@
</methods>
<members>
<member name="global_position" type="Vector2" setter="set_global_position" getter="get_global_position">
- Global position.
+ Global position. See also [member position].
</member>
<member name="global_rotation" type="float" setter="set_global_rotation" getter="get_global_rotation">
- Global rotation in radians.
+ Global rotation in radians. See also [member rotation].
</member>
<member name="global_rotation_degrees" type="float" setter="set_global_rotation_degrees" getter="get_global_rotation_degrees">
- Helper property to access [member global_rotation] in degrees instead of radians.
+ Helper property to access [member global_rotation] in degrees instead of radians. See also [member rotation_degrees].
</member>
<member name="global_scale" type="Vector2" setter="set_global_scale" getter="get_global_scale">
- Global scale.
+ Global scale. See also [member scale].
</member>
<member name="global_skew" type="float" setter="set_global_skew" getter="get_global_skew">
- Global skew in radians.
+ Global skew in radians. See also [member skew].
</member>
<member name="global_transform" type="Transform2D" setter="set_global_transform" getter="get_global_transform">
- Global [Transform2D].
+ Global [Transform2D]. See also [member transform].
</member>
<member name="position" type="Vector2" setter="set_position" getter="get_position" default="Vector2(0, 0)">
- Position, relative to the node's parent.
+ Position, relative to the node's parent. See also [member global_position].
</member>
<member name="rotation" type="float" setter="set_rotation" getter="get_rotation" default="0.0">
- Rotation in radians, relative to the node's parent.
+ Rotation in radians, relative to the node's parent. See also [member global_rotation].
[b]Note:[/b] This property is edited in the inspector in degrees. If you want to use degrees in a script, use [member rotation_degrees].
</member>
<member name="rotation_degrees" type="float" setter="set_rotation_degrees" getter="get_rotation_degrees">
- Helper property to access [member rotation] in degrees instead of radians.
+ Helper property to access [member rotation] in degrees instead of radians. See also [member global_rotation_degrees].
</member>
<member name="scale" type="Vector2" setter="set_scale" getter="get_scale" default="Vector2(1, 1)">
- The node's scale. Unscaled value: [code](1, 1)[/code].
+ The node's scale, relative to the node's parent. Unscaled value: [code](1, 1)[/code]. See also [member global_scale].
[b]Note:[/b] Negative X scales in 2D are not decomposable from the transformation matrix. Due to the way scale is represented with transformation matrices in Godot, negative scales on the X axis will be changed to negative scales on the Y axis and a rotation of 180 degrees when decomposed.
</member>
<member name="skew" type="float" setter="set_skew" getter="get_skew" default="0.0">
- Slants the node.
- [b]Note:[/b] Skew is X axis only.
+ If set to a non-zero value, slants the node in one direction or another. This can be used for pseudo-3D effects. See also [member global_skew].
+ [b]Note:[/b] Skew is performed on the X axis only, and [i]between[/i] rotation and scaling.
+ [b]Note:[/b] This property is edited in the inspector in degrees. If you want to use degrees in a script, use [code]skew = deg_to_rad(value_in_degrees)[/code].
</member>
<member name="transform" type="Transform2D" setter="set_transform" getter="get_transform">
- Local [Transform2D].
+ The node's [Transform2D], relative to the node's parent. See also [member global_transform].
</member>
</members>
</class>
diff --git a/doc/classes/RDShaderSource.xml b/doc/classes/RDShaderSource.xml
index 062c22f11f..a7b897d56e 100644
--- a/doc/classes/RDShaderSource.xml
+++ b/doc/classes/RDShaderSource.xml
@@ -23,6 +23,7 @@
<param index="1" name="source" type="String" />
<description>
Sets [param source] code for the specified shader [param stage]. Equivalent to setting one of [member source_compute], [member source_fragment], [member source_tesselation_control], [member source_tesselation_evaluation] or [member source_vertex].
+ [b]Note:[/b] If you set the compute shader source code using this method directly, remember to remove the Godot-specific hint [code]#[compute][/code].
</description>
</method>
</methods>
diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml
index 78a703c213..bd46c54990 100644
--- a/doc/classes/TreeItem.xml
+++ b/doc/classes/TreeItem.xml
@@ -19,7 +19,7 @@
<param index="3" name="disabled" type="bool" default="false" />
<param index="4" name="tooltip_text" type="String" default="&quot;&quot;" />
<description>
- Adds a button with [Texture2D] [param button] at column [param column]. The [param id] is used to identify the button in the according [signal Tree.button_clicked] signal and can be different from the buttons index. If not specified, the next available index is used, which may be retrieved by calling [method get_button_count] immediately before this method. Optionally, the button can be [param disabled] and have a [param tooltip_text].
+ Adds a button with [Texture2D] [param button] to the end of the cell at column [param column]. The [param id] is used to identify the button in the according [signal Tree.button_clicked] signal and can be different from the buttons index. If not specified, the next available index is used, which may be retrieved by calling [method get_button_count] immediately before this method. Optionally, the button can be [param disabled] and have a [param tooltip_text].
</description>
</method>
<method name="add_child">
@@ -643,7 +643,7 @@
<param index="0" name="column" type="int" />
<param index="1" name="texture" type="Texture2D" />
<description>
- Sets the given cell's icon [Texture2D]. The cell has to be in [constant CELL_MODE_ICON] mode.
+ Sets the given cell's icon [Texture2D]. If the cell is in [constant CELL_MODE_ICON] mode, the icon is displayed in the center of the cell. Otherwise, the icon is displayed before the cell's text. [constant CELL_MODE_RANGE] does not display an icon.
</description>
</method>
<method name="set_icon_max_width">
@@ -811,17 +811,17 @@
</members>
<constants>
<constant name="CELL_MODE_STRING" value="0" enum="TreeCellMode">
- Cell shows a string label. When editable, the text can be edited using a [LineEdit], or a [TextEdit] popup if [method set_edit_multiline] is used.
+ Cell shows a string label, optionally with an icon. When editable, the text can be edited using a [LineEdit], or a [TextEdit] popup if [method set_edit_multiline] is used.
</constant>
<constant name="CELL_MODE_CHECK" value="1" enum="TreeCellMode">
- Cell shows a checkbox, optionally with text. The checkbox can be pressed, released, or indeterminate (via [method set_indeterminate]). The checkbox can't be clicked unless the cell is editable.
+ Cell shows a checkbox, optionally with text and an icon. The checkbox can be pressed, released, or indeterminate (via [method set_indeterminate]). The checkbox can't be clicked unless the cell is editable.
</constant>
<constant name="CELL_MODE_RANGE" value="2" enum="TreeCellMode">
Cell shows a numeric range. When editable, it can be edited using a range slider. Use [method set_range] to set the value and [method set_range_config] to configure the range.
This cell can also be used in a text dropdown mode when you assign a text with [method set_text]. Separate options with a comma, e.g. [code]"Option1,Option2,Option3"[/code].
</constant>
<constant name="CELL_MODE_ICON" value="3" enum="TreeCellMode">
- Cell shows an icon. It can't be edited nor display text.
+ Cell shows an icon. It can't be edited nor display text. The icon is always centered within the cell.
</constant>
<constant name="CELL_MODE_CUSTOM" value="4" enum="TreeCellMode">
Cell shows as a clickable button. It will display an arrow similar to [OptionButton], but doesn't feature a dropdown (for that you can use [constant CELL_MODE_RANGE]). Clicking the button emits the [signal Tree.item_edited] signal. The button is flat by default, you can use [method set_custom_as_button] to display it with a [StyleBox].
diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp
index aab1aadf02..9b976c2206 100644
--- a/drivers/gles3/storage/light_storage.cpp
+++ b/drivers/gles3/storage/light_storage.cpp
@@ -1521,6 +1521,11 @@ bool LightStorage::_shadow_atlas_find_shadow(ShadowAtlas *shadow_atlas, int *p_i
uint64_t min_pass = 0; // Pass of the existing one, try to use the least recently used one (LRU fashion).
for (int j = 0; j < sc; j++) {
+ if (sarr[j].owner_is_omni != is_omni) {
+ // Existing light instance type doesn't match new light instance type skip.
+ continue;
+ }
+
LightInstance *sli = light_instance_owner.get_or_null(sarr[j].owner);
if (!sli) {
// Found a released light instance.
diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp
index a37eba3b15..7d5af48384 100644
--- a/drivers/gles3/storage/material_storage.cpp
+++ b/drivers/gles3/storage/material_storage.cpp
@@ -348,7 +348,7 @@ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataType type, int p_
}
}
-_FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, const Vector<ShaderLanguage::ConstantNode::Value> &value, uint8_t *data) {
+_FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, const Vector<ShaderLanguage::Scalar> &value, uint8_t *data) {
switch (type) {
case ShaderLanguage::TYPE_BOOL: {
uint32_t *gui = (uint32_t *)data;
@@ -572,7 +572,7 @@ void ShaderData::set_default_texture_parameter(const StringName &p_name, RID p_t
Variant ShaderData::get_default_parameter(const StringName &p_parameter) const {
if (uniforms.has(p_parameter)) {
ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter];
- Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value;
+ Vector<ShaderLanguage::Scalar> default_value = uniform.default_value;
return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint);
}
return Variant();
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp
index 063241fd1b..5273b61f37 100644
--- a/editor/connections_dialog.cpp
+++ b/editor/connections_dialog.cpp
@@ -486,11 +486,6 @@ void ConnectDialog::_notification(int p_what) {
type_list->set_item_icon(i, get_editor_theme_icon(type_name));
}
- Ref<StyleBox> style = get_theme_stylebox(CoreStringName(normal), "LineEdit")->duplicate();
- if (style.is_valid()) {
- style->set_content_margin(SIDE_TOP, style->get_content_margin(SIDE_TOP) + 1.0);
- from_signal->add_theme_style_override(CoreStringName(normal), style);
- }
method_search->set_right_icon(get_editor_theme_icon("Search"));
open_method_tree->set_icon(get_editor_theme_icon("Edit"));
} break;
diff --git a/editor/editor_dock_manager.cpp b/editor/editor_dock_manager.cpp
index 75135532aa..0bdda41f26 100644
--- a/editor/editor_dock_manager.cpp
+++ b/editor/editor_dock_manager.cpp
@@ -509,7 +509,7 @@ void EditorDockManager::save_docks_to_config(Ref<ConfigFile> p_layout, const Str
}
for (int i = 0; i < hsplits.size(); i++) {
- p_layout->set_value(p_section, "dock_hsplit_" + itos(i + 1), hsplits[i]->get_split_offset());
+ p_layout->set_value(p_section, "dock_hsplit_" + itos(i + 1), int(hsplits[i]->get_split_offset() / EDSCALE));
}
FileSystemDock::get_singleton()->save_layout_to_config(p_layout, p_section);
@@ -605,7 +605,7 @@ void EditorDockManager::load_docks_from_config(Ref<ConfigFile> p_layout, const S
continue;
}
int ofs = p_layout->get_value(p_section, "dock_hsplit_" + itos(i + 1));
- hsplits[i]->set_split_offset(ofs);
+ hsplits[i]->set_split_offset(ofs * EDSCALE);
}
FileSystemDock::get_singleton()->load_layout_from_config(p_layout, p_section);
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 6899a35ded..39291138a6 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -4404,6 +4404,21 @@ bool EditorNode::is_additional_node_in_scene(Node *p_edited_scene, Node *p_reimp
return true;
}
+void EditorNode::get_scene_editor_data_for_node(Node *p_root, Node *p_node, HashMap<NodePath, SceneEditorDataEntry> &p_table) {
+ SceneEditorDataEntry new_entry;
+ new_entry.is_display_folded = p_node->is_displayed_folded();
+
+ if (p_root != p_node) {
+ new_entry.is_editable = p_root->is_editable_instance(p_node);
+ }
+
+ p_table.insert(p_root->get_path_to(p_node), new_entry);
+
+ for (int i = 0; i < p_node->get_child_count(); i++) {
+ get_scene_editor_data_for_node(p_root, p_node->get_child(i), p_table);
+ }
+}
+
void EditorNode::get_preload_scene_modification_table(
Node *p_edited_scene,
Node *p_reimported_root,
@@ -4510,7 +4525,7 @@ void EditorNode::get_preload_scene_modification_table(
void EditorNode::get_preload_modifications_reference_to_nodes(
Node *p_root,
Node *p_node,
- List<Node *> &p_excluded_nodes,
+ HashSet<Node *> &p_excluded_nodes,
List<Node *> &p_instance_list_with_children,
HashMap<NodePath, ModificationNodeEntry> &p_modification_table) {
if (!p_excluded_nodes.find(p_node)) {
@@ -5984,12 +5999,14 @@ void EditorNode::reload_scene(const String &p_path) {
scene_tabs->set_current_tab(current_tab);
}
-void EditorNode::find_all_instances_inheriting_path_in_node(Node *p_root, Node *p_node, const String &p_instance_path, List<Node *> &p_instance_list) {
+void EditorNode::find_all_instances_inheriting_path_in_node(Node *p_root, Node *p_node, const String &p_instance_path, HashSet<Node *> &p_instance_list) {
String scene_file_path = p_node->get_scene_file_path();
- // This is going to get messy...
+ bool valid_instance_found = false;
+
+ // Attempt to find all the instances matching path we're going to reload.
if (p_node->get_scene_file_path() == p_instance_path) {
- p_instance_list.push_back(p_node);
+ valid_instance_found = true;
} else {
Node *current_node = p_node;
@@ -5997,7 +6014,7 @@ void EditorNode::find_all_instances_inheriting_path_in_node(Node *p_root, Node *
while (inherited_state.is_valid()) {
String inherited_path = inherited_state->get_path();
if (inherited_path == p_instance_path) {
- p_instance_list.push_back(p_node);
+ valid_instance_found = true;
break;
}
@@ -6005,6 +6022,19 @@ void EditorNode::find_all_instances_inheriting_path_in_node(Node *p_root, Node *
}
}
+ // Instead of adding this instance directly, if its not owned by the scene, walk its ancestors
+ // and find the first node still owned by the scene. This is what we will reloading instead.
+ if (valid_instance_found) {
+ Node *current_node = p_node;
+ while (true) {
+ if (current_node->get_owner() == p_root || current_node->get_owner() == nullptr) {
+ p_instance_list.insert(current_node);
+ break;
+ }
+ current_node = current_node->get_parent();
+ }
+ }
+
for (int i = 0; i < p_node->get_child_count(); i++) {
find_all_instances_inheriting_path_in_node(p_root, p_node->get_child(i), p_instance_path, p_instance_list);
}
@@ -6023,20 +6053,20 @@ void EditorNode::preload_reimporting_with_path_in_edited_scenes(const List<Strin
Node *edited_scene_root = editor_data.get_edited_scene_root(current_scene_idx);
if (edited_scene_root) {
- SceneModificationsEntry scene_motifications;
+ SceneModificationsEntry scene_modifications;
for (const String &instance_path : p_scenes) {
if (editor_data.get_scene_path(current_scene_idx) == instance_path) {
continue;
}
- List<Node *> instance_list;
- find_all_instances_inheriting_path_in_node(edited_scene_root, edited_scene_root, instance_path, instance_list);
- if (instance_list.size() > 0) {
+ HashSet<Node *> instances_to_reimport;
+ find_all_instances_inheriting_path_in_node(edited_scene_root, edited_scene_root, instance_path, instances_to_reimport);
+ if (instances_to_reimport.size() > 0) {
editor_data.set_edited_scene(current_scene_idx);
List<Node *> instance_list_with_children;
- for (Node *original_node : instance_list) {
+ for (Node *original_node : instances_to_reimport) {
InstanceModificationsEntry instance_modifications;
// Fetching all the modified properties of the nodes reimported scene.
@@ -6044,19 +6074,19 @@ void EditorNode::preload_reimporting_with_path_in_edited_scenes(const List<Strin
instance_modifications.original_node = original_node;
instance_modifications.instance_path = instance_path;
- scene_motifications.instance_list.push_back(instance_modifications);
+ scene_modifications.instance_list.push_back(instance_modifications);
instance_list_with_children.push_back(original_node);
get_children_nodes(original_node, instance_list_with_children);
}
// Search the scene to find nodes that references the nodes will be recreated.
- get_preload_modifications_reference_to_nodes(edited_scene_root, edited_scene_root, instance_list, instance_list_with_children, scene_motifications.other_instances_modifications);
+ get_preload_modifications_reference_to_nodes(edited_scene_root, edited_scene_root, instances_to_reimport, instance_list_with_children, scene_modifications.other_instances_modifications);
}
}
- if (scene_motifications.instance_list.size() > 0) {
- scenes_modification_table[current_scene_idx] = scene_motifications;
+ if (scene_modifications.instance_list.size() > 0) {
+ scenes_modification_table[current_scene_idx] = scene_modifications;
}
}
}
@@ -6179,10 +6209,10 @@ void EditorNode::reload_instances_with_path_in_edited_scenes() {
// Instantiate early so that caches cleared on load in SceneState can be rebuilt early.
Node *instantiated_node = nullptr;
- // If we are in a inherit scene, it's easier to create a new base scene and
+ // If we are in a inherited scene, it's easier to create a new base scene and
// grab the node from there.
// When scene_path_to_node is '.' and we have scene_inherited_state, it's because
- // it's a muli-level inheritance scene. We should use
+ // it's a multi-level inheritance scene. We should use
NodePath scene_path_to_node = current_edited_scene->get_path_to(original_node);
Ref<SceneState> scene_state = current_edited_scene->get_scene_inherited_state();
if (scene_path_to_node != "." && scene_state.is_valid() && scene_state->get_path() != instance_modifications.instance_path && scene_state->find_node_by_path(scene_path_to_node) >= 0) {
@@ -6206,9 +6236,9 @@ void EditorNode::reload_instances_with_path_in_edited_scenes() {
if (!instantiated_node) {
// If no base scene was found to create the node, we will use the reimported packed scene directly.
- // But, when the current edited scene is the reimported scene, it's because it's a inherited scene
- // of the reimported scene. In that case, we will not instantiate current_packed_scene, because
- // we would reinstanciate ourself. Using the base scene is better.
+ // But, when the current edited scene is the reimported scene, it's because it's an inherited scene
+ // derived from the reimported scene. In that case, we will not instantiate current_packed_scene, because
+ // we would reinstantiate ourself. Using the base scene is better.
if (current_edited_scene == original_node) {
if (base_packed_scene.is_valid()) {
instantiated_node = base_packed_scene->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE);
@@ -6264,6 +6294,17 @@ void EditorNode::reload_instances_with_path_in_edited_scenes() {
// crash when reimporting scenes with animations when "Editable children" was enabled.
replace_history_reimported_nodes(original_node, instantiated_node, original_node);
+ // Reset the editable instance state.
+ HashMap<NodePath, SceneEditorDataEntry> scene_editor_data_table;
+ Node *owner = original_node->get_owner();
+ if (!owner) {
+ owner = original_node;
+ }
+
+ get_scene_editor_data_for_node(owner, original_node, scene_editor_data_table);
+
+ bool original_node_scene_instance_load_placeholder = original_node->get_scene_instance_load_placeholder();
+
// Delete all the remaining node children.
while (original_node->get_child_count()) {
Node *child = original_node->get_child(0);
@@ -6272,16 +6313,6 @@ void EditorNode::reload_instances_with_path_in_edited_scenes() {
child->queue_free();
}
- // Reset the editable instance state.
- bool is_editable = true;
- Node *owner = original_node->get_owner();
- if (owner) {
- is_editable = owner->is_editable_instance(original_node);
- }
-
- bool original_node_is_displayed_folded = original_node->is_displayed_folded();
- bool original_node_scene_instance_load_placeholder = original_node->get_scene_instance_load_placeholder();
-
// Update the name to match
instantiated_node->set_name(original_node->get_name());
@@ -6312,19 +6343,9 @@ void EditorNode::reload_instances_with_path_in_edited_scenes() {
// Mark the old node for deletion.
original_node->queue_free();
- // Restore the folded and placeholder state from the original node.
- instantiated_node->set_display_folded(original_node_is_displayed_folded);
+ // Restore the placeholder state from the original node.
instantiated_node->set_scene_instance_load_placeholder(original_node_scene_instance_load_placeholder);
- if (owner) {
- Ref<SceneState> ss_inst = owner->get_scene_instance_state();
- if (ss_inst.is_valid()) {
- ss_inst->update_instance_resource(instance_modifications.instance_path, current_packed_scene);
- }
-
- owner->set_editable_instance(instantiated_node, is_editable);
- }
-
// Attempt to re-add all the additional nodes.
for (AdditiveNodeEntry additive_node_entry : instance_modifications.addition_list) {
Node *parent_node = instantiated_node->get_node_or_null(additive_node_entry.parent);
@@ -6356,6 +6377,17 @@ void EditorNode::reload_instances_with_path_in_edited_scenes() {
}
}
+ // Restore the scene's editable instance and folded states.
+ for (HashMap<NodePath, SceneEditorDataEntry>::Iterator I = scene_editor_data_table.begin(); I; ++I) {
+ Node *node = owner->get_node_or_null(I->key);
+ if (node) {
+ if (owner != node) {
+ owner->set_editable_instance(node, I->value.is_editable);
+ }
+ node->set_display_folded(I->value.is_display_folded);
+ }
+ }
+
// Restore the selection.
if (selected_node_paths.size()) {
for (NodePath selected_node_path : selected_node_paths) {
@@ -7585,8 +7617,8 @@ EditorNode::EditorNode() {
default_layout->set_value(docks_section, "dock_split_" + itos(i + 1), 0);
}
default_layout->set_value(docks_section, "dock_hsplit_1", 0);
- default_layout->set_value(docks_section, "dock_hsplit_2", 270 * EDSCALE);
- default_layout->set_value(docks_section, "dock_hsplit_3", -270 * EDSCALE);
+ default_layout->set_value(docks_section, "dock_hsplit_2", 270);
+ default_layout->set_value(docks_section, "dock_hsplit_3", -270);
default_layout->set_value(docks_section, "dock_hsplit_4", 0);
_update_layouts_menu();
diff --git a/editor/editor_node.h b/editor/editor_node.h
index e9d2c28528..f1b50cf3fa 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -852,12 +852,19 @@ public:
HashMap<NodePath, ModificationNodeEntry> other_instances_modifications;
};
+ struct SceneEditorDataEntry {
+ bool is_editable;
+ bool is_display_folded;
+ };
+
HashMap<int, SceneModificationsEntry> scenes_modification_table;
List<String> scenes_reimported;
List<String> resources_reimported;
void update_node_from_node_modification_entry(Node *p_node, ModificationNodeEntry &p_node_modification);
+ void get_scene_editor_data_for_node(Node *p_root, Node *p_node, HashMap<NodePath, SceneEditorDataEntry> &p_table);
+
void get_preload_scene_modification_table(
Node *p_edited_scene,
Node *p_reimported_root,
@@ -866,7 +873,7 @@ public:
void get_preload_modifications_reference_to_nodes(
Node *p_root,
Node *p_node,
- List<Node *> &p_excluded_nodes,
+ HashSet<Node *> &p_excluded_nodes,
List<Node *> &p_instance_list_with_children,
HashMap<NodePath, ModificationNodeEntry> &p_modification_table);
void get_children_nodes(Node *p_node, List<Node *> &p_nodes);
@@ -927,7 +934,7 @@ public:
void reload_scene(const String &p_path);
- void find_all_instances_inheriting_path_in_node(Node *p_root, Node *p_node, const String &p_instance_path, List<Node *> &p_instance_list);
+ void find_all_instances_inheriting_path_in_node(Node *p_root, Node *p_node, const String &p_instance_path, HashSet<Node *> &p_instance_list);
void preload_reimporting_with_path_in_edited_scenes(const List<String> &p_scenes);
void reload_instances_with_path_in_edited_scenes();
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index 123d903220..6e375dc1fc 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -2703,7 +2703,11 @@ void EditorPropertyNodePath::_update_menu() {
void EditorPropertyNodePath::_menu_option(int p_idx) {
switch (p_idx) {
case ACTION_CLEAR: {
- emit_changed(get_edited_property(), NodePath());
+ if (editing_node) {
+ emit_changed(get_edited_property(), Variant());
+ } else {
+ emit_changed(get_edited_property(), NodePath());
+ }
update_property();
} break;
diff --git a/editor/gui/editor_spin_slider.cpp b/editor/gui/editor_spin_slider.cpp
index d49d597084..a073a2338b 100644
--- a/editor/gui/editor_spin_slider.cpp
+++ b/editor/gui/editor_spin_slider.cpp
@@ -636,7 +636,7 @@ void EditorSpinSlider::_grabber_mouse_exited() {
void EditorSpinSlider::set_read_only(bool p_enable) {
read_only = p_enable;
- if (read_only && value_input) {
+ if (read_only && value_input && value_input->is_inside_tree()) {
value_input->release_focus();
}
diff --git a/editor/import/3d/scene_import_settings.cpp b/editor/import/3d/scene_import_settings.cpp
index 7ca3cb6c3a..011d0135b4 100644
--- a/editor/import/3d/scene_import_settings.cpp
+++ b/editor/import/3d/scene_import_settings.cpp
@@ -37,6 +37,7 @@
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/gui/editor_file_dialog.h"
+#include "editor/plugins/skeleton_3d_editor_plugin.h"
#include "editor/themes/editor_scale.h"
#include "scene/3d/importer_mesh_instance_3d.h"
#include "scene/animation/animation_player.h"
@@ -419,7 +420,9 @@ void SceneImportSettingsDialog::_fill_scene(Node *p_node, TreeItem *p_parent_ite
animation_player->connect(SceneStringName(animation_finished), callable_mp(this, &SceneImportSettingsDialog::_animation_finished));
} else if (Object::cast_to<Skeleton3D>(p_node)) {
category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE;
- skeletons.push_back(Object::cast_to<Skeleton3D>(p_node));
+ Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_node);
+ skeleton->connect(SceneStringName(tree_entered), callable_mp(this, &SceneImportSettingsDialog::_skeleton_tree_entered).bind(skeleton));
+ skeletons.push_back(skeleton);
} else {
category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_NODE;
}
@@ -480,6 +483,31 @@ void SceneImportSettingsDialog::_fill_scene(Node *p_node, TreeItem *p_parent_ite
contents_aabb.merge_with(aabb);
}
}
+
+ Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_node);
+ if (skeleton) {
+ Ref<ArrayMesh> bones_mesh = Skeleton3DGizmoPlugin::get_bones_mesh(skeleton, -1, true);
+
+ bones_mesh_preview->set_mesh(bones_mesh);
+
+ Transform3D accum_xform;
+ Node3D *base = skeleton;
+ while (base) {
+ accum_xform = base->get_transform() * accum_xform;
+ base = Object::cast_to<Node3D>(base->get_parent());
+ }
+
+ bones_mesh_preview->set_transform(accum_xform * skeleton->get_transform());
+
+ AABB aabb = accum_xform.xform(bones_mesh->get_aabb());
+
+ if (first_aabb) {
+ contents_aabb = aabb;
+ first_aabb = false;
+ } else {
+ contents_aabb.merge_with(aabb);
+ }
+ }
}
void SceneImportSettingsDialog::_update_scene() {
@@ -795,6 +823,7 @@ void SceneImportSettingsDialog::_select(Tree *p_from, const String &p_type, cons
selecting = true;
scene_import_settings_data->hide_options = false;
+ bones_mesh_preview->hide();
if (p_type == "Node") {
node_selected->hide(); // Always hide just in case.
mesh_preview->hide();
@@ -834,6 +863,7 @@ void SceneImportSettingsDialog::_select(Tree *p_from, const String &p_type, cons
scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE;
} else if (Object::cast_to<Skeleton3D>(nd.node)) {
scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE;
+ bones_mesh_preview->show();
} else {
scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_NODE;
scene_import_settings_data->hide_options = editing_animation;
@@ -853,6 +883,8 @@ void SceneImportSettingsDialog::_select(Tree *p_from, const String &p_type, cons
scene_import_settings_data->settings = &ad.settings;
scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_ANIMATION;
+
+ _animation_update_skeleton_visibility();
} else if (p_type == "Mesh") {
node_selected->hide();
if (Object::cast_to<Node3D>(scene)) {
@@ -1055,6 +1087,11 @@ void SceneImportSettingsDialog::_animation_slider_value_changed(double p_value)
animation_player->seek(p_value * animation_map[selected_id].animation->get_length(), true);
}
+void SceneImportSettingsDialog::_skeleton_tree_entered(Skeleton3D *p_skeleton) {
+ bones_mesh_preview->set_skeleton_path(p_skeleton->get_path());
+ bones_mesh_preview->set_skin(p_skeleton->register_skin(p_skeleton->create_skin_from_rest_transforms()));
+}
+
void SceneImportSettingsDialog::_animation_finished(const StringName &p_name) {
Animation::LoopMode loop_mode = animation_loop_mode;
@@ -1080,6 +1117,14 @@ void SceneImportSettingsDialog::_animation_finished(const StringName &p_name) {
}
}
+void SceneImportSettingsDialog::_animation_update_skeleton_visibility() {
+ if (animation_toggle_skeleton_visibility->is_pressed()) {
+ bones_mesh_preview->show();
+ } else {
+ bones_mesh_preview->hide();
+ }
+}
+
void SceneImportSettingsDialog::_material_tree_selected() {
if (selecting) {
return;
@@ -1282,6 +1327,8 @@ void SceneImportSettingsDialog::_notification(int p_what) {
light_1_switch->set_icon(theme_cache.light_1_icon);
light_2_switch->set_icon(theme_cache.light_2_icon);
light_rotate_switch->set_icon(theme_cache.rotate_icon);
+
+ animation_toggle_skeleton_visibility->set_icon(get_editor_theme_icon(SNAME("Skeleton3D")));
} break;
case NOTIFICATION_PROCESS: {
@@ -1661,6 +1708,7 @@ SceneImportSettingsDialog::SceneImportSettingsDialog() {
animation_hbox->add_child(animation_stop_button);
animation_stop_button->set_flat(true);
animation_stop_button->set_focus_mode(Control::FOCUS_NONE);
+ animation_stop_button->set_tooltip_text(TTR("Selected Animation Stop"));
animation_stop_button->connect(SceneStringName(pressed), callable_mp(this, &SceneImportSettingsDialog::_stop_current_animation));
animation_slider = memnew(HSlider);
@@ -1673,6 +1721,15 @@ SceneImportSettingsDialog::SceneImportSettingsDialog() {
animation_slider->set_focus_mode(Control::FOCUS_NONE);
animation_slider->connect(SceneStringName(value_changed), callable_mp(this, &SceneImportSettingsDialog::_animation_slider_value_changed));
+ animation_toggle_skeleton_visibility = memnew(Button);
+ animation_hbox->add_child(animation_toggle_skeleton_visibility);
+ animation_toggle_skeleton_visibility->set_toggle_mode(true);
+ animation_toggle_skeleton_visibility->set_flat(true);
+ animation_toggle_skeleton_visibility->set_focus_mode(Control::FOCUS_NONE);
+ animation_toggle_skeleton_visibility->set_tooltip_text(TTR("Toggle Animation Skeleton Visibility"));
+
+ animation_toggle_skeleton_visibility->connect(SceneStringName(pressed), callable_mp(this, &SceneImportSettingsDialog::_animation_update_skeleton_visibility));
+
base_viewport->set_use_own_world_3d(true);
HBoxContainer *viewport_hbox = memnew(HBoxContainer);
@@ -1800,6 +1857,13 @@ SceneImportSettingsDialog::SceneImportSettingsDialog() {
collider_mat->set_albedo(Color(0.5, 0.5, 1.0));
}
+ {
+ bones_mesh_preview = memnew(MeshInstance3D);
+ bones_mesh_preview->set_cast_shadows_setting(GeometryInstance3D::SHADOW_CASTING_SETTING_OFF);
+ bones_mesh_preview->set_skeleton_path(NodePath());
+ base_viewport->add_child(bones_mesh_preview);
+ }
+
inspector = memnew(EditorInspector);
inspector->set_custom_minimum_size(Size2(300 * EDSCALE, 0));
inspector->connect(SNAME("property_edited"), callable_mp(this, &SceneImportSettingsDialog::_inspector_property_edited));
diff --git a/editor/import/3d/scene_import_settings.h b/editor/import/3d/scene_import_settings.h
index bbd0d2c22d..1088acf772 100644
--- a/editor/import/3d/scene_import_settings.h
+++ b/editor/import/3d/scene_import_settings.h
@@ -109,10 +109,12 @@ class SceneImportSettingsDialog : public ConfirmationDialog {
HSlider *animation_slider = nullptr;
Button *animation_play_button = nullptr;
Button *animation_stop_button = nullptr;
+ Button *animation_toggle_skeleton_visibility = nullptr;
Animation::LoopMode animation_loop_mode = Animation::LOOP_NONE;
bool animation_pingpong = false;
bool previous_import_as_skeleton = false;
bool previous_rest_as_reset = false;
+ MeshInstance3D *bones_mesh_preview = nullptr;
Ref<StandardMaterial3D> collider_mat;
@@ -187,9 +189,11 @@ class SceneImportSettingsDialog : public ConfirmationDialog {
void _reset_animation(const String &p_animation_name = "");
void _animation_slider_value_changed(double p_value);
void _animation_finished(const StringName &p_name);
+ void _animation_update_skeleton_visibility();
void _material_tree_selected();
void _mesh_tree_selected();
void _scene_tree_selected();
+ void _skeleton_tree_entered(Skeleton3D *p_skeleton);
void _cleanup();
void _on_light_1_switch_pressed();
void _on_light_2_switch_pressed();
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 4b7bf1674f..d1032470f2 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -5384,7 +5384,7 @@ CanvasItemEditor::CanvasItemEditor() {
main_menu_hbox->add_child(pivot_button);
pivot_button->set_toggle_mode(true);
pivot_button->connect(SceneStringName(pressed), callable_mp(this, &CanvasItemEditor::_button_tool_select).bind(TOOL_EDIT_PIVOT));
- pivot_button->set_tooltip_text(TTR("Click to change object's rotation pivot.") + "\n" + TTR("Shift: Set temporary rotation pivot.") + "\n" + TTR("Click this button while holding Shift to put the rotation pivot in the center of the selected nodes."));
+ pivot_button->set_tooltip_text(TTR("Click to change object's rotation pivot.") + "\n" + TTR("Shift: Set temporary rotation pivot.") + "\n" + TTR("Click this button while holding Shift to put the temporary rotation pivot in the center of the selected nodes."));
pan_button = memnew(Button);
pan_button->set_theme_type_variation("FlatButton");
diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp
index 7ba464f4e5..890035e6a6 100644
--- a/editor/plugins/path_3d_editor_plugin.cpp
+++ b/editor/plugins/path_3d_editor_plugin.cpp
@@ -687,11 +687,11 @@ void Path3DEditorPlugin::make_visible(bool p_visible) {
}
void Path3DEditorPlugin::_mode_changed(int p_mode) {
- curve_create->set_pressed(p_mode == MODE_CREATE);
- curve_edit_curve->set_pressed(p_mode == MODE_EDIT_CURVE);
- curve_edit_tilt->set_pressed(p_mode == MODE_EDIT_TILT);
- curve_edit->set_pressed(p_mode == MODE_EDIT);
- curve_del->set_pressed(p_mode == MODE_DELETE);
+ curve_create->set_pressed_no_signal(p_mode == MODE_CREATE);
+ curve_edit_curve->set_pressed_no_signal(p_mode == MODE_EDIT_CURVE);
+ curve_edit_tilt->set_pressed_no_signal(p_mode == MODE_EDIT_TILT);
+ curve_edit->set_pressed_no_signal(p_mode == MODE_EDIT);
+ curve_del->set_pressed_no_signal(p_mode == MODE_DELETE);
Node3DEditor::get_singleton()->clear_subgizmo_selection();
}
@@ -790,17 +790,14 @@ void Path3DEditorPlugin::_restore_curve_points(const PackedVector3Array &p_point
}
void Path3DEditorPlugin::_update_theme() {
- // TODO: Split the EditorPlugin instance from the UI instance and connect this properly.
- // See the 2D path editor for inspiration.
- curve_edit->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("CurveEdit"), EditorStringName(EditorIcons)));
- curve_edit_curve->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("CurveCurve"), EditorStringName(EditorIcons)));
- curve_edit_tilt->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("CurveTilt"), EditorStringName(EditorIcons)));
- curve_create->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("CurveCreate"), EditorStringName(EditorIcons)));
- curve_del->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("CurveDelete"), EditorStringName(EditorIcons)));
- curve_close->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("CurveClose"), EditorStringName(EditorIcons)));
- curve_clear_points->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Clear"), EditorStringName(EditorIcons)));
-
- create_curve_button->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Curve3D"), EditorStringName(EditorIcons)));
+ curve_edit->set_icon(topmenu_bar->get_editor_theme_icon(SNAME("CurveEdit")));
+ curve_edit_curve->set_icon(topmenu_bar->get_editor_theme_icon(SNAME("CurveCurve")));
+ curve_edit_tilt->set_icon(topmenu_bar->get_editor_theme_icon(SNAME("CurveTilt")));
+ curve_create->set_icon(topmenu_bar->get_editor_theme_icon(SNAME("CurveCreate")));
+ curve_del->set_icon(topmenu_bar->get_editor_theme_icon(SNAME("CurveDelete")));
+ curve_close->set_icon(topmenu_bar->get_editor_theme_icon(SNAME("CurveClose")));
+ curve_clear_points->set_icon(topmenu_bar->get_editor_theme_icon(SNAME("Clear")));
+ create_curve_button->set_icon(topmenu_bar->get_editor_theme_icon(SNAME("Curve3D")));
}
void Path3DEditorPlugin::_update_toolbar() {
@@ -812,42 +809,14 @@ void Path3DEditorPlugin::_update_toolbar() {
create_curve_button->set_visible(!has_curve);
}
-void Path3DEditorPlugin::_notification(int p_what) {
- switch (p_what) {
- case NOTIFICATION_ENTER_TREE: {
- curve_create->connect(SceneStringName(pressed), callable_mp(this, &Path3DEditorPlugin::_mode_changed).bind(MODE_CREATE));
- curve_edit_curve->connect(SceneStringName(pressed), callable_mp(this, &Path3DEditorPlugin::_mode_changed).bind(MODE_EDIT_CURVE));
- curve_edit_tilt->connect(SceneStringName(pressed), callable_mp(this, &Path3DEditorPlugin::_mode_changed).bind(MODE_EDIT_TILT));
- curve_edit->connect(SceneStringName(pressed), callable_mp(this, &Path3DEditorPlugin::_mode_changed).bind(MODE_EDIT));
- curve_del->connect(SceneStringName(pressed), callable_mp(this, &Path3DEditorPlugin::_mode_changed).bind(MODE_DELETE));
- curve_close->connect(SceneStringName(pressed), callable_mp(this, &Path3DEditorPlugin::_close_curve));
-
- _update_theme();
- } break;
-
- case NOTIFICATION_READY: {
- // FIXME: This can trigger theme updates when the nodes that we want to update are not yet available.
- // The toolbar should be extracted to a dedicated control and theme updates should be handled through
- // the notification.
- Node3DEditor::get_singleton()->connect(SceneStringName(theme_changed), callable_mp(this, &Path3DEditorPlugin::_update_theme));
- } break;
- }
-}
-
void Path3DEditorPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("_update_toolbar"), &Path3DEditorPlugin::_update_toolbar);
ClassDB::bind_method(D_METHOD("_clear_curve_points"), &Path3DEditorPlugin::_clear_curve_points);
ClassDB::bind_method(D_METHOD("_restore_curve_points"), &Path3DEditorPlugin::_restore_curve_points);
}
-Path3DEditorPlugin *Path3DEditorPlugin::singleton = nullptr;
-
Path3DEditorPlugin::Path3DEditorPlugin() {
- path = nullptr;
singleton = this;
- mirror_handle_angle = true;
- mirror_handle_length = true;
-
disk_size = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_settings/path3d_tilt_disk_size", 0.8);
Ref<Path3DGizmoPlugin> gizmo_plugin = memnew(Path3DGizmoPlugin(disk_size));
@@ -856,7 +825,6 @@ Path3DEditorPlugin::Path3DEditorPlugin() {
topmenu_bar = memnew(HBoxContainer);
topmenu_bar->hide();
- Node3DEditor::get_singleton()->add_control_to_menu_panel(topmenu_bar);
toolbar = memnew(HBoxContainer);
topmenu_bar->add_child(toolbar);
@@ -867,6 +835,7 @@ Path3DEditorPlugin::Path3DEditorPlugin() {
curve_edit->set_focus_mode(Control::FOCUS_NONE);
curve_edit->set_tooltip_text(TTR("Select Points") + "\n" + TTR("Shift+Click: Select multiple Points") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL) + TTR("Click: Add Point") + "\n" + TTR("Right Click: Delete Point"));
toolbar->add_child(curve_edit);
+ curve_edit->connect(SceneStringName(pressed), callable_mp(this, &Path3DEditorPlugin::_mode_changed).bind(MODE_EDIT));
curve_edit_curve = memnew(Button);
curve_edit_curve->set_theme_type_variation("FlatButton");
@@ -874,6 +843,7 @@ Path3DEditorPlugin::Path3DEditorPlugin() {
curve_edit_curve->set_focus_mode(Control::FOCUS_NONE);
curve_edit_curve->set_tooltip_text(TTR("Select Control Points") + "\n" + TTR("Shift+Click: Drag out Control Points"));
toolbar->add_child(curve_edit_curve);
+ curve_edit_curve->connect(SceneStringName(pressed), callable_mp(this, &Path3DEditorPlugin::_mode_changed).bind(MODE_EDIT_CURVE));
curve_edit_tilt = memnew(Button);
curve_edit_tilt->set_theme_type_variation("FlatButton");
@@ -881,6 +851,7 @@ Path3DEditorPlugin::Path3DEditorPlugin() {
curve_edit_tilt->set_focus_mode(Control::FOCUS_NONE);
curve_edit_tilt->set_tooltip_text(TTR("Select Tilt Handles"));
toolbar->add_child(curve_edit_tilt);
+ curve_edit_tilt->connect(SceneStringName(pressed), callable_mp(this, &Path3DEditorPlugin::_mode_changed).bind(MODE_EDIT_TILT));
curve_create = memnew(Button);
curve_create->set_theme_type_variation("FlatButton");
@@ -888,6 +859,7 @@ Path3DEditorPlugin::Path3DEditorPlugin() {
curve_create->set_focus_mode(Control::FOCUS_NONE);
curve_create->set_tooltip_text(TTR("Add Point (in empty space)") + "\n" + TTR("Split Segment (in curve)"));
toolbar->add_child(curve_create);
+ curve_create->connect(SceneStringName(pressed), callable_mp(this, &Path3DEditorPlugin::_mode_changed).bind(MODE_CREATE));
curve_del = memnew(Button);
curve_del->set_theme_type_variation("FlatButton");
@@ -895,12 +867,14 @@ Path3DEditorPlugin::Path3DEditorPlugin() {
curve_del->set_focus_mode(Control::FOCUS_NONE);
curve_del->set_tooltip_text(TTR("Delete Point"));
toolbar->add_child(curve_del);
+ curve_del->connect(SceneStringName(pressed), callable_mp(this, &Path3DEditorPlugin::_mode_changed).bind(MODE_DELETE));
curve_close = memnew(Button);
curve_close->set_theme_type_variation("FlatButton");
curve_close->set_focus_mode(Control::FOCUS_NONE);
curve_close->set_tooltip_text(TTR("Close Curve"));
toolbar->add_child(curve_close);
+ curve_close->connect(SceneStringName(pressed), callable_mp(this, &Path3DEditorPlugin::_close_curve));
curve_clear_points = memnew(Button);
curve_clear_points->set_theme_type_variation("FlatButton");
@@ -927,18 +901,17 @@ Path3DEditorPlugin::Path3DEditorPlugin() {
topmenu_bar->add_child(create_curve_button);
create_curve_button->connect(SceneStringName(pressed), callable_mp(this, &Path3DEditorPlugin::_create_curve));
- PopupMenu *menu;
- menu = handle_menu->get_popup();
+ PopupMenu *menu = handle_menu->get_popup();
menu->add_check_item(TTR("Mirror Handle Angles"));
menu->set_item_checked(HANDLE_OPTION_ANGLE, mirror_handle_angle);
menu->add_check_item(TTR("Mirror Handle Lengths"));
menu->set_item_checked(HANDLE_OPTION_LENGTH, mirror_handle_length);
menu->connect(SceneStringName(id_pressed), callable_mp(this, &Path3DEditorPlugin::_handle_option_pressed));
- curve_edit->set_pressed(true);
-}
+ curve_edit->set_pressed_no_signal(true);
-Path3DEditorPlugin::~Path3DEditorPlugin() {
+ topmenu_bar->connect(SceneStringName(theme_changed), callable_mp(this, &Path3DEditorPlugin::_update_theme));
+ Node3DEditor::get_singleton()->add_control_to_menu_panel(topmenu_bar);
}
Ref<EditorNode3DGizmo> Path3DGizmoPlugin::create_gizmo(Node3D *p_spatial) {
diff --git a/editor/plugins/path_3d_editor_plugin.h b/editor/plugins/path_3d_editor_plugin.h
index 09a7a46803..60cb7f940f 100644
--- a/editor/plugins/path_3d_editor_plugin.h
+++ b/editor/plugins/path_3d_editor_plugin.h
@@ -162,13 +162,12 @@ class Path3DEditorPlugin : public EditorPlugin {
};
protected:
- void _notification(int p_what);
static void _bind_methods();
public:
Path3D *get_edited_path() { return path; }
- static Path3DEditorPlugin *singleton;
+ inline static Path3DEditorPlugin *singleton = nullptr;
virtual EditorPlugin::AfterGUIInput forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override;
virtual String get_name() const override { return "Path3D"; }
@@ -183,7 +182,6 @@ public:
void set_handle_clicked(bool clicked) { handle_clicked = clicked; }
Path3DEditorPlugin();
- ~Path3DEditorPlugin();
};
#endif // PATH_3D_EDITOR_PLUGIN_H
diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp
index dc4d4db3f8..9fc88b3a6a 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_3d_editor_plugin.cpp
@@ -1348,16 +1348,18 @@ int Skeleton3DEditor::get_selected_bone() const {
return selected_bone;
}
+Skeleton3DGizmoPlugin::SelectionMaterials Skeleton3DGizmoPlugin::selection_materials;
+
Skeleton3DGizmoPlugin::Skeleton3DGizmoPlugin() {
- unselected_mat = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
- unselected_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
- unselected_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
- unselected_mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
- unselected_mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
- unselected_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
-
- selected_mat = Ref<ShaderMaterial>(memnew(ShaderMaterial));
- selected_sh = Ref<Shader>(memnew(Shader));
+ selection_materials.unselected_mat.instantiate();
+ selection_materials.unselected_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ selection_materials.unselected_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
+ selection_materials.unselected_mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+ selection_materials.unselected_mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
+ selection_materials.unselected_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
+
+ selection_materials.selected_mat.instantiate();
+ Ref<Shader> selected_sh = Ref<Shader>(memnew(Shader));
selected_sh->set_code(R"(
// Skeleton 3D gizmo bones shader.
@@ -1376,7 +1378,7 @@ void fragment() {
ALPHA = COLOR.a;
}
)");
- selected_mat->set_shader(selected_sh);
+ selection_materials.selected_mat->set_shader(selected_sh);
// Register properties in editor settings.
EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/skeleton", Color(1, 0.8, 0.4));
@@ -1386,6 +1388,11 @@ void fragment() {
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "editors/3d_gizmos/gizmo_settings/bone_shape", PROPERTY_HINT_ENUM, "Wire,Octahedron"));
}
+Skeleton3DGizmoPlugin::~Skeleton3DGizmoPlugin() {
+ selection_materials.unselected_mat.unref();
+ selection_materials.selected_mat.unref();
+}
+
bool Skeleton3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<Skeleton3D>(p_spatial) != nullptr;
}
@@ -1526,6 +1533,11 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
selected = se->get_selected_bone();
}
+ Ref<ArrayMesh> m = get_bones_mesh(skeleton, selected, p_gizmo->is_selected());
+ p_gizmo->add_mesh(m, Ref<Material>(), Transform3D(), skeleton->register_skin(skeleton->create_skin_from_rest_transforms()));
+}
+
+Ref<ArrayMesh> Skeleton3DGizmoPlugin::get_bones_mesh(Skeleton3D *p_skeleton, int p_selected, bool p_is_selected) {
Color bone_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/skeleton");
Color selected_bone_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/selected_bone");
real_t bone_axis_length = EDITOR_GET("editors/3d_gizmos/gizmo_settings/bone_axis_length");
@@ -1539,11 +1551,11 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
Ref<SurfaceTool> surface_tool(memnew(SurfaceTool));
surface_tool->begin(Mesh::PRIMITIVE_LINES);
- if (p_gizmo->is_selected()) {
- surface_tool->set_material(selected_mat);
+ if (p_is_selected) {
+ surface_tool->set_material(selection_materials.selected_mat);
} else {
- unselected_mat->set_albedo(bone_color);
- surface_tool->set_material(unselected_mat);
+ selection_materials.unselected_mat->set_albedo(bone_color);
+ surface_tool->set_material(selection_materials.unselected_mat);
}
LocalVector<int> bones;
@@ -1557,16 +1569,16 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
weights[0] = 1;
int current_bone_index = 0;
- Vector<int> bones_to_process = skeleton->get_parentless_bones();
+ Vector<int> bones_to_process = p_skeleton->get_parentless_bones();
while (bones_to_process.size() > current_bone_index) {
int current_bone_idx = bones_to_process[current_bone_index];
current_bone_index++;
- Color current_bone_color = (current_bone_idx == selected) ? selected_bone_color : bone_color;
+ Color current_bone_color = (current_bone_idx == p_selected) ? selected_bone_color : bone_color;
Vector<int> child_bones_vector;
- child_bones_vector = skeleton->get_bone_children(current_bone_idx);
+ child_bones_vector = p_skeleton->get_bone_children(current_bone_idx);
int child_bones_size = child_bones_vector.size();
for (int i = 0; i < child_bones_size; i++) {
@@ -1577,8 +1589,8 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
int child_bone_idx = child_bones_vector[i];
- Vector3 v0 = skeleton->get_bone_global_rest(current_bone_idx).origin;
- Vector3 v1 = skeleton->get_bone_global_rest(child_bone_idx).origin;
+ Vector3 v0 = p_skeleton->get_bone_global_rest(current_bone_idx).origin;
+ Vector3 v1 = p_skeleton->get_bone_global_rest(child_bone_idx).origin;
Vector3 d = (v1 - v0).normalized();
real_t dist = v0.distance_to(v1);
@@ -1586,7 +1598,7 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
int closest = -1;
real_t closest_d = 0.0;
for (int j = 0; j < 3; j++) {
- real_t dp = Math::abs(skeleton->get_bone_global_rest(current_bone_idx).basis[j].normalized().dot(d));
+ real_t dp = Math::abs(p_skeleton->get_bone_global_rest(current_bone_idx).basis[j].normalized().dot(d));
if (j == 0 || dp > closest_d) {
closest = j;
}
@@ -1613,7 +1625,7 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
for (int j = 0; j < 3; j++) {
Vector3 axis;
if (first == Vector3()) {
- axis = d.cross(d.cross(skeleton->get_bone_global_rest(current_bone_idx).basis[j])).normalized();
+ axis = d.cross(d.cross(p_skeleton->get_bone_global_rest(current_bone_idx).basis[j])).normalized();
first = axis;
} else {
axis = d.cross(first).normalized();
@@ -1668,7 +1680,7 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
surface_tool->add_vertex(v0);
surface_tool->set_bones(bones);
surface_tool->set_weights(weights);
- surface_tool->add_vertex(v0 + (skeleton->get_bone_global_rest(current_bone_idx).basis.inverse())[j].normalized() * dist * bone_axis_length);
+ surface_tool->add_vertex(v0 + (p_skeleton->get_bone_global_rest(current_bone_idx).basis.inverse())[j].normalized() * dist * bone_axis_length);
if (j == closest) {
continue;
@@ -1685,7 +1697,7 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
surface_tool->add_vertex(v1);
surface_tool->set_bones(bones);
surface_tool->set_weights(weights);
- surface_tool->add_vertex(v1 + (skeleton->get_bone_global_rest(child_bone_idx).basis.inverse())[j].normalized() * dist * bone_axis_length);
+ surface_tool->add_vertex(v1 + (p_skeleton->get_bone_global_rest(child_bone_idx).basis.inverse())[j].normalized() * dist * bone_axis_length);
if (j == closest) {
continue;
@@ -1698,6 +1710,5 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
}
}
- Ref<ArrayMesh> m = surface_tool->commit();
- p_gizmo->add_mesh(m, Ref<Material>(), Transform3D(), skeleton->register_skin(skeleton->create_skin_from_rest_transforms()));
+ return surface_tool->commit();
}
diff --git a/editor/plugins/skeleton_3d_editor_plugin.h b/editor/plugins/skeleton_3d_editor_plugin.h
index 0bb58aac23..d4dee1f16f 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.h
+++ b/editor/plugins/skeleton_3d_editor_plugin.h
@@ -260,11 +260,15 @@ public:
class Skeleton3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(Skeleton3DGizmoPlugin, EditorNode3DGizmoPlugin);
- Ref<StandardMaterial3D> unselected_mat;
- Ref<ShaderMaterial> selected_mat;
- Ref<Shader> selected_sh;
+ struct SelectionMaterials {
+ Ref<StandardMaterial3D> unselected_mat;
+ Ref<ShaderMaterial> selected_mat;
+ };
+ static SelectionMaterials selection_materials;
public:
+ static Ref<ArrayMesh> get_bones_mesh(Skeleton3D *p_skeleton, int p_selected, bool p_is_selected);
+
bool has_gizmo(Node3D *p_spatial) override;
String get_gizmo_name() const override;
int get_priority() const override;
@@ -277,6 +281,7 @@ public:
void redraw(EditorNode3DGizmo *p_gizmo) override;
Skeleton3DGizmoPlugin();
+ ~Skeleton3DGizmoPlugin();
};
#endif // SKELETON_3D_EDITOR_PLUGIN_H
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index add52ce566..2a39b11815 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -1241,6 +1241,10 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
break;
}
+ if (!_validate_no_foreign()) {
+ break;
+ }
+
List<Node *> selection = editor_selection->get_selected_node_list();
List<Node *>::Element *e = selection.front();
if (e) {
diff --git a/editor/themes/editor_theme_manager.cpp b/editor/themes/editor_theme_manager.cpp
index 119508f59b..3041857d83 100644
--- a/editor/themes/editor_theme_manager.cpp
+++ b/editor/themes/editor_theme_manager.cpp
@@ -865,7 +865,6 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the
// CheckBox.
{
Ref<StyleBoxFlat> checkbox_style = p_config.panel_container_style->duplicate();
- checkbox_style->set_content_margin_individual((p_config.increased_margin + 2) * EDSCALE, p_config.base_margin * EDSCALE, (p_config.increased_margin + 2) * EDSCALE, p_config.base_margin * EDSCALE);
p_theme->set_stylebox(CoreStringName(normal), "CheckBox", checkbox_style);
p_theme->set_stylebox(SceneStringName(pressed), "CheckBox", checkbox_style);
@@ -1165,9 +1164,6 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the
// LineEdit & TextEdit.
{
Ref<StyleBoxFlat> text_editor_style = p_config.button_style->duplicate();
- // The original button_style style has an extra 1 pixel offset that makes LineEdits not align with Buttons,
- // so this compensates for that.
- text_editor_style->set_content_margin(SIDE_TOP, text_editor_style->get_content_margin(SIDE_TOP) - 1 * EDSCALE);
// Don't round the bottom corners to make the line look sharper.
text_editor_style->set_corner_radius(CORNER_BOTTOM_LEFT, 0);
diff --git a/modules/minimp3/audio_stream_mp3.cpp b/modules/minimp3/audio_stream_mp3.cpp
index 394213963a..e29fe9295a 100644
--- a/modules/minimp3/audio_stream_mp3.cpp
+++ b/modules/minimp3/audio_stream_mp3.cpp
@@ -159,6 +159,9 @@ Ref<AudioSamplePlayback> AudioStreamPlaybackMP3::get_sample_playback() const {
void AudioStreamPlaybackMP3::set_sample_playback(const Ref<AudioSamplePlayback> &p_playback) {
sample_playback = p_playback;
+ if (sample_playback.is_valid()) {
+ sample_playback->stream_playback = Ref<AudioStreamPlayback>(this);
+ }
}
void AudioStreamPlaybackMP3::set_parameter(const StringName &p_name, const Variant &p_value) {
diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp
index ff032c88c6..c89534a60c 100644
--- a/modules/vorbis/audio_stream_ogg_vorbis.cpp
+++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp
@@ -390,6 +390,9 @@ Ref<AudioSamplePlayback> AudioStreamPlaybackOggVorbis::get_sample_playback() con
void AudioStreamPlaybackOggVorbis::set_sample_playback(const Ref<AudioSamplePlayback> &p_playback) {
sample_playback = p_playback;
+ if (sample_playback.is_valid()) {
+ sample_playback->stream_playback = Ref<AudioStreamPlayback>(this);
+ }
}
AudioStreamPlaybackOggVorbis::~AudioStreamPlaybackOggVorbis() {
diff --git a/platform/linuxbsd/wayland/wayland_thread.cpp b/platform/linuxbsd/wayland/wayland_thread.cpp
index ab13105d18..66be313ff6 100644
--- a/platform/linuxbsd/wayland/wayland_thread.cpp
+++ b/platform/linuxbsd/wayland/wayland_thread.cpp
@@ -169,12 +169,13 @@ Vector<uint8_t> WaylandThread::_wp_primary_selection_offer_read(struct wl_displa
int fds[2];
if (pipe(fds) == 0) {
- // This function expects to return a string, so we can only ask for a MIME of
- // "text/plain"
zwp_primary_selection_offer_v1_receive(p_offer, p_mime, fds[1]);
- // Wait for the compositor to know about the pipe.
- wl_display_roundtrip(p_display);
+ // NOTE: It's important to just flush and not roundtrip here as we would risk
+ // running some cleanup event, like for example `wl_data_device::leave`. We're
+ // going to wait for the message anyways as the read will probably block if
+ // the compositor doesn't read from the other end of the pipe.
+ wl_display_flush(p_display);
// Close the write end of the pipe, which we don't need and would otherwise
// just stall our next `read`s.
diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp
index 770871ae19..c99e9cdd0c 100644
--- a/platform/macos/export/export_plugin.cpp
+++ b/platform/macos/export/export_plugin.cpp
@@ -357,17 +357,13 @@ List<String> EditorExportPlatformMacOS::get_binary_extensions(const Ref<EditorEx
list.push_back("dmg");
#endif
list.push_back("zip");
-#ifndef WINDOWS_ENABLED
list.push_back("app");
-#endif
} else if (dist_type == 1) {
#ifdef MACOS_ENABLED
list.push_back("dmg");
#endif
list.push_back("zip");
-#ifndef WINDOWS_ENABLED
list.push_back("app");
-#endif
} else if (dist_type == 2) {
#ifdef MACOS_ENABLED
list.push_back("pkg");
@@ -1903,6 +1899,11 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
if (is_executable(file)) {
// chmod with 0755 if the file is executable.
FileAccess::set_unix_permissions(file, 0755);
+#ifndef UNIX_ENABLED
+ if (export_format == "app") {
+ add_message(EXPORT_MESSAGE_INFO, TTR("Export"), vformat(TTR("Unable to set Unix permissions for executable \"%s\". Use \"chmod +x\" to set it after transferring the exported .app to macOS or Linux."), "Contents/MacOS/" + file.get_file()));
+ }
+#endif
}
} else {
add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not open \"%s\"."), file));
@@ -1928,6 +1929,11 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
if ((con_scr == 1 && p_debug) || (con_scr == 2)) {
err = _export_debug_script(p_preset, pkg_name, tmp_app_path_name.get_file() + "/Contents/MacOS/" + pkg_name, scr_path);
FileAccess::set_unix_permissions(scr_path, 0755);
+#ifndef UNIX_ENABLED
+ if (export_format == "app") {
+ add_message(EXPORT_MESSAGE_INFO, TTR("Export"), vformat(TTR("Unable to set Unix permissions for executable \"%s\". Use \"chmod +x\" to set it after transferring the exported .app to macOS or Linux."), scr_path.get_file()));
+ }
+#endif
if (err != OK) {
add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Could not create console wrapper."));
}
@@ -2156,6 +2162,11 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
_code_sign(p_preset, tmp_app_path_name + "/Contents/Helpers/" + hlp_path.get_file(), hlp_ent_path, false, true);
}
FileAccess::set_unix_permissions(tmp_app_path_name + "/Contents/Helpers/" + hlp_path.get_file(), 0755);
+#ifndef UNIX_ENABLED
+ if (export_format == "app") {
+ add_message(EXPORT_MESSAGE_INFO, TTR("Export"), vformat(TTR("Unable to set Unix permissions for executable \"%s\". Use \"chmod +x\" to set it after transferring the exported .app to macOS or Linux."), "Contents/Helpers/" + hlp_path.get_file()));
+ }
+#endif
}
}
diff --git a/scene/2d/canvas_modulate.cpp b/scene/2d/canvas_modulate.cpp
index 2c5c6a1a16..dc83775c71 100644
--- a/scene/2d/canvas_modulate.cpp
+++ b/scene/2d/canvas_modulate.cpp
@@ -114,7 +114,7 @@ Color CanvasModulate::get_color() const {
}
PackedStringArray CanvasModulate::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = Node2D::get_configuration_warnings();
if (is_in_canvas && is_visible_in_tree()) {
List<Node *> nodes;
diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp
index 5ce26b3ed4..50c5873781 100644
--- a/scene/2d/light_2d.cpp
+++ b/scene/2d/light_2d.cpp
@@ -417,7 +417,7 @@ Vector2 PointLight2D::get_texture_offset() const {
}
PackedStringArray PointLight2D::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = Node2D::get_configuration_warnings();
if (!texture.is_valid()) {
warnings.push_back(RTR("A texture with the shape of the light must be supplied to the \"Texture\" property."));
diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp
index 092c987ac0..7c3fb61d04 100644
--- a/scene/2d/light_occluder_2d.cpp
+++ b/scene/2d/light_occluder_2d.cpp
@@ -263,7 +263,7 @@ int LightOccluder2D::get_occluder_light_mask() const {
}
PackedStringArray LightOccluder2D::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = Node2D::get_configuration_warnings();
if (!occluder_polygon.is_valid()) {
warnings.push_back(RTR("An occluder polygon must be set (or drawn) for this occluder to take effect."));
diff --git a/scene/2d/navigation_link_2d.cpp b/scene/2d/navigation_link_2d.cpp
index 111f5a7b78..4961e18dc9 100644
--- a/scene/2d/navigation_link_2d.cpp
+++ b/scene/2d/navigation_link_2d.cpp
@@ -328,7 +328,7 @@ void NavigationLink2D::set_travel_cost(real_t p_travel_cost) {
}
PackedStringArray NavigationLink2D::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = Node2D::get_configuration_warnings();
if (start_position.is_equal_approx(end_position)) {
warnings.push_back(RTR("NavigationLink2D start position should be different than the end position to be useful."));
diff --git a/scene/2d/parallax_layer.cpp b/scene/2d/parallax_layer.cpp
index 023e9201fc..24f261deb6 100644
--- a/scene/2d/parallax_layer.cpp
+++ b/scene/2d/parallax_layer.cpp
@@ -133,7 +133,7 @@ void ParallaxLayer::set_base_offset_and_scale(const Point2 &p_offset, real_t p_s
}
PackedStringArray ParallaxLayer::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = Node2D::get_configuration_warnings();
if (!Object::cast_to<ParallaxBackground>(get_parent())) {
warnings.push_back(RTR("ParallaxLayer node only works when set as child of a ParallaxBackground node."));
diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp
index c3768386e9..5813ab02e3 100644
--- a/scene/2d/path_2d.cpp
+++ b/scene/2d/path_2d.cpp
@@ -288,7 +288,7 @@ void PathFollow2D::_validate_property(PropertyInfo &p_property) const {
}
PackedStringArray PathFollow2D::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = Node2D::get_configuration_warnings();
if (is_visible_in_tree() && is_inside_tree()) {
if (!Object::cast_to<Path2D>(get_parent())) {
diff --git a/scene/2d/physics/collision_object_2d.cpp b/scene/2d/physics/collision_object_2d.cpp
index 00b6085f0c..27ee6b883c 100644
--- a/scene/2d/physics/collision_object_2d.cpp
+++ b/scene/2d/physics/collision_object_2d.cpp
@@ -582,7 +582,7 @@ void CollisionObject2D::_update_pickable() {
}
PackedStringArray CollisionObject2D::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = Node2D::get_configuration_warnings();
if (shapes.is_empty()) {
warnings.push_back(RTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape2D or CollisionPolygon2D as a child to define its shape."));
diff --git a/scene/2d/physics/collision_polygon_2d.cpp b/scene/2d/physics/collision_polygon_2d.cpp
index a9b47ef4d4..b49badac1f 100644
--- a/scene/2d/physics/collision_polygon_2d.cpp
+++ b/scene/2d/physics/collision_polygon_2d.cpp
@@ -232,7 +232,7 @@ bool CollisionPolygon2D::_edit_is_selected_on_click(const Point2 &p_point, doubl
#endif
PackedStringArray CollisionPolygon2D::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = Node2D::get_configuration_warnings();
if (!Object::cast_to<CollisionObject2D>(get_parent())) {
warnings.push_back(RTR("CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, CharacterBody2D, etc. to give them a shape."));
diff --git a/scene/2d/physics/collision_shape_2d.cpp b/scene/2d/physics/collision_shape_2d.cpp
index 6fc29ffe63..bdd0d06b5e 100644
--- a/scene/2d/physics/collision_shape_2d.cpp
+++ b/scene/2d/physics/collision_shape_2d.cpp
@@ -174,7 +174,7 @@ bool CollisionShape2D::_edit_is_selected_on_click(const Point2 &p_point, double
}
PackedStringArray CollisionShape2D::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = Node2D::get_configuration_warnings();
CollisionObject2D *col_object = Object::cast_to<CollisionObject2D>(get_parent());
if (col_object == nullptr) {
diff --git a/scene/2d/physics/physical_bone_2d.cpp b/scene/2d/physics/physical_bone_2d.cpp
index 77bb8c24b8..19274c8084 100644
--- a/scene/2d/physics/physical_bone_2d.cpp
+++ b/scene/2d/physics/physical_bone_2d.cpp
@@ -107,7 +107,7 @@ void PhysicalBone2D::_find_joint_child() {
}
PackedStringArray PhysicalBone2D::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = RigidBody2D::get_configuration_warnings();
if (!parent_skeleton) {
warnings.push_back(RTR("A PhysicalBone2D only works with a Skeleton2D or another PhysicalBone2D as a parent node!"));
diff --git a/scene/2d/remote_transform_2d.cpp b/scene/2d/remote_transform_2d.cpp
index 920f5720fa..1816a3409b 100644
--- a/scene/2d/remote_transform_2d.cpp
+++ b/scene/2d/remote_transform_2d.cpp
@@ -211,7 +211,7 @@ void RemoteTransform2D::force_update_cache() {
}
PackedStringArray RemoteTransform2D::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = Node2D::get_configuration_warnings();
if (!has_node(remote_node) || !Object::cast_to<Node2D>(get_node(remote_node))) {
warnings.push_back(RTR("Path property must point to a valid Node2D node to work."));
diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp
index f9e8c831d1..90bfb4c84c 100644
--- a/scene/2d/skeleton_2d.cpp
+++ b/scene/2d/skeleton_2d.cpp
@@ -412,7 +412,7 @@ int Bone2D::get_index_in_skeleton() const {
}
PackedStringArray Bone2D::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = Node2D::get_configuration_warnings();
if (!skeleton) {
if (parent_bone) {
warnings.push_back(RTR("This Bone2D chain should end at a Skeleton2D node."));
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index b10f2097da..45cfb8cf33 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -827,7 +827,7 @@ TypedArray<Vector2i> TileMap::get_surrounding_cells(const Vector2i &p_coords) {
}
PackedStringArray TileMap::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = Node2D::get_configuration_warnings();
warnings.push_back(RTR("The TileMap node is deprecated as it is superseded by the use of multiple TileMapLayer nodes.\nTo convert a TileMap to a set of TileMapLayer nodes, open the TileMap bottom panel with this node selected, click the toolbox icon in the top-right corner and choose \"Extract TileMap layers as individual TileMapLayer nodes\"."));
diff --git a/scene/3d/decal.cpp b/scene/3d/decal.cpp
index 485599d0fb..8702b1d3da 100644
--- a/scene/3d/decal.cpp
+++ b/scene/3d/decal.cpp
@@ -163,7 +163,7 @@ void Decal::_validate_property(PropertyInfo &p_property) const {
}
PackedStringArray Decal::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = VisualInstance3D::get_configuration_warnings();
if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
warnings.push_back(RTR("Decals are only available when using the Forward+ or Mobile rendering backends."));
diff --git a/scene/3d/fog_volume.cpp b/scene/3d/fog_volume.cpp
index 54631a8dff..195074ba2f 100644
--- a/scene/3d/fog_volume.cpp
+++ b/scene/3d/fog_volume.cpp
@@ -116,7 +116,7 @@ AABB FogVolume::get_aabb() const {
}
PackedStringArray FogVolume::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = VisualInstance3D::get_configuration_warnings();
Ref<Environment> environment = get_viewport()->find_world_3d()->get_environment();
diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp
index 3a05ec9c9e..9791f23bc3 100644
--- a/scene/3d/gpu_particles_collision_3d.cpp
+++ b/scene/3d/gpu_particles_collision_3d.cpp
@@ -524,7 +524,7 @@ Ref<Image> GPUParticlesCollisionSDF3D::bake() {
}
PackedStringArray GPUParticlesCollisionSDF3D::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = GPUParticlesCollision3D::get_configuration_warnings();
if (bake_mask == 0) {
warnings.push_back(RTR("The Bake Mask has no bits enabled, which means baking will not produce any collision for this GPUParticlesCollisionSDF3D.\nTo resolve this, enable at least one bit in the Bake Mask property."));
diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp
index 4048a8bd62..26a574cd26 100644
--- a/scene/3d/lightmap_gi.cpp
+++ b/scene/3d/lightmap_gi.cpp
@@ -1600,7 +1600,7 @@ Ref<CameraAttributes> LightmapGI::get_camera_attributes() const {
}
PackedStringArray LightmapGI::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = VisualInstance3D::get_configuration_warnings();
if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
warnings.push_back(RTR("Lightmap can only be baked from a device that supports the RD backends. Lightmap baking may fail."));
diff --git a/scene/3d/navigation_link_3d.cpp b/scene/3d/navigation_link_3d.cpp
index bebba9a6c0..0cce21b9d0 100644
--- a/scene/3d/navigation_link_3d.cpp
+++ b/scene/3d/navigation_link_3d.cpp
@@ -453,7 +453,7 @@ void NavigationLink3D::set_travel_cost(real_t p_travel_cost) {
}
PackedStringArray NavigationLink3D::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = Node3D::get_configuration_warnings();
if (start_position.is_equal_approx(end_position)) {
warnings.push_back(RTR("NavigationLink3D start position should be different than the end position to be useful."));
diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp
index d7397a932d..c0c254e7ed 100644
--- a/scene/3d/navigation_region_3d.cpp
+++ b/scene/3d/navigation_region_3d.cpp
@@ -271,7 +271,7 @@ bool NavigationRegion3D::is_baking() const {
}
PackedStringArray NavigationRegion3D::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = Node3D::get_configuration_warnings();
if (is_visible_in_tree() && is_inside_tree()) {
if (!navigation_mesh.is_valid()) {
diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp
index 6982df12f6..6d88323c76 100644
--- a/scene/3d/occluder_instance_3d.cpp
+++ b/scene/3d/occluder_instance_3d.cpp
@@ -691,7 +691,7 @@ OccluderInstance3D::BakeError OccluderInstance3D::bake_scene(Node *p_from_node,
}
PackedStringArray OccluderInstance3D::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = VisualInstance3D::get_configuration_warnings();
if (!bool(GLOBAL_GET("rendering/occlusion_culling/use_occlusion_culling"))) {
warnings.push_back(RTR("Occlusion culling is disabled in the Project Settings, which means occlusion culling won't be performed in the root viewport.\nTo resolve this, open the Project Settings and enable Rendering > Occlusion Culling > Use Occlusion Culling."));
diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp
index dc030b6a0f..20d646fe1e 100644
--- a/scene/3d/path_3d.cpp
+++ b/scene/3d/path_3d.cpp
@@ -318,7 +318,7 @@ void PathFollow3D::_validate_property(PropertyInfo &p_property) const {
}
PackedStringArray PathFollow3D::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = Node3D::get_configuration_warnings();
if (is_visible_in_tree() && is_inside_tree()) {
if (!Object::cast_to<Path3D>(get_parent())) {
diff --git a/scene/3d/physics/collision_object_3d.cpp b/scene/3d/physics/collision_object_3d.cpp
index f11aa7012a..f0a5013ca2 100644
--- a/scene/3d/physics/collision_object_3d.cpp
+++ b/scene/3d/physics/collision_object_3d.cpp
@@ -731,7 +731,7 @@ bool CollisionObject3D::get_capture_input_on_drag() const {
}
PackedStringArray CollisionObject3D::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = Node3D::get_configuration_warnings();
if (shapes.is_empty()) {
warnings.push_back(RTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape3D or CollisionPolygon3D as a child to define its shape."));
diff --git a/scene/3d/physics/collision_polygon_3d.cpp b/scene/3d/physics/collision_polygon_3d.cpp
index 76cd4db779..bf8dec7b54 100644
--- a/scene/3d/physics/collision_polygon_3d.cpp
+++ b/scene/3d/physics/collision_polygon_3d.cpp
@@ -169,7 +169,7 @@ void CollisionPolygon3D::set_margin(real_t p_margin) {
}
PackedStringArray CollisionPolygon3D::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = Node3D::get_configuration_warnings();
if (!Object::cast_to<CollisionObject3D>(get_parent())) {
warnings.push_back(RTR("CollisionPolygon3D only serves to provide a collision shape to a CollisionObject3D derived node.\nPlease only use it as a child of Area3D, StaticBody3D, RigidBody3D, CharacterBody3D, etc. to give them a shape."));
diff --git a/scene/3d/physics/collision_shape_3d.cpp b/scene/3d/physics/collision_shape_3d.cpp
index f3492a3cf3..304fa74b06 100644
--- a/scene/3d/physics/collision_shape_3d.cpp
+++ b/scene/3d/physics/collision_shape_3d.cpp
@@ -120,7 +120,7 @@ void CollisionShape3D::resource_changed(Ref<Resource> res) {
#endif
PackedStringArray CollisionShape3D::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = Node3D::get_configuration_warnings();
CollisionObject3D *col_object = Object::cast_to<CollisionObject3D>(get_parent());
if (col_object == nullptr) {
diff --git a/scene/3d/physics/vehicle_body_3d.cpp b/scene/3d/physics/vehicle_body_3d.cpp
index b4c321cf5f..5073705145 100644
--- a/scene/3d/physics/vehicle_body_3d.cpp
+++ b/scene/3d/physics/vehicle_body_3d.cpp
@@ -106,7 +106,7 @@ void VehicleWheel3D::_notification(int p_what) {
}
PackedStringArray VehicleWheel3D::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = Node3D::get_configuration_warnings();
if (!Object::cast_to<VehicleBody3D>(get_parent())) {
warnings.push_back(RTR("VehicleWheel3D serves to provide a wheel system to a VehicleBody3D. Please use it as a child of a VehicleBody3D."));
diff --git a/scene/3d/remote_transform_3d.cpp b/scene/3d/remote_transform_3d.cpp
index e580882c46..f970879aa4 100644
--- a/scene/3d/remote_transform_3d.cpp
+++ b/scene/3d/remote_transform_3d.cpp
@@ -211,7 +211,7 @@ void RemoteTransform3D::force_update_cache() {
}
PackedStringArray RemoteTransform3D::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = Node3D::get_configuration_warnings();
if (!has_node(remote_node) || !Object::cast_to<Node3D>(get_node(remote_node))) {
warnings.push_back(RTR("The \"Remote Path\" property must point to a valid Node3D or Node3D-derived node to work."));
diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp
index 4fe5dd2385..7f67bde0cf 100644
--- a/scene/3d/soft_body_3d.cpp
+++ b/scene/3d/soft_body_3d.cpp
@@ -383,7 +383,7 @@ void SoftBody3D::_bind_methods() {
}
PackedStringArray SoftBody3D::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = MeshInstance3D::get_configuration_warnings();
if (mesh.is_null()) {
warnings.push_back(RTR("This body will be ignored until you set a mesh."));
diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp
index 79a01450dd..a59754c8cc 100644
--- a/scene/3d/visual_instance_3d.cpp
+++ b/scene/3d/visual_instance_3d.cpp
@@ -497,7 +497,7 @@ bool GeometryInstance3D::is_ignoring_occlusion_culling() {
}
PackedStringArray GeometryInstance3D::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = VisualInstance3D::get_configuration_warnings();
if (!Math::is_zero_approx(visibility_range_end) && visibility_range_end <= visibility_range_begin) {
warnings.push_back(RTR("The GeometryInstance3D visibility range's End distance is set to a non-zero value, but is lower than the Begin distance.\nThis means the GeometryInstance3D will never be visible.\nTo resolve this, set the End distance to 0 or to a value greater than the Begin distance."));
diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp
index ffca856fba..80ff176a98 100644
--- a/scene/3d/voxel_gi.cpp
+++ b/scene/3d/voxel_gi.cpp
@@ -518,7 +518,7 @@ AABB VoxelGI::get_aabb() const {
}
PackedStringArray VoxelGI::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = VisualInstance3D::get_configuration_warnings();
if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
warnings.push_back(RTR("VoxelGI nodes are not supported when using the GL Compatibility backend yet. Support will be added in a future release."));
diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp
index b71f9bc0c4..214c1f77ca 100644
--- a/scene/3d/xr_nodes.cpp
+++ b/scene/3d/xr_nodes.cpp
@@ -77,7 +77,7 @@ void XRCamera3D::_pose_changed(const Ref<XRPose> &p_pose) {
}
PackedStringArray XRCamera3D::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = Camera3D::get_configuration_warnings();
if (is_visible() && is_inside_tree()) {
// Warn if the node has a parent which isn't an XROrigin3D!
@@ -461,7 +461,7 @@ XRNode3D::~XRNode3D() {
}
PackedStringArray XRNode3D::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = Node3D::get_configuration_warnings();
if (is_visible() && is_inside_tree()) {
// Warn if the node has a parent which isn't an XROrigin3D!
@@ -644,7 +644,7 @@ Plane XRAnchor3D::get_plane() const {
Vector<XROrigin3D *> XROrigin3D::origin_nodes;
PackedStringArray XROrigin3D::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = Node3D::get_configuration_warnings();
if (is_visible() && is_inside_tree()) {
bool has_camera = false;
diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp
index 1c5f40f56e..664302d45e 100644
--- a/scene/animation/animation_mixer.cpp
+++ b/scene/animation/animation_mixer.cpp
@@ -1636,6 +1636,9 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
}
if (t_obj->call(SNAME("get_is_sample"))) {
+ if (t->audio_stream_playback->get_sample_playback().is_valid()) {
+ AudioServer::get_singleton()->stop_sample_playback(t->audio_stream_playback->get_sample_playback());
+ }
Ref<AudioSamplePlayback> sample_playback;
sample_playback.instantiate();
sample_playback->stream = stream;
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index 867bbda4b3..19080e61de 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -681,7 +681,7 @@ uint64_t AnimationTree::get_last_process_pass() const {
}
PackedStringArray AnimationTree::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = AnimationMixer::get_configuration_warnings();
if (!root_animation_node.is_valid()) {
warnings.push_back(RTR("No root AnimationNode for the graph is set."));
}
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 15ada0021a..e030828595 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -248,7 +248,7 @@ void Control::get_argument_options(const StringName &p_function, int p_idx, List
PackedStringArray Control::get_configuration_warnings() const {
ERR_READ_THREAD_GUARD_V(PackedStringArray());
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = CanvasItem::get_configuration_warnings();
if (data.mouse_filter == MOUSE_FILTER_IGNORE && !data.tooltip.is_empty()) {
warnings.push_back(RTR("The Hint Tooltip won't be displayed as the control's Mouse Filter is set to \"Ignore\". To solve this, set the Mouse Filter to \"Stop\" or \"Pass\"."));
@@ -2356,6 +2356,24 @@ void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, cons
points[2] = xform.xform(c->get_size());
points[3] = xform.xform(Point2(0, c->get_size().y));
+ // Tie-breaking aims to address situations where a potential focus neighbor's bounding rect
+ // is right next to the currently focused control (e.g. in BoxContainer with
+ // separation overridden to 0). This needs specific handling so that the correct
+ // focus neighbor is selected.
+
+ // Calculate centers of the potential neighbor, currently focused, and closest controls.
+ Point2 center = xform.xform(0.5 * c->get_size());
+ // We only have the points, not an actual reference.
+ Point2 p_center = 0.25 * (p_points[0] + p_points[1] + p_points[2] + p_points[3]);
+ Point2 closest_center;
+ bool should_tiebreak = false;
+ if (*r_closest != nullptr) {
+ should_tiebreak = true;
+ Control *closest = *r_closest;
+ Transform2D closest_xform = closest->get_global_transform();
+ closest_center = closest_xform.xform(0.5 * closest->get_size());
+ }
+
real_t min = 1e7;
for (int i = 0; i < 4; i++) {
@@ -2376,10 +2394,15 @@ void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, cons
Vector2 pa, pb;
real_t d = Geometry2D::get_closest_points_between_segments(la, lb, fa, fb, pa, pb);
- //real_t d = Geometry2D::get_closest_distance_between_segments(Vector3(la.x,la.y,0),Vector3(lb.x,lb.y,0),Vector3(fa.x,fa.y,0),Vector3(fb.x,fb.y,0));
if (d < r_closest_dist) {
r_closest_dist = d;
*r_closest = c;
+ } else if (should_tiebreak && d == r_closest_dist) {
+ // Tie-break in favor of the control most aligned with p_dir.
+ if (p_dir.dot((center - p_center).normalized()) > p_dir.dot((closest_center - p_center).normalized())) {
+ r_closest_dist = d;
+ *r_closest = c;
+ }
}
}
}
diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp
index 83359653f1..d7b1a4933d 100644
--- a/scene/gui/range.cpp
+++ b/scene/gui/range.cpp
@@ -31,7 +31,7 @@
#include "range.h"
PackedStringArray Range::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = Control::get_configuration_warnings();
if (shared->exp_ratio && shared->min <= 0) {
warnings.push_back(RTR("If \"Exp Edit\" is enabled, \"Min Value\" must be greater than 0."));
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 715b682342..552458245c 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -3412,6 +3412,21 @@ bool RichTextLabel::remove_paragraph(int p_paragraph, bool p_no_invalidate) {
selection.click_item = nullptr;
selection.active = false;
+ if (is_processing_internal()) {
+ bool process_enabled = false;
+ Item *it = main;
+ while (it) {
+ Vector<ItemFX *> fx_stack;
+ _fetch_item_fx_stack(it, fx_stack);
+ if (fx_stack.size()) {
+ process_enabled = true;
+ break;
+ }
+ it = _get_next_item(it, true);
+ }
+ set_process_internal(process_enabled);
+ }
+
if (p_no_invalidate) {
// Do not invalidate cache, only update vertical offsets of the paragraphs after deleted one and scrollbar.
int to_line = main->first_invalid_line.load() - 1;
@@ -3985,6 +4000,7 @@ void RichTextLabel::pop_all() {
void RichTextLabel::clear() {
_stop_thread();
+ set_process_internal(false);
MutexLock data_lock(data_mutex);
main->_clear_children();
@@ -4177,8 +4193,6 @@ void RichTextLabel::append_text(const String &p_bbcode) {
bool after_list_open_tag = false;
bool after_list_close_tag = false;
- set_process_internal(false);
-
while (pos <= p_bbcode.length()) {
int brk_pos = p_bbcode.find_char('[', pos);
@@ -5253,17 +5267,6 @@ void RichTextLabel::append_text(const String &p_bbcode) {
}
}
}
-
- Vector<ItemFX *> fx_items;
- for (Item *E : main->subitems) {
- Item *subitem = static_cast<Item *>(E);
- _fetch_item_fx_stack(subitem, fx_items);
-
- if (fx_items.size()) {
- set_process_internal(true);
- break;
- }
- }
}
void RichTextLabel::scroll_to_selection() {
diff --git a/scene/gui/subviewport_container.cpp b/scene/gui/subviewport_container.cpp
index c715aceb0b..a443ae9abf 100644
--- a/scene/gui/subviewport_container.cpp
+++ b/scene/gui/subviewport_container.cpp
@@ -259,7 +259,7 @@ void SubViewportContainer::remove_child_notify(Node *p_child) {
}
PackedStringArray SubViewportContainer::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ PackedStringArray warnings = Container::get_configuration_warnings();
bool has_viewport = false;
for (int i = 0; i < get_child_count(); i++) {
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index edd25d1d5c..8871af23cb 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -3466,29 +3466,37 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {
accept_event();
}
- if (!selected_item || select_mode == SELECT_ROW || selected_col > (columns.size() - 1)) {
+ if (!selected_item || selected_col > (columns.size() - 1)) {
return;
}
+
if (k.is_valid() && k->is_shift_pressed()) {
selected_item->set_collapsed_recursive(false);
- } else {
+ } else if (select_mode != SELECT_ROW) {
_go_right();
+ } else if (selected_item->get_first_child() != nullptr && selected_item->is_collapsed()) {
+ selected_item->set_collapsed(false);
+ } else {
+ _go_down();
}
} else if (p_event->is_action("ui_left") && p_event->is_pressed()) {
if (!cursor_can_exit_tree) {
accept_event();
}
- if (!selected_item || select_mode == SELECT_ROW || selected_col < 0) {
+ if (!selected_item || selected_col < 0) {
return;
}
if (k.is_valid() && k->is_shift_pressed()) {
selected_item->set_collapsed_recursive(true);
- } else {
+ } else if (select_mode != SELECT_ROW) {
_go_left();
+ } else if (selected_item->get_first_child() != nullptr && !selected_item->is_collapsed()) {
+ selected_item->set_collapsed(true);
+ } else {
+ _go_up();
}
-
} else if (p_event->is_action("ui_up") && p_event->is_pressed() && !is_command) {
if (!cursor_can_exit_tree) {
accept_event();
diff --git a/scene/resources/audio_stream_polyphonic.cpp b/scene/resources/audio_stream_polyphonic.cpp
index 999b0c9f0a..c7b8b1c723 100644
--- a/scene/resources/audio_stream_polyphonic.cpp
+++ b/scene/resources/audio_stream_polyphonic.cpp
@@ -247,6 +247,11 @@ AudioStreamPlaybackPolyphonic::ID AudioStreamPlaybackPolyphonic::play_stream(con
sp->volume_vector.write[2] = AudioFrame(linear_volume, linear_volume);
sp->volume_vector.write[3] = AudioFrame(linear_volume, linear_volume);
sp->bus = p_bus;
+
+ if (streams[i].stream_playback->get_sample_playback().is_valid()) {
+ AudioServer::get_singleton()->stop_playback_stream(sp);
+ }
+
streams[i].stream_playback->set_sample_playback(sp);
AudioServer::get_singleton()->start_sample_playback(sp);
}
@@ -315,6 +320,9 @@ Ref<AudioSamplePlayback> AudioStreamPlaybackPolyphonic::get_sample_playback() co
void AudioStreamPlaybackPolyphonic::set_sample_playback(const Ref<AudioSamplePlayback> &p_playback) {
sample_playback = p_playback;
+ if (sample_playback.is_valid()) {
+ sample_playback->stream_playback = Ref<AudioStreamPlayback>(this);
+ }
}
void AudioStreamPlaybackPolyphonic::_bind_methods() {
diff --git a/scene/resources/audio_stream_wav.cpp b/scene/resources/audio_stream_wav.cpp
index 08ebacc2b3..de67a93bd1 100644
--- a/scene/resources/audio_stream_wav.cpp
+++ b/scene/resources/audio_stream_wav.cpp
@@ -123,10 +123,8 @@ void AudioStreamPlaybackWAV::do_resample(const Depth *p_src, AudioFrame *p_dst,
int16_t nibble, diff, step;
p_ima_adpcm[i].last_nibble++;
- const uint8_t *src_ptr = (const uint8_t *)base->data;
- src_ptr += AudioStreamWAV::DATA_PAD;
- uint8_t nbb = src_ptr[(p_ima_adpcm[i].last_nibble >> 1) * (is_stereo ? 2 : 1) + i];
+ uint8_t nbb = p_src[(p_ima_adpcm[i].last_nibble >> 1) * (is_stereo ? 2 : 1) + i];
nibble = (p_ima_adpcm[i].last_nibble & 1) ? (nbb >> 4) : (nbb & 0xF);
step = _ima_adpcm_step_table[p_ima_adpcm[i].step_index];
@@ -184,9 +182,8 @@ void AudioStreamPlaybackWAV::do_resample(const Depth *p_src, AudioFrame *p_dst,
if (p_qoa->data_ofs != new_data_ofs) {
p_qoa->data_ofs = new_data_ofs;
- const uint8_t *src_ptr = (const uint8_t *)base->data;
- src_ptr += p_qoa->data_ofs + AudioStreamWAV::DATA_PAD;
- qoa_decode_frame(src_ptr, p_qoa->frame_len, &p_qoa->desc, p_qoa->dec, &p_qoa->dec_len);
+ const uint8_t *ofs_src = (uint8_t *)p_src + p_qoa->data_ofs;
+ qoa_decode_frame(ofs_src, p_qoa->frame_len, &p_qoa->desc, p_qoa->dec.ptr(), &p_qoa->dec_len);
}
uint32_t dec_idx = (interp_pos % QOA_FRAME_LEN) * p_qoa->desc.channels;
@@ -267,7 +264,7 @@ void AudioStreamPlaybackWAV::do_resample(const Depth *p_src, AudioFrame *p_dst,
}
int AudioStreamPlaybackWAV::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) {
- if (!base->data || !active) {
+ if (base->data.is_empty() || !active) {
for (int i = 0; i < p_frames; i++) {
p_buffer[i] = AudioFrame(0, 0);
}
@@ -324,8 +321,7 @@ int AudioStreamPlaybackWAV::mix(AudioFrame *p_buffer, float p_rate_scale, int p_
/* audio data */
- uint8_t *dataptr = (uint8_t *)base->data;
- const void *data = dataptr + AudioStreamWAV::DATA_PAD;
+ const uint8_t *data = base->data.ptr() + AudioStreamWAV::DATA_PAD;
AudioFrame *dst_buff = p_buffer;
if (format == AudioStreamWAV::FORMAT_IMA_ADPCM) {
@@ -479,15 +475,14 @@ Ref<AudioSamplePlayback> AudioStreamPlaybackWAV::get_sample_playback() const {
void AudioStreamPlaybackWAV::set_sample_playback(const Ref<AudioSamplePlayback> &p_playback) {
sample_playback = p_playback;
+ if (sample_playback.is_valid()) {
+ sample_playback->stream_playback = Ref<AudioStreamPlayback>(this);
+ }
}
AudioStreamPlaybackWAV::AudioStreamPlaybackWAV() {}
-AudioStreamPlaybackWAV::~AudioStreamPlaybackWAV() {
- if (qoa.dec) {
- memfree(qoa.dec);
- }
-}
+AudioStreamPlaybackWAV::~AudioStreamPlaybackWAV() {}
/////////////////////
@@ -554,7 +549,7 @@ double AudioStreamWAV::get_length() const {
break;
case AudioStreamWAV::FORMAT_QOA:
qoa_desc desc = {};
- qoa_decode_header((uint8_t *)data + DATA_PAD, data_bytes, &desc);
+ qoa_decode_header(data.ptr() + DATA_PAD, data_bytes, &desc);
len = desc.samples * desc.channels;
break;
}
@@ -572,22 +567,16 @@ bool AudioStreamWAV::is_monophonic() const {
void AudioStreamWAV::set_data(const Vector<uint8_t> &p_data) {
AudioServer::get_singleton()->lock();
- if (data) {
- memfree(data);
- data = nullptr;
- data_bytes = 0;
- }
- int datalen = p_data.size();
- if (datalen) {
- const uint8_t *r = p_data.ptr();
- int alloc_len = datalen + DATA_PAD * 2;
- data = memalloc(alloc_len); //alloc with some padding for interpolation
- memset(data, 0, alloc_len);
- uint8_t *dataptr = (uint8_t *)data;
- memcpy(dataptr + DATA_PAD, r, datalen);
- data_bytes = datalen;
- }
+ int src_data_len = p_data.size();
+
+ data.clear();
+
+ int alloc_len = src_data_len + DATA_PAD * 2;
+ data.resize(alloc_len);
+ memset(data.ptr(), 0, alloc_len);
+ memcpy(data.ptr() + DATA_PAD, p_data.ptr(), src_data_len);
+ data_bytes = src_data_len;
AudioServer::get_singleton()->unlock();
}
@@ -595,13 +584,9 @@ void AudioStreamWAV::set_data(const Vector<uint8_t> &p_data) {
Vector<uint8_t> AudioStreamWAV::get_data() const {
Vector<uint8_t> pv;
- if (data) {
+ if (!data.is_empty()) {
pv.resize(data_bytes);
- {
- uint8_t *w = pv.ptrw();
- uint8_t *dataptr = (uint8_t *)data;
- memcpy(w, dataptr + DATA_PAD, data_bytes);
- }
+ memcpy(pv.ptrw(), data.ptr() + DATA_PAD, data_bytes);
}
return pv;
@@ -693,12 +678,12 @@ Ref<AudioStreamPlayback> AudioStreamWAV::instantiate_playback() {
sample->base = Ref<AudioStreamWAV>(this);
if (format == AudioStreamWAV::FORMAT_QOA) {
- uint32_t ffp = qoa_decode_header((uint8_t *)data + DATA_PAD, data_bytes, &sample->qoa.desc);
+ uint32_t ffp = qoa_decode_header(data.ptr() + DATA_PAD, data_bytes, &sample->qoa.desc);
ERR_FAIL_COND_V(ffp != 8, Ref<AudioStreamPlaybackWAV>());
sample->qoa.frame_len = qoa_max_frame_size(&sample->qoa.desc);
int samples_len = (sample->qoa.desc.samples > QOA_FRAME_LEN ? QOA_FRAME_LEN : sample->qoa.desc.samples);
- int alloc_len = sample->qoa.desc.channels * samples_len * sizeof(int16_t);
- sample->qoa.dec = (int16_t *)memalloc(alloc_len);
+ int dec_len = sample->qoa.desc.channels * samples_len;
+ sample->qoa.dec.resize(dec_len);
}
return sample;
@@ -780,10 +765,4 @@ void AudioStreamWAV::_bind_methods() {
AudioStreamWAV::AudioStreamWAV() {}
-AudioStreamWAV::~AudioStreamWAV() {
- if (data) {
- memfree(data);
- data = nullptr;
- data_bytes = 0;
- }
-}
+AudioStreamWAV::~AudioStreamWAV() {}
diff --git a/scene/resources/audio_stream_wav.h b/scene/resources/audio_stream_wav.h
index 47aa10e790..bc62e8883a 100644
--- a/scene/resources/audio_stream_wav.h
+++ b/scene/resources/audio_stream_wav.h
@@ -62,7 +62,7 @@ class AudioStreamPlaybackWAV : public AudioStreamPlayback {
qoa_desc desc = {};
uint32_t data_ofs = 0;
uint32_t frame_len = 0;
- int16_t *dec = nullptr;
+ LocalVector<int16_t> dec;
uint32_t dec_len = 0;
int64_t cache_pos = -1;
int16_t cache[2] = { 0, 0 };
@@ -137,7 +137,7 @@ private:
int loop_begin = 0;
int loop_end = 0;
int mix_rate = 44100;
- void *data = nullptr;
+ LocalVector<uint8_t> data;
uint32_t data_bytes = 0;
protected:
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index 62ea749465..69dc71e414 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -1354,25 +1354,6 @@ Ref<SceneState> SceneState::get_base_scene_state() const {
return Ref<SceneState>();
}
-void SceneState::update_instance_resource(String p_path, Ref<PackedScene> p_packed_scene) {
- ERR_FAIL_COND(p_packed_scene.is_null());
-
- for (const NodeData &nd : nodes) {
- if (nd.instance >= 0) {
- if (!(nd.instance & FLAG_INSTANCE_IS_PLACEHOLDER)) {
- int instance_id = nd.instance & FLAG_MASK;
- Ref<PackedScene> original_packed_scene = variants[instance_id];
- if (original_packed_scene.is_valid()) {
- if (original_packed_scene->get_path() == p_path) {
- variants.remove_at(instance_id);
- variants.insert(instance_id, p_packed_scene);
- }
- }
- }
- }
- }
-}
-
int SceneState::find_node_by_path(const NodePath &p_node) const {
ERR_FAIL_COND_V_MSG(node_path_cache.is_empty(), -1, "This operation requires the node cache to have been built.");
diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp
index e2c8686911..ece088a694 100644
--- a/servers/audio/audio_stream.cpp
+++ b/servers/audio/audio_stream.cpp
@@ -173,12 +173,12 @@ int AudioStreamPlaybackResampled::mix(AudioFrame *p_buffer, float p_rate_scale,
}
float mu2 = mu * mu;
- AudioFrame a0 = 3 * y1 - 3 * y2 + y3 - y0;
- AudioFrame a1 = 2 * y0 - 5 * y1 + 4 * y2 - y3;
- AudioFrame a2 = y2 - y0;
- AudioFrame a3 = 2 * y1;
+ float h11 = mu2 * (mu - 1);
+ float z = mu2 - h11;
+ float h01 = z - h11;
+ float h10 = mu - z;
- p_buffer[i] = (a0 * mu * mu2 + a1 * mu2 + a2 * mu + a3) / 2;
+ p_buffer[i] = y1 + (y2 - y1) * h01 + ((y2 - y0) * h10 + (y3 - y1) * h11) * 0.5;
mix_offset += mix_increment;
diff --git a/servers/audio/audio_stream.h b/servers/audio/audio_stream.h
index c41545aeba..7f2b68a796 100644
--- a/servers/audio/audio_stream.h
+++ b/servers/audio/audio_stream.h
@@ -48,6 +48,7 @@ class AudioSamplePlayback : public RefCounted {
public:
Ref<AudioStream> stream;
+ Ref<AudioStreamPlayback> stream_playback;
float offset = 0.0f;
float pitch_scale = 1.0;
diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp
index 332f8984a2..e06079efe8 100644
--- a/servers/audio_server.cpp
+++ b/servers/audio_server.cpp
@@ -501,12 +501,7 @@ void AudioServer::_mix_step() {
switch (playback->state.load()) {
case AudioStreamPlaybackListNode::AWAITING_DELETION:
case AudioStreamPlaybackListNode::FADE_OUT_TO_DELETION:
- playback_list.erase(playback, [](AudioStreamPlaybackListNode *p) {
- delete p->prev_bus_details;
- delete p->bus_details.load();
- p->stream_playback.unref();
- delete p;
- });
+ _delete_stream_playback_list_node(playback);
break;
case AudioStreamPlaybackListNode::FADE_OUT_TO_PAUSE: {
// Pause the stream.
@@ -697,6 +692,23 @@ AudioServer::AudioStreamPlaybackListNode *AudioServer::_find_playback_list_node(
return nullptr;
}
+void AudioServer::_delete_stream_playback(Ref<AudioStreamPlayback> p_playback) {
+ ERR_FAIL_COND(p_playback.is_null());
+ AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback);
+ if (playback_node) {
+ _delete_stream_playback_list_node(playback_node);
+ }
+}
+
+void AudioServer::_delete_stream_playback_list_node(AudioStreamPlaybackListNode *p_playback_node) {
+ playback_list.erase(p_playback_node, [](AudioStreamPlaybackListNode *p) {
+ delete p->prev_bus_details;
+ delete p->bus_details.load();
+ p->stream_playback.unref();
+ delete p;
+ });
+}
+
bool AudioServer::thread_has_channel_mix_buffer(int p_bus, int p_buffer) const {
if (p_bus < 0 || p_bus >= buses.size()) {
return false;
@@ -1227,8 +1239,12 @@ void AudioServer::stop_playback_stream(Ref<AudioStreamPlayback> p_playback) {
ERR_FAIL_COND(p_playback.is_null());
// Handle sample playback.
- if (p_playback->get_is_sample() && p_playback->get_sample_playback().is_valid()) {
- AudioServer::get_singleton()->stop_sample_playback(p_playback->get_sample_playback());
+ if (p_playback->get_is_sample()) {
+ if (p_playback->get_sample_playback().is_valid()) {
+ AudioServer::get_singleton()->stop_sample_playback(p_playback->get_sample_playback());
+ } else {
+ _delete_stream_playback(p_playback);
+ }
return;
}
@@ -1370,8 +1386,12 @@ void AudioServer::set_playback_highshelf_params(Ref<AudioStreamPlayback> p_playb
bool AudioServer::is_playback_active(Ref<AudioStreamPlayback> p_playback) {
ERR_FAIL_COND_V(p_playback.is_null(), false);
- if (p_playback->get_is_sample() && p_playback->get_sample_playback().is_valid()) {
- return sample_playback_list.has(p_playback->get_sample_playback());
+ if (p_playback->get_is_sample()) {
+ if (p_playback->get_sample_playback().is_valid()) {
+ return sample_playback_list.has(p_playback->get_sample_playback());
+ } else {
+ return false;
+ }
}
AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback);
@@ -1845,8 +1865,12 @@ void AudioServer::start_sample_playback(const Ref<AudioSamplePlayback> &p_playba
void AudioServer::stop_sample_playback(const Ref<AudioSamplePlayback> &p_playback) {
ERR_FAIL_COND_MSG(p_playback.is_null(), "Parameter p_playback is null.");
- AudioDriver::get_singleton()->stop_sample_playback(p_playback);
- sample_playback_list.erase(p_playback);
+ if (sample_playback_list.has(p_playback)) {
+ sample_playback_list.erase(p_playback);
+ AudioDriver::get_singleton()->stop_sample_playback(p_playback);
+ p_playback->stream_playback->set_sample_playback(nullptr);
+ stop_playback_stream(p_playback->stream_playback);
+ }
}
void AudioServer::set_sample_playback_pause(const Ref<AudioSamplePlayback> &p_playback, bool p_paused) {
diff --git a/servers/audio_server.h b/servers/audio_server.h
index 2d6fc60860..16fcc029b3 100644
--- a/servers/audio_server.h
+++ b/servers/audio_server.h
@@ -297,6 +297,8 @@ private:
SafeList<AudioStreamPlaybackListNode *> playback_list;
SafeList<AudioStreamPlaybackBusDetails *> bus_details_graveyard;
+ void _delete_stream_playback(Ref<AudioStreamPlayback> p_playback);
+ void _delete_stream_playback_list_node(AudioStreamPlaybackListNode *p_node);
// TODO document if this is necessary.
SafeList<AudioStreamPlaybackBusDetails *> bus_details_graveyard_frame_old;
diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
index 63dc54e24c..9f390c99f9 100644
--- a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
@@ -342,7 +342,7 @@ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataType type, int p_
}
}
-_FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, const Vector<ShaderLanguage::ConstantNode::Value> &value, uint8_t *data) {
+_FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, const Vector<ShaderLanguage::Scalar> &value, uint8_t *data) {
switch (type) {
case ShaderLanguage::TYPE_BOOL: {
uint32_t *gui = (uint32_t *)data;
@@ -566,7 +566,7 @@ void MaterialStorage::ShaderData::set_default_texture_parameter(const StringName
Variant MaterialStorage::ShaderData::get_default_parameter(const StringName &p_parameter) const {
if (uniforms.has(p_parameter)) {
ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter];
- Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value;
+ Vector<ShaderLanguage::Scalar> default_value = uniform.default_value;
return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint);
}
return Variant();
diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp
index 49e005ca96..43703f8656 100644
--- a/servers/rendering/shader_compiler.cpp
+++ b/servers/rendering/shader_compiler.cpp
@@ -185,7 +185,7 @@ static String f2sp0(float p_float) {
return num;
}
-static String get_constant_text(SL::DataType p_type, const Vector<SL::ConstantNode::Value> &p_values) {
+static String get_constant_text(SL::DataType p_type, const Vector<SL::Scalar> &p_values) {
switch (p_type) {
case SL::TYPE_BOOL:
return p_values[0].boolean ? "true" : "false";
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 4eaf7fcb55..2249cd2010 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -1362,7 +1362,7 @@ void ShaderLanguage::_parse_used_identifier(const StringName &p_identifier, Iden
}
#endif // DEBUG_ENABLED
-bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type, IdentifierType *r_type, bool *r_is_const, int *r_array_size, StringName *r_struct_name, ConstantNode::Value *r_constant_value) {
+bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type, IdentifierType *r_type, bool *r_is_const, int *r_array_size, StringName *r_struct_name, Vector<Scalar> *r_constant_values) {
if (is_shader_inc) {
for (int i = 0; i < RenderingServer::SHADER_MAX; i++) {
for (const KeyValue<StringName, FunctionInfo> &E : ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(i))) {
@@ -1424,8 +1424,8 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea
if (r_struct_name) {
*r_struct_name = p_block->variables[p_identifier].struct_name;
}
- if (r_constant_value) {
- *r_constant_value = p_block->variables[p_identifier].value;
+ if (r_constant_values && !p_block->variables[p_identifier].values.is_empty()) {
+ *r_constant_values = p_block->variables[p_identifier].values;
}
if (r_type) {
*r_type = IDENTIFIER_LOCAL_VAR;
@@ -1507,13 +1507,9 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea
if (r_struct_name) {
*r_struct_name = shader->constants[p_identifier].struct_name;
}
- if (r_constant_value) {
- if (shader->constants[p_identifier].initializer && shader->constants[p_identifier].initializer->type == Node::NODE_TYPE_CONSTANT) {
- ConstantNode *cnode = static_cast<ConstantNode *>(shader->constants[p_identifier].initializer);
-
- if (cnode->values.size() == 1) {
- *r_constant_value = cnode->values[0];
- }
+ if (r_constant_values) {
+ if (shader->constants[p_identifier].initializer && !shader->constants[p_identifier].initializer->get_values().is_empty()) {
+ *r_constant_values = shader->constants[p_identifier].initializer->get_values();
}
}
if (r_type) {
@@ -1544,7 +1540,7 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea
return false;
}
-bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type, int *r_ret_size) {
+bool ShaderLanguage::_validate_operator(const BlockNode *p_block, OperatorNode *p_op, DataType *r_ret_type, int *r_ret_size) {
bool valid = false;
DataType ret_type = TYPE_VOID;
int ret_size = 0;
@@ -2007,9 +2003,384 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type
if (r_ret_size) {
*r_ret_size = ret_size;
}
+
+ if (valid && (!p_block || p_block->use_op_eval)) {
+ // Need to be placed here and not in the `_reduce_expression` because otherwise expressions like `1 + 2 / 2` will not work correctly.
+ valid = _eval_operator(p_block, p_op);
+ }
+
return valid;
}
+Vector<ShaderLanguage::Scalar> ShaderLanguage::_get_node_values(const BlockNode *p_block, Node *p_node) {
+ Vector<Scalar> result;
+
+ switch (p_node->type) {
+ case Node::NODE_TYPE_VARIABLE: {
+ _find_identifier(p_block, false, FunctionInfo(), static_cast<VariableNode *>(p_node)->name, nullptr, nullptr, nullptr, nullptr, nullptr, &result);
+ } break;
+ default: {
+ result = p_node->get_values();
+ } break;
+ }
+
+ return result;
+}
+
+bool ShaderLanguage::_eval_operator(const BlockNode *p_block, OperatorNode *p_op) {
+ bool is_valid = true;
+
+ switch (p_op->op) {
+ case OP_EQUAL:
+ case OP_NOT_EQUAL:
+ case OP_LESS:
+ case OP_LESS_EQUAL:
+ case OP_GREATER:
+ case OP_GREATER_EQUAL:
+ case OP_AND:
+ case OP_OR:
+ case OP_ADD:
+ case OP_SUB:
+ case OP_MUL:
+ case OP_DIV:
+ case OP_MOD:
+ case OP_SHIFT_LEFT:
+ case OP_SHIFT_RIGHT:
+ case OP_BIT_AND:
+ case OP_BIT_OR:
+ case OP_BIT_XOR: {
+ DataType a = p_op->arguments[0]->get_datatype();
+ DataType b = p_op->arguments[1]->get_datatype();
+
+ bool is_op_vec_transform = false;
+ if (p_op->op == OP_MUL) {
+ DataType ta = a;
+ DataType tb = b;
+
+ if (ta > tb) {
+ SWAP(ta, tb);
+ }
+ if (ta == TYPE_VEC2 && tb == TYPE_MAT2) {
+ is_op_vec_transform = true;
+ } else if (ta == TYPE_VEC3 && tb == TYPE_MAT3) {
+ is_op_vec_transform = true;
+ } else if (ta == TYPE_VEC4 && tb == TYPE_MAT4) {
+ is_op_vec_transform = true;
+ }
+ }
+
+ Vector<Scalar> va = _get_node_values(p_block, p_op->arguments[0]);
+ Vector<Scalar> vb = _get_node_values(p_block, p_op->arguments[1]);
+
+ if (is_op_vec_transform) {
+ p_op->values = _eval_vector_transform(va, vb, a, b, p_op->get_datatype());
+ } else {
+ p_op->values = _eval_vector(va, vb, a, b, p_op->get_datatype(), p_op->op, is_valid);
+ }
+ } break;
+ case OP_NOT:
+ case OP_NEGATE:
+ case OP_BIT_INVERT: {
+ p_op->values = _eval_unary_vector(_get_node_values(p_block, p_op->arguments[0]), p_op->get_datatype(), p_op->op);
+ } break;
+ default: {
+ } break;
+ }
+
+ return is_valid;
+}
+
+ShaderLanguage::Scalar ShaderLanguage::_eval_unary_scalar(const Scalar &p_a, Operator p_op, DataType p_ret_type) {
+ Scalar scalar;
+
+ switch (p_op) {
+ case OP_NOT: {
+ scalar.boolean = !p_a.boolean;
+ } break;
+ case OP_NEGATE: {
+ if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+ scalar.sint = -p_a.sint;
+ } else if (p_ret_type >= TYPE_UINT && p_ret_type <= TYPE_UVEC4) {
+ // Intentionally wrap the unsigned int value, because GLSL does.
+ scalar.uint = 0 - p_a.uint;
+ } else { // float types
+ scalar.real = -scalar.real;
+ }
+ } break;
+ case OP_BIT_INVERT: {
+ if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+ scalar.sint = ~p_a.sint;
+ } else { // uint types
+ scalar.uint = ~p_a.uint;
+ }
+ } break;
+ default: {
+ } break;
+ }
+
+ return scalar;
+}
+
+ShaderLanguage::Scalar ShaderLanguage::_eval_scalar(const Scalar &p_a, const Scalar &p_b, Operator p_op, DataType p_ret_type, bool &r_is_valid) {
+ Scalar scalar;
+
+ switch (p_op) {
+ case OP_EQUAL: {
+ scalar.boolean = p_a.boolean == p_b.boolean;
+ } break;
+ case OP_NOT_EQUAL: {
+ scalar.boolean = p_a.boolean != p_b.boolean;
+ } break;
+ case OP_LESS: {
+ if (p_ret_type == TYPE_INT) {
+ scalar.boolean = p_a.sint < p_b.sint;
+ } else if (p_ret_type == TYPE_UINT) {
+ scalar.boolean = p_a.uint < p_b.uint;
+ } else { // float type
+ scalar.boolean = p_a.real < p_b.real;
+ }
+ } break;
+ case OP_LESS_EQUAL: {
+ if (p_ret_type == TYPE_INT) {
+ scalar.boolean = p_a.sint <= p_b.sint;
+ } else if (p_ret_type == TYPE_UINT) {
+ scalar.boolean = p_a.uint <= p_b.uint;
+ } else { // float type
+ scalar.boolean = p_a.real <= p_b.real;
+ }
+ } break;
+ case OP_GREATER: {
+ if (p_ret_type == TYPE_INT) {
+ scalar.boolean = p_a.sint > p_b.sint;
+ } else if (p_ret_type == TYPE_UINT) {
+ scalar.boolean = p_a.uint > p_b.uint;
+ } else { // float type
+ scalar.boolean = p_a.real > p_b.real;
+ }
+ } break;
+ case OP_GREATER_EQUAL: {
+ if (p_ret_type == TYPE_INT) {
+ scalar.boolean = p_a.sint >= p_b.sint;
+ } else if (p_ret_type == TYPE_UINT) {
+ scalar.boolean = p_a.uint >= p_b.uint;
+ } else { // float type
+ scalar.boolean = p_a.real >= p_b.real;
+ }
+ } break;
+ case OP_AND: {
+ scalar.boolean = p_a.boolean && p_b.boolean;
+ } break;
+ case OP_OR: {
+ scalar.boolean = p_a.boolean || p_b.boolean;
+ } break;
+ case OP_ADD: {
+ if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+ scalar.sint = p_a.sint + p_b.sint;
+ } else if (p_ret_type >= TYPE_UINT && p_ret_type <= TYPE_UVEC4) {
+ scalar.uint = p_a.uint + p_b.uint;
+ } else { // float + matrix types
+ scalar.real = p_a.real + p_b.real;
+ }
+ } break;
+ case OP_SUB: {
+ if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+ scalar.sint = p_a.sint - p_b.sint;
+ } else if (p_ret_type >= TYPE_UINT && p_ret_type <= TYPE_UVEC4) {
+ scalar.uint = p_a.uint - p_b.uint;
+ } else { // float + matrix types
+ scalar.real = p_a.real - p_b.real;
+ }
+ } break;
+ case OP_MUL: {
+ if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+ scalar.sint = p_a.sint * p_b.sint;
+ } else if (p_ret_type >= TYPE_UINT && p_ret_type <= TYPE_UVEC4) {
+ scalar.uint = p_a.uint * p_b.uint;
+ } else { // float + matrix types
+ scalar.real = p_a.real * p_b.real;
+ }
+ } break;
+ case OP_DIV: {
+ if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+ if (p_b.sint == 0) {
+ _set_error(RTR("Division by zero error."));
+ r_is_valid = false;
+ break;
+ }
+ scalar.sint = p_a.sint / p_b.sint;
+ } else if (p_ret_type == TYPE_UINT && p_ret_type <= TYPE_UVEC4) {
+ if (p_b.uint == 0U) {
+ _set_error(RTR("Division by zero error."));
+ r_is_valid = false;
+ break;
+ }
+ scalar.uint = p_a.uint / p_b.uint;
+ } else { // float + matrix types
+ scalar.real = p_a.real / p_b.real;
+ }
+ } break;
+ case OP_MOD: {
+ if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+ if (p_b.sint == 0) {
+ _set_error(RTR("Modulo by zero error."));
+ r_is_valid = false;
+ break;
+ }
+ scalar.sint = p_a.sint % p_b.sint;
+ } else { // uint types
+ if (p_b.uint == 0U) {
+ _set_error(RTR("Modulo by zero error."));
+ r_is_valid = false;
+ break;
+ }
+ scalar.uint = p_a.uint % p_b.uint;
+ }
+ } break;
+ case OP_SHIFT_LEFT: {
+ if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+ scalar.sint = p_a.sint << p_b.sint;
+ } else { // uint types
+ scalar.uint = p_a.uint << p_b.uint;
+ }
+ } break;
+ case OP_SHIFT_RIGHT: {
+ if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+ scalar.sint = p_a.sint >> p_b.sint;
+ } else { // uint types
+ scalar.uint = p_a.uint >> p_b.uint;
+ }
+ } break;
+ case OP_BIT_AND: {
+ if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+ scalar.sint = p_a.sint & p_b.sint;
+ } else { // uint types
+ scalar.uint = p_a.uint & p_b.uint;
+ }
+ } break;
+ case OP_BIT_OR: {
+ if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+ scalar.sint = p_a.sint | p_b.sint;
+ } else { // uint types
+ scalar.uint = p_a.uint | p_b.uint;
+ }
+ } break;
+ case OP_BIT_XOR: {
+ if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+ scalar.sint = p_a.sint ^ p_b.sint;
+ } else { // uint types
+ scalar.uint = p_a.uint ^ p_b.uint;
+ }
+ } break;
+ default: {
+ } break;
+ }
+
+ return scalar;
+}
+
+Vector<ShaderLanguage::Scalar> ShaderLanguage::_eval_unary_vector(const Vector<Scalar> &p_va, DataType p_ret_type, Operator p_op) {
+ uint32_t size = get_datatype_component_count(p_ret_type);
+ if (p_va.size() != p_ret_type) {
+ return Vector<Scalar>(); // Non-evaluable values should not be parsed further.
+ }
+ Vector<Scalar> value;
+ value.resize(size);
+
+ Scalar *w = value.ptrw();
+ for (uint32_t i = 0U; i < size; i++) {
+ w[i] = _eval_unary_scalar(p_va[i], p_op, p_ret_type);
+ }
+ return value;
+}
+
+Vector<ShaderLanguage::Scalar> ShaderLanguage::_eval_vector(const Vector<Scalar> &p_va, const Vector<Scalar> &p_vb, DataType p_left_type, DataType p_right_type, DataType p_ret_type, Operator p_op, bool &r_is_valid) {
+ uint32_t left_size = get_datatype_component_count(p_left_type);
+ uint32_t right_size = get_datatype_component_count(p_right_type);
+
+ if (p_va.size() != left_size || p_vb.size() != right_size) {
+ return Vector<Scalar>(); // Non-evaluable values should not be parsed further.
+ }
+
+ uint32_t ret_size = get_datatype_component_count(p_ret_type);
+ Vector<Scalar> value;
+ value.resize(ret_size);
+
+ Scalar *w = value.ptrw();
+ for (uint32_t i = 0U; i < ret_size; i++) {
+ w[i] = _eval_scalar(p_va[MIN(i, left_size - 1)], p_vb[MIN(i, right_size - 1)], p_op, p_ret_type, r_is_valid);
+ if (!r_is_valid) {
+ return value;
+ }
+ }
+ return value;
+}
+
+Vector<ShaderLanguage::Scalar> ShaderLanguage::_eval_vector_transform(const Vector<Scalar> &p_va, const Vector<Scalar> &p_vb, DataType p_left_type, DataType p_right_type, DataType p_ret_type) {
+ uint32_t left_size = get_datatype_component_count(p_left_type);
+ uint32_t right_size = get_datatype_component_count(p_right_type);
+
+ if (p_va.size() != left_size || p_vb.size() != right_size) {
+ return Vector<Scalar>(); // Non-evaluable values should not be parsed further.
+ }
+
+ uint32_t ret_size = get_datatype_component_count(p_ret_type);
+ Vector<Scalar> value;
+ value.resize_zeroed(ret_size);
+
+ Scalar *w = value.ptrw();
+ switch (p_ret_type) {
+ case TYPE_VEC2: {
+ if (left_size == 2) { // v * m
+ Vector2 v = Vector2(p_va[0].real, p_va[1].real);
+
+ w[0].real = (p_vb[0].real * v.x + p_vb[1].real * v.y);
+ w[1].real = (p_vb[2].real * v.x + p_vb[3].real * v.y);
+ } else { // m * v
+ Vector2 v = Vector2(p_vb[0].real, p_vb[1].real);
+
+ w[0].real = (p_va[0].real * v.x + p_va[2].real * v.y);
+ w[1].real = (p_va[1].real * v.x + p_va[3].real * v.y);
+ }
+ } break;
+ case TYPE_VEC3: {
+ if (left_size == 3) { // v * m
+ Vector3 v = Vector3(p_va[0].real, p_va[1].real, p_va[2].real);
+
+ w[0].real = (p_vb[0].real * v.x + p_vb[1].real * v.y + p_vb[2].real * v.z);
+ w[1].real = (p_vb[3].real * v.x + p_vb[4].real * v.y + p_vb[5].real * v.z);
+ w[2].real = (p_vb[6].real * v.x + p_vb[7].real * v.y + p_vb[8].real * v.z);
+ } else { // m * v
+ Vector3 v = Vector3(p_vb[0].real, p_vb[1].real, p_vb[2].real);
+
+ w[0].real = (p_va[0].real * v.x + p_va[3].real * v.y + p_va[6].real * v.z);
+ w[1].real = (p_va[1].real * v.x + p_va[4].real * v.y + p_va[7].real * v.z);
+ w[2].real = (p_va[2].real * v.x + p_va[5].real * v.y + p_va[8].real * v.z);
+ }
+ } break;
+ case TYPE_VEC4: {
+ if (left_size == 4) { // v * m
+ Vector4 v = Vector4(p_va[0].real, p_va[1].real, p_va[2].real, p_va[3].real);
+
+ w[0].real = (p_vb[0].real * v.x + p_vb[1].real * v.y + p_vb[2].real * v.z + p_vb[3].real * v.w);
+ w[1].real = (p_vb[4].real * v.x + p_vb[5].real * v.y + p_vb[6].real * v.z + p_vb[7].real * v.w);
+ w[2].real = (p_vb[8].real * v.x + p_vb[9].real * v.y + p_vb[10].real * v.z + p_vb[11].real * v.w);
+ w[3].real = (p_vb[12].real * v.x + p_vb[13].real * v.y + p_vb[14].real * v.z + p_vb[15].real * v.w);
+ } else { // m * v
+ Vector4 v = Vector4(p_vb[0].real, p_vb[1].real, p_vb[2].real, p_vb[3].real);
+
+ w[0].real = (p_vb[0].real * v.x + p_vb[4].real * v.y + p_vb[8].real * v.z + p_vb[12].real * v.w);
+ w[1].real = (p_vb[1].real * v.x + p_vb[5].real * v.y + p_vb[9].real * v.z + p_vb[13].real * v.w);
+ w[2].real = (p_vb[2].real * v.x + p_vb[6].real * v.y + p_vb[10].real * v.z + p_vb[14].real * v.w);
+ w[3].real = (p_vb[3].real * v.x + p_vb[7].real * v.y + p_vb[11].real * v.z + p_vb[15].real * v.w);
+ }
+ } break;
+ default: {
+ } break;
+ }
+
+ return value;
+}
+
const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = {
// Constructors.
@@ -3271,34 +3642,15 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
int max = builtin_func_const_args[constarg_idx].max;
bool error = false;
- if (p_func->arguments[arg]->type == Node::NODE_TYPE_VARIABLE) {
- const VariableNode *vn = static_cast<VariableNode *>(p_func->arguments[arg]);
-
- bool is_const = false;
- ConstantNode::Value value;
- value.sint = -1;
-
- _find_identifier(p_block, false, p_function_info, vn->name, nullptr, nullptr, &is_const, nullptr, nullptr, &value);
- if (!is_const || value.sint < min || value.sint > max) {
+ Vector<Scalar> values = _get_node_values(p_block, p_func->arguments[arg]);
+ if (p_func->arguments[arg]->get_datatype() == TYPE_INT && !values.is_empty()) {
+ if (values[0].sint < min || values[0].sint > max) {
error = true;
}
} else {
- if (p_func->arguments[arg]->type == Node::NODE_TYPE_CONSTANT) {
- const ConstantNode *cn = static_cast<ConstantNode *>(p_func->arguments[arg]);
-
- if (cn->get_datatype() == TYPE_INT && cn->values.size() == 1) {
- int value = cn->values[0].sint;
-
- if (value < min || value > max) {
- error = true;
- }
- } else {
- error = true;
- }
- } else {
- error = true;
- }
+ error = true;
}
+
if (error) {
_set_error(vformat(RTR("Expected integer constant within [%d..%d] range."), min, max));
return false;
@@ -3760,7 +4112,7 @@ bool ShaderLanguage::is_token_hint(TokenType p_type) {
return int(p_type) > int(TK_RENDER_MODE) && int(p_type) < int(TK_SHADER_TYPE);
}
-bool ShaderLanguage::convert_constant(ConstantNode *p_constant, DataType p_to_type, ConstantNode::Value *p_value) {
+bool ShaderLanguage::convert_constant(ConstantNode *p_constant, DataType p_to_type, Scalar *p_value) {
if (p_constant->datatype == p_to_type) {
if (p_value) {
for (int i = 0; i < p_constant->values.size(); i++) {
@@ -3828,7 +4180,7 @@ bool ShaderLanguage::is_sampler_type(DataType p_type) {
return p_type > TYPE_MAT4 && p_type < TYPE_STRUCT;
}
-Variant ShaderLanguage::constant_value_to_variant(const Vector<ShaderLanguage::ConstantNode::Value> &p_value, DataType p_type, int p_array_size, ShaderLanguage::ShaderNode::Uniform::Hint p_hint) {
+Variant ShaderLanguage::constant_value_to_variant(const Vector<Scalar> &p_value, DataType p_type, int p_array_size, ShaderLanguage::ShaderNode::Uniform::Hint p_hint) {
int array_size = p_array_size;
if (p_value.size() > 0) {
@@ -4437,6 +4789,52 @@ uint32_t ShaderLanguage::get_datatype_size(ShaderLanguage::DataType p_type) {
ERR_FAIL_V(0);
}
+uint32_t ShaderLanguage::get_datatype_component_count(ShaderLanguage::DataType p_type) {
+ switch (p_type) {
+ case TYPE_BOOL:
+ return 1U;
+ case TYPE_BVEC2:
+ return 2U;
+ case TYPE_BVEC3:
+ return 3U;
+ case TYPE_BVEC4:
+ return 4U;
+ case TYPE_INT:
+ return 1U;
+ case TYPE_IVEC2:
+ return 2U;
+ case TYPE_IVEC3:
+ return 3U;
+ case TYPE_IVEC4:
+ return 4U;
+ case TYPE_UINT:
+ return 1U;
+ case TYPE_UVEC2:
+ return 2U;
+ case TYPE_UVEC3:
+ return 3U;
+ case TYPE_UVEC4:
+ return 4U;
+ case TYPE_FLOAT:
+ return 1U;
+ case TYPE_VEC2:
+ return 2U;
+ case TYPE_VEC3:
+ return 3U;
+ case TYPE_VEC4:
+ return 4U;
+ case TYPE_MAT2:
+ return 4U;
+ case TYPE_MAT3:
+ return 9U;
+ case TYPE_MAT4:
+ return 16U;
+ default:
+ break;
+ }
+ return 0U;
+}
+
void ShaderLanguage::get_keyword_list(List<String> *r_keywords) {
HashSet<String> kws;
@@ -4929,45 +5327,30 @@ Error ShaderLanguage::_parse_array_size(BlockNode *p_block, const FunctionInfo &
*r_unknown_size = true;
}
} else {
- int array_size = 0;
+ _set_tkpos(pos);
- if (!tk.is_integer_constant() || ((int)tk.constant) <= 0) {
- _set_tkpos(pos);
- Node *n = _parse_and_reduce_expression(p_block, p_function_info);
- if (n) {
- if (n->type == Node::NODE_TYPE_VARIABLE) {
- VariableNode *vn = static_cast<VariableNode *>(n);
- if (vn) {
- ConstantNode::Value v;
- DataType data_type;
- bool is_const = false;
+ int array_size = 0;
+ Node *expr = _parse_and_reduce_expression(p_block, p_function_info);
- _find_identifier(p_block, false, p_function_info, vn->name, &data_type, nullptr, &is_const, nullptr, nullptr, &v);
+ if (expr) {
+ Vector<Scalar> values = _get_node_values(p_block, expr);
- if (is_const) {
- if (data_type == TYPE_INT) {
- int32_t value = v.sint;
- if (value > 0) {
- array_size = value;
- }
- } else if (data_type == TYPE_UINT) {
- uint32_t value = v.uint;
- if (value > 0U) {
- array_size = value;
- }
- }
- }
- }
- } else if (n->type == Node::NODE_TYPE_OPERATOR) {
- _set_error(vformat(RTR("Array size expressions are not supported.")));
- return ERR_PARSE_ERROR;
- }
- if (r_size_expression != nullptr) {
- *r_size_expression = n;
+ if (!values.is_empty()) {
+ switch (expr->get_datatype()) {
+ case TYPE_INT: {
+ array_size = values[0].sint;
+ } break;
+ case TYPE_UINT: {
+ array_size = (int)values[0].uint;
+ } break;
+ default: {
+ } break;
}
}
- } else if (((int)tk.constant) > 0) {
- array_size = (uint32_t)tk.constant;
+
+ if (r_size_expression != nullptr) {
+ *r_size_expression = expr;
+ }
}
if (array_size <= 0) {
@@ -5064,7 +5447,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_bloc
idx++;
}
if (!auto_size && !undefined_size && an->initializer.size() != array_size) {
- _set_error(RTR("Array size mismatch."));
+ _set_error(vformat(RTR("Array size mismatch. Expected %d elements (found %d)."), array_size, an->initializer.size()));
return nullptr;
}
} else {
@@ -5185,7 +5568,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_bloc
}
}
if (an->initializer.size() != p_array_size) {
- _set_error(RTR("Array size mismatch."));
+ _set_error(vformat(RTR("Array size mismatch. Expected %d elements (found %d)."), p_array_size, an->initializer.size()));
return nullptr;
}
} else {
@@ -5230,7 +5613,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
} else if (tk.type == TK_FLOAT_CONSTANT) {
ConstantNode *constant = alloc_node<ConstantNode>();
- ConstantNode::Value v;
+ Scalar v;
v.real = tk.constant;
constant->values.push_back(v);
constant->datatype = TYPE_FLOAT;
@@ -5238,7 +5621,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
} else if (tk.type == TK_INT_CONSTANT) {
ConstantNode *constant = alloc_node<ConstantNode>();
- ConstantNode::Value v;
+ Scalar v;
v.sint = tk.constant;
constant->values.push_back(v);
constant->datatype = TYPE_INT;
@@ -5246,7 +5629,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
} else if (tk.type == TK_UINT_CONSTANT) {
ConstantNode *constant = alloc_node<ConstantNode>();
- ConstantNode::Value v;
+ Scalar v;
v.uint = tk.constant;
constant->values.push_back(v);
constant->datatype = TYPE_UINT;
@@ -5255,7 +5638,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
} else if (tk.type == TK_TRUE) {
//handle true constant
ConstantNode *constant = alloc_node<ConstantNode>();
- ConstantNode::Value v;
+ Scalar v;
v.boolean = true;
constant->values.push_back(v);
constant->datatype = TYPE_BOOL;
@@ -5264,7 +5647,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
} else if (tk.type == TK_FALSE) {
//handle false constant
ConstantNode *constant = alloc_node<ConstantNode>();
- ConstantNode::Value v;
+ Scalar v;
v.boolean = false;
constant->values.push_back(v);
constant->datatype = TYPE_BOOL;
@@ -6527,7 +6910,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
op->op = tk.type == TK_OP_DECREMENT ? OP_POST_DECREMENT : OP_POST_INCREMENT;
op->arguments.push_back(expr);
- if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) {
+ if (!_validate_operator(p_block, op, &op->return_cache, &op->return_array_size)) {
_set_error(RTR("Invalid base type for increment/decrement operator."));
return nullptr;
}
@@ -6876,7 +7259,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
expression.write[i].is_op = false;
expression.write[i].node = op;
- if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) {
+ if (!_validate_operator(p_block, op, &op->return_cache, &op->return_array_size)) {
+ if (error_set) {
+ return nullptr;
+ }
+
String at;
for (int j = 0; j < op->arguments.size(); j++) {
if (j > 0) {
@@ -6914,7 +7301,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
expression.write[next_op - 1].is_op = false;
expression.write[next_op - 1].node = op;
- if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) {
+ if (!_validate_operator(p_block, op, &op->return_cache, &op->return_array_size)) {
+ if (error_set) {
+ return nullptr;
+ }
+
String at;
for (int i = 0; i < op->arguments.size(); i++) {
if (i > 0) {
@@ -6950,6 +7341,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
if (_is_operator_assign(op->op)) {
+ if (p_block && expression[next_op - 1].node->type == Node::NODE_TYPE_VARIABLE) {
+ VariableNode *vn = static_cast<VariableNode *>(expression[next_op - 1].node);
+ p_block->use_op_eval = vn->is_const;
+ }
+
String assign_message;
if (!_validate_assign(expression[next_op - 1].node, p_function_info, &assign_message)) {
_set_error(assign_message);
@@ -6972,7 +7368,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
//replace all 3 nodes by this operator and make it an expression
- if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) {
+ if (!_validate_operator(p_block, op, &op->return_cache, &op->return_array_size)) {
+ if (error_set) {
+ return nullptr;
+ }
+
String at;
for (int i = 0; i < op->arguments.size(); i++) {
if (i > 0) {
@@ -6998,6 +7398,10 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
}
+ if (p_block) {
+ p_block->use_op_eval = true;
+ }
+
if (p_previous_expression_info != nullptr) {
p_previous_expression_info->expression->push_back(expression[0]);
}
@@ -7020,7 +7424,7 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha
DataType base = get_scalar_type(type);
int cardinality = get_cardinality(type);
- Vector<ConstantNode::Value> values;
+ Vector<Scalar> values;
for (int i = 1; i < op->arguments.size(); i++) {
op->arguments.write[i] = _reduce_expression(p_block, op->arguments[i]);
@@ -7032,7 +7436,7 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha
values.push_back(cn->values[j]);
}
} else if (get_scalar_type(cn->datatype) == cn->datatype) {
- ConstantNode::Value v;
+ Scalar v;
if (!convert_constant(cn, base, &v)) {
return p_node;
}
@@ -7048,8 +7452,8 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha
if (values.size() == 1) {
if (type >= TYPE_MAT2 && type <= TYPE_MAT4) {
- ConstantNode::Value value = values[0];
- ConstantNode::Value zero;
+ Scalar value = values[0];
+ Scalar zero;
zero.real = 0.0f;
int size = 2 + (type - TYPE_MAT2);
@@ -7060,7 +7464,7 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha
}
}
} else {
- ConstantNode::Value value = values[0];
+ Scalar value = values[0];
for (int i = 1; i < cardinality; i++) {
values.push_back(value);
}
@@ -7081,10 +7485,10 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha
DataType base = get_scalar_type(cn->datatype);
- Vector<ConstantNode::Value> values;
+ Vector<Scalar> values;
for (int i = 0; i < cn->values.size(); i++) {
- ConstantNode::Value nv;
+ Scalar nv;
switch (base) {
case TYPE_BOOL: {
nv.boolean = !cn->values[i].boolean;
@@ -7515,7 +7919,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
decl.size = decl.initializer.size();
var.array_size = decl.initializer.size();
} else if (decl.initializer.size() != var.array_size) {
- _set_error(RTR("Array size mismatch."));
+ _set_error(vformat(RTR("Array size mismatch. Expected %d elements (found %d)."), var.array_size, decl.initializer.size()));
return ERR_PARSE_ERROR;
}
tk = _get_token();
@@ -7537,7 +7941,9 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
array_size = var.array_size;
} else if (tk.type == TK_OP_ASSIGN) {
- //variable created with assignment! must parse an expression
+ p_block->use_op_eval = is_const;
+
+ // Variable created with assignment! Must parse an expression.
Node *n = _parse_and_reduce_expression(p_block, p_function_info);
if (!n) {
return ERR_PARSE_ERROR;
@@ -7552,11 +7958,8 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
}
}
- if (n->type == Node::NODE_TYPE_CONSTANT) {
- ConstantNode *const_node = static_cast<ConstantNode *>(n);
- if (const_node && const_node->values.size() == 1) {
- var.value = const_node->values[0];
- }
+ if (is_const) {
+ var.values = n->get_values();
}
if (!_compare_datatypes(var.type, var.struct_name, var.array_size, n->get_datatype(), n->get_datatype_name(), n->get_array_size())) {
@@ -7734,13 +8137,13 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
if (!vn) {
return ERR_PARSE_ERROR;
}
- ConstantNode::Value v;
+ Vector<Scalar> v = { Scalar() };
_find_identifier(p_block, false, p_function_info, vn->name, nullptr, nullptr, nullptr, nullptr, nullptr, &v);
- if (constants.has(v.sint)) {
- _set_error(vformat(RTR("Duplicated case label: %d."), v.sint));
+ if (constants.has(v[0].sint)) {
+ _set_error(vformat(RTR("Duplicated case label: %d."), v[0].sint));
return ERR_PARSE_ERROR;
}
- constants.insert(v.sint);
+ constants.insert(v[0].sint);
}
} else if (flow->flow_op == FLOW_OP_DEFAULT) {
continue;
@@ -7801,7 +8204,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
vn->name = tk.text;
n = vn;
} else {
- ConstantNode::Value v;
+ Scalar v;
if (tk.type == TK_UINT_CONSTANT) {
v.uint = (uint32_t)tk.constant;
} else {
@@ -9678,7 +10081,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
decl.size = decl.initializer.size();
constant.array_size = decl.initializer.size();
} else if (decl.initializer.size() != constant.array_size) {
- _set_error(RTR("Array size mismatch."));
+ _set_error(vformat(RTR("Array size mismatch. Expected %d elements (found %d)."), constant.array_size, decl.initializer.size()));
return ERR_PARSE_ERROR;
}
} else {
diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h
index 63dca99654..ba02e181b9 100644
--- a/servers/rendering/shader_language.h
+++ b/servers/rendering/shader_language.h
@@ -355,6 +355,13 @@ public:
}
};
+ union Scalar {
+ bool boolean = false;
+ float real;
+ int32_t sint;
+ uint32_t uint;
+ };
+
struct Node {
Node *next = nullptr;
@@ -379,6 +386,7 @@ public:
virtual String get_datatype_name() const { return ""; }
virtual int get_array_size() const { return 0; }
virtual bool is_indexed() const { return false; }
+ virtual Vector<Scalar> get_values() const { return Vector<Scalar>(); }
Node(Type t) :
type(t) {}
@@ -402,11 +410,13 @@ public:
Operator op = OP_EQUAL;
StringName struct_name;
Vector<Node *> arguments;
+ Vector<Scalar> values;
virtual DataType get_datatype() const override { return return_cache; }
virtual String get_datatype_name() const override { return String(struct_name); }
virtual int get_array_size() const override { return return_array_size; }
virtual bool is_indexed() const override { return op == OP_INDEX; }
+ virtual Vector<Scalar> get_values() const override { return values; }
OperatorNode() :
Node(NODE_TYPE_OPERATOR) {}
@@ -485,19 +495,15 @@ public:
String struct_name = "";
int array_size = 0;
- union Value {
- bool boolean = false;
- float real;
- int32_t sint;
- uint32_t uint;
- };
-
- Vector<Value> values;
+ Vector<Scalar> values;
Vector<VariableDeclarationNode::Declaration> array_declarations;
virtual DataType get_datatype() const override { return datatype; }
virtual String get_datatype_name() const override { return struct_name; }
virtual int get_array_size() const override { return array_size; }
+ virtual Vector<Scalar> get_values() const override {
+ return values;
+ }
ConstantNode() :
Node(NODE_TYPE_CONSTANT) {}
@@ -529,13 +535,14 @@ public:
int line; //for completion
int array_size;
bool is_const;
- ConstantNode::Value value;
+ Vector<Scalar> values;
};
HashMap<StringName, Variable> variables;
List<Node *> statements;
bool single_statement = false;
bool use_comma_between_statements = false;
+ bool use_op_eval = true;
BlockNode() :
Node(NODE_TYPE_BLOCK) {}
@@ -657,7 +664,7 @@ public:
DataType type = TYPE_VOID;
DataPrecision precision = PRECISION_DEFAULT;
int array_size = 0;
- Vector<ConstantNode::Value> default_value;
+ Vector<Scalar> default_value;
Scope scope = SCOPE_LOCAL;
Hint hint = HINT_NONE;
bool use_color = false;
@@ -803,15 +810,16 @@ public:
static bool is_token_operator_assign(TokenType p_type);
static bool is_token_hint(TokenType p_type);
- static bool convert_constant(ConstantNode *p_constant, DataType p_to_type, ConstantNode::Value *p_value = nullptr);
+ static bool convert_constant(ConstantNode *p_constant, DataType p_to_type, Scalar *p_value = nullptr);
static DataType get_scalar_type(DataType p_type);
static int get_cardinality(DataType p_type);
static bool is_scalar_type(DataType p_type);
static bool is_float_type(DataType p_type);
static bool is_sampler_type(DataType p_type);
- static Variant constant_value_to_variant(const Vector<ShaderLanguage::ConstantNode::Value> &p_value, DataType p_type, int p_array_size, ShaderLanguage::ShaderNode::Uniform::Hint p_hint = ShaderLanguage::ShaderNode::Uniform::HINT_NONE);
+ static Variant constant_value_to_variant(const Vector<Scalar> &p_value, DataType p_type, int p_array_size, ShaderLanguage::ShaderNode::Uniform::Hint p_hint = ShaderLanguage::ShaderNode::Uniform::HINT_NONE);
static PropertyInfo uniform_to_property_info(const ShaderNode::Uniform &p_uniform);
static uint32_t get_datatype_size(DataType p_type);
+ static uint32_t get_datatype_component_count(DataType p_type);
static void get_keyword_list(List<String> *r_keywords);
static bool is_control_flow_keyword(String p_keyword);
@@ -1070,13 +1078,21 @@ private:
IdentifierType last_type = IDENTIFIER_MAX;
- bool _find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type = nullptr, IdentifierType *r_type = nullptr, bool *r_is_const = nullptr, int *r_array_size = nullptr, StringName *r_struct_name = nullptr, ConstantNode::Value *r_constant_value = nullptr);
+ bool _find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type = nullptr, IdentifierType *r_type = nullptr, bool *r_is_const = nullptr, int *r_array_size = nullptr, StringName *r_struct_name = nullptr, Vector<Scalar> *r_constant_values = nullptr);
#ifdef DEBUG_ENABLED
void _parse_used_identifier(const StringName &p_identifier, IdentifierType p_type, const StringName &p_function);
#endif // DEBUG_ENABLED
bool _is_operator_assign(Operator p_op) const;
bool _validate_assign(Node *p_node, const FunctionInfo &p_function_info, String *r_message = nullptr);
- bool _validate_operator(OperatorNode *p_op, DataType *r_ret_type = nullptr, int *r_ret_size = nullptr);
+ bool _validate_operator(const BlockNode *p_block, OperatorNode *p_op, DataType *r_ret_type = nullptr, int *r_ret_size = nullptr);
+
+ Vector<Scalar> _get_node_values(const BlockNode *p_block, Node *p_node);
+ bool _eval_operator(const BlockNode *p_block, OperatorNode *p_op);
+ Scalar _eval_unary_scalar(const Scalar &p_a, Operator p_op, DataType p_ret_type);
+ Scalar _eval_scalar(const Scalar &p_a, const Scalar &p_b, Operator p_op, DataType p_ret_type, bool &r_is_valid);
+ Vector<Scalar> _eval_unary_vector(const Vector<Scalar> &p_va, DataType p_ret_type, Operator p_op);
+ Vector<Scalar> _eval_vector(const Vector<Scalar> &p_va, const Vector<Scalar> &p_vb, DataType p_left_type, DataType p_right_type, DataType p_ret_type, Operator p_op, bool &r_is_valid);
+ Vector<Scalar> _eval_vector_transform(const Vector<Scalar> &p_va, const Vector<Scalar> &p_vb, DataType p_left_type, DataType p_right_type, DataType p_ret_type);
struct BuiltinEntry {
const char *name;