Skip to content

Commit

Permalink
Add the ability to expose nodes for direct access in instantiated scenes
Browse files Browse the repository at this point in the history
  • Loading branch information
yahkr committed Aug 28, 2024
1 parent 40b378e commit 5593ff8
Show file tree
Hide file tree
Showing 14 changed files with 566 additions and 20 deletions.
3 changes: 3 additions & 0 deletions doc/classes/Node.xml
Original file line number Diff line number Diff line change
Expand Up @@ -982,6 +982,9 @@
<member name="editor_description" type="String" setter="set_editor_description" getter="get_editor_description" default="&quot;&quot;">
An optional description to the node. It will be displayed as a tooltip when hovering over the node in the editor's Scene dock.
</member>
<member name="exposed_in_owner" type="bool" setter="set_exposed_in_owner" getter="is_exposed_in_owner" default="false">
Sets this node's state as an exposed node in its [member owner]. This allows the node to be accessed in the editor when the scene is instantiated in another. It also enforces that the node is unique, see [member unique_name_in_owner].
</member>
<member name="multiplayer" type="MultiplayerAPI" setter="" getter="get_multiplayer">
The [MultiplayerAPI] instance associated with this node. See [method SceneTree.get_multiplayer].
[b]Note:[/b] Renaming the node, or moving it in the tree, will not move the [MultiplayerAPI] to the new path, you will have to update this manually.
Expand Down
3 changes: 3 additions & 0 deletions editor/editor_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,9 @@ bool EditorData::check_and_update_scene(int p_idx) {
}
}

// Apply Overrides when instanced scene gets updated.
pscene->get_state()->apply_overrides(new_scene);

new_scene->set_scene_file_path(edited_scene[p_idx].root->get_scene_file_path());
Node *old_root = edited_scene[p_idx].root;
EditorNode::get_singleton()->set_edited_scene(new_scene);
Expand Down
2 changes: 2 additions & 0 deletions editor/editor_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4193,6 +4193,8 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
}
}
}
// Apply Overrides when loading a scene.
sdata->get_state()->apply_overrides(new_scene);

if (!restoring_scenes) {
save_editor_layout_delayed();
Expand Down
27 changes: 25 additions & 2 deletions editor/gui/scene_tree_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,17 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i
undo_redo->create_action(TTR("Disable Scene Unique Name"));
undo_redo->add_do_method(n, "set_unique_name_in_owner", false);
undo_redo->add_undo_method(n, "set_unique_name_in_owner", true);
if (n->is_exposed_in_owner()) {
undo_redo->add_do_method(n, "set_exposed_in_owner", false);
undo_redo->add_undo_method(n, "set_exposed_in_owner", true);
}
undo_redo->add_do_method(this, "_update_tree");
undo_redo->add_undo_method(this, "_update_tree");
undo_redo->commit_action();
} else if (p_id == BUTTON_EXPOSED) {
undo_redo->create_action(TTR("Disable Scene Exposed Node"));
undo_redo->add_do_method(n, "set_exposed_in_owner", false);
undo_redo->add_undo_method(n, "set_exposed_in_owner", true);
undo_redo->add_do_method(this, "_update_tree");
undo_redo->add_undo_method(this, "_update_tree");
undo_redo->commit_action();
Expand All @@ -203,9 +214,14 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) {
bool part_of_subscene = false;

if (!display_foreign && p_node->get_owner() != get_scene_node() && p_node != get_scene_node()) {
if ((show_enabled_subscene || can_open_instance) && p_node->get_owner() && (get_scene_node()->is_editable_instance(p_node->get_owner()))) {
if (((show_enabled_subscene || can_open_instance) && p_node->get_owner() && (get_scene_node()->is_editable_instance(p_node->get_owner()))) || (p_node->get_owner() != get_scene_node() && p_node->is_exposed_in_owner())) {
part_of_subscene = true;
//allow
} else if (p_node->has_exposed_nodes(true)) {
for (int i = 0; i < p_node->get_child_count(); i++) {
_add_nodes(p_node->get_child(i), p_parent);
}
return;
} else {
return;
}
Expand All @@ -217,7 +233,7 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) {

item->set_text(0, p_node->get_name());
item->set_text_overrun_behavior(0, TextServer::OVERRUN_NO_TRIMMING);
if (can_rename && !part_of_subscene) {
if (can_rename && !part_of_subscene && (!p_node->is_exposed_in_owner() || p_node->get_owner() == get_scene_node())) {
item->set_editable(0, true);
}

Expand Down Expand Up @@ -322,6 +338,13 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) {
item->add_button(0, get_editor_theme_icon(SNAME("SceneUniqueName")), BUTTON_UNIQUE, p_node->get_owner() != EditorNode::get_singleton()->get_edited_scene(), vformat(TTR("This node can be accessed from within anywhere in the scene by preceding it with the '%s' prefix in a node path.\nClick to disable this."), UNIQUE_NODE_PREFIX));
}

if (p_node->is_exposed_in_owner()) {
item->add_button(0, get_editor_theme_icon(SNAME("SceneExposedNode")), BUTTON_EXPOSED, p_node->get_owner() != EditorNode::get_singleton()->get_edited_scene(), TTR("This node will be exposed in the editor when this scene is instantiated.\nClick to disable this."));
if (p_node->get_owner() != get_scene_node()) {
_set_item_custom_color(item, get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));
}
}

int num_connections = p_node->get_persistent_signal_connection_count();
int num_groups = p_node->get_persistent_group_count();

Expand Down
1 change: 1 addition & 0 deletions editor/gui/scene_tree_editor.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class SceneTreeEditor : public Control {
BUTTON_GROUPS = 7,
BUTTON_PIN = 8,
BUTTON_UNIQUE = 9,
BUTTON_EXPOSED = 10,
};

Tree *tree = nullptr;
Expand Down
1 change: 1 addition & 0 deletions editor/icons/SceneExposedNode.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions editor/import/3d/resource_importer_scene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1024,6 +1024,11 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, HashMap<R
}
}

