Merge pull request #120917 from Repiteo/4.7-cherry-picks-3

[4.7] Cherry-picks for the 4.7 branch (future 4.7.1) - 3rd batch
This commit is contained in:
Thaddeus Crews authored and GitHub committed 2026-07-07 18:57:38 -05:00
commit 7b97b90e14
32 files changed
+379 -285

No files matched your search

+5 -5
View File
@@ -1115,10 +1115,10 @@
The node's global position, relative to the world (usually to the [CanvasLayer]).
</member>
<member name="grow_horizontal" type="int" setter="set_h_grow_direction" getter="get_h_grow_direction" enum="Control.GrowDirection" default="1">
Controls the direction on the horizontal axis in which the control should grow or shrink if its horizontal size is changed.
Controls the direction on the horizontal axis in which the control should grow if its horizontal minimum size is changed to be greater than its current size, as the control always has to be at least the minimum size.
</member>
<member name="grow_vertical" type="int" setter="set_v_grow_direction" getter="get_v_grow_direction" enum="Control.GrowDirection" default="1">
Controls the direction on the vertical axis in which the control should grow or shrink if its vertical size is changed.
Controls the direction on the vertical axis in which the control should grow if its vertical minimum size is changed to be greater than its current size, as the control always has to be at least the minimum size.
</member>
<member name="layout_direction" type="int" setter="set_layout_direction" getter="get_layout_direction" enum="Control.LayoutDirection" default="0">
Controls layout direction and text writing direction. Right-to-left layouts are necessary for certain languages (e.g. Arabic and Hebrew). See also [method is_layout_rtl].
@@ -1568,13 +1568,13 @@
[b]Note:[/b] If the control has received [signal mouse_entered] but not [signal mouse_exited], changing the [member mouse_filter] to [constant MOUSE_FILTER_IGNORE] will cause [signal mouse_exited] to be emitted.
</constant>
<constant name="GROW_DIRECTION_BEGIN" value="0" enum="GrowDirection">
The control will grow/shrink to the left or top if its size is changed to be larger/smaller than its current size on the respective axis.
The control will grow to the left or top to make up if its minimum size is changed to be greater than its current size on the respective axis.
</constant>
<constant name="GROW_DIRECTION_END" value="1" enum="GrowDirection">
The control will grow/shrink to the right or bottom if its size is changed to be larger/smaller than its current size on the respective axis.
The control will grow to the right or bottom to make up if its minimum size is changed to be greater than its current size on the respective axis.
</constant>
<constant name="GROW_DIRECTION_BOTH" value="2" enum="GrowDirection">
The control will grow/shrink in both directions equally if its size is changed to be larger/smaller than its current size.
The control will grow in both directions equally to make up if its minimum size is changed to be greater than its current size.
</constant>
<constant name="ANCHOR_BEGIN" value="0" enum="Anchor">
Snaps one of the 4 anchor's sides to the origin of the node's [code]Rect[/code], in the top left. Use it with one of the [code]anchor_*[/code] member variables, like [member anchor_left]. To change all 4 anchors at once, use [method set_anchors_preset].
@@ -446,7 +446,13 @@ void EditorDebuggerInspector::add_stack_variable(const Array &p_array, int p_off
v = Object::cast_to<EncodedObjectAsID>(v)->get_object_id();
h = PROPERTY_HINT_OBJECT_ID;
hs = var.type_hint;
// Makes the call stack select the node in the remote tree. See https://github.com/godotengine/godot/issues/79477
if (n == "self") {
_object_selected(v);
}
}
String type;
switch (var.type) {
case 0:
+1 -1
View File
@@ -691,7 +691,7 @@ void ImportDock::_reimport() {
}
EditorFileSystem::get_singleton()->reimport_files(params->paths);
EditorFileSystem::get_singleton()->emit_signal(SNAME("filesystem_changed")); //it changed, so force emitting the signal
EditorFileSystem::get_singleton()->filesystem_changed(); // It changed, so force emitting the signal.
_set_dirty(false);
}
+73 -49
View File
@@ -1398,14 +1398,19 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
break;
}
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
placeholder = !placeholder;
undo_redo->create_action(TTR("Toggle Load as Placeholder"));
undo_redo->add_undo_method(node, "set_scene_instance_load_placeholder", !placeholder);
undo_redo->add_do_method(node, "set_scene_instance_load_placeholder", placeholder);
if (placeholder) {
EditorNode::get_singleton()->get_edited_scene()->set_editable_instance(node, false);
}
node->set_scene_instance_load_placeholder(placeholder);
scene_tree->update_tree();
undo_redo->commit_action();
}
}
} break;
@@ -1955,6 +1960,21 @@ void SceneTreeDock::_node_renamed() {
_node_selected();
}
void SceneTreeDock::_make_owners_map(Node *p_node, Dictionary &r_owners) {
r_owners[p_node] = p_node->get_owner();
for (Node *child : p_node->iterate_children()) {
_make_owners_map(child, r_owners);
}
}
void SceneTreeDock::_apply_owners_map(Node *p_node, const Dictionary &p_owners) {
Node *owner = Object::cast_to<Node>(p_owners[p_node]);
p_node->set_owner(owner);
for (Node *child : p_node->iterate_children()) {
_apply_owners_map(child, p_owners);
}
}
void SceneTreeDock::_set_owners(Node *p_owner, const Array &p_nodes) {
for (int i = 0; i < p_nodes.size(); i++) {
Node *n = Object::cast_to<Node>(p_nodes[i]);
@@ -4498,7 +4518,7 @@ List<Node *> SceneTreeDock::paste_nodes(bool p_paste_as_sibling) {
// When copying, all nodes that should have an owner assigned here were given nullptr as an owner
// and added to the node_clipboard_edited_scene_owned list.
if (d != dup && E2.key->get_owner() == nullptr) {
if (node_clipboard_edited_scene_owned.find(const_cast<Node *>(E2.key))) {
if (node_clipboard_edited_scene_owned.has(const_cast<Node *>(E2.key))) {
ur->add_do_method(d, "set_owner", owner);
}
}
@@ -4536,14 +4556,20 @@ List<Node *> SceneTreeDock::paste_nodes(bool p_paste_as_sibling) {
void SceneTreeDock::paste_node_as_replacement() {
List<Node *> selected_node_list = editor_selection->get_top_selected_node_list();
Node *clipboard_node = node_clipboard.front()->get();
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Paste Node(s) as Replacement"), UndoRedo::MERGE_DISABLE, edited_scene);
for (Node *selected : selected_node_list) {
Node *clipboard_node = node_clipboard.front()->get();
HashMap<const Node *, Node *> duplimap;
Node *new_node = clipboard_node->duplicate_from_editor(duplimap);
if (!new_node) {
continue;
}
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->add_do_reference(new_node);
ur->add_undo_reference(selected);
String old_scene_file_path = selected->get_scene_file_path();
String new_scene_file_path = clipboard_node->get_scene_file_path();
@@ -4551,34 +4577,16 @@ void SceneTreeDock::paste_node_as_replacement() {
bool new_is_scene = !new_scene_file_path.is_empty();
LocalVector<Node *> old_children;
Dictionary owners_map;
ur->create_action(TTR("Paste Node(s) as Replacement"), UndoRedo::MERGE_ALL, selected);
int child_count = selected->get_child_count();
for (int i = 0; i < child_count; i++) {
Node *child = selected->get_child(0);
_make_owners_map(child, owners_map);
old_children.push_back(child);
if (old_is_scene) {
for (int i = 0; i < selected->get_child_count(); i++) {
Node *child = selected->get_child(i);
if (child->get_owner() == selected) {
ur->add_do_method(selected, "remove_child", child);
selected->remove_child(child);
old_children.push_back(child);
}
}
}
ur->add_do_method(this, "replace_node", selected, new_node, false);
ur->add_do_method(new_node, "set_scene_file_path", new_scene_file_path);
ur->add_do_reference(new_node);
if (new_is_scene) {
for (int i = 0; i < new_node->get_child_count(); i++) {
Node *child = new_node->get_child(i);
if (child->get_owner() == new_node) {
ur->add_undo_method(new_node, "add_child", child, true);
ur->add_undo_method(child, "set_owner", new_node);
ur->add_undo_reference(child);
}
}
ur->add_do_method(selected, "remove_child", child);
selected->remove_child(child);
}
const bool is_missing_node_selected = Object::cast_to<MissingNode>(selected) != nullptr;
@@ -4592,9 +4600,6 @@ void SceneTreeDock::paste_node_as_replacement() {
selected_transform_3d = selected_node_3d->get_transform();
}
_replace_node(selected, new_node, is_missing_node_selected);
new_node->set_scene_file_path(new_scene_file_path);
if (selected_canvas_item) {
Node2D *new_node_2d = Object::cast_to<Node2D>(new_node);
Control *new_control = Object::cast_to<Control>(new_node);
@@ -4612,29 +4617,47 @@ void SceneTreeDock::paste_node_as_replacement() {
}
}
if (new_is_scene) {
for (int i = 0; i < new_node->get_child_count(); i++) {
Node *child = new_node->get_child(i);
if (child->get_owner() == new_node) {
ur->add_undo_method(new_node, "remove_child", child);
for (Node *child : new_node->iterate_children()) {
ur->add_do_reference(child);
ur->add_do_method(new_node, "add_child", child);
ur->add_undo_method(new_node, "remove_child", child);
}
// Note that due to the new node possibly having child nodes, it's not possible to unify the operations to use only do methods.
_replace_node(selected, new_node, is_missing_node_selected);
ur->add_do_method(this, "replace_node", selected, new_node, false);
if (old_is_scene || new_is_scene) {
new_node->set_scene_file_path(new_scene_file_path);
ur->add_do_method(new_node, "set_scene_file_path", new_scene_file_path);
}
for (KeyValue<const Node *, Node *> &E2 : duplimap) {
Node *d = E2.value;
// When copying, all nodes that should have an owner assigned here were given nullptr as an owner
// and added to the node_clipboard_edited_scene_owned list.
if (d != new_node && E2.key->get_owner() == nullptr) {
if (node_clipboard_edited_scene_owned.has(const_cast<Node *>(E2.key))) {
d->set_owner(edited_scene);
ur->add_do_method(d, "set_owner", edited_scene);
}
}
}
ur->add_undo_method(this, "replace_node", new_node, selected, false);
ur->add_undo_method(selected, "set_scene_file_path", old_scene_file_path);
ur->add_undo_reference(selected);
if (old_is_scene) {
for (Node *child : old_children) {
ur->add_undo_method(selected, "add_child", child, true);
ur->add_undo_method(child, "set_owner", selected);
ur->add_undo_reference(child);
}
if (old_is_scene || new_is_scene) {
ur->add_undo_method(selected, "set_scene_file_path", old_scene_file_path);
}
ur->commit_action(false);
for (Node *child : old_children) {
ur->add_undo_method(selected, "add_child", child);
ur->add_undo_method(this, "_apply_owners_map", child, owners_map);
ur->add_undo_reference(child);
}
}
ur->commit_action(false);
// FIXME: This shouldn't be needed, but children of the pasted node do not appear immediately for some reason.
scene_tree->clear_cache();
scene_tree->update_tree();
}
List<Node *> SceneTreeDock::get_node_clipboard() const {
@@ -4945,6 +4968,7 @@ void SceneTreeDock::_edit_subresource(int p_idx, const PopupMenu *p_from_menu) {
void SceneTreeDock::_bind_methods() {
ClassDB::bind_method(D_METHOD("_post_do_create"), &SceneTreeDock::_post_do_create);
ClassDB::bind_method(D_METHOD("_apply_owners_map"), &SceneTreeDock::_apply_owners_map);
ClassDB::bind_method(D_METHOD("_set_owners"), &SceneTreeDock::_set_owners);
ClassDB::bind_method(D_METHOD("_reparent_nodes_to_root"), &SceneTreeDock::_reparent_nodes_to_root);
ClassDB::bind_method(D_METHOD("_reparent_nodes_to_paths_with_transform_and_name"), &SceneTreeDock::_reparent_nodes_to_paths_with_transform_and_name);
+2
View File
@@ -195,6 +195,8 @@ class SceneTreeDock : public EditorDock {
void _node_reparent(NodePath p_path, bool p_keep_global_xform);
void _do_reparent(Node *p_new_parent, int p_position_in_parent, Vector<Node *> p_nodes, bool p_keep_global_xform);
void _make_owners_map(Node *p_node, Dictionary &r_owners);
void _apply_owners_map(Node *p_node, const Dictionary &p_owners);
void _set_owners(Node *p_owner, const Array &p_nodes);
enum ReplaceOwnerMode {
+1 -1
View File
@@ -368,7 +368,7 @@ void EditorInterface::make_scene_preview(const String &p_path, Node *p_scene, in
}
EditorResourcePreview::get_singleton()->check_for_invalidation(p_path);
EditorFileSystem::get_singleton()->emit_signal(SNAME("filesystem_changed"));
EditorFileSystem::get_singleton()->filesystem_changed();
}
void EditorInterface::add_root_node(Node *p_node) {
+1 -1
View File
@@ -4720,7 +4720,6 @@ void EditorNode::_set_current_scene_nocheck(int p_idx, bool p_ignore_state) {
Node *old_scene = get_editor_data().get_edited_scene_root();
resource_count.clear();
editor_selection->clear();
SceneTreeDock::get_singleton()->clear_previous_node_selection();
editor_data.set_edited_scene(p_idx);
@@ -4752,6 +4751,7 @@ void EditorNode::_set_current_scene_nocheck(int p_idx, bool p_ignore_state) {
EditorUndoRedoManager::get_singleton()->clear_history(editor_data.get_scene_history_id(p_idx), false);
}
resource_count.clear();
SceneTreeDock::get_singleton()->get_tree_editor()->update_tree();
_update_title();
+17 -5
View File
@@ -2552,16 +2552,16 @@ void EditorFileSystem::update_files(const Vector<String> &p_script_paths) {
if (!is_scanning()) {
_process_update_pending();
}
if (!filesystem_changed_queued) {
filesystem_changed_queued = true;
if (!filesystem_changed_queued.is_set()) {
filesystem_changed_queued.set();
callable_mp(this, &EditorFileSystem::_notify_filesystem_changed).call_deferred();
}
}
}
void EditorFileSystem::_notify_filesystem_changed() {
emit_signal("filesystem_changed");
filesystem_changed_queued = false;
emit_signal(SNAME("filesystem_changed"));
filesystem_changed_queued.clear();
}
HashSet<String> EditorFileSystem::get_valid_extensions() const {
@@ -2602,6 +2602,18 @@ void EditorFileSystem::register_global_class_script(const String &p_search_path,
}
}
void EditorFileSystem::filesystem_changed() {
if (Thread::is_main_thread()) {
_notify_filesystem_changed();
return;
}
// If not already queued, queue a deferred call to notify about filesystem changes.
if (!filesystem_changed_queued.is_set()) {
filesystem_changed_queued.set();
callable_mp(this, &EditorFileSystem::_notify_filesystem_changed).call_deferred();
}
}
Error EditorFileSystem::_reimport_group(const String &p_group_file, const Vector<String> &p_files) {
String importer_name;
@@ -3720,7 +3732,7 @@ void EditorFileSystem::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_file_type", "path"), &EditorFileSystem::get_file_type);
ClassDB::bind_method(D_METHOD("reimport_files", "files"), &EditorFileSystem::reimport_files);
ADD_SIGNAL(MethodInfo("filesystem_changed"));
ADD_SIGNAL(MethodInfo("filesystem_changed")); // May only be emitted on the main thread.
ADD_SIGNAL(MethodInfo("script_classes_updated"));
ADD_SIGNAL(MethodInfo("sources_changed", PropertyInfo(Variant::BOOL, "exist")));
ADD_SIGNAL(MethodInfo("resources_reimporting", PropertyInfo(Variant::PACKED_STRING_ARRAY, "resources")));
+3 -1
View File
@@ -182,7 +182,7 @@ class EditorFileSystem : public Node {
EditorFileSystemDirectory *new_filesystem = nullptr;
static ScannedDirectory *first_scan_root_dir;
bool filesystem_changed_queued = false;
SafeFlag filesystem_changed_queued;
bool scanning = false;
bool importing = false;
bool first_scan = true;
@@ -392,6 +392,8 @@ public:
HashSet<String> get_valid_extensions() const;
void register_global_class_script(const String &p_search_path, const String &p_target_path);
void filesystem_changed();
EditorFileSystemDirectory *get_filesystem_path(const String &p_path);
String get_file_type(const String &p_file) const;
EditorFileSystemDirectory *find_file(const String &p_file, int *r_index) const;
+4 -5
View File
@@ -209,15 +209,14 @@ bool EditorQuickOpenDialog::_is_instant_preview_active() const {
}
void EditorQuickOpenDialog::selection_changed() {
if (!_is_instant_preview_active()) {
return;
}
// This prevents the property from being changed the first time the Quick Open
// window is opened.
if (!initial_selection_performed) {
initial_selection_performed = true;
} else {
return;
}
if (_is_instant_preview_active()) {
preview_property();
}
}
+39 -13
View File
@@ -1613,12 +1613,35 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<
continue;
}
const String mat_id = mat->get_meta("import_id", mat->get_name());
if (mat_id.is_empty() || !p_material_data.has(mat_id)) {
if (mat_id.is_empty()) {
continue;
}
Dictionary matdata = p_material_data[mat_id];
// Start with per-material import settings, if any. This is populated by the
// Advanced Import Settings dialog, but may be empty when using only the Import dock.
Dictionary matdata;
if (p_material_data.has(mat_id)) {
matdata = p_material_data[mat_id];
}
String file_path;
// Read any existing "use_external" settings to get the enabled status and file path.
if (matdata.has("use_external/enabled")) {
const bool enabled = bool(matdata["use_external/enabled"]);
// If the user has explicitly overwrote this material to not be external, skip it.
if (!enabled) {
continue;
}
// Read any existing file path from this material's import settings, if present.
if (matdata.has("use_external/fallback_path")) {
file_path = matdata["use_external/fallback_path"];
} else if (matdata.has("use_external/path")) {
file_path = matdata["use_external/path"];
if (file_path.begins_with("uid://")) {
file_path = ResourceUID::get_singleton()->uid_to_path(file_path);
}
}
}
// For any material settings that are missing, fill them with default values.
{
//fill node settings for this node with default values
List<ImportOption> iopts;
get_internal_import_options(INTERNAL_IMPORT_CATEGORY_MATERIAL, &iopts);
for (const ImportOption &E : iopts) {
@@ -1631,18 +1654,21 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<
post_importer_plugins.write[j]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MATERIAL, p_root, p_node, mat, matdata);
}
if (extract_mat != 0) {
const String ext = material_extension[p_options.has("materials/extract_format") ? (int)p_options["materials/extract_format"] : 0];
const String path = spath.path_join(mat_id.validate_filename() + ext);
const String uid_path = ResourceUID::path_to_uid(path);
matdata["use_external/enabled"] = true;
matdata["use_external/path"] = uid_path;
matdata["use_external/fallback_path"] = path;
if (!FileAccess::exists(path) || extract_mat == 2 /*overwrite*/) {
ResourceSaver::save(mat, path);
// If no file path was specified, generate one based on the material name and the selected format.
if (file_path.is_empty()) {
const String ext = material_extension[p_options.has("materials/extract_format") ? (int)p_options["materials/extract_format"] : 0];
file_path = spath.path_join(mat_id.validate_filename() + ext);
const String uid_path = ResourceUID::path_to_uid(file_path);
matdata["use_external/enabled"] = true;
matdata["use_external/path"] = uid_path;
matdata["use_external/fallback_path"] = file_path;
}
// Write the file if it doesn't exist, or if the user has selected to overwrite existing files.
if (!FileAccess::exists(file_path) || extract_mat == 2 /*overwrite*/) {
ResourceSaver::save(mat, file_path);
}
Ref<Material> external_mat = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REPLACE);
Ref<Material> external_mat = ResourceLoader::load(file_path, "", ResourceFormatLoader::CACHE_MODE_REPLACE);
if (external_mat.is_valid()) {
m->set_surface_material(i, external_mat);
}
+1 -1
View File
@@ -1771,7 +1771,7 @@ void EditorAudioStreamPicker::_preview_draw() {
float text_width = size.width - 4 * EDSCALE - icon->get_width();
if (text_width > 0) {
stream_preview_rect->draw_string(font, Point2i(EDSCALE * 4 + icon->get_width(), rect.position.y + font->get_ascent(font_size) + (rect.size.height - font->get_height(font_size)) / 2), text, HORIZONTAL_ALIGNMENT_CENTER, text_width, font_size, get_theme_color(SceneStringName(font_color), EditorStringName(Editor)));
stream_preview_rect->draw_string(font, Point2i(size.width - text_width, rect.position.y + font->get_ascent(font_size) + (rect.size.height - font->get_height(font_size)) / 2), text, HORIZONTAL_ALIGNMENT_CENTER, text_width, font_size, get_theme_color(SceneStringName(font_color), EditorStringName(Editor)));
}
}
+2 -1
View File
@@ -46,6 +46,7 @@
#include "editor/run/editor_run_bar.h"
#include "editor/run/embedded_process.h"
#include "editor/run/run_instances_dialog.h"
#include "editor/script/script_editor_plugin.h"
#include "editor/settings/editor_feature_profile.h"
#include "editor/settings/editor_settings.h"
#include "editor/themes/editor_scale.h"
@@ -1769,7 +1770,7 @@ void GameViewPluginBase::_save_last_editor(const String &p_editor) {
void GameViewPluginBase::_focus_another_editor() {
if (_is_window_wrapper_enabled()) {
if (last_editor.is_empty()) {
if (last_editor.is_empty() || (last_editor == "Script" && ScriptEditor::get_singleton()->is_editor_floating())) {
EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_2D);
} else {
EditorInterface::get_singleton()->set_main_screen_editor(last_editor);
+44 -75
View File
@@ -6290,42 +6290,59 @@ bool CanvasItemEditorViewport::_cyclical_dependency_exists(const String &p_targe
return false;
}
void CanvasItemEditorViewport::_add_node_to_scene(Node *p_parent, Node *p_child, const Vector2 &p_target_position) {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (p_parent) {
undo_redo->add_do_method(p_parent, "add_child", p_child, true);
undo_redo->add_do_method(p_child, "set_owner", EditorNode::get_singleton()->get_edited_scene());
undo_redo->add_do_reference(p_child);
undo_redo->add_undo_method(p_parent, "remove_child", p_child);
const String new_name = p_parent->validate_child_name(p_child);
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
undo_redo->add_do_method(ed, "live_debug_create_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent), p_child->get_class(), new_name);
undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent)) + "/" + new_name));
} else { // If no parent is selected, set as root node of the scene.
undo_redo->add_do_method(EditorNode::get_singleton(), "set_edited_scene", p_child);
undo_redo->add_do_reference(p_child);
undo_redo->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr);
}
// There's nothing to be used as source position, so snapping will work as absolute if enabled.
Vector2 target_position = canvas_item_editor->snap_point(p_target_position);
CanvasItem *parent_ci = Object::cast_to<CanvasItem>(p_parent);
// Set position via undo_redo for proper live editing support.
undo_redo->add_do_method(p_child, "set_position", parent_ci ? parent_ci->get_global_transform().affine_inverse().xform(target_position) : target_position);
EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection();
undo_redo->add_do_method(editor_selection, "add_node", p_child);
}
void CanvasItemEditorViewport::_create_texture_node(Node *p_parent, Node *p_child, const String &p_path, const Point2 &p_point) {
// Adjust casing according to project setting. The file name is expected to be in snake_case, but will work for others.
const String &node_name = Node::adjust_name_casing(p_path.get_file().get_basename());
if (!node_name.is_empty()) {
p_child->set_name(node_name);
}
// Compute the global position.
Transform2D xform = canvas_item_editor->get_canvas_transform();
Point2 target_position = xform.affine_inverse().xform(p_point);
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
// Adjust position for Control and TouchScreenButton.
Ref<Texture2D> texture = ResourceCache::get_ref(p_path);
if (p_parent) {
undo_redo->add_do_method(p_parent, "add_child", p_child, true);
undo_redo->add_do_method(p_child, "set_owner", EditorNode::get_singleton()->get_edited_scene());
undo_redo->add_do_reference(p_child);
undo_redo->add_undo_method(p_parent, "remove_child", p_child);
} else { // If no parent is selected, set as root node of the scene.
undo_redo->add_do_method(EditorNode::get_singleton(), "set_edited_scene", p_child);
undo_redo->add_do_method(p_child, "set_owner", EditorNode::get_singleton()->get_edited_scene());
undo_redo->add_do_reference(p_child);
undo_redo->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr);
}
if (p_parent) {
String new_name = p_parent->validate_child_name(p_child);
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
undo_redo->add_do_method(ed, "live_debug_create_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent), p_child->get_class(), new_name);
undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent)) + "/" + new_name));
if (Object::cast_to<Control>(p_child) || Object::cast_to<TouchScreenButton>(p_child) || Object::cast_to<Polygon2D>(p_child)) {
target_position -= texture->get_size() / 2;
}
_add_node_to_scene(p_parent, p_child, target_position);
// Use undo_redo for properties to support live editing.
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (Object::cast_to<TouchScreenButton>(p_child) || Object::cast_to<TextureButton>(p_child)) {
undo_redo->add_do_property(p_child, "texture_normal", texture);
} else {
undo_redo->add_do_property(p_child, "texture", texture);
}
// make visible for certain node type
// Make visible for certain node type.
if (Object::cast_to<Control>(p_child)) {
Size2 texture_size = texture->get_size();
undo_redo->add_do_property(p_child, "size", texture_size);
@@ -6339,28 +6356,10 @@ void CanvasItemEditorViewport::_create_texture_node(Node *p_parent, Node *p_chil
};
undo_redo->add_do_property(p_child, "polygon", list);
}
// Compute the global position
Transform2D xform = canvas_item_editor->get_canvas_transform();
Point2 target_position = xform.affine_inverse().xform(p_point);
// Adjust position for Control and TouchScreenButton
if (Object::cast_to<Control>(p_child) || Object::cast_to<TouchScreenButton>(p_child)) {
target_position -= texture->get_size() / 2;
}
// There's nothing to be used as source position, so snapping will work as absolute if enabled.
target_position = canvas_item_editor->snap_point(target_position);
CanvasItem *parent_ci = Object::cast_to<CanvasItem>(p_parent);
Point2 local_target_pos = parent_ci ? parent_ci->get_global_transform().affine_inverse().xform(target_position) : target_position;
undo_redo->add_do_method(p_child, "set_position", local_target_pos);
}
void CanvasItemEditorViewport::_create_audio_node(Node *p_parent, const String &p_path, const Point2 &p_point) {
AudioStreamPlayer2D *child = memnew(AudioStreamPlayer2D);
child->set_stream(ResourceCache::get_ref(p_path));
// Adjust casing according to project setting. The file name is expected to be in snake_case, but will work for others.
const String &node_name = Node::adjust_name_casing(p_path.get_file().get_basename());
@@ -6368,41 +6367,14 @@ void CanvasItemEditorViewport::_create_audio_node(Node *p_parent, const String &
child->set_name(node_name);
}
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (p_parent) {
undo_redo->add_do_method(p_parent, "add_child", child, true);
undo_redo->add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene());
undo_redo->add_do_reference(child);
undo_redo->add_undo_method(p_parent, "remove_child", child);
} else { // If no parent is selected, set as root node of the scene.
undo_redo->add_do_method(EditorNode::get_singleton(), "set_edited_scene", child);
undo_redo->add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene());
undo_redo->add_do_reference(child);
undo_redo->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr);
}
if (p_parent) {
String new_name = p_parent->validate_child_name(child);
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
undo_redo->add_do_method(ed, "live_debug_create_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent), child->get_class(), new_name);
undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent)) + "/" + new_name));
}
// Compute the global position
Transform2D xform = canvas_item_editor->get_canvas_transform();
Point2 target_position = xform.affine_inverse().xform(p_point);
// There's nothing to be used as source position, so snapping will work as absolute if enabled.
target_position = canvas_item_editor->snap_point(target_position);
_add_node_to_scene(p_parent, child, target_position);
CanvasItem *parent_ci = Object::cast_to<CanvasItem>(p_parent);
Point2 local_target_pos = parent_ci ? parent_ci->get_global_transform().affine_inverse().xform(target_position) : target_position;
undo_redo->add_do_method(child, "set_position", local_target_pos);
EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection();
undo_redo->add_do_method(editor_selection, "add_node", child);
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->add_do_property(child, "stream", ResourceCache::get_ref(p_path));
}
bool CanvasItemEditorViewport::_create_instance(Node *p_parent, const String &p_path, const Point2 &p_point) {
@@ -6512,7 +6484,6 @@ void CanvasItemEditorViewport::_perform_drop_data() {
if (texture.is_valid()) {
Node *child = Object::cast_to<Node>(ClassDB::instantiate(default_texture_node_type));
_create_texture_node(target_node, child, path, drop_pos);
undo_redo->add_do_method(editor_selection, "add_node", child);
}
Ref<AudioStream> audio = res;
@@ -6567,9 +6538,9 @@ bool CanvasItemEditorViewport::can_drop_data(const Point2 &p_point, const Varian
String error_message;
for (const String &path : files) {
const String &res_type = ResourceLoader::get_resource_type(path);
const StringName res_type = ResourceLoader::get_resource_type(path);
if (ClassDB::is_parent_class(res_type, "PackedScene")) {
if (ClassDB::is_parent_class(res_type, SNAME("PackedScene"))) {
Ref<PackedScene> scn = ResourceLoader::load(path);
ERR_CONTINUE(scn.is_null());
@@ -6583,11 +6554,9 @@ bool CanvasItemEditorViewport::can_drop_data(const Point2 &p_point, const Varian
}
memdelete(instantiated_scene);
instantiate_type |= SCENE;
}
if (ClassDB::is_parent_class(res_type, "Texture2D")) {
} else if (ClassDB::is_parent_class(res_type, SNAME("Texture2D"))) {
instantiate_type |= TEXTURE;
}
if (ClassDB::is_parent_class(res_type, "AudioStream")) {
} else if (ClassDB::is_parent_class(res_type, SNAME("AudioStream"))) {
instantiate_type |= AUDIO;
}
}
+1
View File
@@ -685,6 +685,7 @@ class CanvasItemEditorViewport : public Control {
bool _cyclical_dependency_exists(const String &p_target_scene_path, Node *p_desired_node) const;
bool _is_any_texture_selected() const;
void _add_node_to_scene(Node *p_parent, Node *p_child, const Vector2 &p_target_position);
void _create_texture_node(Node *p_parent, Node *p_child, const String &p_path, const Point2 &p_point);
void _create_audio_node(Node *p_parent, const String &p_path, const Point2 &p_point);
bool _create_instance(Node *p_parent, const String &p_path, const Point2 &p_point);
+1 -1
View File
@@ -78,7 +78,7 @@ void EditorSceneTabs::_notification(int p_what) {
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_TRANSLATION_CHANGED: {
_scene_tabs_resized();
_update_tab_titles();
} break;
}
}
+1 -1
View File
@@ -502,7 +502,7 @@ void EditorAutoloadSettings::update_autoload() {
if (old_info.path == info.path) {
// Still the same resource, check status
info.node = old_info.node;
if (info.node) {
if (info.node && info.node->is_instance()) {
Ref<Script> scr = info.node->get_script();
info.in_editor = scr.is_valid() && scr->is_tool();
if (info.is_singleton == old_info.is_singleton && info.in_editor == old_info.in_editor) {
+1
View File
@@ -110,6 +110,7 @@ void editor_register_icons(const Ref<Theme> &p_theme, bool p_dark_theme, float p
HashSet<StringName> saturation_exceptions;
saturation_exceptions.insert("DefaultProjectIcon");
saturation_exceptions.insert("Godot");
saturation_exceptions.insert("GodotFile");
saturation_exceptions.insert("Logo");
saturation_exceptions.insert("TitleBarLogo");
+38 -45
View File
@@ -7188,27 +7188,25 @@ Error GLTFDocument::_parse_gltf_state(Ref<GLTFState> p_state, const String &p_se
}
PackedByteArray GLTFDocument::generate_buffer(Ref<GLTFState> p_state) {
Ref<GLTFState> state = p_state;
ERR_FAIL_COND_V(state.is_null(), PackedByteArray());
ERR_FAIL_COND_V(p_state.is_null(), PackedByteArray());
// For buffers, set the state filename to an empty string, but
// don't touch the base path, in case the user set it manually.
state->filename = "";
Error err = _serialize(state);
p_state->filename = "";
Error err = _serialize(p_state);
ERR_FAIL_COND_V(err != OK, PackedByteArray());
PackedByteArray bytes = _serialize_glb_buffer(state, &err);
PackedByteArray bytes = _serialize_glb_buffer(p_state, &err);
return bytes;
}
Error GLTFDocument::write_to_filesystem(Ref<GLTFState> p_state, const String &p_path) {
Ref<GLTFState> state = p_state;
ERR_FAIL_COND_V(state.is_null(), ERR_INVALID_PARAMETER);
state->set_base_path(p_path.get_base_dir());
state->filename = p_path.get_file();
Error err = _serialize(state);
ERR_FAIL_COND_V(p_state.is_null(), ERR_INVALID_PARAMETER);
p_state->set_base_path(p_path.get_base_dir());
p_state->filename = p_path.get_file();
Error err = _serialize(p_state);
if (err != OK) {
return err;
}
err = _serialize_file(state, p_path);
err = _serialize_file(p_state, p_path);
if (err != OK) {
return Error::FAILED;
}
@@ -7266,15 +7264,14 @@ Node *GLTFDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, boo
}
Error GLTFDocument::append_from_scene(Node *p_node, Ref<GLTFState> p_state, uint32_t p_flags) {
ERR_FAIL_NULL_V(p_node, FAILED);
Ref<GLTFState> state = p_state;
ERR_FAIL_COND_V(state.is_null(), FAILED);
state->use_named_skin_binds = p_flags & ImportFlags::IMPORT_FLAG_USE_NAMED_SKIN_BINDS;
state->discard_meshes_and_materials = p_flags & ImportFlags::IMPORT_FLAG_DISCARD_MESHES_AND_MATERIALS;
state->force_generate_tangents = p_flags & ImportFlags::IMPORT_FLAG_GENERATE_TANGENT_ARRAYS;
state->force_disable_compression = p_flags & ImportFlags::IMPORT_FLAG_FORCE_DISABLE_MESH_COMPRESSION;
if (!state->buffers.size()) {
state->buffers.push_back(Vector<uint8_t>());
ERR_FAIL_NULL_V(p_node, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(p_state.is_null(), ERR_INVALID_PARAMETER);
p_state->use_named_skin_binds = p_flags & ImportFlags::IMPORT_FLAG_USE_NAMED_SKIN_BINDS;
p_state->discard_meshes_and_materials = p_flags & ImportFlags::IMPORT_FLAG_DISCARD_MESHES_AND_MATERIALS;
p_state->force_generate_tangents = p_flags & ImportFlags::IMPORT_FLAG_GENERATE_TANGENT_ARRAYS;
p_state->force_disable_compression = p_flags & ImportFlags::IMPORT_FLAG_FORCE_DISABLE_MESH_COMPRESSION;
if (!p_state->buffers.size()) {
p_state->buffers.push_back(Vector<uint8_t>());
}
// Perform export preflight for document extensions. Only extensions that
// return OK will be used for the rest of the export steps.
@@ -7285,7 +7282,7 @@ Error GLTFDocument::append_from_scene(Node *p_node, Ref<GLTFState> p_state, uint
if (ClassDB::is_class_exposed(ext->get_class_name())) {
ext_dup = ext->duplicate();
}
Error err = ext_dup->export_preflight(state, p_node);
Error err = ext_dup->export_preflight(p_state, p_node);
if (err == OK) {
document_extensions.push_back(ext_dup);
}
@@ -7294,14 +7291,14 @@ Error GLTFDocument::append_from_scene(Node *p_node, Ref<GLTFState> p_state, uint
if (_root_node_mode == RootNodeMode::ROOT_NODE_MODE_MULTI_ROOT) {
const int child_count = p_node->get_child_count();
for (int i = 0; i < child_count; i++) {
_convert_scene_node(state, p_node->get_child(i), -1, -1);
_convert_scene_node(p_state, p_node->get_child(i), -1, -1);
}
state->scene_name = p_node->get_name();
p_state->scene_name = p_node->get_name();
} else {
if (_root_node_mode == RootNodeMode::ROOT_NODE_MODE_SINGLE_ROOT) {
state->extensions_used.append("GODOT_single_root");
p_state->extensions_used.append("GODOT_single_root");
}
_convert_scene_node(state, p_node, -1, -1);
_convert_scene_node(p_state, p_node, -1, -1);
}
// Run post-convert for each extension, in case an extension needs to do something after converting the scene.
for (Ref<GLTFDocumentExtension> ext : document_extensions) {
@@ -7313,40 +7310,36 @@ Error GLTFDocument::append_from_scene(Node *p_node, Ref<GLTFState> p_state, uint
}
Error GLTFDocument::append_from_buffer(const PackedByteArray &p_bytes, const String &p_base_path, Ref<GLTFState> p_state, uint32_t p_flags) {
Ref<GLTFState> state = p_state;
ERR_FAIL_COND_V(state.is_null(), FAILED);
ERR_FAIL_COND_V(p_state.is_null(), ERR_INVALID_PARAMETER);
// TODO Add missing texture and missing .bin file paths to r_missing_deps 2021-09-10 fire
Error err = FAILED;
state->use_named_skin_binds = p_flags & ImportFlags::IMPORT_FLAG_USE_NAMED_SKIN_BINDS;
state->discard_meshes_and_materials = p_flags & ImportFlags::IMPORT_FLAG_DISCARD_MESHES_AND_MATERIALS;
state->force_generate_tangents = p_flags & ImportFlags::IMPORT_FLAG_GENERATE_TANGENT_ARRAYS;
state->force_disable_compression = p_flags & ImportFlags::IMPORT_FLAG_FORCE_DISABLE_MESH_COMPRESSION;
p_state->use_named_skin_binds = p_flags & ImportFlags::IMPORT_FLAG_USE_NAMED_SKIN_BINDS;
p_state->discard_meshes_and_materials = p_flags & ImportFlags::IMPORT_FLAG_DISCARD_MESHES_AND_MATERIALS;
p_state->force_generate_tangents = p_flags & ImportFlags::IMPORT_FLAG_GENERATE_TANGENT_ARRAYS;
p_state->force_disable_compression = p_flags & ImportFlags::IMPORT_FLAG_FORCE_DISABLE_MESH_COMPRESSION;
Ref<FileAccessMemory> file_access;
file_access.instantiate();
file_access->open_custom(p_bytes.ptr(), p_bytes.size());
state->set_base_path(p_base_path.get_base_dir());
err = _parse(p_state, state->base_path, file_access);
p_state->set_base_path(p_base_path.get_base_dir());
err = _parse(p_state, p_state->base_path, file_access);
ERR_FAIL_COND_V(err != OK, err);
for (Ref<GLTFDocumentExtension> ext : document_extensions) {
ERR_CONTINUE(ext.is_null());
err = ext->import_post_parse(state);
err = ext->import_post_parse(p_state);
ERR_FAIL_COND_V(err != OK, err);
}
return OK;
}
Error GLTFDocument::append_from_file(const String &p_path, Ref<GLTFState> p_state, uint32_t p_flags, const String &p_base_path) {
Ref<GLTFState> state = p_state;
ERR_FAIL_COND_V(p_state.is_null(), ERR_INVALID_PARAMETER);
// TODO Add missing texture and missing .bin file paths to r_missing_deps 2021-09-10 fire
if (state == Ref<GLTFState>()) {
state.instantiate();
}
state->set_filename(p_path.get_file().get_basename());
state->use_named_skin_binds = p_flags & ImportFlags::IMPORT_FLAG_USE_NAMED_SKIN_BINDS;
state->discard_meshes_and_materials = p_flags & ImportFlags::IMPORT_FLAG_DISCARD_MESHES_AND_MATERIALS;
state->force_generate_tangents = p_flags & ImportFlags::IMPORT_FLAG_GENERATE_TANGENT_ARRAYS;
state->force_disable_compression = p_flags & ImportFlags::IMPORT_FLAG_FORCE_DISABLE_MESH_COMPRESSION;
p_state->set_filename(p_path.get_file().get_basename());
p_state->use_named_skin_binds = p_flags & ImportFlags::IMPORT_FLAG_USE_NAMED_SKIN_BINDS;
p_state->discard_meshes_and_materials = p_flags & ImportFlags::IMPORT_FLAG_DISCARD_MESHES_AND_MATERIALS;
p_state->force_generate_tangents = p_flags & ImportFlags::IMPORT_FLAG_GENERATE_TANGENT_ARRAYS;
p_state->force_disable_compression = p_flags & ImportFlags::IMPORT_FLAG_FORCE_DISABLE_MESH_COMPRESSION;
Error err;
Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::READ, &err);
@@ -7356,7 +7349,7 @@ Error GLTFDocument::append_from_file(const String &p_path, Ref<GLTFState> p_stat
if (base_path.is_empty()) {
base_path = p_path.get_base_dir();
}
state->set_base_path(base_path);
p_state->set_base_path(base_path);
err = _parse(p_state, base_path, file);
ERR_FAIL_COND_V(err != OK, err);
for (Ref<GLTFDocumentExtension> ext : document_extensions) {
@@ -7368,7 +7361,7 @@ Error GLTFDocument::append_from_file(const String &p_path, Ref<GLTFState> p_stat
}
Error GLTFDocument::_parse_gltf_extensions(Ref<GLTFState> p_state) {
ERR_FAIL_COND_V(p_state.is_null(), ERR_PARSE_ERROR);
ERR_FAIL_COND_V(p_state.is_null(), ERR_INVALID_PARAMETER);
if (p_state->json.has("extensionsUsed")) {
Vector<String> ext_array = p_state->json["extensionsUsed"];
p_state->extensions_used = ext_array;
@@ -1227,6 +1227,9 @@ void GridMapEditor::edit(GridMap *p_gridmap) {
return;
}
// Prevent the cursor from being at an incorrect position before any inputs happen.
cursor_origin = (Vector3(cursor_gridpos) + Vector3(0.5 * node->get_center_x(), 0.5 * node->get_center_y(), 0.5 * node->get_center_z())) * node->get_cell_size();
update_palette();
_update_cursor_instance();
@@ -1411,27 +1414,29 @@ void GridMapEditor::_update_cursor_instance() {
const RID scenario = get_tree()->get_root()->get_world_3d()->get_scenario();
if (mode_buttons_group->get_pressed_button() == paint_mode_button) {
if (selected_palette >= 0 && node && node->get_mesh_library().is_valid()) {
Ref<Mesh> mesh = node->get_mesh_library()->get_item_mesh(selected_palette);
if (mesh.is_valid() && mesh->get_rid().is_valid()) {
cursor_instance = RenderingServer::get_singleton()->instance_create2(mesh->get_rid(), scenario);
RSE::ShadowCastingSetting cast_shadows = (RSE::ShadowCastingSetting)node->get_mesh_library()->get_item_mesh_cast_shadow(selected_palette);
RS::get_singleton()->instance_geometry_set_cast_shadows_setting(cursor_instance, cast_shadows);
if (mesh_library.is_valid()) {
if (mode_buttons_group->get_pressed_button() == paint_mode_button) {
if (selected_palette >= 0 && node && node->get_mesh_library().is_valid()) {
Ref<Mesh> mesh = node->get_mesh_library()->get_item_mesh(selected_palette);
if (mesh.is_valid() && mesh->get_rid().is_valid()) {
cursor_instance = RenderingServer::get_singleton()->instance_create2(mesh->get_rid(), scenario);
RSE::ShadowCastingSetting cast_shadows = (RSE::ShadowCastingSetting)node->get_mesh_library()->get_item_mesh_cast_shadow(selected_palette);
RS::get_singleton()->instance_geometry_set_cast_shadows_setting(cursor_instance, cast_shadows);
}
}
} else if (mode_buttons_group->get_pressed_button() == select_mode_button) {
cursor_inner_mat->set_albedo(Color(default_color, 0.2));
cursor_outer_mat->set_albedo(Color(default_color, 0.8));
cursor_instance = RenderingServer::get_singleton()->instance_create2(cursor_mesh, scenario);
} else if (mode_buttons_group->get_pressed_button() == erase_mode_button) {
cursor_inner_mat->set_albedo(Color(erase_color, 0.2));
cursor_outer_mat->set_albedo(Color(erase_color, 0.8));
cursor_instance = RenderingServer::get_singleton()->instance_create2(cursor_mesh, scenario);
} else if (mode_buttons_group->get_pressed_button() == pick_mode_button) {
cursor_inner_mat->set_albedo(Color(pick_color, 0.2));
cursor_outer_mat->set_albedo(Color(pick_color, 0.8));
cursor_instance = RenderingServer::get_singleton()->instance_create2(cursor_mesh, scenario);
}
} else if (mode_buttons_group->get_pressed_button() == select_mode_button) {
cursor_inner_mat->set_albedo(Color(default_color, 0.2));
cursor_outer_mat->set_albedo(Color(default_color, 0.8));
cursor_instance = RenderingServer::get_singleton()->instance_create2(cursor_mesh, scenario);
} else if (mode_buttons_group->get_pressed_button() == erase_mode_button) {
cursor_inner_mat->set_albedo(Color(erase_color, 0.2));
cursor_outer_mat->set_albedo(Color(erase_color, 0.8));
cursor_instance = RenderingServer::get_singleton()->instance_create2(cursor_mesh, scenario);
} else if (mode_buttons_group->get_pressed_button() == pick_mode_button) {
cursor_inner_mat->set_albedo(Color(pick_color, 0.2));
cursor_outer_mat->set_albedo(Color(pick_color, 0.8));
cursor_instance = RenderingServer::get_singleton()->instance_create2(cursor_mesh, scenario);
}
bool was_visible = cursor_visible;
@@ -112,7 +112,7 @@ void JoltProjectSettings::read_settings() {
joint_world_node = (JoltJointWorldNode)(int)GLOBAL_GET("physics/jolt_physics_3d/joints/world_node");
temp_memory_mib = GLOBAL_GET("physics/jolt_physics_3d/limits/temporary_memory_buffer_size");
temp_memory_b = temp_memory_mib * 1024 * 1024;
temp_memory_b = (int64_t)temp_memory_mib * 1024 * 1024;
world_boundary_shape_size = GLOBAL_GET("physics/jolt_physics_3d/limits/world_boundary_shape_size");
max_linear_velocity = GLOBAL_GET("physics/jolt_physics_3d/limits/max_linear_velocity");
max_angular_velocity = GLOBAL_GET("physics/jolt_physics_3d/limits/max_angular_velocity");
+8 -8
View File
@@ -600,15 +600,15 @@ AnimationNode::NodeTimeInfo AnimationNodeOneShot::_process(ProcessState &p_proce
bool p_is_external_seeking = p_playback_info.is_external_seeking;
bool do_start = cur_request == ONE_SHOT_REQUEST_FIRE;
bool is_reset = Math::is_zero_approx(p_time) && p_seek && !p_is_external_seeking;
if (is_reset && cur_internal_active) {
do_start = true;
}
bool is_abort = cur_request == ONE_SHOT_REQUEST_ABORT;
if (is_reset && !do_start && (is_fading_out || (abort_on_reset && cur_active))) {
is_abort = true;
bool is_reset = Math::is_zero_approx(p_time) && p_seek && !p_is_external_seeking;
if (is_reset) {
if (!do_start && (is_fading_out || (abort_on_reset && cur_active))) {
is_abort = true;
}
if (cur_internal_active) {
do_start = true;
}
}
if (is_abort) {
+4
View File
@@ -1363,6 +1363,10 @@ void RuntimeNodeSelect::_find_3d_items_at_rect(const Rect2 &p_rect, Vector<Selec
const int num_hits = ss->intersect_shape(shape_params, results, 32);
for (int i = 0; i < num_hits; i++) {
const PhysicsDirectSpaceState3D::ShapeResult &result = results[i];
if (!result.collider) {
continue;
}
SelectResult res;
res.item = Object::cast_to<Node>(result.collider);
res.order = -dist_pos.distance_to(Object::cast_to<Node3D>(res.item)->get_global_transform().origin);
+29 -38
View File
@@ -918,49 +918,32 @@ Control::GrowDirection Control::get_v_grow_direction() const {
return data.v_grow;
}
void Control::_compute_layout_rect(Rect2 p_rect, bool p_keep_offsets) {
void Control::_compute_anchors(Rect2 p_rect, const real_t p_offsets[4], real_t (&r_anchors)[4]) {
Size2 parent_rect_size = get_parent_anchorable_rect().size;
if (p_keep_offsets) {
// If computing anchors, we need to ensure the parent rect size is valid to avoid division by zero.
ERR_FAIL_COND(parent_rect_size.x == 0.0);
ERR_FAIL_COND(parent_rect_size.y == 0.0);
}
ERR_FAIL_COND(parent_rect_size.x == 0.0);
ERR_FAIL_COND(parent_rect_size.y == 0.0);
real_t x = p_rect.position.x;
real_t y = p_rect.position.y;
if (_get_layout_mode() != LayoutMode::LAYOUT_MODE_CONTAINER) {
float left_grow_factor =
(data.h_grow == GROW_DIRECTION_BEGIN) ? 1.0f
: (data.h_grow == GROW_DIRECTION_END) ? 0.0f
: 0.5f;
float top_grow_factor =
(data.v_grow == GROW_DIRECTION_BEGIN) ? 1.0f
: (data.v_grow == GROW_DIRECTION_END) ? 0.0f
: 0.5f;
Size2 size_diff = p_rect.size - data.size_cache;
x -= size_diff.x * (is_layout_rtl() ? (1.0f - left_grow_factor) : left_grow_factor);
y -= size_diff.y * top_grow_factor;
}
if (is_layout_rtl()) {
x = parent_rect_size.x - x - p_rect.size.x;
}
r_anchors[0] = (x - p_offsets[0]) / parent_rect_size.x;
r_anchors[1] = (p_rect.position.y - p_offsets[1]) / parent_rect_size.y;
r_anchors[2] = (x + p_rect.size.x - p_offsets[2]) / parent_rect_size.x;
r_anchors[3] = (p_rect.position.y + p_rect.size.y - p_offsets[3]) / parent_rect_size.y;
}
if (p_keep_offsets) {
data.anchor[0] = (x - data.offset[0]) / parent_rect_size.x;
data.anchor[1] = (y - data.offset[1]) / parent_rect_size.y;
data.anchor[2] = (x + p_rect.size.x - data.offset[2]) / parent_rect_size.x;
data.anchor[3] = (y + p_rect.size.y - data.offset[3]) / parent_rect_size.y;
} else {
data.offset[0] = x - (data.anchor[0] * parent_rect_size.x);
data.offset[1] = y - (data.anchor[1] * parent_rect_size.y);
data.offset[2] = x + p_rect.size.x - (data.anchor[2] * parent_rect_size.x);
data.offset[3] = y + p_rect.size.y - (data.anchor[3] * parent_rect_size.y);
void Control::_compute_offsets(Rect2 p_rect, const real_t p_anchors[4], real_t (&r_offsets)[4]) {
Size2 parent_rect_size = get_parent_anchorable_rect().size;
real_t x = p_rect.position.x;
if (is_layout_rtl()) {
x = parent_rect_size.x - x - p_rect.size.x;
}
r_offsets[0] = x - (p_anchors[0] * parent_rect_size.x);
r_offsets[1] = p_rect.position.y - (p_anchors[1] * parent_rect_size.y);
r_offsets[2] = x + p_rect.size.x - (p_anchors[2] * parent_rect_size.x);
r_offsets[3] = p_rect.position.y + p_rect.size.y - (p_anchors[3] * parent_rect_size.y);
}
/// Presets and layout modes.
@@ -1493,7 +1476,11 @@ void Control::set_position(const Point2 &p_point, bool p_keep_offsets) {
}
#endif // TOOLS_ENABLED
_compute_layout_rect(Rect2(p_point, data.size_cache), p_keep_offsets);
if (p_keep_offsets) {
_compute_anchors(Rect2(p_point, data.size_cache), data.offset, data.anchor);
} else {
_compute_offsets(Rect2(p_point, data.size_cache), data.anchor, data.offset);
}
_size_changed();
}
@@ -1565,7 +1552,11 @@ void Control::set_size(const Size2 &p_size, bool p_keep_offsets) {
data.expanded_by_desired_size = false;
_compute_layout_rect(Rect2(data.pos_cache, new_size), p_keep_offsets);
if (p_keep_offsets) {
_compute_anchors(Rect2(data.pos_cache, new_size), data.offset, data.anchor);
} else {
_compute_offsets(Rect2(data.pos_cache, new_size), data.anchor, data.offset);
}
_size_changed();
}
@@ -1585,7 +1576,7 @@ void Control::set_rect(const Rect2 &p_rect) {
data.anchor[i] = ANCHOR_BEGIN;
}
_compute_layout_rect(p_rect);
_compute_offsets(p_rect, data.anchor, data.offset);
if (is_inside_tree()) {
_size_changed();
}
+2 -1
View File
@@ -365,7 +365,8 @@ private:
void _set_global_position(const Point2 &p_point);
void _set_size(const Size2 &p_size);
void _compute_layout_rect(Rect2 p_rect, bool p_keep_offsets = false);
void _compute_offsets(Rect2 p_rect, const real_t p_anchors[4], real_t (&r_offsets)[4]);
void _compute_anchors(Rect2 p_rect, const real_t p_offsets[4], real_t (&r_anchors)[4]);
void _set_layout_mode(LayoutMode p_mode);
void _update_layout_mode();
+1
View File
@@ -3795,6 +3795,7 @@ PopupMenu::PopupMenu() {
search_bar->set_clear_button_enabled(true);
search_bar->set_placeholder(ETR("Search"));
search_bar->set_keep_editing_on_text_submit(true);
search_bar->set_virtual_keyboard_show_on_focus(false);
search_bar->connect(SceneStringName(text_changed), callable_mp(this, &PopupMenu::_search_bar_text_changed));
search_bar->connect(SceneStringName(focus_entered), callable_mp(this, &PopupMenu::_search_bar_focus_entered));
vbox_container->add_child(search_bar, false, INTERNAL_MODE_FRONT);
+7 -2
View File
@@ -189,8 +189,13 @@ double Range::_calc_value(double p_val, double p_step) const {
}
if (p_step > 0) {
// Subtract min to support cases like min = 0.1, step = 0.2, snaps to 0.1, 0.3, 0.5, etc.
p_val = _snapped_r128(p_val - shared->min, p_step) + shared->min;
if (Math::abs(shared->min) > p_step * 1e14) {
// Min is too big to use for snapping offset, so snap without it.
p_val = _snapped_r128(p_val, p_step);
} else {
// Subtract min to support cases like min = 0.1, step = 0.2, snaps to 0.1, 0.3, 0.5, etc.
p_val = _snapped_r128(p_val - shared->min, p_step) + shared->min;
}
}
if (_rounded_values) {
+1
View File
@@ -735,6 +735,7 @@ void SpinBox::_bind_methods() {
SpinBox::SpinBox() {
line_edit = memnew(SpinBoxLineEdit);
line_edit->set_emoji_menu_enabled(false);
line_edit->set_use_parent_material(true);
add_child(line_edit, false, INTERNAL_MODE_FRONT);
line_edit->set_theme_type_variation("SpinBoxInnerLineEdit");
+6
View File
@@ -729,6 +729,12 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("file_thumbnail", "FileDialog", icons["file_thumbnail"]);
theme->set_icon("folder_thumbnail", "FileDialog", icons["folder_thumbnail"]);
theme->set_icon("menu_copy_path", "FileDialog", empty_icon);
theme->set_icon("menu_delete", "FileDialog", empty_icon);
theme->set_icon("menu_new_folder", "FileDialog", empty_icon);
theme->set_icon("menu_refresh", "FileDialog", empty_icon);
theme->set_icon("menu_show_in_file_manager", "FileDialog", empty_icon);
theme->set_icon("menu_open_bundle", "FileDialog", empty_icon);
theme->set_color("folder_icon_color", "FileDialog", Color(1, 1, 1));
theme->set_color("file_icon_color", "FileDialog", Color(1, 1, 1));
theme->set_color("file_disabled_color", "FileDialog", Color(1, 1, 1, 0.25));
+15 -2
View File
@@ -122,8 +122,21 @@ public:
virtual void mesh_surface_update_skin_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) override {}
virtual void mesh_surface_update_index_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) override {}
virtual void mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material) override {}
virtual RID mesh_surface_get_material(RID p_mesh, int p_surface) const override { return RID(); }
virtual void mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material) override {
DummyMesh *m = mesh_owner.get_or_null(p_mesh);
ERR_FAIL_NULL(m);
ERR_FAIL_UNSIGNED_INDEX((uint32_t)p_surface, m->surfaces.size());
RenderingServerTypes::SurfaceData s = m->surfaces.get(p_surface);
s.material = p_material;
m->surfaces.set(p_surface, s);
m->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MATERIAL);
}
virtual RID mesh_surface_get_material(RID p_mesh, int p_surface) const override {
DummyMesh *m = mesh_owner.get_or_null(p_mesh);
ERR_FAIL_NULL_V(m, RID());
ERR_FAIL_UNSIGNED_INDEX_V((uint32_t)p_surface, m->surfaces.size(), RID());
return m->surfaces[p_surface].material;
}
virtual RenderingServerTypes::SurfaceData mesh_get_surface(RID p_mesh, int p_surface) const override {
DummyMesh *m = mesh_owner.get_or_null(p_mesh);
@@ -418,7 +418,7 @@ TextureStorage::TextureStorage() {
}
}
{ //create default array white
{ // Creates a default 2D texture array filled with a white color.
RD::TextureFormat tformat;
tformat.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
@@ -444,7 +444,33 @@ TextureStorage::TextureStorage() {
}
}
{ //create default array black
{ // Сreates a default 2D texture array filled with a black color.
RD::TextureFormat tformat;
tformat.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
tformat.width = 4;
tformat.height = 4;
tformat.array_layers = 1;
tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT;
tformat.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
Vector<uint8_t> pv;
pv.resize(16 * 4);
for (int i = 0; i < 16; i++) {
pv.set(i * 4 + 0, 0);
pv.set(i * 4 + 1, 0);
pv.set(i * 4 + 2, 0);
pv.set(i * 4 + 3, 255);
}
{
Vector<Vector<uint8_t>> vpv;
vpv.push_back(pv);
default_rd_textures[DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK] = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv);
}
}
{ // Creates a default 2D texture array filled with a transparent color.
RD::TextureFormat tformat;
tformat.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
@@ -466,7 +492,7 @@ TextureStorage::TextureStorage() {
{
Vector<Vector<uint8_t>> vpv;
vpv.push_back(pv);
default_rd_textures[DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK] = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv);
default_rd_textures[DEFAULT_RD_TEXTURE_2D_ARRAY_TRANSPARENT] = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv);
}
}
+11 -6
View File
@@ -147,15 +147,20 @@ bool RenderingLightCuller::_prepare_light(const RendererSceneCull::Instance &p_i
Plane boundary_planes[5];
{
constexpr const int MAX_PLANES = 5;
// Orthogonal cameras have fixed max shadow distance, determined by the camera far plane.
// That must be accounted for, or else GH-120457 happens.
// For perspective cameras, shadow far must also never be further than the camera far plane (otherwise things break).
float camera_far = data.camera_projection.get_z_far();
float shadow_far = data.camera_projection.is_orthogonal() ? camera_far : MIN(lsource.range, camera_far);
real_t plane_distances[MAX_PLANES] = {
data.camera_projection.get_z_near(),
lsource.cascade_splits[0] * lsource.range,
lsource.cascade_splits[1] * lsource.range,
lsource.cascade_splits[2] * lsource.range,
lsource.range,
lsource.cascade_splits[0] * shadow_far,
lsource.cascade_splits[1] * shadow_far,
lsource.cascade_splits[2] * shadow_far,
shadow_far,
};
//If not 4 cascades, replace last used cascade plane distance with max shadow range (shadow far plane distance).
plane_distances[used_planes - 1] = lsource.range;
// If not 4 cascades, replace last used cascade plane distance with max shadow range (shadow far plane distance).
plane_distances[used_planes - 1] = shadow_far;
#ifdef LIGHT_CULLER_DEBUG_LOGGING
if (is_logging()) {
print_line("cascade split planes (first " + itos(used_planes) + " used): " +