From c26f3b9b71b114e4ad3fc41d4c2a3cc3f55ee3f4 Mon Sep 17 00:00:00 2001 From: Lynix Date: Mon, 18 May 2020 19:55:12 +0200 Subject: [PATCH] Add shadernode (big WIP) --- .gitignore | 3 +- build/scripts/tools/shadernodes.lua | 42 ++++++ src/ShaderNode/DataModels/FragmentOutput.cpp | 44 +++++++ src/ShaderNode/DataModels/FragmentOutput.hpp | 31 +++++ src/ShaderNode/DataModels/FragmentOutput.inl | 1 + src/ShaderNode/DataModels/ShaderNode.cpp | 1 + src/ShaderNode/DataModels/ShaderNode.hpp | 17 +++ src/ShaderNode/DataModels/ShaderNode.inl | 1 + src/ShaderNode/DataModels/VecValue.cpp | 34 +++++ src/ShaderNode/DataModels/VecValue.hpp | 66 ++++++++++ src/ShaderNode/DataModels/VecValue.inl | 44 +++++++ src/ShaderNode/main.cpp | 127 +++++++++++++++++++ 12 files changed, 410 insertions(+), 1 deletion(-) create mode 100644 build/scripts/tools/shadernodes.lua create mode 100644 src/ShaderNode/DataModels/FragmentOutput.cpp create mode 100644 src/ShaderNode/DataModels/FragmentOutput.hpp create mode 100644 src/ShaderNode/DataModels/FragmentOutput.inl create mode 100644 src/ShaderNode/DataModels/ShaderNode.cpp create mode 100644 src/ShaderNode/DataModels/ShaderNode.hpp create mode 100644 src/ShaderNode/DataModels/ShaderNode.inl create mode 100644 src/ShaderNode/DataModels/VecValue.cpp create mode 100644 src/ShaderNode/DataModels/VecValue.hpp create mode 100644 src/ShaderNode/DataModels/VecValue.inl create mode 100644 src/ShaderNode/main.cpp diff --git a/.gitignore b/.gitignore index af2c0073f..2a1c7ff50 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,8 @@ # Nazara build build/config.lua -# Nazara libraries +# Nazara binaries +bin/* lib/* # Self-hosted thirdparty libraries binaries diff --git a/build/scripts/tools/shadernodes.lua b/build/scripts/tools/shadernodes.lua new file mode 100644 index 000000000..33f9628e9 --- /dev/null +++ b/build/scripts/tools/shadernodes.lua @@ -0,0 +1,42 @@ +TOOL.Name = "ShaderNodes" + +TOOL.ClientOnly = true +TOOL.EnableConsole = true +TOOL.Kind = "Application" +TOOL.TargetDirectory = "../bin" + +TOOL.Defines = { + "NODE_EDITOR_SHARED" +} + +TOOL.Includes = { + "../include", + "../extlibs/include", + "../src/ShaderNode", + [[E:\Qt\5.14.1\msvc2017_64\include]], + [[E:\Qt\5.14.1\msvc2017_64\include\QtCore]], + [[E:\Qt\5.14.1\msvc2017_64\include\QtGui]], + [[E:\Qt\5.14.1\msvc2017_64\include\QtWidgets]], + [[C:\Users\Lynix\Documents\GitHub\nodeeditor\include]], +} + +TOOL.Files = { + "../src/ShaderNode/**.hpp", + "../src/ShaderNode/**.inl", + "../src/ShaderNode/**.cpp" +} + +TOOL.Libraries = { + "NazaraCore", + "NazaraRenderer", + "NazaraUtility", + "Qt5Cored", + "Qt5Guid", + "Qt5Widgetsd", + "nodes" +} + +TOOL.LibraryPaths.x64 = { + [[E:\Qt\5.14.1\msvc2017_64\lib]], + [[C:\Users\Lynix\Documents\GitHub\nodeeditor\build\lib\Debug]] +} diff --git a/src/ShaderNode/DataModels/FragmentOutput.cpp b/src/ShaderNode/DataModels/FragmentOutput.cpp new file mode 100644 index 000000000..15244e0d8 --- /dev/null +++ b/src/ShaderNode/DataModels/FragmentOutput.cpp @@ -0,0 +1,44 @@ +#include +#include +#include + +Nz::ShaderAst::ExpressionPtr FragmentOutput::GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const +{ + using namespace Nz::ShaderAst; + using namespace Nz::ShaderBuilder; + + assert(count == 1); + + auto output = Nz::ShaderBuilder::Output("RenderTarget0", ExpressionType::Float4); + + return Nz::ShaderBuilder::Assign(output, *expressions); +} + +QtNodes::NodeDataType FragmentOutput::dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const +{ + assert(portType == QtNodes::PortType::In); + assert(portIndex == 0); + + return Vec4Data::Type(); +} + +QWidget* FragmentOutput::embeddedWidget() +{ + return nullptr; +} + +unsigned int FragmentOutput::nPorts(QtNodes::PortType portType) const +{ + switch (portType) + { + case QtNodes::PortType::In: return 1; + case QtNodes::PortType::Out: return 0; + } + + return 0; +} + +std::shared_ptr FragmentOutput::outData(QtNodes::PortIndex /*port*/) +{ + return {}; +} diff --git a/src/ShaderNode/DataModels/FragmentOutput.hpp b/src/ShaderNode/DataModels/FragmentOutput.hpp new file mode 100644 index 000000000..30aa74b90 --- /dev/null +++ b/src/ShaderNode/DataModels/FragmentOutput.hpp @@ -0,0 +1,31 @@ +#pragma once + +#ifndef NAZARA_SHADERNODES_FRAGMENTOUTPUT_HPP +#define NAZARA_SHADERNODES_FRAGMENTOUTPUT_HPP + +#include +#include +#include + +class FragmentOutput : public ShaderNode +{ + public: + Nz::ShaderAst::ExpressionPtr GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const override; + + QString caption() const override { return "Fragment shader output"; } + QString name() const override { return "FragmentShaderOutput"; } + + QtNodes::NodeDataType dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; + + QWidget* embeddedWidget() override; + unsigned int nPorts(QtNodes::PortType portType) const override; + + std::shared_ptr outData(QtNodes::PortIndex port) override; + + void setInData(std::shared_ptr, int) override {}; + +}; + +#include + +#endif diff --git a/src/ShaderNode/DataModels/FragmentOutput.inl b/src/ShaderNode/DataModels/FragmentOutput.inl new file mode 100644 index 000000000..f8287a921 --- /dev/null +++ b/src/ShaderNode/DataModels/FragmentOutput.inl @@ -0,0 +1 @@ +#include diff --git a/src/ShaderNode/DataModels/ShaderNode.cpp b/src/ShaderNode/DataModels/ShaderNode.cpp new file mode 100644 index 000000000..fba232649 --- /dev/null +++ b/src/ShaderNode/DataModels/ShaderNode.cpp @@ -0,0 +1 @@ +#include diff --git a/src/ShaderNode/DataModels/ShaderNode.hpp b/src/ShaderNode/DataModels/ShaderNode.hpp new file mode 100644 index 000000000..5c8f6e7f8 --- /dev/null +++ b/src/ShaderNode/DataModels/ShaderNode.hpp @@ -0,0 +1,17 @@ +#pragma once + +#ifndef NAZARA_SHADERNODES_SHADERNODE_HPP +#define NAZARA_SHADERNODES_SHADERNODE_HPP + +#include +#include + +class ShaderNode : public QtNodes::NodeDataModel +{ + public: + virtual Nz::ShaderAst::ExpressionPtr GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const = 0; +}; + +#include + +#endif diff --git a/src/ShaderNode/DataModels/ShaderNode.inl b/src/ShaderNode/DataModels/ShaderNode.inl new file mode 100644 index 000000000..fba232649 --- /dev/null +++ b/src/ShaderNode/DataModels/ShaderNode.inl @@ -0,0 +1 @@ +#include diff --git a/src/ShaderNode/DataModels/VecValue.cpp b/src/ShaderNode/DataModels/VecValue.cpp new file mode 100644 index 000000000..7986f0b9d --- /dev/null +++ b/src/ShaderNode/DataModels/VecValue.cpp @@ -0,0 +1,34 @@ +#include +#include + +Nz::ShaderAst::ExpressionPtr Vec4Value::GetExpression(Nz::ShaderAst::ExpressionPtr* /*expressions*/, std::size_t count) const +{ + assert(count == 0); + + return Nz::ShaderBuilder::Constant(GetValue()); +} + +auto Vec4Value::dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const -> QtNodes::NodeDataType +{ + assert(portType == QtNodes::PortType::Out); + assert(portIndex == 0); + + return Vec4Data::Type(); +} + +std::shared_ptr Vec4Value::outData(QtNodes::PortIndex port) +{ + assert(port == 0); + + return std::make_shared(GetValue()); +} + +Nz::Vector4f Vec4Value::GetValue() const +{ + float x = float(m_values[0]->value()); + float y = float(m_values[1]->value()); + float z = float(m_values[2]->value()); + float w = float(m_values[3]->value()); + + return Nz::Vector4f(x, y, z, w); +} diff --git a/src/ShaderNode/DataModels/VecValue.hpp b/src/ShaderNode/DataModels/VecValue.hpp new file mode 100644 index 000000000..83bd8b3a8 --- /dev/null +++ b/src/ShaderNode/DataModels/VecValue.hpp @@ -0,0 +1,66 @@ +#pragma once + +#ifndef NAZARA_SHADERNODES_VECVALUE_HPP +#define NAZARA_SHADERNODES_VECVALUE_HPP + +#include +#include +#include +#include + +template +class VecValue : public ShaderNode +{ + public: + VecValue(); + ~VecValue() = default; + + QWidget* embeddedWidget() override; + unsigned int nPorts(QtNodes::PortType portType) const override; + + protected: + QWidget* m_widget; + QFormLayout* m_layout; + std::array m_values; +}; + +class Vec4Data : public QtNodes::NodeData +{ + public: + inline Vec4Data(const Nz::Vector4f& vec); + + QtNodes::NodeDataType type() const override + { + return Type(); + } + + static QtNodes::NodeDataType Type() + { + return { "vec4", "Vec4" }; + } + + Nz::Vector4f value; +}; + +class Vec4Value : public VecValue<4> +{ + public: + Nz::ShaderAst::ExpressionPtr GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const override; + + QString caption() const override { return "Vec4 value"; } + bool captionVisible() const override { return true; } + QString name() const override { return "Vec4Value"; } + + QtNodes::NodeDataType dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; + + std::shared_ptr outData(QtNodes::PortIndex port) override; + + void setInData(std::shared_ptr, int) override {}; + + private: + Nz::Vector4f GetValue() const; +}; + +#include + +#endif diff --git a/src/ShaderNode/DataModels/VecValue.inl b/src/ShaderNode/DataModels/VecValue.inl new file mode 100644 index 000000000..7377fdbad --- /dev/null +++ b/src/ShaderNode/DataModels/VecValue.inl @@ -0,0 +1,44 @@ +#include +#include + +template +VecValue::VecValue() +{ + constexpr std::array componentName = { 'X', 'Y', 'Z', 'W' }; + static_assert(N <= componentName.size()); + + m_layout = new QFormLayout; + for (std::size_t i = 0; i < N; ++i) + { + m_values[i] = new QDoubleSpinBox; + m_values[i]->setStyleSheet("background-color: rgba(255,255,255,255)"); + m_layout->addRow(QString::fromUtf8(&componentName[i], 1), m_values[i]); + } + + m_widget = new QWidget; + m_widget->setStyleSheet("background-color: rgba(0,0,0,0)"); + m_widget->setLayout(m_layout); +} + +template +QWidget* VecValue::embeddedWidget() +{ + return m_widget; +} + +template +unsigned int VecValue::nPorts(QtNodes::PortType portType) const +{ + switch (portType) + { + case QtNodes::PortType::In: return 0; + case QtNodes::PortType::Out: return 1; + } + + return 0; +} + +Vec4Data::Vec4Data(const Nz::Vector4f& vec) : +value(vec) +{ +} diff --git a/src/ShaderNode/main.cpp b/src/ShaderNode/main.cpp new file mode 100644 index 000000000..34141730c --- /dev/null +++ b/src/ShaderNode/main.cpp @@ -0,0 +1,127 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +std::shared_ptr registerDataModels() +{ + auto ret = std::make_shared(); + ret->registerModel(); + ret->registerModel(); + + return ret; +} + +void GenerateGLSL(QtNodes::FlowScene* scene) +{ + /*using namespace ShaderBuilder; + using ShaderAst::BuiltinEntry; + using ShaderAst::ExpressionType; + + // Fragment shader + { + auto rt0 = Output("RenderTarget0", ExpressionType::Float4); + auto color = Uniform("Color", ExpressionType::Float4); + + fragmentShader = writer.Generate(ExprStatement(Assign(rt0, color))); + }*/ + + Nz::GlslWriter writer; + std::vector statements; + + std::function HandleNode; + HandleNode = [&](QtNodes::Node* node) -> Nz::ShaderAst::ExpressionPtr + { + ShaderNode* shaderNode = static_cast(node->nodeDataModel()); + + qDebug() << shaderNode->name(); + std::size_t inputCount = shaderNode->nPorts(QtNodes::PortType::In); + Nz::StackArray expressions = NazaraStackArray(Nz::ShaderAst::ExpressionPtr, inputCount); + std::size_t i = 0; + + for (const auto& connectionSet : node->nodeState().getEntries(QtNodes::PortType::In)) + { + for (const auto& [uuid, conn] : connectionSet) + { + assert(i < expressions.size()); + expressions[i] = HandleNode(conn->getNode(QtNodes::PortType::Out)); + i++; + } + } + + return shaderNode->GetExpression(expressions.data(), expressions.size()); + }; + + scene->iterateOverNodes([&](QtNodes::Node* node) + { + if (node->nodeDataModel()->nPorts(QtNodes::PortType::Out) == 0) + { + statements.emplace_back(Nz::ShaderBuilder::ExprStatement(HandleNode(node))); + //qDebug() << node->nodeDataModel()->name(); + } + }); + + Nz::String glsl = writer.Generate(std::make_shared(std::move(statements))); + + QTextEdit* output = new QTextEdit; + output->setText(QString::fromUtf8(glsl.GetConstBuffer(), glsl.GetSize())); + output->setAttribute(Qt::WA_DeleteOnClose, true); + output->setWindowTitle("GLSL Output"); + output->show(); + + std::cout << glsl << std::endl; +} + +void SetupTestScene(QtNodes::FlowScene* scene) +{ + auto& node1 = scene->createNode(std::make_unique()); + node1.nodeGraphicsObject().setPos(200, 200); + + auto& node2 = scene->createNode(std::make_unique()); + node2.nodeGraphicsObject().setPos(500, 300); + + scene->createConnection(node2, 0, node1, 0); +} + +int main(int argc, char* argv[]) +{ + QApplication app(argc, argv); + + QWidget mainWindow; + QVBoxLayout* layout = new QVBoxLayout; + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + QtNodes::FlowScene* scene = new QtNodes::FlowScene(registerDataModels()); + SetupTestScene(scene); + + QMenuBar* menuBar = new QMenuBar; + QAction* glslCode = menuBar->addAction("To GLSL"); + QObject::connect(glslCode, &QAction::triggered, [&](bool) { GenerateGLSL(scene); }); + + layout->addWidget(new QtNodes::FlowView(scene)); + layout->addWidget(menuBar); + + mainWindow.setLayout(layout); + mainWindow.setWindowTitle("Nazara Shader nodes"); + mainWindow.resize(1280, 720); + mainWindow.show(); + + return app.exec(); +}