if (!isroot && _teststr(name, "exp")) {
p_node->set_exposed_in_owner(true);
p_node->set_name(_fixstr(name, "exp"));
}

return p_node;
}

Expand Down Expand Up @@ -1766,6 +1771,10 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<
}
}

if (node_settings.has("import/exposed_in_owner")) {
p_node->set_exposed_in_owner(node_settings["import/exposed_in_owner"].operator bool());
}

return p_node;
}

Expand Down Expand Up @@ -1976,9 +1985,11 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
switch (p_category) {
case INTERNAL_IMPORT_CATEGORY_NODE: {
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/exposed_in_owner", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
} break;
case INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE: {
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/exposed_in_owner", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate/physics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/navmesh", PROPERTY_HINT_ENUM, "Disabled,Mesh + NavMesh,NavMesh Only"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/body_type", PROPERTY_HINT_ENUM, "Static,Dynamic,Area"), 0));
Expand Down Expand Up @@ -2057,6 +2068,7 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
} break;
case INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE: {
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/exposed_in_owner", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "optimizer/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_velocity_error", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.01));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_angular_error", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.01));
Expand All @@ -2069,6 +2081,7 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
} break;
case INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE: {
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/exposed_in_owner", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "rest_pose/load_pose", PROPERTY_HINT_ENUM, "Default Pose,Use AnimationPlayer,Load External Animation", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "rest_pose/external_animation_library", PROPERTY_HINT_RESOURCE_TYPE, "Animation,AnimationLibrary", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), Variant()));
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "rest_pose/selected_animation", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), ""));
Expand Down Expand Up @@ -2615,6 +2628,7 @@ Node *ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_
} break;
}

