Add shadernode (big WIP)

This commit is contained in:
Lynix 2020-05-18 19:55:12 +02:00
parent 8c0d34313e
commit c26f3b9b71
12 changed files with 410 additions and 1 deletions

3
.gitignore vendored
View File

@ -1,7 +1,8 @@
# Nazara build
build/config.lua
# Nazara libraries
# Nazara binaries
bin/*
lib/*
# Self-hosted thirdparty libraries binaries

View File

@ -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]]
}

View File

@ -0,0 +1,44 @@
#include <DataModels/FragmentOutput.hpp>
#include <Nazara/Renderer/ShaderBuilder.hpp>
#include <DataModels/VecValue.hpp>
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<QtNodes::NodeData> FragmentOutput::outData(QtNodes::PortIndex /*port*/)
{
return {};
}

View File

@ -0,0 +1,31 @@
#pragma once
#ifndef NAZARA_SHADERNODES_FRAGMENTOUTPUT_HPP
#define NAZARA_SHADERNODES_FRAGMENTOUTPUT_HPP
#include <DataModels/ShaderNode.hpp>
#include <QtWidgets/QDoubleSpinBox>
#include <QtWidgets/QFormLayout>
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<QtNodes::NodeData> outData(QtNodes::PortIndex port) override;
void setInData(std::shared_ptr<QtNodes::NodeData>, int) override {};
};
#include <DataModels/FragmentOutput.inl>
#endif

View File

@ -0,0 +1 @@
#include <DataModels/FragmentOutput.hpp>

View File

@ -0,0 +1 @@
#include <DataModels/ShaderNode.hpp>

View File

@ -0,0 +1,17 @@
#pragma once
#ifndef NAZARA_SHADERNODES_SHADERNODE_HPP
#define NAZARA_SHADERNODES_SHADERNODE_HPP
#include <Nazara/Renderer/ShaderAst.hpp>
#include <nodes/NodeDataModel>
class ShaderNode : public QtNodes::NodeDataModel
{
public:
virtual Nz::ShaderAst::ExpressionPtr GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const = 0;
};
#include <DataModels/ShaderNode.inl>
#endif

View File

@ -0,0 +1 @@
#include <DataModels/ShaderNode.hpp>

View File

@ -0,0 +1,34 @@
#include <DataModels/VecValue.hpp>
#include <Nazara/Renderer/ShaderBuilder.hpp>
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<QtNodes::NodeData> Vec4Value::outData(QtNodes::PortIndex port)
{
assert(port == 0);
return std::make_shared<Vec4Data>(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);
}

View File

@ -0,0 +1,66 @@
#pragma once
#ifndef NAZARA_SHADERNODES_VECVALUE_HPP
#define NAZARA_SHADERNODES_VECVALUE_HPP
#include <QtWidgets/QDoubleSpinBox>
#include <QtWidgets/QFormLayout>
#include <DataModels/ShaderNode.hpp>
#include <array>
template<std::size_t N>
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<QDoubleSpinBox*, N> 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<QtNodes::NodeData> outData(QtNodes::PortIndex port) override;
void setInData(std::shared_ptr<QtNodes::NodeData>, int) override {};
private:
Nz::Vector4f GetValue() const;
};
#include <DataModels/VecValue.inl>
#endif

View File

@ -0,0 +1,44 @@
#include <DataModels/VecValue.hpp>
#include <array>
template<std::size_t N>
VecValue<N>::VecValue()
{
constexpr std::array<char, 4> 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<std::size_t N>
QWidget* VecValue<N>::embeddedWidget()
{
return m_widget;
}
template<std::size_t N>
unsigned int VecValue<N>::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)
{
}

127
src/ShaderNode/main.cpp Normal file
View File

@ -0,0 +1,127 @@
#include <Nazara/Core/StackArray.hpp>
#include <Nazara/Renderer/ShaderAst.hpp>
#include <Nazara/Renderer/ShaderBuilder.hpp>
#include <Nazara/Renderer/GlslWriter.hpp>
#include <DataModels/FragmentOutput.hpp>
#include <DataModels/ShaderNode.hpp>
#include <DataModels/VecValue.hpp>
#include <QtCore/QDebug>
#include <QtWidgets/QApplication>
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QTextEdit>
#include <QtWidgets/QWidget>
#include <nodes/Node>
#include <nodes/NodeData>
#include <nodes/NodeGeometry>
#include <nodes/FlowScene>
#include <nodes/FlowView>
#include <nodes/DataModelRegistry>
#include <iostream>
std::shared_ptr<QtNodes::DataModelRegistry> registerDataModels()
{
auto ret = std::make_shared<QtNodes::DataModelRegistry>();
ret->registerModel<FragmentOutput>();
ret->registerModel<Vec4Value>();
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<Nz::ShaderAst::StatementPtr> statements;
std::function<Nz::ShaderAst::ExpressionPtr(QtNodes::Node*)> HandleNode;
HandleNode = [&](QtNodes::Node* node) -> Nz::ShaderAst::ExpressionPtr
{
ShaderNode* shaderNode = static_cast<ShaderNode*>(node->nodeDataModel());
qDebug() << shaderNode->name();
std::size_t inputCount = shaderNode->nPorts(QtNodes::PortType::In);
Nz::StackArray<Nz::ShaderAst::ExpressionPtr> 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<Nz::ShaderAst::StatementBlock>(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<Vec4Value>());
node1.nodeGraphicsObject().setPos(200, 200);
auto& node2 = scene->createNode(std::make_unique<FragmentOutput>());
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();
}