Use the inspector to edit graph node properties, including sub-resources
parent
33d38e61b1
commit
bf96ec362f
|
@ -5,12 +5,14 @@
|
|||
#include <scene/gui/graph_edit.h>
|
||||
#include <scene/gui/label.h>
|
||||
|
||||
const char *VoxelGraphEditor::SIGNAL_NODE_SELECTED = "node_selected";
|
||||
const char *VoxelGraphEditor::SIGNAL_NOTHING_SELECTED = "nothing_selected";
|
||||
|
||||
// Graph node with a few custom data attached.
|
||||
class VoxelGraphEditorNode : public GraphNode {
|
||||
GDCLASS(VoxelGraphEditorNode, GraphNode)
|
||||
public:
|
||||
uint32_t node_id = 0;
|
||||
std::vector<Control *> input_controls;
|
||||
std::vector<Control *> param_controls;
|
||||
};
|
||||
|
||||
VoxelGraphEditor::VoxelGraphEditor() {
|
||||
|
@ -21,6 +23,8 @@ VoxelGraphEditor::VoxelGraphEditor() {
|
|||
_graph_edit->connect("connection_request", this, "_on_graph_edit_connection_request");
|
||||
_graph_edit->connect("delete_nodes_request", this, "_on_graph_edit_delete_nodes_request");
|
||||
_graph_edit->connect("disconnection_request", this, "_on_graph_edit_disconnection_request");
|
||||
_graph_edit->connect("node_selected", this, "_on_graph_edit_node_selected");
|
||||
_graph_edit->connect("node_unselected", this, "_on_graph_edit_node_unselected");
|
||||
add_child(_graph_edit);
|
||||
|
||||
_context_menu = memnew(PopupMenu);
|
||||
|
@ -105,41 +109,9 @@ void VoxelGraphEditor::build_gui_from_graph() {
|
|||
ERR_FAIL_COND(to_node_view == nullptr);
|
||||
const Error err = _graph_edit->connect_node(from_node_name, con.src.port_index, to_node_view->get_name(), con.dst.port_index);
|
||||
ERR_FAIL_COND(err != OK);
|
||||
ERR_FAIL_COND(con.dst.port_index >= to_node_view->input_controls.size());
|
||||
Control *input_control = to_node_view->input_controls[con.dst.port_index];
|
||||
ERR_FAIL_COND(input_control == nullptr);
|
||||
input_control->hide();
|
||||
}
|
||||
}
|
||||
|
||||
Control *VoxelGraphEditor::create_param_control(uint32_t node_id, Variant value, const VoxelGraphNodeDB::Param ¶m, bool is_input) {
|
||||
Control *param_control = nullptr;
|
||||
|
||||
switch (param.type) {
|
||||
case Variant::REAL: {
|
||||
SpinBox *spinbox = memnew(SpinBox);
|
||||
spinbox->set_step(0.001);
|
||||
spinbox->set_min(-10000.0);
|
||||
spinbox->set_max(10000.0);
|
||||
spinbox->set_value(value);
|
||||
spinbox->connect("value_changed", this, "_on_node_param_spinbox_value_changed", varray(node_id, param.index, is_input));
|
||||
param_control = spinbox;
|
||||
} break;
|
||||
|
||||
case Variant::OBJECT: {
|
||||
Label *placeholder = memnew(Label);
|
||||
placeholder->set_text("<Resource>");
|
||||
param_control = placeholder;
|
||||
} break;
|
||||
|
||||
default:
|
||||
CRASH_NOW();
|
||||
break;
|
||||
}
|
||||
|
||||
return param_control;
|
||||
}
|
||||
|
||||
void VoxelGraphEditor::create_node_gui(uint32_t node_id) {
|
||||
// Build one GUI node
|
||||
|
||||
|
@ -160,9 +132,8 @@ void VoxelGraphEditor::create_node_gui(uint32_t node_id) {
|
|||
//node_view.resizable = true
|
||||
//node_view.rect_size = Vector2(200, 100)
|
||||
|
||||
const int row_count = max(node_type.inputs.size(), node_type.outputs.size()) + node_type.params.size();
|
||||
const int row_count = max(node_type.inputs.size(), node_type.outputs.size());
|
||||
const Color port_color(0.4, 0.4, 1.0);
|
||||
uint32_t param_index = 0;
|
||||
|
||||
for (int i = 0; i < row_count; ++i) {
|
||||
const bool has_left = i < node_type.inputs.size();
|
||||
|
@ -175,16 +146,6 @@ void VoxelGraphEditor::create_node_gui(uint32_t node_id) {
|
|||
Label *label = memnew(Label);
|
||||
label->set_text(node_type.inputs[i].name);
|
||||
property_control->add_child(label);
|
||||
|
||||
Variant defval = graph.get_node_default_input(node_id, i);
|
||||
VoxelGraphNodeDB::Param param("", defval.get_type());
|
||||
param.index = i;
|
||||
Control *control = create_param_control(node_id, defval, param, true);
|
||||
|
||||
control->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
property_control->add_child(control);
|
||||
CRASH_COND(i != node_view->input_controls.size());
|
||||
node_view->input_controls.push_back(control);
|
||||
}
|
||||
|
||||
if (has_right) {
|
||||
|
@ -199,24 +160,6 @@ void VoxelGraphEditor::create_node_gui(uint32_t node_id) {
|
|||
property_control->add_child(label);
|
||||
}
|
||||
|
||||
if (!has_left && !has_right) {
|
||||
const VoxelGraphNodeDB::Param ¶m = node_type.params[param_index];
|
||||
|
||||
Label *label = memnew(Label);
|
||||
label->set_text(param.name);
|
||||
property_control->add_child(label);
|
||||
|
||||
Variant param_value = graph.get_node_param(node_id, param_index);
|
||||
Control *param_control = create_param_control(node_id, param_value, param, false);
|
||||
|
||||
param_control->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
property_control->add_child(param_control);
|
||||
CRASH_COND(param_index != node_view->param_controls.size());
|
||||
node_view->param_controls.push_back(param_control);
|
||||
|
||||
++param_index;
|
||||
}
|
||||
|
||||
node_view->add_child(property_control);
|
||||
node_view->set_slot(i, has_left, Variant::REAL, port_color, has_right, Variant::REAL, port_color);
|
||||
}
|
||||
|
@ -224,84 +167,6 @@ void VoxelGraphEditor::create_node_gui(uint32_t node_id) {
|
|||
_graph_edit->add_child(node_view);
|
||||
}
|
||||
|
||||
void VoxelGraphEditor::_on_node_param_spinbox_value_changed(float value, int node_id, int param_index, bool is_input) {
|
||||
if (_updating_param_gui) {
|
||||
// When undoing, editor controls will emit "changed" signals,
|
||||
// but we don't want to treat those as actual actions
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_input) {
|
||||
_undo_redo->create_action(TTR("Set Node Default Input"));
|
||||
Variant previous_value = _graph->get_node_default_input(node_id, param_index);
|
||||
_undo_redo->add_do_method(*_graph, "set_node_default_input", node_id, param_index, value);
|
||||
_undo_redo->add_undo_method(*_graph, "set_node_default_input", node_id, param_index, previous_value);
|
||||
|
||||
} else {
|
||||
_undo_redo->create_action(TTR("Set Node Param"));
|
||||
Variant previous_value = _graph->get_node_param(node_id, param_index);
|
||||
|
||||
_undo_redo->add_do_method(*_graph, "set_node_param", node_id, param_index, value);
|
||||
|
||||
// TODO I had to complicate this because UndoRedo confuses `null` as `absence of argument`, which causes method call errors
|
||||
// See https://github.com/godotengine/godot/issues/36895
|
||||
if (previous_value.get_type() == Variant::NIL) {
|
||||
_undo_redo->add_undo_method(*_graph, "set_node_param_null", node_id, param_index);
|
||||
} else {
|
||||
_undo_redo->add_undo_method(*_graph, "set_node_param", node_id, param_index, previous_value);
|
||||
}
|
||||
}
|
||||
|
||||
_undo_redo->add_do_method(this, "update_node_param_gui", node_id, param_index, is_input);
|
||||
_undo_redo->add_undo_method(this, "update_node_param_gui", node_id, param_index, is_input);
|
||||
_undo_redo->commit_action();
|
||||
}
|
||||
|
||||
void VoxelGraphEditor::update_node_param_gui(int node_id, int param_index, bool is_default_input) {
|
||||
VoxelGraphEditorNode *node_view = Object::cast_to<VoxelGraphEditorNode>(_graph_edit->get_node(node_to_gui_name(node_id)));
|
||||
|
||||
Variant value;
|
||||
Variant::Type value_type;
|
||||
Control *param_control;
|
||||
|
||||
if (is_default_input) {
|
||||
value_type = Variant::REAL;
|
||||
value = _graph->get_node_default_input(node_id, param_index);
|
||||
CRASH_COND(param_index >= node_view->input_controls.size());
|
||||
param_control = node_view->input_controls[param_index];
|
||||
|
||||
} else {
|
||||
const uint32_t node_type_id = _graph->get_node_type_id(node_id);
|
||||
const VoxelGraphNodeDB::NodeType &node_type = VoxelGraphNodeDB::get_singleton()->get_type(node_type_id);
|
||||
CRASH_COND(param_index >= node_type.params.size());
|
||||
const VoxelGraphNodeDB::Param ¶m = node_type.params[param_index];
|
||||
value_type = param.type;
|
||||
value = _graph->get_node_param(node_id, param_index);
|
||||
CRASH_COND(param_index >= node_view->param_controls.size());
|
||||
param_control = node_view->param_controls[param_index];
|
||||
}
|
||||
|
||||
_updating_param_gui = true;
|
||||
|
||||
switch (value_type) {
|
||||
case Variant::REAL: {
|
||||
SpinBox *spinbox = Object::cast_to<SpinBox>(param_control);
|
||||
ERR_FAIL_COND(spinbox == nullptr);
|
||||
spinbox->set_value(value);
|
||||
} break;
|
||||
|
||||
case Variant::OBJECT: {
|
||||
// TODO
|
||||
} break;
|
||||
|
||||
default:
|
||||
CRASH_NOW();
|
||||
break;
|
||||
}
|
||||
|
||||
_updating_param_gui = false;
|
||||
}
|
||||
|
||||
void remove_connections_from_and_to(GraphEdit *graph_edit, StringName node_name) {
|
||||
// Get copy of connection list
|
||||
List<GraphEdit::Connection> connections;
|
||||
|
@ -329,11 +194,37 @@ void VoxelGraphEditor::remove_node_gui(StringName gui_node_name) {
|
|||
memdelete(node_view);
|
||||
}
|
||||
|
||||
// There is no API for this (and no internal function either), so like the implementation, I copy/pasted it
|
||||
// static const GraphNode *get_graph_node_under_mouse(const GraphEdit *graph_edit) {
|
||||
// for (int i = graph_edit->get_child_count() - 1; i >= 0; i--) {
|
||||
// const GraphNode *gn = Object::cast_to<GraphNode>(graph_edit->get_child(i));
|
||||
// if (gn != nullptr) {
|
||||
// Rect2 r = gn->get_rect();
|
||||
// r.size *= graph_edit->get_zoom();
|
||||
// if (r.has_point(graph_edit->get_local_mouse_position())) {
|
||||
// return gn;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return nullptr;
|
||||
// }
|
||||
|
||||
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));
|
||||
if (node != nullptr && node->is_selected()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void VoxelGraphEditor::_on_graph_edit_gui_input(Ref<InputEvent> event) {
|
||||
Ref<InputEventMouseButton> mb = event;
|
||||
|
||||
if (mb.is_valid()) {
|
||||
if (mb->is_pressed()) {
|
||||
|
||||
if (mb->get_button_index() == BUTTON_RIGHT) {
|
||||
_click_position = mb->get_position();
|
||||
_context_menu->set_position(get_global_mouse_position());
|
||||
|
@ -354,11 +245,9 @@ void VoxelGraphEditor::_on_graph_edit_connection_request(String from_node_name,
|
|||
|
||||
_undo_redo->add_do_method(*_graph, "add_connection", src_node_view->node_id, from_slot, dst_node_view->node_id, to_slot);
|
||||
_undo_redo->add_do_method(_graph_edit, "connect_node", from_node_name, from_slot, to_node_name, to_slot);
|
||||
_undo_redo->add_do_method(this, "set_input_control_visible", to_node_name, to_slot, false);
|
||||
|
||||
_undo_redo->add_undo_method(*_graph, "remove_connection", src_node_view->node_id, from_slot, dst_node_view->node_id, to_slot);
|
||||
_undo_redo->add_undo_method(_graph_edit, "disconnect_node", from_node_name, from_slot, to_node_name, to_slot);
|
||||
_undo_redo->add_undo_method(this, "set_input_control_visible", to_node_name, to_slot, true);
|
||||
|
||||
_undo_redo->commit_action();
|
||||
}
|
||||
|
@ -374,24 +263,14 @@ void VoxelGraphEditor::_on_graph_edit_disconnection_request(String from_node_nam
|
|||
|
||||
_undo_redo->add_do_method(*_graph, "remove_connection", src_node_view->node_id, from_slot, dst_node_view->node_id, to_slot);
|
||||
_undo_redo->add_do_method(_graph_edit, "disconnect_node", from_node_name, from_slot, to_node_name, to_slot);
|
||||
_undo_redo->add_do_method(this, "set_input_control_visible", to_node_name, to_slot, true);
|
||||
|
||||
_undo_redo->add_undo_method(*_graph, "add_connection", src_node_view->node_id, from_slot, dst_node_view->node_id, to_slot);
|
||||
_undo_redo->add_undo_method(_graph_edit, "connect_node", from_node_name, from_slot, to_node_name, to_slot);
|
||||
_undo_redo->add_undo_method(this, "set_input_control_visible", to_node_name, to_slot, false);
|
||||
|
||||
_undo_redo->commit_action();
|
||||
}
|
||||
|
||||
void VoxelGraphEditor::set_input_control_visible(String node_name, int input_index, bool visible) {
|
||||
VoxelGraphEditorNode *node_view = Object::cast_to<VoxelGraphEditorNode>(_graph_edit->get_node(node_name));
|
||||
ERR_FAIL_COND(node_view == nullptr);
|
||||
ERR_FAIL_INDEX(input_index, node_view->input_controls.size());
|
||||
node_view->input_controls[input_index]->set_visible(visible);
|
||||
}
|
||||
|
||||
void VoxelGraphEditor::_on_graph_edit_delete_nodes_request() {
|
||||
|
||||
std::vector<VoxelGraphEditorNode *> to_erase;
|
||||
|
||||
for (int i = 0; i < _graph_edit->get_child_count(); ++i) {
|
||||
|
@ -485,22 +364,45 @@ void VoxelGraphEditor::_on_context_menu_index_pressed(int idx) {
|
|||
_undo_redo->commit_action();
|
||||
}
|
||||
|
||||
void VoxelGraphEditor::_bind_methods() {
|
||||
void VoxelGraphEditor::_on_graph_edit_node_selected(Node *p_node) {
|
||||
VoxelGraphEditorNode *node = Object::cast_to<VoxelGraphEditorNode>(p_node);
|
||||
emit_signal(SIGNAL_NODE_SELECTED, node->node_id);
|
||||
}
|
||||
|
||||
void VoxelGraphEditor::_on_graph_edit_node_unselected(Node *p_node) {
|
||||
// Just checking if nothing is selected _now_ is unreliable, because the user could have just selected another
|
||||
// node, and I don't know when `GraphEdit` will update the `selected` flags in the current call stack.
|
||||
// GraphEdit doesn't have an API giving us enough context to guess that, so have to rely on dirty workaround.
|
||||
if (!_nothing_selected_check_scheduled) {
|
||||
_nothing_selected_check_scheduled = true;
|
||||
call_deferred("_check_nothing_selected");
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelGraphEditor::_check_nothing_selected() {
|
||||
_nothing_selected_check_scheduled = false;
|
||||
if (is_nothing_selected(_graph_edit)) {
|
||||
emit_signal(SIGNAL_NOTHING_SELECTED);
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelGraphEditor::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("_on_graph_edit_gui_input", "event"), &VoxelGraphEditor::_on_graph_edit_gui_input);
|
||||
ClassDB::bind_method(D_METHOD("_on_graph_edit_connection_request", "from_node_name", "from_slot", "to_node_name", "to_slot"),
|
||||
&VoxelGraphEditor::_on_graph_edit_connection_request);
|
||||
ClassDB::bind_method(D_METHOD("_on_graph_edit_disconnection_request", "from_node_name", "from_slot", "to_node_name", "to_slot"),
|
||||
&VoxelGraphEditor::_on_graph_edit_disconnection_request);
|
||||
ClassDB::bind_method(D_METHOD("_on_graph_edit_delete_nodes_request"), &VoxelGraphEditor::_on_graph_edit_delete_nodes_request);
|
||||
ClassDB::bind_method(D_METHOD("_on_graph_edit_node_selected"), &VoxelGraphEditor::_on_graph_edit_node_selected);
|
||||
ClassDB::bind_method(D_METHOD("_on_graph_edit_node_unselected"), &VoxelGraphEditor::_on_graph_edit_node_unselected);
|
||||
ClassDB::bind_method(D_METHOD("_on_graph_node_dragged", "from", "to", "id"), &VoxelGraphEditor::_on_graph_node_dragged);
|
||||
ClassDB::bind_method(D_METHOD("_on_context_menu_index_pressed", "idx"), &VoxelGraphEditor::_on_context_menu_index_pressed);
|
||||
ClassDB::bind_method(D_METHOD("_on_node_param_spinbox_value_changed", "value", "node_id", "param_index", "is_input"),
|
||||
&VoxelGraphEditor::_on_node_param_spinbox_value_changed);
|
||||
ClassDB::bind_method(D_METHOD("_check_nothing_selected"), &VoxelGraphEditor::_check_nothing_selected);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("create_node_gui", "node_id"), &VoxelGraphEditor::create_node_gui);
|
||||
ClassDB::bind_method(D_METHOD("remove_node_gui", "node_name"), &VoxelGraphEditor::remove_node_gui);
|
||||
ClassDB::bind_method(D_METHOD("set_node_position", "node_id", "offset"), &VoxelGraphEditor::set_node_position);
|
||||
ClassDB::bind_method(D_METHOD("update_node_param_gui", "node_id", "param_id", "is_default_input"), &VoxelGraphEditor::update_node_param_gui);
|
||||
ClassDB::bind_method(D_METHOD("set_input_control_visible", "node_name", "input_index", "visible"), &VoxelGraphEditor::set_input_control_visible);
|
||||
|
||||
ADD_SIGNAL(MethodInfo(SIGNAL_NODE_SELECTED, PropertyInfo(Variant::INT, "node_id")));
|
||||
ADD_SIGNAL(MethodInfo(SIGNAL_NOTHING_SELECTED, PropertyInfo(Variant::INT, "nothing_selected")));
|
||||
}
|
||||
|
|
|
@ -12,38 +12,42 @@ class UndoRedo;
|
|||
class VoxelGraphEditor : public Control {
|
||||
GDCLASS(VoxelGraphEditor, Control)
|
||||
public:
|
||||
static const char *SIGNAL_NODE_SELECTED;
|
||||
static const char *SIGNAL_NOTHING_SELECTED;
|
||||
|
||||
VoxelGraphEditor();
|
||||
|
||||
void set_graph(Ref<VoxelGeneratorGraph> graph);
|
||||
inline Ref<VoxelGeneratorGraph> get_graph() const { return _graph; }
|
||||
|
||||
void set_undo_redo(UndoRedo *undo_redo);
|
||||
|
||||
private:
|
||||
void clear();
|
||||
void build_gui_from_graph();
|
||||
Control *create_param_control(uint32_t node_id, Variant value, const VoxelGraphNodeDB::Param ¶m, bool is_input);
|
||||
void create_node_gui(uint32_t node_id);
|
||||
void remove_node_gui(StringName gui_node_name);
|
||||
void set_node_position(int id, Vector2 offset);
|
||||
void update_node_param_gui(int node_id, int param_index, bool is_default_input);
|
||||
void set_input_control_visible(String node_name, int input_index, bool visible);
|
||||
|
||||
void _on_graph_edit_gui_input(Ref<InputEvent> event);
|
||||
void _on_graph_edit_connection_request(String from_node_name, int from_slot, String to_node_name, int to_slot);
|
||||
void _on_graph_edit_disconnection_request(String from_node_name, int from_slot, String to_node_name, int to_slot);
|
||||
void _on_graph_edit_delete_nodes_request();
|
||||
void _on_graph_edit_node_selected(Node *p_node);
|
||||
void _on_graph_edit_node_unselected(Node *p_node);
|
||||
void _on_graph_node_dragged(Vector2 from, Vector2 to, int id);
|
||||
void _on_node_param_spinbox_value_changed(float value, int node_id, int param_index, bool is_input);
|
||||
void _on_context_menu_index_pressed(int idx);
|
||||
|
||||
void _check_nothing_selected();
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
Ref<VoxelGeneratorGraph> _graph;
|
||||
GraphEdit *_graph_edit = nullptr;
|
||||
PopupMenu *_context_menu = nullptr;
|
||||
UndoRedo *_undo_redo = nullptr;
|
||||
|
||||
Vector2 _click_position;
|
||||
bool _updating_param_gui = false;
|
||||
bool _nothing_selected_check_scheduled = false;
|
||||
};
|
||||
|
||||
#endif // VOXEL_GRAPH_EDITOR_H
|
||||
|
|
|
@ -2,11 +2,14 @@
|
|||
#include "../generators/graph/voxel_generator_graph.h"
|
||||
#include "editor/editor_scale.h"
|
||||
#include "voxel_graph_editor.h"
|
||||
#include "voxel_graph_node_inspector_wrapper.h"
|
||||
|
||||
VoxelGraphEditorPlugin::VoxelGraphEditorPlugin(EditorNode *p_node) {
|
||||
//EditorInterface *ed = get_editor_interface();
|
||||
_graph_editor = memnew(VoxelGraphEditor);
|
||||
_graph_editor->set_custom_minimum_size(Size2(0, 300) * EDSCALE);
|
||||
_graph_editor->connect(VoxelGraphEditor::SIGNAL_NODE_SELECTED, this, "_on_graph_editor_node_selected");
|
||||
_graph_editor->connect(VoxelGraphEditor::SIGNAL_NOTHING_SELECTED, this, "_on_graph_editor_nothing_selected");
|
||||
_bottom_panel_button = add_control_to_bottom_panel(_graph_editor, TTR("Voxel Graph"));
|
||||
_bottom_panel_button->hide();
|
||||
}
|
||||
|
@ -16,11 +19,24 @@ bool VoxelGraphEditorPlugin::handles(Object *p_object) const {
|
|||
return false;
|
||||
}
|
||||
VoxelGeneratorGraph *graph_ptr = Object::cast_to<VoxelGeneratorGraph>(p_object);
|
||||
return graph_ptr != nullptr;
|
||||
if (graph_ptr != nullptr) {
|
||||
return true;
|
||||
}
|
||||
VoxelGraphNodeInspectorWrapper *wrapper = Object::cast_to<VoxelGraphNodeInspectorWrapper>(p_object);
|
||||
if (wrapper != nullptr) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void VoxelGraphEditorPlugin::edit(Object *p_object) {
|
||||
VoxelGeneratorGraph *graph_ptr = Object::cast_to<VoxelGeneratorGraph>(p_object);
|
||||
if (graph_ptr == nullptr) {
|
||||
VoxelGraphNodeInspectorWrapper *wrapper = Object::cast_to<VoxelGraphNodeInspectorWrapper>(p_object);
|
||||
if (wrapper != nullptr) {
|
||||
graph_ptr = *wrapper->get_graph();
|
||||
}
|
||||
}
|
||||
Ref<VoxelGeneratorGraph> graph(graph_ptr);
|
||||
_graph_editor->set_undo_redo(&get_undo_redo()); // UndoRedo isn't available in constructor
|
||||
_graph_editor->set_graph(graph);
|
||||
|
@ -30,11 +46,57 @@ void VoxelGraphEditorPlugin::make_visible(bool visible) {
|
|||
if (visible) {
|
||||
_bottom_panel_button->show();
|
||||
make_bottom_panel_item_visible(_graph_editor);
|
||||
|
||||
} else {
|
||||
_bottom_panel_button->hide();
|
||||
edit(nullptr);
|
||||
if (_graph_editor->is_visible_in_tree()) {
|
||||
hide_bottom_panel();
|
||||
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelGraphEditorPlugin::_hide_deferred() {
|
||||
_deferred_visibility_scheduled = false;
|
||||
if (_bottom_panel_button->is_visible()) {
|
||||
// Still visible actually? Don't hide then
|
||||
return;
|
||||
}
|
||||
// The point is when the plugin's UI closed (for real, not closed and re-opened simultaneously!),
|
||||
// it should cleanup its UI to not waste RAM (as it references stuff).
|
||||
edit(nullptr);
|
||||
if (_graph_editor->is_visible_in_tree()) {
|
||||
hide_bottom_panel();
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelGraphEditorPlugin::_on_graph_editor_node_selected(uint32_t node_id) {
|
||||
Ref<VoxelGraphNodeInspectorWrapper> wrapper;
|
||||
wrapper.instance();
|
||||
wrapper->setup(_graph_editor->get_graph(), node_id);
|
||||
// Note: it's neither explicit nor documented, but the reference will stay alive due to EditorHistory::_add_object
|
||||
get_editor_interface()->inspect_object(*wrapper);
|
||||
// TODO Absurd situation here...
|
||||
// `inspect_object()` gets to a point where Godot hides ALL plugins for some reason...
|
||||
// And all this, to have the graph editor rebuilt and shown again, because it DOES also handle that resource type -_-
|
||||
// https://github.com/godotengine/godot/issues/40166
|
||||
}
|
||||
|
||||
void VoxelGraphEditorPlugin::_on_graph_editor_nothing_selected() {
|
||||
// The inspector is a glorious singleton, so when we select nodes to edit their properties, it prevents
|
||||
// from accessing the graph resource itself, to save it for example.
|
||||
// I'd like to embed properties inside the nodes themselves, but it's a bit more work,
|
||||
// so for now I make it so deselecting all nodes in the graph (like clicking in the background) selects the graph.
|
||||
Ref<VoxelGeneratorGraph> graph = _graph_editor->get_graph();
|
||||
if (graph.is_valid()) {
|
||||
get_editor_interface()->inspect_object(*graph);
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelGraphEditorPlugin::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("_on_graph_editor_node_selected", "node_id"), &VoxelGraphEditorPlugin::_on_graph_editor_node_selected);
|
||||
ClassDB::bind_method(D_METHOD("_on_graph_editor_nothing_selected"), &VoxelGraphEditorPlugin::_on_graph_editor_nothing_selected);
|
||||
ClassDB::bind_method(D_METHOD("_hide_deferred"), &VoxelGraphEditorPlugin::_hide_deferred);
|
||||
}
|
||||
|
|
|
@ -16,8 +16,15 @@ public:
|
|||
void make_visible(bool visible) override;
|
||||
|
||||
private:
|
||||
void _on_graph_editor_node_selected(uint32_t node_id);
|
||||
void _on_graph_editor_nothing_selected();
|
||||
void _hide_deferred();
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
VoxelGraphEditor *_graph_editor = nullptr;
|
||||
ToolButton *_bottom_panel_button = nullptr;
|
||||
bool _deferred_visibility_scheduled = false;
|
||||
};
|
||||
|
||||
#endif // VOXEL_GRAPH_EDITOR_PLUGIN_H
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
#include "voxel_graph_node_inspector_wrapper.h"
|
||||
#include "../generators/graph/voxel_graph_node_db.h"
|
||||
|
||||
void VoxelGraphNodeInspectorWrapper::setup(Ref<VoxelGeneratorGraph> p_graph, uint32_t p_node_id) {
|
||||
_graph = p_graph;
|
||||
_node_id = p_node_id;
|
||||
}
|
||||
|
||||
void VoxelGraphNodeInspectorWrapper::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
Ref<VoxelGeneratorGraph> graph = get_graph();
|
||||
ERR_FAIL_COND(graph.is_null());
|
||||
|
||||
if (!graph->has_node(_node_id)) {
|
||||
// Maybe got erased by the user?
|
||||
#ifdef DEBUG_ENABLED
|
||||
print_line("VoxelGeneratorGraph node was not found");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t node_type_id = graph->get_node_type_id(_node_id);
|
||||
const VoxelGraphNodeDB::NodeType &node_type = VoxelGraphNodeDB::get_singleton()->get_type(node_type_id);
|
||||
|
||||
// Params
|
||||
|
||||
p_list->push_back(PropertyInfo(Variant::NIL, "Params", PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY));
|
||||
|
||||
for (size_t i = 0; i < node_type.params.size(); ++i) {
|
||||
const VoxelGraphNodeDB::Param ¶m = node_type.params[i];
|
||||
PropertyInfo pi;
|
||||
pi.name = param.name;
|
||||
pi.type = param.type;
|
||||
pi.class_name = param.class_name;
|
||||
if (!param.class_name.empty()) {
|
||||
pi.hint = PROPERTY_HINT_RESOURCE_TYPE;
|
||||
pi.hint_string = pi.class_name;
|
||||
}
|
||||
pi.usage = PROPERTY_USAGE_EDITOR;
|
||||
p_list->push_back(pi);
|
||||
}
|
||||
|
||||
// Inputs
|
||||
|
||||
p_list->push_back(
|
||||
PropertyInfo(Variant::NIL, "Input Defaults", PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY));
|
||||
|
||||
for (size_t i = 0; i < node_type.inputs.size(); ++i) {
|
||||
const VoxelGraphNodeDB::Port &port = node_type.inputs[i];
|
||||
PropertyInfo pi;
|
||||
pi.name = port.name;
|
||||
pi.type = port.default_value.get_type();
|
||||
p_list->push_back(pi);
|
||||
}
|
||||
}
|
||||
|
||||
bool VoxelGraphNodeInspectorWrapper::_set(const StringName &p_name, const Variant &p_value) {
|
||||
Ref<VoxelGeneratorGraph> graph = get_graph();
|
||||
ERR_FAIL_COND_V(graph.is_null(), false);
|
||||
|
||||
const uint32_t node_type_id = graph->get_node_type_id(_node_id);
|
||||
|
||||
uint32_t index;
|
||||
if (VoxelGraphNodeDB::get_singleton()->try_get_param_index_from_name(node_type_id, p_name, index)) {
|
||||
graph->set_node_param(_node_id, index, p_value);
|
||||
|
||||
} else if (VoxelGraphNodeDB::get_singleton()->try_get_input_index_from_name(node_type_id, p_name, index)) {
|
||||
graph->set_node_default_input(_node_id, index, p_value);
|
||||
|
||||
} else {
|
||||
ERR_PRINT(String("Invalid param name {0}").format(varray(p_name)));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VoxelGraphNodeInspectorWrapper::_get(const StringName &p_name, Variant &r_ret) const {
|
||||
Ref<VoxelGeneratorGraph> graph = get_graph();
|
||||
ERR_FAIL_COND_V(graph.is_null(), false);
|
||||
|
||||
const uint32_t node_type_id = graph->get_node_type_id(_node_id);
|
||||
|
||||
uint32_t index;
|
||||
if (VoxelGraphNodeDB::get_singleton()->try_get_param_index_from_name(node_type_id, p_name, index)) {
|
||||
r_ret = graph->get_node_param(_node_id, index);
|
||||
|
||||
} else if (VoxelGraphNodeDB::get_singleton()->try_get_input_index_from_name(node_type_id, p_name, index)) {
|
||||
r_ret = graph->get_node_default_input(_node_id, index);
|
||||
|
||||
} else {
|
||||
ERR_PRINT(String("Invalid param name {0}").format(varray(p_name)));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
#ifndef VOXEL_GRAPH_NODE_INSPECTOR_WRAPPER_H
|
||||
#define VOXEL_GRAPH_NODE_INSPECTOR_WRAPPER_H
|
||||
|
||||
#include "../generators/graph/program_graph.h"
|
||||
#include <core/reference.h>
|
||||
|
||||
class VoxelGeneratorGraph;
|
||||
|
||||
// Nodes aren't resources so this translates them into a form the inspector can understand.
|
||||
// This makes it easier to support undo/redo and sub-resources.
|
||||
// WARNING: `AnimationPlayer` will allow to keyframe properties, but there really is no support for that.
|
||||
class VoxelGraphNodeInspectorWrapper : public Reference {
|
||||
GDCLASS(VoxelGraphNodeInspectorWrapper, Reference)
|
||||
public:
|
||||
void setup(Ref<VoxelGeneratorGraph> p_graph, uint32_t p_node_id);
|
||||
inline Ref<VoxelGeneratorGraph> get_graph() const { return _graph; }
|
||||
|
||||
protected:
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
|
||||
private:
|
||||
Ref<VoxelGeneratorGraph> _graph;
|
||||
uint32_t _node_id = ProgramGraph::NULL_ID;
|
||||
};
|
||||
|
||||
#endif // VOXEL_GRAPH_NODE_INSPECTOR_WRAPPER_H
|
|
@ -75,6 +75,10 @@ void VoxelGeneratorGraph::get_connections(std::vector<ProgramGraph::Connection>
|
|||
// _graph.get_connections_from_and_to(connections, node_id);
|
||||
//}
|
||||
|
||||
bool VoxelGeneratorGraph::has_node(uint32_t node_id) const {
|
||||
return _graph.try_get_node(node_id) != nullptr;
|
||||
}
|
||||
|
||||
void VoxelGeneratorGraph::set_node_param(uint32_t node_id, uint32_t param_index, Variant value) {
|
||||
ProgramGraph::Node *node = _graph.try_get_node(node_id);
|
||||
ERR_FAIL_COND(node == nullptr);
|
||||
|
@ -131,7 +135,6 @@ int VoxelGeneratorGraph::get_used_channels_mask() const {
|
|||
}
|
||||
|
||||
void VoxelGeneratorGraph::generate_block(VoxelBlockRequest &input) {
|
||||
|
||||
VoxelBuffer &out_buffer = **input.voxel_buffer;
|
||||
|
||||
const Vector3i bs = out_buffer.get_size();
|
||||
|
@ -649,8 +652,11 @@ void VoxelGeneratorGraph::_b_set_node_param_null(int node_id, int param_index) {
|
|||
set_node_param(node_id, param_index, Variant());
|
||||
}
|
||||
|
||||
void VoxelGeneratorGraph::_bind_methods() {
|
||||
float VoxelGeneratorGraph::_b_generate_single(Vector3 pos) {
|
||||
return generate_single(Vector3i(pos));
|
||||
}
|
||||
|
||||
void VoxelGeneratorGraph::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("clear"), &VoxelGeneratorGraph::clear);
|
||||
ClassDB::bind_method(D_METHOD("create_node", "type_id", "position", "id"), &VoxelGeneratorGraph::create_node, DEFVAL(ProgramGraph::NULL_ID));
|
||||
ClassDB::bind_method(D_METHOD("remove_node", "node_id"), &VoxelGeneratorGraph::remove_node);
|
||||
|
@ -674,6 +680,8 @@ void VoxelGeneratorGraph::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("get_node_type_count"), &VoxelGeneratorGraph::_b_get_node_type_count);
|
||||
ClassDB::bind_method(D_METHOD("get_node_type_info", "type_id"), &VoxelGeneratorGraph::_b_get_node_type_info);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("generate_single"), &VoxelGeneratorGraph::_b_generate_single);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("debug_load_waves_preset"), &VoxelGeneratorGraph::debug_load_waves_preset);
|
||||
ClassDB::bind_method(D_METHOD("debug_measure_microseconds_per_voxel"), &VoxelGeneratorGraph::debug_measure_microseconds_per_voxel);
|
||||
|
||||
|
|
|
@ -58,6 +58,8 @@ public:
|
|||
void get_connections(std::vector<ProgramGraph::Connection> &connections) const;
|
||||
//void get_connections_from_and_to(std::vector<ProgramGraph::Connection> &connections, uint32_t node_id) const;
|
||||
|
||||
bool has_node(uint32_t node_id) const;
|
||||
|
||||
Variant get_node_param(uint32_t node_id, uint32_t param_index) const;
|
||||
void set_node_param(uint32_t node_id, uint32_t param_index, Variant value);
|
||||
|
||||
|
|
Loading…
Reference in New Issue