mesh_node->set_exposed_in_owner(src_mesh_node->is_exposed_in_owner());
mesh_node->set_layer_mask(src_mesh_node->get_layer_mask());
mesh_node->set_cast_shadows_setting(src_mesh_node->get_cast_shadows_setting());
mesh_node->set_visibility_range_begin(src_mesh_node->get_visibility_range_begin());
Expand Down
83 changes: 82 additions & 1 deletion editor/scene_tree_dock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ void SceneTreeDock::shortcut_input(const Ref<InputEvent> &p_event) {
_tool_selected(TOOL_SHOW_IN_FILE_SYSTEM);
} else if (ED_IS_SHORTCUT("scene_tree/toggle_unique_name", p_event)) {
_tool_selected(TOOL_TOGGLE_SCENE_UNIQUE_NAME);
} else if (ED_IS_SHORTCUT("scene_tree/toggle_exposed_node", p_event)) {
_tool_selected(TOOL_TOGGLE_SCENE_EXPOSED);
} else if (ED_IS_SHORTCUT("scene_tree/toggle_editable_children", p_event)) {
_tool_selected(TOOL_SCENE_EDITABLE_CHILDREN);
} else if (ED_IS_SHORTCUT("scene_tree/delete", p_event)) {
Expand Down Expand Up @@ -1424,6 +1426,82 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
}
undo_redo->add_do_method(node, "set_unique_name_in_owner", false);
undo_redo->add_undo_method(node, "set_unique_name_in_owner", true);
if (node->is_exposed_in_owner()) {
undo_redo->add_undo_method(node, "set_exposed_in_owner", true);
}
}
undo_redo->commit_action();
}
} break;
case TOOL_TOGGLE_SCENE_EXPOSED: {
// Enabling/disabling based on the same node based on which the checkbox in the menu is checked/unchecked.
List<Node *>::Element *first_selected = editor_selection->get_selected_node_list().front();
if (first_selected == nullptr) {
return;
}
if (first_selected->get() == EditorNode::get_singleton()->get_edited_scene()) {
// Exclude Root Node. It should never be unique name in its own scene!
editor_selection->remove_node(first_selected->get());
first_selected = editor_selection->get_selected_node_list().front();
if (first_selected == nullptr) {
return;
}
}
bool enabling = !first_selected->get()->is_exposed_in_owner();

List<Node *> full_selection = editor_selection->get_full_selected_node_list();
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();

if (enabling) {
Vector<Node *> new_exposed_nodes;
Vector<StringName> new_unique_names;
Vector<StringName> cant_be_set_unique_names;

for (Node *node : full_selection) {
if (node->is_exposed_in_owner()) {
continue;
}
if (node->get_owner() != get_tree()->get_edited_scene_root()) {
continue;
}

StringName name = node->get_name();
Node *unique_node = get_tree()->get_edited_scene_root()->get_node_or_null(UNIQUE_NODE_PREFIX + String(name));
if (new_unique_names.has(name) || (unique_node != nullptr && unique_node != this && unique_node->is_exposed_in_owner())) {
cant_be_set_unique_names.push_back(name);
} else {
new_exposed_nodes.push_back(node);
new_unique_names.push_back(name);
}
}
if (new_exposed_nodes.size()) {
undo_redo->create_action(TTR("Enable Exposed Node(s)"));
for (Node *node : new_exposed_nodes) {
if (!node->is_unique_name_in_owner()) {
undo_redo->add_undo_method(node, "set_unique_name_in_owner", false);
}
undo_redo->add_do_method(node, "set_exposed_in_owner", true);
undo_redo->add_undo_method(node, "set_exposed_in_owner", false);
}
undo_redo->commit_action();
}
if (cant_be_set_unique_names.size()) {
String popup_text = TTR("Unique names already used by another node in the scene:");
popup_text += "\n";
for (const StringName &name : cant_be_set_unique_names) {
popup_text += "\n" + String(name);
}
accept->set_text(popup_text);
accept->popup_centered();
}
} else { // Disabling.
undo_redo->create_action(TTR("Disable Exposed Node(s)"));
for (Node *node : full_selection) {
if (!node->is_unique_name_in_owner()) {
continue;
}
undo_redo->add_do_method(node, "set_exposed_in_owner", false);
undo_redo->add_undo_method(node, "set_exposed_in_owner", true);
}
undo_redo->commit_action();
}
Expand Down Expand Up @@ -2601,7 +2679,7 @@ void SceneTreeDock::_toggle_editable_children(Node *p_node) {
Array name_array;

for (Node *owned_node : owned) {
if (owned_node != p_node && owned_node != edited_scene && owned_node->get_owner() == edited_scene && owned_node->get_parent()->get_owner() != edited_scene) {
if (owned_node != p_node && owned_node != edited_scene && owned_node->get_owner() == edited_scene && owned_node->get_parent()->get_owner() != edited_scene && owned_node->is_exposed_in_owner()) {
owned_nodes_array.push_back(owned_node);
paths_array.push_back(p_node->get_path_to(owned_node->get_parent()));
name_array.push_back(owned_node->get_name());
Expand Down Expand Up @@ -3673,6 +3751,8 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
Node *node = full_selection.front()->get();
menu->add_icon_shortcut(get_editor_theme_icon(SNAME("SceneUniqueName")), ED_GET_SHORTCUT("scene_tree/toggle_unique_name"), TOOL_TOGGLE_SCENE_UNIQUE_NAME);
menu->set_item_text(menu->get_item_index(TOOL_TOGGLE_SCENE_UNIQUE_NAME), node->is_unique_name_in_owner() ? TTR("Revoke Unique Name") : TTR("Access as Unique Name"));
menu->add_icon_shortcut(get_editor_theme_icon(SNAME("SceneExposedNode")), ED_GET_SHORTCUT("scene_tree/toggle_exposed_node"), TOOL_TOGGLE_SCENE_EXPOSED);
menu->set_item_text(menu->get_item_index(TOOL_TOGGLE_SCENE_EXPOSED), node->is_exposed_in_owner() ? TTR("Unexpose Node in Instantiated Scenes") : TTR("Expose Node in Instantiated Scenes"));
}
}

Expand Down Expand Up @@ -4430,6 +4510,7 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec
ED_SHORTCUT("scene_tree/show_in_file_system", TTR("Show in FileSystem"));
ED_SHORTCUT("scene_tree/toggle_unique_name", TTR("Toggle Access as Unique Name"));
ED_SHORTCUT("scene_tree/toggle_editable_children", TTR("Toggle Editable Children"));
ED_SHORTCUT("scene_tree/toggle_exposed_node", TTR("Toggle node as Exposed in scene"));
ED_SHORTCUT("scene_tree/delete_no_confirm", TTR("Delete (No Confirm)"), KeyModifierMask::SHIFT | Key::KEY_DELETE);
ED_SHORTCUT("scene_tree/delete", TTR("Delete"), Key::KEY_DELETE);

Expand Down
1 change: 1 addition & 0 deletions editor/scene_tree_dock.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ class SceneTreeDock : public VBoxContainer {
TOOL_SCENE_CLEAR_INHERITANCE_CONFIRM,
TOOL_SCENE_OPEN_INHERITED,
TOOL_TOGGLE_SCENE_UNIQUE_NAME,
TOOL_TOGGLE_SCENE_EXPOSED,
TOOL_CREATE_2D_SCENE,
TOOL_CREATE_3D_SCENE,
TOOL_CREATE_USER_INTERFACE,
Expand Down
Loading

0 comments on commit 5593ff8

Please sign in to comment.