Skip to content

Commit

Permalink
added the ability to add a node
Browse files Browse the repository at this point in the history
Signed-off-by: Saif Kandil <74428638+k0T0z@users.noreply.github.com>
  • Loading branch information
k0T0z committed Sep 8, 2024
1 parent 06d3ea5 commit 9822da3
Show file tree
Hide file tree
Showing 3 changed files with 206 additions and 48 deletions.
204 changes: 174 additions & 30 deletions Editors/VisualShaderEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
#include <QtNodes/GraphicsView>

#include "VisualShader.pb.h"
#include "ResourceTransformations/VisualShader/visual_shader_nodes.h"
#include "ResourceTransformations/VisualShader/vs_noise_nodes.h"

using QtNodes::GraphicsView;
using QtNodes::NodeRole;
Expand All @@ -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),
Expand All @@ -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
Expand All @@ -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);

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

Expand All @@ -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);
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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;
Expand All @@ -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<VisualShaderNode> node;

if (type == "VisualShaderNodeInput") {
node = std::make_shared<VisualShaderNodeInput>();
} else if (type == "VisualShaderNodeColorConstant") {
node = std::make_shared<VisualShaderNodeColorConstant>();
} else if (type == "VisualShaderNodeBooleanConstant") {
node = std::make_shared<VisualShaderNodeBooleanConstant>();
} else if (type == "VisualShaderNodeFloatConstant") {
node = std::make_shared<VisualShaderNodeFloatConstant>();
} else if (type == "VisualShaderNodeIntConstant") {
node = std::make_shared<VisualShaderNodeIntConstant>();
} else if (type == "VisualShaderNodeUIntConstant") {
node = std::make_shared<VisualShaderNodeUIntConstant>();
} else if (type == "VisualShaderNodeVec2Constant") {
node = std::make_shared<VisualShaderNodeVec2Constant>();
} else if (type == "VisualShaderNodeVec3Constant") {
node = std::make_shared<VisualShaderNodeVec3Constant>();
} else if (type == "VisualShaderNodeVec4Constant") {
node = std::make_shared<VisualShaderNodeVec4Constant>();
} else if (type == "VisualShaderNodeFloatFunc") {
node = std::make_shared<VisualShaderNodeFloatFunc>();
} else if (type == "VisualShaderNodeIntFunc") {
node = std::make_shared<VisualShaderNodeIntFunc>();
} else if (type == "VisualShaderNodeUIntFunc") {
node = std::make_shared<VisualShaderNodeUIntFunc>();
} else if (type == "VisualShaderNodeDerivativeFunc") {
node = std::make_shared<VisualShaderNodeDerivativeFunc>();
} else if (type == "VisualShaderNodeFloatOp") {
node = std::make_shared<VisualShaderNodeFloatOp>();
} else if (type == "VisualShaderNodeIntOp") {
node = std::make_shared<VisualShaderNodeIntOp>();
} else if (type == "VisualShaderNodeUIntOp") {
node = std::make_shared<VisualShaderNodeUIntOp>();
} else if (type == "VisualShaderNodeValueNoise") {
node = std::make_shared<VisualShaderNodeValueNoise>();
} else if (type == "VisualShaderNodeCompare") {
node = std::make_shared<VisualShaderNodeCompare>();
} else if (type == "VisualShaderNodeIf") {
node = std::make_shared<VisualShaderNodeIf>();
} else if (type == "VisualShaderNodeIs") {
node = std::make_shared<VisualShaderNodeIs>();
} else if (type == "VisualShaderNodeSwitch") {
node = std::make_shared<VisualShaderNodeSwitch>();
} 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;
Expand All @@ -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<std::string> VisualShaderEditor::pasre_node_category_path(const std::string& node_category_path) {
std::vector<std::string> tokens;
std::stringstream ss(node_category_path);
Expand Down Expand Up @@ -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);
Expand All @@ -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.
Expand All @@ -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

Expand Down Expand Up @@ -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 */
/*************************************/
Expand Down
Loading

0 comments on commit 9822da3

Please sign in to comment.