From 073017cfeb3c44bb21f9faf669ddec8c376c3b8a Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Sun, 8 Sep 2024 22:50:54 +0300 Subject: [PATCH] few improvements and bug fixes Signed-off-by: Saif Kandil <74428638+k0T0z@users.noreply.github.com> --- Editors/VisualShaderEditor.cpp | 346 +++++++++++++++++++++------------ Editors/VisualShaderEditor.h | 104 +++++----- Tests/CMakeLists.txt | 0 3 files changed, 278 insertions(+), 172 deletions(-) create mode 100644 Tests/CMakeLists.txt diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index b108836a6..90d4c4ea0 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 + + // Create the node and get the new node id. + NodeId new_node_id {graph->addNode()}; + + // Add the node to the graph. + visual_shader->add_node(node, {(float)offset.x(), (float)offset.y()}, new_node_id); + graph->setNodeData(new_node_id, NodeRole::Caption, QString::fromStdString(node->get_caption())); + graph->setNodeData(new_node_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() { + Q_EMIT on_create_node_dialog_requested(); +} + +void VisualShaderEditor::on_create_node_action_triggered() { + QPointF pos {view->mapToScene(view->mapFromGlobal(QCursor::pos()))}; + Q_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,19 +496,30 @@ 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 */ /*************************************/ -VisualShaderGraph::VisualShaderGraph() : _nextNodeId{0} { +VisualShaderGraph::VisualShaderGraph() { } @@ -385,76 +529,75 @@ VisualShaderGraph::~VisualShaderGraph() { std::unordered_set VisualShaderGraph::allNodeIds() const { - return _nodeIds; + return _node_ids; } -std::unordered_set VisualShaderGraph::allConnectionIds(NodeId const nodeId) const +std::unordered_set VisualShaderGraph::allConnectionIds(NodeId const node_id) const { std::unordered_set result; std::copy_if(_connectivity.begin(), _connectivity.end(), std::inserter(result, std::end(result)), - [&nodeId](ConnectionId const &cid) { - return cid.inNodeId == nodeId || cid.outNodeId == nodeId; + [&node_id](ConnectionId const &cid) { + return cid.inNodeId == node_id || cid.outNodeId == node_id; }); return result; } -std::unordered_set VisualShaderGraph::connections(NodeId nodeId, - PortType portType, - PortIndex portIndex) const -{ +std::unordered_set VisualShaderGraph::connections(NodeId node_id, + PortType port_type, + PortIndex port_index) const { std::unordered_set result; std::copy_if(_connectivity.begin(), _connectivity.end(), std::inserter(result, std::end(result)), - [&portType, &portIndex, &nodeId](ConnectionId const &cid) { - return (getNodeId(portType, cid) == nodeId - && getPortIndex(portType, cid) == portIndex); + [&port_type, &port_index, &node_id](ConnectionId const &cid) { + return (getNodeId(port_type, cid) == node_id + && getPortIndex(port_type, cid) == port_index); }); return result; } -bool VisualShaderGraph::connectionExists(ConnectionId const connectionId) const +bool VisualShaderGraph::connectionExists(ConnectionId const connection_id) const { - return (_connectivity.find(connectionId) != _connectivity.end()); + return (_connectivity.find(connection_id) != _connectivity.end()); } -NodeId VisualShaderGraph::addNode(QString const nodeType) +NodeId VisualShaderGraph::addNode(QString const node_type) { - NodeId newId = _nextNodeId++; + NodeId new_id {newNodeId()}; + // Create new node. - _nodeIds.insert(newId); + _node_ids.insert(new_id); - Q_EMIT nodeCreated(newId); + Q_EMIT nodeCreated(new_id); - return newId; + return new_id; } -bool VisualShaderGraph::connectionPossible(ConnectionId const connectionId) const +bool VisualShaderGraph::connectionPossible(ConnectionId const connection_id) const { - return _connectivity.find(connectionId) == _connectivity.end(); + return _connectivity.find(connection_id) == _connectivity.end(); } -void VisualShaderGraph::addConnection(ConnectionId const connectionId) +void VisualShaderGraph::addConnection(ConnectionId const connection_id) { - _connectivity.insert(connectionId); + _connectivity.insert(connection_id); - Q_EMIT connectionCreated(connectionId); + Q_EMIT connectionCreated(connection_id); } -bool VisualShaderGraph::nodeExists(NodeId const nodeId) const +bool VisualShaderGraph::nodeExists(NodeId const node_id) const { - return (_nodeIds.find(nodeId) != _nodeIds.end()); + return (_node_ids.find(node_id) != _node_ids.end()); } -QVariant VisualShaderGraph::nodeData(NodeId nodeId, NodeRole role) const -{ - Q_UNUSED(nodeId); +QVariant VisualShaderGraph::nodeData(NodeId node_id, NodeRole role) const { + Q_UNUSED(node_id); QVariant result; @@ -464,11 +607,11 @@ QVariant VisualShaderGraph::nodeData(NodeId nodeId, NodeRole role) const break; case NodeRole::Position: - result = _nodeGeometryData[nodeId].pos; + result = _node_geometry_data[node_id].pos; break; case NodeRole::Size: - result = _nodeGeometryData[nodeId].size; + result = _node_geometry_data[node_id].size; break; case NodeRole::CaptionVisible: @@ -503,23 +646,22 @@ QVariant VisualShaderGraph::nodeData(NodeId nodeId, NodeRole role) const return result; } -bool VisualShaderGraph::setNodeData(NodeId nodeId, NodeRole role, QVariant value) -{ - bool result = false; +bool VisualShaderGraph::setNodeData(NodeId node_id, NodeRole role, QVariant value) { + bool result {false}; switch (role) { case NodeRole::Type: break; case NodeRole::Position: { - _nodeGeometryData[nodeId].pos = value.value(); + _node_geometry_data[node_id].pos = value.value(); - Q_EMIT nodePositionUpdated(nodeId); + Q_EMIT nodePositionUpdated(node_id); result = true; } break; case NodeRole::Size: { - _nodeGeometryData[nodeId].size = value.value(); + _node_geometry_data[node_id].size = value.value(); result = true; } break; @@ -548,9 +690,9 @@ bool VisualShaderGraph::setNodeData(NodeId nodeId, NodeRole role, QVariant value return result; } -QVariant VisualShaderGraph::portData(NodeId nodeId, - PortType portType, - PortIndex portIndex, +QVariant VisualShaderGraph::portData(NodeId node_id, + PortType port_type, + PortIndex port_index, PortRole role) const { switch (role) { @@ -571,7 +713,7 @@ QVariant VisualShaderGraph::portData(NodeId nodeId, break; case PortRole::Caption: - if (portType == PortType::In) + if (port_type == PortType::In) return QString::fromUtf8("Port In"); else return QString::fromUtf8("Port Out"); @@ -583,22 +725,22 @@ QVariant VisualShaderGraph::portData(NodeId nodeId, } bool VisualShaderGraph::setPortData( - NodeId nodeId, PortType portType, PortIndex portIndex, QVariant const &value, PortRole role) + NodeId node_id, PortType port_type, PortIndex port_index, QVariant const &value, PortRole role) { - Q_UNUSED(nodeId); - Q_UNUSED(portType); - Q_UNUSED(portIndex); + Q_UNUSED(node_id); + Q_UNUSED(port_type); + Q_UNUSED(port_index); Q_UNUSED(value); Q_UNUSED(role); return false; } -bool VisualShaderGraph::deleteConnection(ConnectionId const connectionId) +bool VisualShaderGraph::deleteConnection(ConnectionId const connection_id) { bool disconnected = false; - auto it = _connectivity.find(connectionId); + auto it = _connectivity.find(connection_id); if (it != _connectivity.end()) { disconnected = true; @@ -607,61 +749,23 @@ bool VisualShaderGraph::deleteConnection(ConnectionId const connectionId) } if (disconnected) - Q_EMIT connectionDeleted(connectionId); + Q_EMIT connectionDeleted(connection_id); return disconnected; } -bool VisualShaderGraph::deleteNode(NodeId const nodeId) +bool VisualShaderGraph::deleteNode(NodeId const node_id) { // Delete connections to this node first. - auto connectionIds = allConnectionIds(nodeId); + auto connectionIds = allConnectionIds(node_id); for (auto &cId : connectionIds) { deleteConnection(cId); } - _nodeIds.erase(nodeId); - _nodeGeometryData.erase(nodeId); + _node_ids.erase(node_id); + _node_geometry_data.erase(node_id); - Q_EMIT nodeDeleted(nodeId); + Q_EMIT nodeDeleted(node_id); return true; } - -QJsonObject VisualShaderGraph::saveNode(NodeId const nodeId) const -{ - QJsonObject nodeJson; - - nodeJson["id"] = static_cast(nodeId); - - { - QPointF const pos = nodeData(nodeId, NodeRole::Position).value(); - - QJsonObject posJson; - posJson["x"] = pos.x(); - posJson["y"] = pos.y(); - nodeJson["position"] = posJson; - } - - return nodeJson; -} - -void VisualShaderGraph::loadNode(QJsonObject const &nodeJson) -{ - NodeId restoredNodeId = nodeJson["id"].toInt(); - - // Next NodeId must be larger that any id existing in the graph - _nextNodeId = std::max(restoredNodeId + 1, _nextNodeId); - - // Create new node. - _nodeIds.insert(restoredNodeId); - - Q_EMIT nodeCreated(restoredNodeId); - - { - QJsonObject posJson = nodeJson["position"].toObject(); - QPointF const pos(posJson["x"].toDouble(), posJson["y"].toDouble()); - - setNodeData(restoredNodeId, NodeRole::Position, pos); - } -} diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index c7a0b1887..899893345 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); + Q_SIGNALS: + void on_create_node_dialog_requested(const QPointF& pos = {0, 0}); // {0, 0} is the top-left corner of the scene. + + private Q_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; } - private slots: - void on_CreateButtonTriggered(); - void on_CancelButtonTriggered(); + QTreeWidgetItem* get_selected_item() const { return selected_item; } + + private Q_SLOTS: + 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; }; /*************************************/ @@ -190,58 +201,51 @@ class VisualShaderGraph : public QtNodes::AbstractGraphModel std::unordered_set allNodeIds() const override; - std::unordered_set allConnectionIds(NodeId const nodeId) const override; + std::unordered_set allConnectionIds(NodeId const node_id) const override; - std::unordered_set connections(NodeId nodeId, - PortType portType, - PortIndex portIndex) const override; + std::unordered_set connections(NodeId node_id, + PortType port_type, + PortIndex port_index) const override; - bool connectionExists(ConnectionId const connectionId) const override; + bool connectionExists(ConnectionId const connection_id) const override; - NodeId addNode(QString const nodeType = QString()) override; + NodeId addNode(QString const node_type = QString()) override; /** * Connection is possible when graph contains no connectivity data * in both directions `Out -> In` and `In -> Out`. */ - bool connectionPossible(ConnectionId const connectionId) const override; + bool connectionPossible(ConnectionId const connection_id) const override; - void addConnection(ConnectionId const connectionId) override; + void addConnection(ConnectionId const connection_id) override; - bool nodeExists(NodeId const nodeId) const override; + bool nodeExists(NodeId const node_id) const override; - QVariant nodeData(NodeId nodeId, NodeRole role) const override; + QVariant nodeData(NodeId node_id, NodeRole role) const override; - bool setNodeData(NodeId nodeId, NodeRole role, QVariant value) override; + bool setNodeData(NodeId node_id, NodeRole role, QVariant value) override; - QVariant portData(NodeId nodeId, - PortType portType, - PortIndex portIndex, + QVariant portData(NodeId node_id, + PortType port_type, + PortIndex port_index, PortRole role) const override; - bool setPortData(NodeId nodeId, - PortType portType, - PortIndex portIndex, + bool setPortData(NodeId node_id, + PortType port_type, + PortIndex port_index, QVariant const &value, PortRole role = PortRole::Data) override; - bool deleteConnection(ConnectionId const connectionId) override; + bool deleteConnection(ConnectionId const connection_id) override; - bool deleteNode(NodeId const nodeId) override; + bool deleteNode(NodeId const node_id) override; - QJsonObject saveNode(NodeId const) const override; + void register_visual_shader(VisualShader* visual_shader) const { this->visual_shader = visual_shader; } - /// @brief Creates a new node based on the informatoin in `nodeJson`. - /** - * @param nodeJson conains a `NodeId`, node's position, internal node - * information. - */ - void loadNode(QJsonObject const &nodeJson) override; - - void set_visual_shader_editor(VisualShaderEditor* visual_shader_editor) { this->visual_shader_editor = visual_shader_editor; } + void set_visual_shader_editor(VisualShaderEditor* visual_shader_editor) const { this->visual_shader_editor = visual_shader_editor; } private: - std::unordered_set _nodeIds; + std::unordered_set _node_ids; /// [Important] This is a user defined data structure backing your model. /// In your case it could be anything else representing a graph, for example, a @@ -252,14 +256,12 @@ class VisualShaderGraph : public QtNodes::AbstractGraphModel /// directions, i.e. from Node1 to Node2 and from Node2 to Node1. std::unordered_set _connectivity; - mutable std::unordered_map _nodeGeometryData; - - /// A convenience variable needed for generating unique node ids. - NodeId _nextNodeId; + mutable std::unordered_map _node_geometry_data; - NodeId newNodeId() override { return _nextNodeId++; } + NodeId newNodeId() override { return (NodeId)visual_shader->get_valid_node_id(); } - VisualShaderEditor* visual_shader_editor; + mutable VisualShader* visual_shader; + mutable VisualShaderEditor* visual_shader_editor; }; #endif // ENIGMA_VISUAL_SHADER_EDITOR_H diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt new file mode 100644 index 000000000..e69de29bb