Use the inspector to edit graph node properties, including sub-resources

master
Marc Gilleron 2020-07-06 22:07:36 +01:00
parent 33d38e61b1
commit bf96ec362f
8 changed files with 280 additions and 171 deletions

View File

@ -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 &param, 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 &param = 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 &param = 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")));
}

View File

@ -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 &param, 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

View File

@ -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);
}

View File

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

View File

@ -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 &param = 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;
}

View File

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

View File

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

View File

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