Added preview nodes (has a few limitations, may fix them later)

master
Marc Gilleron 2020-07-07 23:28:02 +01:00
parent 4ff29ce3ce
commit b8c235fce3
11 changed files with 268 additions and 18 deletions

View File

@ -1,6 +1,9 @@
#include "voxel_graph_editor.h" #include "voxel_graph_editor.h"
#include "../generators/graph/voxel_generator_graph.h" #include "../generators/graph/voxel_generator_graph.h"
#include "editor/editor_scale.h" #include "editor/editor_scale.h"
#include <core/core_string_names.h>
#include <core/os/os.h>
#include <core/undo_redo.h> #include <core/undo_redo.h>
#include <scene/gui/graph_edit.h> #include <scene/gui/graph_edit.h>
#include <scene/gui/label.h> #include <scene/gui/label.h>
@ -8,11 +11,44 @@
const char *VoxelGraphEditor::SIGNAL_NODE_SELECTED = "node_selected"; const char *VoxelGraphEditor::SIGNAL_NODE_SELECTED = "node_selected";
const char *VoxelGraphEditor::SIGNAL_NOTHING_SELECTED = "nothing_selected"; const char *VoxelGraphEditor::SIGNAL_NOTHING_SELECTED = "nothing_selected";
// Shows a 2D slice of the 3D set of values coming from an output port
class VoxelGraphEditorNodePreview : public VBoxContainer {
GDCLASS(VoxelGraphEditorNodePreview, VBoxContainer)
public:
static const int RESOLUTION = 64;
VoxelGraphEditorNodePreview() {
_image.instance();
_image->create(RESOLUTION, RESOLUTION, false, Image::FORMAT_L8);
_texture.instance();
update_texture();
_texture_rect = memnew(TextureRect);
_texture_rect->set_stretch_mode(TextureRect::STRETCH_SCALE);
_texture_rect->set_custom_minimum_size(Vector2(RESOLUTION, RESOLUTION));
_texture_rect->set_texture(_texture);
add_child(_texture_rect);
}
Ref<Image> get_image() const {
return _image;
}
void update_texture() {
_texture->create_from_image(_image, 0);
}
private:
TextureRect *_texture_rect = nullptr;
Ref<ImageTexture> _texture;
Ref<Image> _image;
};
// Graph node with a few custom data attached. // Graph node with a few custom data attached.
class VoxelGraphEditorNode : public GraphNode { class VoxelGraphEditorNode : public GraphNode {
GDCLASS(VoxelGraphEditorNode, GraphNode) GDCLASS(VoxelGraphEditorNode, GraphNode)
public: public:
uint32_t node_id = 0; uint32_t node_id = 0;
VoxelGraphEditorNodePreview *preview = nullptr;
}; };
VoxelGraphEditor::VoxelGraphEditor() { VoxelGraphEditor::VoxelGraphEditor() {
@ -43,13 +79,15 @@ void VoxelGraphEditor::set_graph(Ref<VoxelGeneratorGraph> graph) {
return; return;
} }
// if (_graph.is_valid()) { if (_graph.is_valid()) {
// } _graph->disconnect(CoreStringNames::get_singleton()->changed, this, "_on_graph_changed");
}
_graph = graph; _graph = graph;
// if (_graph.is_valid()) { if (_graph.is_valid()) {
// } _graph->connect(CoreStringNames::get_singleton()->changed, this, "_on_graph_changed");
}
build_gui_from_graph(); build_gui_from_graph();
} }
@ -58,6 +96,27 @@ void VoxelGraphEditor::set_undo_redo(UndoRedo *undo_redo) {
_undo_redo = undo_redo; _undo_redo = undo_redo;
} }
void VoxelGraphEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_INTERNAL_PROCESS:
_process(get_tree()->get_idle_process_time());
break;
case NOTIFICATION_VISIBILITY_CHANGED:
set_process_internal(is_visible());
break;
}
}
void VoxelGraphEditor::_process(float delta) {
if (_time_before_preview_update > 0.f) {
_time_before_preview_update -= delta;
if (_time_before_preview_update < 0.f) {
update_previews();
}
}
}
void VoxelGraphEditor::clear() { void VoxelGraphEditor::clear() {
_graph_edit->clear_connections(); _graph_edit->clear_connections();
for (int i = 0; i < _graph_edit->get_child_count(); ++i) { for (int i = 0; i < _graph_edit->get_child_count(); ++i) {
@ -164,6 +223,11 @@ void VoxelGraphEditor::create_node_gui(uint32_t node_id) {
node_view->set_slot(i, has_left, Variant::REAL, port_color, has_right, Variant::REAL, port_color); node_view->set_slot(i, has_left, Variant::REAL, port_color, has_right, Variant::REAL, port_color);
} }
if (node_type_id == VoxelGeneratorGraph::NODE_SDF_PREVIEW) {
node_view->preview = memnew(VoxelGraphEditorNodePreview);
node_view->add_child(node_view->preview);
}
_graph_edit->add_child(node_view); _graph_edit->add_child(node_view);
} }
@ -386,6 +450,89 @@ void VoxelGraphEditor::_check_nothing_selected() {
} }
} }
void VoxelGraphEditor::update_previews() {
if (_graph.is_null()) {
return;
}
uint64_t time_before = OS::get_singleton()->get_ticks_usec();
_graph->compile();
// TODO Use a thread?
print_line("Updating previews");
struct PreviewInfo {
VoxelGraphEditorNodePreview *control;
uint16_t address;
float min_value;
float value_scale;
};
std::vector<PreviewInfo> previews;
const VoxelGraphRuntime &runtime = _graph->get_runtime();
for (int i = 0; i < _graph_edit->get_child_count(); ++i) {
VoxelGraphEditorNode *node = Object::cast_to<VoxelGraphEditorNode>(_graph_edit->get_child(i));
if (node == nullptr || node->preview == nullptr) {
continue;
}
ProgramGraph::PortLocation dst;
dst.node_id = node->node_id;
dst.port_index = 0;
ProgramGraph::PortLocation src;
if (!_graph->try_get_connection_to(dst, src)) {
// Not connected?
continue;
}
PreviewInfo info;
info.control = node->preview;
info.address = runtime.get_output_port_address(src);
info.min_value = _graph->get_node_param(dst.node_id, 0);
const float max_value = _graph->get_node_param(dst.node_id, 1);
info.value_scale = 1.f / (max_value - info.min_value);
previews.push_back(info);
}
for (size_t i = 0; i < previews.size(); ++i) {
previews[i].control->get_image()->lock();
}
for (int iy = 0; iy < VoxelGraphEditorNodePreview::RESOLUTION; ++iy) {
for (int ix = 0; ix < VoxelGraphEditorNodePreview::RESOLUTION; ++ix) {
{
const int x = ix - VoxelGraphEditorNodePreview::RESOLUTION / 2;
const int y = (VoxelGraphEditorNodePreview::RESOLUTION - iy) - VoxelGraphEditorNodePreview::RESOLUTION / 2;
_graph->generate_single(Vector3i(x, y, 0));
}
for (size_t i = 0; i < previews.size(); ++i) {
PreviewInfo &info = previews[i];
const float v = runtime.get_memory_value(info.address);
const float g = clamp((v - info.min_value) * info.value_scale, 0.f, 1.f);
Color c(g, g, g);
info.control->get_image()->set_pixel(ix, iy, Color(g, g, g));
}
}
}
for (size_t i = 0; i < previews.size(); ++i) {
previews[i].control->get_image()->unlock();
previews[i].control->update_texture();
}
uint64_t time_taken = OS::get_singleton()->get_ticks_usec() - time_before;
print_line(String("Previews generated in {0} us").format(varray(time_taken)));
}
void VoxelGraphEditor::schedule_preview_update() {
_time_before_preview_update = 0.5f;
}
void VoxelGraphEditor::_on_graph_changed() {
schedule_preview_update();
}
void VoxelGraphEditor::_bind_methods() { 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_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"), ClassDB::bind_method(D_METHOD("_on_graph_edit_connection_request", "from_node_name", "from_slot", "to_node_name", "to_slot"),
@ -397,6 +544,8 @@ void VoxelGraphEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_on_graph_edit_node_unselected"), &VoxelGraphEditor::_on_graph_edit_node_unselected); 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_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_context_menu_index_pressed", "idx"), &VoxelGraphEditor::_on_context_menu_index_pressed);
ClassDB::bind_method(D_METHOD("_on_graph_changed"), &VoxelGraphEditor::_on_graph_changed);
ClassDB::bind_method(D_METHOD("_check_nothing_selected"), &VoxelGraphEditor::_check_nothing_selected); 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("create_node_gui", "node_id"), &VoxelGraphEditor::create_node_gui);

