diff options
60 files changed, 388 insertions, 290 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="""" /> <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/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/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/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_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_wav.cpp b/scene/resources/audio_stream_wav.cpp index 08ebacc2b3..d0e0f0eef3 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) { @@ -483,11 +479,7 @@ void AudioStreamPlaybackWAV::set_sample_playback(const Ref<AudioSamplePlayback> AudioStreamPlaybackWAV::AudioStreamPlaybackWAV() {} -AudioStreamPlaybackWAV::~AudioStreamPlaybackWAV() { - if (qoa.dec) { - memfree(qoa.dec); - } -} +AudioStreamPlaybackWAV::~AudioStreamPlaybackWAV() {} ///////////////////// @@ -554,7 +546,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 +564,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 +581,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 +675,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 +762,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; |
