Added pinning to voxel graph editor

This commit is contained in:
Marc Gilleron 2022-08-02 00:38:21 +01:00
parent cff51fbe89
commit f81e64dd14
5 changed files with 79 additions and 11 deletions

View File

@ -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

View File

@ -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<StringName> 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<GraphNode>(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

View File

@ -8,6 +8,7 @@ class GraphEdit;
class PopupMenu;
class AcceptDialog;
class UndoRedo;
class Button;
namespace zylann::voxel {
@ -23,6 +24,7 @@ public:
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 @@ public:
// 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 @@ private:
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,

View File

@ -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 <typename F>
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<VoxelGeneratorGraph> generator = _graph_editor->get_graph();
ERR_FAIL_COND(generator.is_null());
for_each_node(root, [&generator](Node *node) {
VoxelNode *vnode = Object::cast_to<VoxelNode>(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);

View File

@ -6,6 +6,7 @@
namespace zylann::voxel {
class VoxelGraphEditor;
class VoxelNode;
class VoxelGraphEditorPlugin : public EditorPlugin {
GDCLASS(VoxelGraphEditorPlugin, EditorPlugin)
@ -20,6 +21,7 @@ private:
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 @@ private:
VoxelGraphEditor *_graph_editor = nullptr;
Button *_bottom_panel_button = nullptr;
bool _deferred_visibility_scheduled = false;
VoxelNode *_voxel_node = nullptr;
};
} // namespace zylann::voxel