From f81e64dd14f31eca6c31720f5ce4e656babd8964 Mon Sep 17 00:00:00 2001 From: Marc Gilleron Date: Tue, 2 Aug 2022 00:38:21 +0100 Subject: [PATCH] Added pinning to voxel graph editor --- doc/source/changelog.md | 1 + editor/graph/voxel_graph_editor.cpp | 32 +++++++++++--- editor/graph/voxel_graph_editor.h | 5 +++ editor/graph/voxel_graph_editor_plugin.cpp | 49 +++++++++++++++++++--- editor/graph/voxel_graph_editor_plugin.h | 3 ++ 5 files changed, 79 insertions(+), 11 deletions(-) diff --git a/doc/source/changelog.md b/doc/source/changelog.md index cf94a63ed..91f6af565 100644 --- a/doc/source/changelog.md +++ b/doc/source/changelog.md @@ -28,6 +28,7 @@ Godot 4 is required from this version. - `VoxelGeneratorGraph`: editor: allow to change the axes on preview nodes 3D slices - `VoxelGeneratorGraph`: editor: replace existing connection if dragging from/to an input port having one already - `VoxelGeneratorGraph`: editor: creating noise and curve nodes now auto-create their resource instead of coming up null + - `VoxelGeneratorGraph`: editor: added pin button to keep the graph editor shown even after deselecting the terrain. - `VoxelGeneratorGraph`: added `OutputSingleTexture` node for outputting a single texture index per voxel, as an alternative to weights. This is specific to smooth voxels. - `VoxelGeneratorGraph`: added math expression node - `VoxelGeneratorGraph`: added Pow and Powi nodes diff --git a/editor/graph/voxel_graph_editor.cpp b/editor/graph/voxel_graph_editor.cpp index e19ac061a..2350b794a 100644 --- a/editor/graph/voxel_graph_editor.cpp +++ b/editor/graph/voxel_graph_editor.cpp @@ -28,6 +28,7 @@ namespace zylann::voxel { const char *VoxelGraphEditor::SIGNAL_NODE_SELECTED = "node_selected"; const char *VoxelGraphEditor::SIGNAL_NOTHING_SELECTED = "nothing_selected"; const char *VoxelGraphEditor::SIGNAL_NODES_DELETED = "nodes_deleted"; +const char *VoxelGraphEditor::SIGNAL_REGENERATE_REQUESTED = "regenerate_requested"; static NodePath to_node_path(StringName sn) { Vector path; @@ -88,6 +89,17 @@ VoxelGraphEditor::VoxelGraphEditor() { live_update_checkbox->connect("toggled", callable_mp(this, &VoxelGraphEditor::_on_live_update_toggled)); toolbar->add_child(live_update_checkbox); + Control *spacer = memnew(Control); + spacer->set_h_size_flags(Control::SIZE_EXPAND_FILL); + toolbar->add_child(spacer); + + _pin_button = memnew(Button); + _pin_button->set_flat(true); + _pin_button->set_toggle_mode(true); + _pin_button->set_tooltip(TTR("Pin AnimationPlayer")); + toolbar->add_child(_pin_button); + //_pin_button->connect("pressed", callable_mp(this, &AnimationPlayerEditor::_pin_pressed)); + vbox_container->add_child(toolbar); } @@ -195,6 +207,10 @@ void VoxelGraphEditor::_notification(int p_what) { case NOTIFICATION_VISIBILITY_CHANGED: set_process_internal(is_visible()); break; + + case NOTIFICATION_THEME_CHANGED: + _pin_button->set_icon(get_theme_icon(SNAME("Pin"), SNAME("EditorIcons"))); + break; } } @@ -371,6 +387,10 @@ void VoxelGraphEditor::update_node_layout(uint32_t node_id) { } } +bool VoxelGraphEditor::is_pinned_hint() const { + return _pin_button->is_pressed(); +} + static bool is_nothing_selected(GraphEdit *graph_edit) { for (int i = 0; i < graph_edit->get_child_count(); ++i) { GraphNode *node = Object::cast_to(graph_edit->get_child(i)); @@ -660,12 +680,11 @@ void VoxelGraphEditor::update_previews(bool with_live_update) { if (hash != _last_output_graph_hash) { _last_output_graph_hash = hash; - // We could be editing the graph standalone with no terrain loaded - if (_voxel_node != nullptr) { - // Re-generate the terrain. - // Only do that if the graph is valid. - _voxel_node->restart_stream(); - } + // Not calling into `_voxel_node` directly because the editor could be pinned and the terrain not actually + // selected. In this situation the plugin may reset the node to null. But it is desirable for terrains + // using the current graph to update if they are in the edited scene, so this may be delegated to the editor + // plugin. There isn't enough context from here to do this cleanly. + emit_signal(SIGNAL_REGENERATE_REQUESTED); } } } @@ -983,6 +1002,7 @@ void VoxelGraphEditor::_bind_methods() { ADD_SIGNAL(MethodInfo(SIGNAL_NODE_SELECTED, PropertyInfo(Variant::INT, "node_id"))); ADD_SIGNAL(MethodInfo(SIGNAL_NOTHING_SELECTED)); ADD_SIGNAL(MethodInfo(SIGNAL_NODES_DELETED)); + ADD_SIGNAL(MethodInfo(SIGNAL_REGENERATE_REQUESTED)); } } // namespace zylann::voxel diff --git a/editor/graph/voxel_graph_editor.h b/editor/graph/voxel_graph_editor.h index 4812eb261..f9e1c6e48 100644 --- a/editor/graph/voxel_graph_editor.h +++ b/editor/graph/voxel_graph_editor.h @@ -8,6 +8,7 @@ class GraphEdit; class PopupMenu; class AcceptDialog; class UndoRedo; +class Button; namespace zylann::voxel { @@ -23,6 +24,7 @@ class VoxelGraphEditor : public Control { static const char *SIGNAL_NODE_SELECTED; static const char *SIGNAL_NOTHING_SELECTED; static const char *SIGNAL_NODES_DELETED; + static const char *SIGNAL_REGENERATE_REQUESTED; VoxelGraphEditor(); @@ -38,6 +40,8 @@ class VoxelGraphEditor : public Control { // Rebuilds the node's internal controls, and updates GUI connections going to it from the graph. void update_node_layout(uint32_t node_id); + bool is_pinned_hint() const; + private: void _notification(int p_what); void _process(float delta); @@ -95,6 +99,7 @@ class VoxelGraphEditor : public Control { VoxelGraphEditorShaderDialog *_shader_dialog = nullptr; bool _live_update_enabled = false; uint64_t _last_output_graph_hash = 0; + Button *_pin_button = nullptr; enum PreviewAxes { // PREVIEW_XY = 0, diff --git a/editor/graph/voxel_graph_editor_plugin.cpp b/editor/graph/voxel_graph_editor_plugin.cpp index e5ad274e6..0e936aa1d 100644 --- a/editor/graph/voxel_graph_editor_plugin.cpp +++ b/editor/graph/voxel_graph_editor_plugin.cpp @@ -41,6 +41,8 @@ VoxelGraphEditorPlugin::VoxelGraphEditorPlugin() { callable_mp(this, &VoxelGraphEditorPlugin::_on_graph_editor_nothing_selected)); _graph_editor->connect(VoxelGraphEditor::SIGNAL_NODES_DELETED, callable_mp(this, &VoxelGraphEditorPlugin::_on_graph_editor_nodes_deleted)); + _graph_editor->connect(VoxelGraphEditor::SIGNAL_REGENERATE_REQUESTED, + callable_mp(this, &VoxelGraphEditorPlugin::_on_graph_editor_regenerate_requested)); _bottom_panel_button = add_control_to_bottom_panel(_graph_editor, TTR("Voxel Graph")); _bottom_panel_button->hide(); @@ -87,6 +89,7 @@ void VoxelGraphEditorPlugin::edit(Object *p_object) { break; } } + _voxel_node = voxel_node; _graph_editor->set_voxel_node(voxel_node); } @@ -96,13 +99,17 @@ void VoxelGraphEditorPlugin::make_visible(bool visible) { make_bottom_panel_item_visible(_graph_editor); } else { - _bottom_panel_button->hide(); + _voxel_node = nullptr; _graph_editor->set_voxel_node(nullptr); - // TODO Awful hack to handle the nonsense happening in `_on_graph_editor_node_selected` - if (!_deferred_visibility_scheduled) { - _deferred_visibility_scheduled = true; - call_deferred("_hide_deferred"); + if (!_graph_editor->is_pinned_hint()) { + _bottom_panel_button->hide(); + + // TODO Awful hack to handle the nonsense happening in `_on_graph_editor_node_selected` + if (!_deferred_visibility_scheduled) { + _deferred_visibility_scheduled = true; + call_deferred("_hide_deferred"); + } } } } @@ -152,6 +159,38 @@ void VoxelGraphEditorPlugin::_on_graph_editor_nodes_deleted() { get_editor_interface()->inspect_object(*graph); } +template +void for_each_node(Node *parent, F action) { + action(parent); + for (int i = 0; i < parent->get_child_count(); ++i) { + for_each_node(parent->get_child(i), action); + } +} + +void VoxelGraphEditorPlugin::_on_graph_editor_regenerate_requested() { + // We could be editing the graph standalone with no terrain loaded + if (_voxel_node != nullptr) { + // Re-generate the selected terrain. + _voxel_node->restart_stream(); + + } else { + // The node is not selected, but it might be in the tree + Node *root = get_editor_interface()->get_edited_scene_root(); + + if (root != nullptr) { + Ref generator = _graph_editor->get_graph(); + ERR_FAIL_COND(generator.is_null()); + + for_each_node(root, [&generator](Node *node) { + VoxelNode *vnode = Object::cast_to(node); + if (vnode != nullptr && vnode->get_generator() == generator) { + vnode->restart_stream(); + } + }); + } + } +} + void VoxelGraphEditorPlugin::_bind_methods() { // ClassDB::bind_method(D_METHOD("_on_graph_editor_node_selected", "node_id"), // &VoxelGraphEditorPlugin::_on_graph_editor_node_selected); diff --git a/editor/graph/voxel_graph_editor_plugin.h b/editor/graph/voxel_graph_editor_plugin.h index 2a12c017f..d859d38c4 100644 --- a/editor/graph/voxel_graph_editor_plugin.h +++ b/editor/graph/voxel_graph_editor_plugin.h @@ -6,6 +6,7 @@ namespace zylann::voxel { class VoxelGraphEditor; +class VoxelNode; class VoxelGraphEditorPlugin : public EditorPlugin { GDCLASS(VoxelGraphEditorPlugin, EditorPlugin) @@ -20,6 +21,7 @@ class VoxelGraphEditorPlugin : public EditorPlugin { void _on_graph_editor_node_selected(uint32_t node_id); void _on_graph_editor_nothing_selected(); void _on_graph_editor_nodes_deleted(); + void _on_graph_editor_regenerate_requested(); void _hide_deferred(); static void _bind_methods(); @@ -27,6 +29,7 @@ class VoxelGraphEditorPlugin : public EditorPlugin { VoxelGraphEditor *_graph_editor = nullptr; Button *_bottom_panel_button = nullptr; bool _deferred_visibility_scheduled = false; + VoxelNode *_voxel_node = nullptr; }; } // namespace zylann::voxel