View File

@ -23,12 +23,18 @@ public:
void set_undo_redo(UndoRedo *undo_redo); void set_undo_redo(UndoRedo *undo_redo);
private: private:
void _notification(int p_what);
void _process(float delta);
void clear(); void clear();
void build_gui_from_graph(); void build_gui_from_graph();
void create_node_gui(uint32_t node_id); void create_node_gui(uint32_t node_id);
void remove_node_gui(StringName gui_node_name); void remove_node_gui(StringName gui_node_name);
void set_node_position(int id, Vector2 offset); void set_node_position(int id, Vector2 offset);
void schedule_preview_update();
void update_previews();
void _on_graph_edit_gui_input(Ref<InputEvent> event); 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_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_disconnection_request(String from_node_name, int from_slot, String to_node_name, int to_slot);
@ -37,6 +43,7 @@ private:
void _on_graph_edit_node_unselected(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_graph_node_dragged(Vector2 from, Vector2 to, int id);
void _on_context_menu_index_pressed(int idx); void _on_context_menu_index_pressed(int idx);
void _on_graph_changed();
void _check_nothing_selected(); void _check_nothing_selected();
@ -48,6 +55,7 @@ private:
UndoRedo *_undo_redo = nullptr; UndoRedo *_undo_redo = nullptr;
Vector2 _click_position; Vector2 _click_position;
bool _nothing_selected_check_scheduled = false; bool _nothing_selected_check_scheduled = false;
float _time_before_preview_update = 0.f;
}; };
#endif // VOXEL_GRAPH_EDITOR_H #endif // VOXEL_GRAPH_EDITOR_H

