diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index b108836a6..27b20655c 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -37,6 +37,8 @@ #include #include "VisualShader.pb.h" +#include "ResourceTransformations/VisualShader/visual_shader_nodes.h" +#include "ResourceTransformations/VisualShader/vs_noise_nodes.h" using QtNodes::GraphicsView; using QtNodes::NodeRole; @@ -46,6 +48,7 @@ using QtNodes::NodeRole; /*************************************/ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : BaseEditor(model, parent), + visual_shader(nullptr), layout(nullptr), scene_layer_layout(nullptr), scene_layer(nullptr), @@ -56,7 +59,10 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : B menu_bar(nullptr), create_node_button(nullptr), preview_shader_button(nullptr), + create_node_action(nullptr), create_node_dialog(nullptr) { + visual_shader = new VisualShader(); + // Create the main layout. layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom @@ -80,17 +86,6 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : B graph = new VisualShaderGraph(); - // Initialize and connect two nodes. - { - NodeId id1 = graph->addNode(); - graph->setNodeData(id1, NodeRole::Position, QPointF(0, 0)); - - NodeId id2 = graph->addNode(); - graph->setNodeData(id2, NodeRole::Position, QPointF(300, 300)); - - graph->addConnection(ConnectionId{id1, 0, id2, 0}); - } - scene = new BasicGraphicsScene(*graph); scene->setOrientation(Qt::Horizontal); @@ -100,6 +95,12 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : B scene_layer_layout->addWidget(view); + // Setup context menu for creating new nodes. + view->setContextMenuPolicy(Qt::ActionsContextMenu); + create_node_action = new QAction(QStringLiteral("Create Node"), view); + QObject::connect(create_node_action, &QAction::triggered, this, &VisualShaderEditor::on_create_node_action_triggered); + view->insertAction(view->actions().front(), create_node_action); + // Set the scene layer layout. scene_layer->setLayout(scene_layer_layout); @@ -115,12 +116,14 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : B menu_bar->setAlignment(Qt::AlignTop | Qt::AlignLeft); menu_bar->setSizeConstraint(QLayout::SetMinimumSize); - // Create the add node button. - create_node_button = new QPushButton("Add Node", top_layer); + // Create the create node button. + create_node_button = new QPushButton("Create Node", top_layer); create_node_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); create_node_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom menu_bar->addWidget(create_node_button); - QObject::connect(create_node_button, &QPushButton::clicked, this, &VisualShaderEditor::show_create_node_dialog); + QObject::connect(create_node_button, &QPushButton::pressed, this, &VisualShaderEditor::on_create_node_button_pressed); + + this->connect(this, &VisualShaderEditor::on_create_node_dialog_requested, this, &VisualShaderEditor::show_create_node_dialog); // Create the preview shader button. preview_shader_button = new QPushButton("Preview Shader", top_layer); @@ -154,7 +157,7 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : B int i{0}; - while (items[i].return_type != VisualShaderNode::PortType::PORT_TYPE_ENUM_SIZE) { + while (!items[i].type.empty()) { const CreateNodeDialogNodesTreeItem& item {items[i]}; // Parse the category string into a vector of strings @@ -176,12 +179,15 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : B // Now add the item to its corresponding parent category QTreeWidgetItem *node_item = new QTreeWidgetItem(parent); node_item->setText(0, QString::fromStdString(item.name)); + node_item->setData(0, Qt::UserRole, QString::fromStdString(item.type)); + node_item->setData(0, Qt::UserRole + 1, QString::fromStdString(item.description)); i++; } //////////////// Start of Footer //////////////// + graph->register_visual_shader(visual_shader); graph->set_visual_shader_editor(this); this->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom @@ -195,6 +201,7 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : B VisualShaderEditor::~VisualShaderEditor() { // TODO: We don't need to delete the pointers as they are destroyed when the parent is destroyed. if (create_node_dialog) delete create_node_dialog; + if (create_node_action) delete create_node_action; if (preview_shader_button) delete preview_shader_button; if (create_node_button) delete create_node_button; if (menu_bar) delete menu_bar; @@ -205,30 +212,145 @@ VisualShaderEditor::~VisualShaderEditor() { if (scene_layer) delete scene_layer; if (scene_layer_layout) delete scene_layer_layout; if (layout) delete layout; + if (visual_shader) delete visual_shader; } const VisualShaderEditor::CreateNodeDialogNodesTreeItem VisualShaderEditor::create_node_dialog_nodes_tree_items[] = { - {"Color", "Input/All", "VisualShaderNodeInput", "Color", { "color" }, VisualShaderNode::PortType::PORT_TYPE_VECTOR_4D}, - {"Time", "Input/All", "VisualShaderNodeInput", "Time", { "time" }, VisualShaderNode::PortType::PORT_TYPE_SCALAR}, - {"UV", "Input/All", "VisualShaderNodeInput", "UV", { "uv" }, VisualShaderNode::PortType::PORT_TYPE_VECTOR_2D}, - {"", "", "", "", {}, VisualShaderNode::PortType::PORT_TYPE_ENUM_SIZE}, // End of list. + // Input + + {"Input", "Input/Basic", "VisualShaderNodeInput", "Input parameter."}, + + {"ColorConstant", "Input/Basic", "VisualShaderNodeColorConstant", "Color constant."}, + {"BooleanConstant", "Input/Basic", "VisualShaderNodeBooleanConstant", "Boolean constant."}, + {"FloatConstant", "Input/Basic", "VisualShaderNodeFloatConstant", "Scalar floating-point constant."}, + {"IntConstant", "Input/Basic", "VisualShaderNodeIntConstant", "Scalar integer constant."}, + {"UIntConstant", "Input/Basic", "VisualShaderNodeUIntConstant", "Scalar unsigned integer constant."}, + {"Vector2Constant", "Input/Basic", "VisualShaderNodeVec2Constant", "2D vector constant."}, + {"Vector3Constant", "Input/Basic", "VisualShaderNodeVec3Constant", "3D vector constant."}, + {"Vector4Constant", "Input/Basic", "VisualShaderNodeVec4Constant", "4D vector constant."}, + + // Functions + + {"FloatFunc", "Functions/Scalar", "VisualShaderNodeFloatFunc", "Float function."}, + {"IntFunc", "Functions/Scalar", "VisualShaderNodeIntFunc", "Integer function."}, + {"UIntFunc", "Functions/Scalar", "VisualShaderNodeUIntFunc", "Unsigned integer function."}, + {"VectorFunc", "Functions/Vector", "VisualShaderNodeVectorFunc", "Vector function."}, + {"DerivativeFunc", "Functions/Others", "VisualShaderNodeDerivativeFunc", "Derivative function."}, + + // Operators + + {"FloatOp", "Operators/Scalar", "VisualShaderNodeFloatOp", "Float operator."}, + {"IntOp", "Operators/Scalar", "VisualShaderNodeIntOp", "Integer operator."}, + {"UIntOp", "Operators/Scalar", "VisualShaderNodeUIntOp", "Unsigned integer operator."}, + {"VectorOp", "Operators/Vector", "VisualShaderNodeVectorOp", "Vector operator."}, + {"VectorCompose", "Operators/Vector", "VisualShaderNodeVectorCompose", "Composes vector from scalars."}, + {"VectorDecompose", "Operators/Vector", "VisualShaderNodeVectorDecompose", "Decomposes vector to scalars."}, + + // Procedural + + {"ValueNoise", "Procedural/Noise", "VisualShaderNodeValueNoise", "Generates a simple, or Value, noise based on input 'UV'. The scale of the generated noise is controlled by input 'Scale'."}, + + // Utility + + {"Compare", "Utility/Logic", "VisualShaderNodeCompare", "Returns the boolean result of the comparison between two parameters."}, + {"If", "Utility/Logic", "VisualShaderNodeIf", "Returns the value of the 'True' or 'False' input based on the value of the 'Condition' input."}, + {"Switch", "Utility/Logic", "VisualShaderNodeSwitch", "Returns an associated scalar if the provided boolean value is true or false."}, + {"Is", "Utility/Logic", "VisualShaderNodeIs", "Returns the boolean result of the comparison between INF (or NaN) and a scalar parameter."}, + + {"", "", "", ""}, }; -void VisualShaderEditor::create_node() { - std::cout << "Creating node" << std::endl; - VisualShaderEditor::add_node(); +void VisualShaderEditor::create_node(const QPointF& pos) { + QTreeWidgetItem* selected_item = create_node_dialog->get_selected_item(); + + if (!selected_item) { + return; + } + + VisualShaderEditor::add_node(selected_item, pos); } -void VisualShaderEditor::add_node() { +void VisualShaderEditor::add_node(QTreeWidgetItem* selected_item, const QPointF& pos) { + std::string type = selected_item->data(0, Qt::UserRole).toString().toStdString(); + + if (type.empty()) { + return; + } + + // Instantiate the node based on the type + std::shared_ptr node; + + if (type == "VisualShaderNodeInput") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeColorConstant") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeBooleanConstant") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeFloatConstant") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeIntConstant") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeUIntConstant") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeVec2Constant") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeVec3Constant") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeVec4Constant") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeFloatFunc") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeIntFunc") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeUIntFunc") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeDerivativeFunc") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeFloatOp") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeIntOp") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeUIntOp") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeValueNoise") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeCompare") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeIf") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeIs") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeSwitch") { + node = std::make_shared(); + } else { + std::cout << "Unknown node type: " << type << std::endl; + return; + } + + if (!node) { + std::cout << "Failed to create node of type: " << type << std::endl; + return; + } + + QPointF offset {view->mapToScene(pos.toPoint())}; // Top-left corner of the view + + // Get a valid node id + int new_node_id = visual_shader->get_valid_node_id(); + + // Add the node to the graph + visual_shader->add_node(node, {(float)offset.x(), (float)offset.y()}, new_node_id); + int t_id = graph->addNode(); + graph->setNodeData(t_id, NodeRole::Position, offset); } -void VisualShaderEditor::show_create_node_dialog(const bool& custom_mouse_pos) { - int status = create_node_dialog->exec(); +void VisualShaderEditor::show_create_node_dialog(const QPointF& pos) { + int status {create_node_dialog->exec()}; switch (status) { case QDialog::Accepted: std::cout << "Create node dialog accepted" << std::endl; + VisualShaderEditor::create_node(pos); break; case QDialog::Rejected: std::cout << "Create node dialog rejected" << std::endl; @@ -239,6 +361,15 @@ void VisualShaderEditor::show_create_node_dialog(const bool& custom_mouse_pos) { } } +void VisualShaderEditor::on_create_node_button_pressed() { + emit on_create_node_dialog_requested(); +} + +void VisualShaderEditor::on_create_node_action_triggered() { + QPointF pos {view->mapToScene(view->mapFromGlobal(QCursor::pos()))}; + emit on_create_node_dialog_requested(pos); +} + std::vector VisualShaderEditor::pasre_node_category_path(const std::string& node_category_path) { std::vector tokens; std::stringstream ss(node_category_path); @@ -283,7 +414,8 @@ CreateNodeDialog::CreateNodeDialog(QWidget* parent) : QDialog(parent), create_node_dialog_nodes_description(nullptr), buttons_layout(nullptr), create_button(nullptr), - cancel_button(nullptr) { + cancel_button(nullptr), + selected_item(nullptr) { layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom layout->setSizeConstraint(QLayout::SetNoConstraint); @@ -308,6 +440,7 @@ CreateNodeDialog::CreateNodeDialog(QWidget* parent) : QDialog(parent), create_node_dialog_nodes_tree->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom create_node_dialog_nodes_tree->setColumnCount(1); create_node_dialog_nodes_tree->setHeaderHidden(true); + this->connect(create_node_dialog_nodes_tree, &QTreeWidget::itemSelectionChanged, this, &CreateNodeDialog::update_selected_item); // Add the nodes tree to the nodes tree layout. create_node_dialog_nodes_tree_layout->addWidget(create_node_dialog_nodes_tree, 2); // 2x the size of the nodes description. @@ -330,12 +463,12 @@ CreateNodeDialog::CreateNodeDialog(QWidget* parent) : QDialog(parent), layout->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); create_button = new QPushButton("Create"); - QObject::connect(create_button, &QPushButton::clicked, this, &CreateNodeDialog::on_CreateButtonTriggered); + QObject::connect(create_button, &QPushButton::pressed, this, &CreateNodeDialog::on_create_node_button_pressed); create_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); create_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom cancel_button = new QPushButton("Cancel"); - QObject::connect(cancel_button, &QPushButton::clicked, this, &CreateNodeDialog::on_CancelButtonTriggered); + QObject::connect(cancel_button, &QPushButton::pressed, this, &CreateNodeDialog::on_cancel_node_creation_button_pressed); cancel_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); cancel_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom @@ -363,14 +496,25 @@ CreateNodeDialog::~CreateNodeDialog() { if (layout) delete layout; } -void CreateNodeDialog::on_CreateButtonTriggered() { +void CreateNodeDialog::on_create_node_button_pressed() { this->accept(); } -void CreateNodeDialog::on_CancelButtonTriggered() { +void CreateNodeDialog::on_cancel_node_creation_button_pressed() { this->reject(); } +void CreateNodeDialog::update_selected_item() { + QTreeWidgetItem* item = create_node_dialog_nodes_tree->currentItem(); + if (item) { + selected_item = item; + create_node_dialog_nodes_description->setText(item->data(0, Qt::UserRole + 1).toString()); + } else { + selected_item = nullptr; + create_node_dialog_nodes_description->setText(""); + } +} + /*************************************/ /* VisualShaderGraph */ /*************************************/ diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index c7a0b1887..aeb6e4c3f 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -80,16 +80,25 @@ class VisualShaderEditor : public BaseEditor { VisualShaderEditor(MessageModel* model, QWidget* parent = nullptr); ~VisualShaderEditor() override; - void create_node(); + void create_node(const QPointF& pos); - void add_node(); + void add_node(QTreeWidgetItem* selected_item, const QPointF& pos); - void show_create_node_dialog(const bool& custom_mouse_pos = false); + void show_create_node_dialog(const QPointF& pos); std::vector pasre_node_category_path(const std::string& node_category_path); QTreeWidgetItem* find_or_create_category_item(QTreeWidgetItem* parent, const std::string& category, const std::string& category_path, QTreeWidget* create_node_dialog_nodes_tree, std::unordered_map& category_path_map); + signals: + void on_create_node_dialog_requested(const QPointF& pos = {0, 0}); // {0, 0} is the top-left corner of the scene. + + private slots: + void on_create_node_button_pressed(); + void on_create_node_action_triggered(); + private: + VisualShader* visual_shader; + QHBoxLayout* layout; QHBoxLayout* scene_layer_layout; @@ -104,6 +113,8 @@ class VisualShaderEditor : public BaseEditor { QPushButton* create_node_button; QPushButton* preview_shader_button; + QAction* create_node_action; + //////////////////////////////////// // CreateNodeDialog Nodes Tree //////////////////////////////////// @@ -113,20 +124,14 @@ class VisualShaderEditor : public BaseEditor { std::string category_path; std::string type; std::string description; - std::vector ops; - VisualShaderNode::PortType return_type; CreateNodeDialogNodesTreeItem(const std::string& name = std::string(), const std::string& category_path = std::string(), const std::string& type = std::string(), - const std::string& description = std::string(), - const std::vector& ops = std::vector(), - const VisualShaderNode::PortType& return_type = VisualShaderNode::PortType::PORT_TYPE_ENUM_SIZE) : name(name), - category_path(category_path), - type(type), - description(description), - ops(ops), - return_type(return_type) {} + const std::string& description = std::string()) : name(name), + category_path(category_path), + type(type), + description(description) {} }; @@ -148,9 +153,13 @@ class CreateNodeDialog : public QDialog { QTreeWidget* get_nodes_tree() const { return create_node_dialog_nodes_tree; } + QTreeWidgetItem* get_selected_item() const { return selected_item; } + private slots: - void on_CreateButtonTriggered(); - void on_CancelButtonTriggered(); + void on_create_node_button_pressed(); + void on_cancel_node_creation_button_pressed(); + + void update_selected_item(); private: QVBoxLayout* layout; @@ -163,6 +172,8 @@ class CreateNodeDialog : public QDialog { QHBoxLayout* buttons_layout; QPushButton* create_button; QPushButton* cancel_button; + + QTreeWidgetItem* selected_item; }; /*************************************/ @@ -238,7 +249,9 @@ class VisualShaderGraph : public QtNodes::AbstractGraphModel */ void loadNode(QJsonObject const &nodeJson) override; - void set_visual_shader_editor(VisualShaderEditor* visual_shader_editor) { this->visual_shader_editor = visual_shader_editor; } + void register_visual_shader(VisualShader* visual_shader) const { this->visual_shader = visual_shader; } + + void set_visual_shader_editor(VisualShaderEditor* visual_shader_editor) const { this->visual_shader_editor = visual_shader_editor; } private: std::unordered_set _nodeIds; @@ -259,7 +272,8 @@ class VisualShaderGraph : public QtNodes::AbstractGraphModel NodeId newNodeId() override { return _nextNodeId++; } - VisualShaderEditor* visual_shader_editor; + mutable VisualShader* visual_shader; + mutable VisualShaderEditor* visual_shader_editor; }; #endif // ENIGMA_VISUAL_SHADER_EDITOR_H diff --git a/Submodules/enigma-dev b/Submodules/enigma-dev index f30646fac..84302caf3 160000 --- a/Submodules/enigma-dev +++ b/Submodules/enigma-dev @@ -1 +1 @@ -Subproject commit f30646facfea176aa3ce63e205a7eceebcbfa048 +Subproject commit 84302caf3684bb17c3897d2655bdba98b90a5317