View File

@ -116,6 +116,7 @@ bool ProgramGraph::can_connect(PortLocation src, PortLocation dst) const {
return false; return false;
} }
const Node *dst_node = get_node(dst.node_id); const Node *dst_node = get_node(dst.node_id);
// There can be only one connection from a source to a destination
return dst_node->inputs[dst.port_index].connections.size() == 0; return dst_node->inputs[dst.port_index].connections.size() == 0;
} }

View File

@ -2,6 +2,8 @@
#include "../../util/profiling_clock.h" #include "../../util/profiling_clock.h"
#include "voxel_graph_node_db.h" #include "voxel_graph_node_db.h"
#include <core/core_string_names.h>
VoxelGeneratorGraph::VoxelGeneratorGraph() { VoxelGeneratorGraph::VoxelGeneratorGraph() {
clear(); clear();
clear_bounds(); clear_bounds();
@ -47,6 +49,7 @@ ProgramGraph::Node *VoxelGeneratorGraph::create_node_internal(NodeTypeID type_id
void VoxelGeneratorGraph::remove_node(uint32_t node_id) { void VoxelGeneratorGraph::remove_node(uint32_t node_id) {
_graph.remove_node(node_id); _graph.remove_node(node_id);
emit_changed();
} }
bool VoxelGeneratorGraph::can_connect(uint32_t src_node_id, uint32_t src_port_index, uint32_t dst_node_id, uint32_t dst_port_index) const { bool VoxelGeneratorGraph::can_connect(uint32_t src_node_id, uint32_t src_port_index, uint32_t dst_node_id, uint32_t dst_port_index) const {
@ -59,12 +62,14 @@ void VoxelGeneratorGraph::add_connection(uint32_t src_node_id, uint32_t src_port
_graph.connect( _graph.connect(
ProgramGraph::PortLocation{ src_node_id, src_port_index }, ProgramGraph::PortLocation{ src_node_id, src_port_index },
ProgramGraph::PortLocation{ dst_node_id, dst_port_index }); ProgramGraph::PortLocation{ dst_node_id, dst_port_index });
emit_changed();
} }
void VoxelGeneratorGraph::remove_connection(uint32_t src_node_id, uint32_t src_port_index, uint32_t dst_node_id, uint32_t dst_port_index) { void VoxelGeneratorGraph::remove_connection(uint32_t src_node_id, uint32_t src_port_index, uint32_t dst_node_id, uint32_t dst_port_index) {
_graph.disconnect( _graph.disconnect(
ProgramGraph::PortLocation{ src_node_id, src_port_index }, ProgramGraph::PortLocation{ src_node_id, src_port_index },
ProgramGraph::PortLocation{ dst_node_id, dst_port_index }); ProgramGraph::PortLocation{ dst_node_id, dst_port_index });
emit_changed();
} }
void VoxelGeneratorGraph::get_connections(std::vector<ProgramGraph::Connection> &connections) const { void VoxelGeneratorGraph::get_connections(std::vector<ProgramGraph::Connection> &connections) const {
@ -75,6 +80,18 @@ void VoxelGeneratorGraph::get_connections(std::vector<ProgramGraph::Connection>
// _graph.get_connections_from_and_to(connections, node_id); // _graph.get_connections_from_and_to(connections, node_id);
//} //}
bool VoxelGeneratorGraph::try_get_connection_to(ProgramGraph::PortLocation dst, ProgramGraph::PortLocation &out_src) const {
const ProgramGraph::Node *node = _graph.get_node(dst.node_id);
CRASH_COND(node == nullptr);
CRASH_COND(dst.port_index >= node->inputs.size());
const ProgramGraph::Port &port = node->inputs[dst.port_index];
if (port.connections.size() == 0) {
return false;
}
out_src = port.connections[0];
return true;
}
bool VoxelGeneratorGraph::has_node(uint32_t node_id) const { bool VoxelGeneratorGraph::has_node(uint32_t node_id) const {
return _graph.try_get_node(node_id) != nullptr; return _graph.try_get_node(node_id) != nullptr;
} }
@ -83,7 +100,24 @@ void VoxelGeneratorGraph::set_node_param(uint32_t node_id, uint32_t param_index,
ProgramGraph::Node *node = _graph.try_get_node(node_id); ProgramGraph::Node *node = _graph.try_get_node(node_id);
ERR_FAIL_COND(node == nullptr); ERR_FAIL_COND(node == nullptr);
ERR_FAIL_INDEX(param_index, node->params.size()); ERR_FAIL_INDEX(param_index, node->params.size());
node->params[param_index] = value;
// TODO Changing sub-resources won't trigger a change signal
// It's actually very annoying to setup and keep correct. Needs to be done cautiously.
// Ref<Resource> res = node->params[param_index];
// if (res.is_valid()) {
// res->disconnect(CoreStringNames::get_singleton()->changed, this, "_on_subresource_changed");
// }
if (node->params[param_index] != value) {
node->params[param_index] = value;
emit_changed();
}
// res = value;
// if (res.is_valid()) {
// res->connect(CoreStringNames::get_singleton()->changed, this, "_on_subresource_changed");
// }
} }
Variant VoxelGeneratorGraph::get_node_param(uint32_t node_id, uint32_t param_index) const { Variant VoxelGeneratorGraph::get_node_param(uint32_t node_id, uint32_t param_index) const {
@ -104,7 +138,10 @@ void VoxelGeneratorGraph::set_node_default_input(uint32_t node_id, uint32_t inpu
ProgramGraph::Node *node = _graph.try_get_node(node_id); ProgramGraph::Node *node = _graph.try_get_node(node_id);
ERR_FAIL_COND(node == nullptr); ERR_FAIL_COND(node == nullptr);
ERR_FAIL_INDEX(input_index, node->default_inputs.size()); ERR_FAIL_INDEX(input_index, node->default_inputs.size());
node->default_inputs[input_index] = value; if (node->default_inputs[input_index] != value) {
node->default_inputs[input_index] = value;
emit_changed();
}
} }
Vector2 VoxelGeneratorGraph::get_node_gui_position(uint32_t node_id) const { Vector2 VoxelGeneratorGraph::get_node_gui_position(uint32_t node_id) const {
@ -116,7 +153,11 @@ Vector2 VoxelGeneratorGraph::get_node_gui_position(uint32_t node_id) const {
void VoxelGeneratorGraph::set_node_gui_position(uint32_t node_id, Vector2 pos) { void VoxelGeneratorGraph::set_node_gui_position(uint32_t node_id, Vector2 pos) {
ProgramGraph::Node *node = _graph.try_get_node(node_id); ProgramGraph::Node *node = _graph.try_get_node(node_id);
ERR_FAIL_COND(node == nullptr); ERR_FAIL_COND(node == nullptr);
node->gui_position = pos; if (node->gui_position != pos) {
node->gui_position = pos;
// Moving nodes around doesn't functionally change the graph
//emit_changed();
}
} }
VoxelGeneratorGraph::NodeTypeID VoxelGeneratorGraph::get_node_type_id(uint32_t node_id) const { VoxelGeneratorGraph::NodeTypeID VoxelGeneratorGraph::get_node_type_id(uint32_t node_id) const {
@ -658,6 +699,10 @@ float VoxelGeneratorGraph::_b_generate_single(Vector3 pos) {
return generate_single(Vector3i(pos)); return generate_single(Vector3i(pos));
} }
// void VoxelGeneratorGraph::_on_subresource_changed() {
// emit_changed();
// }
void VoxelGeneratorGraph::_bind_methods() { void VoxelGeneratorGraph::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear"), &VoxelGeneratorGraph::clear); 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("create_node", "type_id", "position", "id"), &VoxelGeneratorGraph::create_node, DEFVAL(ProgramGraph::NULL_ID));
@ -690,6 +735,8 @@ void VoxelGeneratorGraph::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_graph_data", "data"), &VoxelGeneratorGraph::load_graph_from_variant_data); ClassDB::bind_method(D_METHOD("_set_graph_data", "data"), &VoxelGeneratorGraph::load_graph_from_variant_data);
ClassDB::bind_method(D_METHOD("_get_graph_data"), &VoxelGeneratorGraph::get_graph_as_variant_data); ClassDB::bind_method(D_METHOD("_get_graph_data"), &VoxelGeneratorGraph::get_graph_as_variant_data);
// ClassDB::bind_method(D_METHOD("_on_subresource_changed"), &VoxelGeneratorGraph::_on_subresource_changed);
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "graph_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "graph_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL),
"_set_graph_data", "_get_graph_data"); "_set_graph_data", "_get_graph_data");
@ -725,5 +772,6 @@ void VoxelGeneratorGraph::_bind_methods() {
BIND_ENUM_CONSTANT(NODE_SDF_BOX); BIND_ENUM_CONSTANT(NODE_SDF_BOX);
BIND_ENUM_CONSTANT(NODE_SDF_SPHERE); BIND_ENUM_CONSTANT(NODE_SDF_SPHERE);
BIND_ENUM_CONSTANT(NODE_SDF_TORUS); BIND_ENUM_CONSTANT(NODE_SDF_TORUS);
BIND_ENUM_CONSTANT(NODE_SDF_PREVIEW);
BIND_ENUM_CONSTANT(NODE_TYPE_COUNT); BIND_ENUM_CONSTANT(NODE_TYPE_COUNT);
} }

View File

@ -41,6 +41,7 @@ public:
NODE_SDF_BOX, NODE_SDF_BOX,
NODE_SDF_SPHERE, NODE_SDF_SPHERE,
NODE_SDF_TORUS, NODE_SDF_TORUS,
NODE_SDF_PREVIEW, // For debugging
NODE_TYPE_COUNT NODE_TYPE_COUNT
}; };
@ -57,6 +58,7 @@ public:
void remove_connection(uint32_t src_node_id, uint32_t src_port_index, uint32_t dst_node_id, uint32_t dst_port_index); void remove_connection(uint32_t src_node_id, uint32_t src_port_index, uint32_t dst_node_id, uint32_t dst_port_index);
void get_connections(std::vector<ProgramGraph::Connection> &connections) const; 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; //void get_connections_from_and_to(std::vector<ProgramGraph::Connection> &connections, uint32_t node_id) const;
bool try_get_connection_to(ProgramGraph::PortLocation dst, ProgramGraph::PortLocation &out_src) const;
bool has_node(uint32_t node_id) const; bool has_node(uint32_t node_id) const;
@ -91,13 +93,17 @@ public:
Ref<Resource> duplicate(bool p_subresources) const override; Ref<Resource> duplicate(bool p_subresources) const override;
// Internal
const VoxelGraphRuntime &get_runtime() const { return _runtime; }
void compile();
// Debug // Debug
float debug_measure_microseconds_per_voxel(); float debug_measure_microseconds_per_voxel();
void debug_load_waves_preset(); void debug_load_waves_preset();
private: private:
void compile();
Interval analyze_range(Vector3i min_pos, Vector3i max_pos); Interval analyze_range(Vector3i min_pos, Vector3i max_pos);
ProgramGraph::Node *create_node_internal(NodeTypeID type_id, Vector2 position, uint32_t id); ProgramGraph::Node *create_node_internal(NodeTypeID type_id, Vector2 position, uint32_t id);
@ -118,6 +124,9 @@ private:
void _b_set_node_param_null(int node_id, int param_index); void _b_set_node_param_null(int node_id, int param_index);
float _b_generate_single(Vector3 pos); float _b_generate_single(Vector3 pos);
void _on_subresource_changed();
void connect_to_subresource_changes();
static void _bind_methods(); static void _bind_methods();
struct Bounds { struct Bounds {

View File

@ -258,6 +258,14 @@ VoxelGraphNodeDB::VoxelGraphNodeDB() {
t.inputs.push_back(Port("radius2", 4.f)); t.inputs.push_back(Port("radius2", 4.f));
t.outputs.push_back(Port("sdf")); t.outputs.push_back(Port("sdf"));
} }
{
NodeType &t = types[VoxelGeneratorGraph::NODE_SDF_PREVIEW];
t.name = "SdfPreview";
t.inputs.push_back(Port("value"));
t.params.push_back(Param("min_value", Variant::REAL, -1.f));
t.params.push_back(Param("max_value", Variant::REAL, 1.f));
t.debug_only = true;
}
for (unsigned int i = 0; i < _types.size(); ++i) { for (unsigned int i = 0; i < _types.size(); ++i) {
NodeType &t = _types[i]; NodeType &t = _types[i];

View File

@ -40,6 +40,8 @@ public:
struct NodeType { struct NodeType {
String name; String name;
bool debug_only = false;
// TODO Category
std::vector<Port> inputs; std::vector<Port> inputs;
std::vector<Port> outputs; std::vector<Port> outputs;
std::vector<Param> params; std::vector<Param> params;

View File

@ -181,15 +181,23 @@ void VoxelGraphRuntime::clear() {
_xzy_program_start = 0; _xzy_program_start = 0;
_last_x = INT_MAX; _last_x = INT_MAX;
_last_z = INT_MAX; _last_z = INT_MAX;
_output_port_addresses.clear();
} }
void VoxelGraphRuntime::compile(const ProgramGraph &graph) { void VoxelGraphRuntime::compile(const ProgramGraph &graph) {
_output_port_addresses.clear();
std::vector<uint32_t> order; std::vector<uint32_t> order;
std::vector<uint32_t> terminal_nodes; std::vector<uint32_t> terminal_nodes;
graph.find_terminal_nodes(terminal_nodes); graph.find_terminal_nodes(terminal_nodes);
// For now only 1 end is supported
ERR_FAIL_COND(terminal_nodes.size() != 1); // Exclude debug nodes
unordered_remove_if(terminal_nodes, [&graph](uint32_t node_id) {
const ProgramGraph::Node *node = graph.get_node(node_id);
const VoxelGraphNodeDB::NodeType &type = VoxelGraphNodeDB::get_singleton()->get_type(node->type_id);
return type.debug_only;
});
graph.find_dependencies(terminal_nodes.back(), order); graph.find_dependencies(terminal_nodes.back(), order);
@ -271,7 +279,6 @@ void VoxelGraphRuntime::compile(const ProgramGraph &graph) {
std::vector<uint8_t> &program = _program; std::vector<uint8_t> &program = _program;
const VoxelGraphNodeDB &type_db = *VoxelGraphNodeDB::get_singleton(); const VoxelGraphNodeDB &type_db = *VoxelGraphNodeDB::get_singleton();
HashMap<ProgramGraph::PortLocation, uint16_t, ProgramGraph::PortLocationHasher> output_port_addresses;
bool has_output = false; bool has_output = false;
// Run through each node in order, and turn them into program instructions // Run through each node in order, and turn them into program instructions
@ -294,19 +301,19 @@ void VoxelGraphRuntime::compile(const ProgramGraph &graph) {
CRASH_COND(type.params.size() != 1); CRASH_COND(type.params.size() != 1);
uint16_t a = _memory.size(); uint16_t a = _memory.size();
_memory.push_back(node->params[0].operator float()); _memory.push_back(node->params[0].operator float());
output_port_addresses[ProgramGraph::PortLocation{ node_id, 0 }] = a; _output_port_addresses[ProgramGraph::PortLocation{ node_id, 0 }] = a;
} break; } break;
case VoxelGeneratorGraph::NODE_INPUT_X: case VoxelGeneratorGraph::NODE_INPUT_X:
output_port_addresses[ProgramGraph::PortLocation{ node_id, 0 }] = 0; _output_port_addresses[ProgramGraph::PortLocation{ node_id, 0 }] = 0;
break; break;
case VoxelGeneratorGraph::NODE_INPUT_Y: case VoxelGeneratorGraph::NODE_INPUT_Y:
output_port_addresses[ProgramGraph::PortLocation{ node_id, 0 }] = 1; _output_port_addresses[ProgramGraph::PortLocation{ node_id, 0 }] = 1;
break; break;
case VoxelGeneratorGraph::NODE_INPUT_Z: case VoxelGeneratorGraph::NODE_INPUT_Z:
output_port_addresses[ProgramGraph::PortLocation{ node_id, 0 }] = 2; _output_port_addresses[ProgramGraph::PortLocation{ node_id, 0 }] = 2;
break; break;
case VoxelGeneratorGraph::NODE_OUTPUT_SDF: case VoxelGeneratorGraph::NODE_OUTPUT_SDF:
@ -337,7 +344,7 @@ void VoxelGraphRuntime::compile(const ProgramGraph &graph) {
} else { } else {
ProgramGraph::PortLocation src_port = node->inputs[j].connections[0]; ProgramGraph::PortLocation src_port = node->inputs[j].connections[0];
const uint16_t *aptr = output_port_addresses.getptr(src_port); const uint16_t *aptr = _output_port_addresses.getptr(src_port);
// Previous node ports must have been registered // Previous node ports must have been registered
CRASH_COND(aptr == nullptr); CRASH_COND(aptr == nullptr);
a = *aptr; a = *aptr;
@ -353,7 +360,7 @@ void VoxelGraphRuntime::compile(const ProgramGraph &graph) {
// This will be used by next nodes // This will be used by next nodes
const ProgramGraph::PortLocation op{ node_id, static_cast<uint32_t>(j) }; const ProgramGraph::PortLocation op{ node_id, static_cast<uint32_t>(j) };
output_port_addresses[op] = a; _output_port_addresses[op] = a;
append(program, a); append(program, a);
} }
@ -980,3 +987,14 @@ Interval VoxelGraphRuntime::analyze_range(Vector3i min_pos, Vector3i max_pos) {
return Interval(min_memory[min_memory.size() - 1], max_memory[max_memory.size() - 1]); return Interval(min_memory[min_memory.size() - 1], max_memory[max_memory.size() - 1]);
} }
uint16_t VoxelGraphRuntime::get_output_port_address(ProgramGraph::PortLocation port) const {
const uint16_t *aptr = _output_port_addresses.getptr(port);
ERR_FAIL_COND_V(aptr == nullptr, 0);
return *aptr;
}
float VoxelGraphRuntime::get_memory_value(uint16_t address) const {
CRASH_COND(address >= _memory.size());
return _memory[address];
}

View File

@ -15,12 +15,18 @@ public:
float generate_single(const Vector3i &position); float generate_single(const Vector3i &position);
Interval analyze_range(Vector3i min_pos, Vector3i max_pos); Interval analyze_range(Vector3i min_pos, Vector3i max_pos);
// Debugging
uint16_t get_output_port_address(ProgramGraph::PortLocation port) const;
float get_memory_value(uint16_t address) const;
private: private:
std::vector<uint8_t> _program; std::vector<uint8_t> _program;
std::vector<float> _memory; std::vector<float> _memory;
uint32_t _xzy_program_start; uint32_t _xzy_program_start;
int _last_x; int _last_x;
int _last_z; int _last_z;
HashMap<ProgramGraph::PortLocation, uint16_t, ProgramGraph::PortLocationHasher> _output_port_addresses;
}; };
#endif // VOXEL_GRAPH_RUNTIME_H #endif // VOXEL_GRAPH_RUNTIME_H

View File

@ -5,7 +5,6 @@
// For interval arithmetic // For interval arithmetic
struct Interval { struct Interval {
// Both inclusive // Both inclusive
float min; float min;
float max; float max;

View File

@ -50,6 +50,8 @@ inline void unordered_remove_if(std::vector<T> &vec, F predicate) {
if (predicate(vec[i])) { if (predicate(vec[i])) {
vec[i] = vec.back(); vec[i] = vec.back();
vec.pop_back(); vec.pop_back();
// Note: can underflow, but it should be fine since it's incremented right after.
// TODO Use a while()?
--i; --i;
} }
} }