ShaderNode: Add texture support

This commit is contained in:
Lynix 2020-05-22 15:39:10 +02:00
parent effaa9b88f
commit 33c8fe2562
20 changed files with 493 additions and 260 deletions

View File

@ -4,12 +4,21 @@
#include <Nazara/Renderer/ShaderBuilder.hpp>
SampleTexture::SampleTexture(ShaderGraph& graph) :
ShaderNode(graph)
ShaderNode(graph),
m_currentTextureIndex(0)
{
m_layout = new QVBoxLayout;
m_textureSelection = new QComboBox;
m_textureSelection->setStyleSheet("background-color: rgba(255,255,255,255)");
connect(m_textureSelection, qOverload<int>(&QComboBox::currentIndexChanged), [&](int index)
{
if (index < 0)
return;
m_currentTextureIndex = static_cast<std::size_t>(index);
UpdatePreview();
});
m_layout->addWidget(m_textureSelection);
@ -25,8 +34,15 @@ ShaderNode(graph)
m_widget->setStyleSheet("background-color: rgba(0,0,0,0)");
m_widget->setLayout(m_layout);
m_onTextureListUpdate.Connect(GetGraph().OnTextureListUpdate, [&](ShaderGraph*) { UpdateTextureList(); });
m_onTextureListUpdateSlot.Connect(GetGraph().OnTextureListUpdate, [&](ShaderGraph*) { UpdateTextureList(); });
m_onTexturePreviewUpdateSlot.Connect(GetGraph().OnTexturePreviewUpdate, [&](ShaderGraph*, std::size_t textureIndex)
{
if (m_currentTextureIndex == textureIndex)
UpdatePreview();
});
UpdateTextureList();
UpdatePreview();
}
QWidget* SampleTexture::embeddedWidget()
@ -47,8 +63,13 @@ unsigned int SampleTexture::nPorts(QtNodes::PortType portType) const
void SampleTexture::UpdatePreview()
{
if (m_textureSelection->count() == 0)
return;
ComputePreview(m_pixmap);
m_pixmapLabel->setPixmap(m_pixmap);
Q_EMIT dataUpdated(0);
}
void SampleTexture::UpdateTextureList()
@ -64,7 +85,9 @@ void SampleTexture::UpdateTextureList()
void SampleTexture::ComputePreview(QPixmap& pixmap) const
{
pixmap.fill();
const auto& textureEntry = GetGraph().GetTexture(m_currentTextureIndex);
pixmap = QPixmap::fromImage(textureEntry.preview).scaled(128, 128, Qt::KeepAspectRatio);
}
Nz::ShaderAst::ExpressionPtr SampleTexture::GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const
@ -102,5 +125,10 @@ std::shared_ptr<QtNodes::NodeData> SampleTexture::outData(QtNodes::PortIndex por
{
assert(port == 0);
return std::make_shared<Vec4Data>(Nz::Vector4f::Zero());
const auto& textureEntry = GetGraph().GetTexture(m_currentTextureIndex);
auto vecData = std::make_shared<Vec4Data>();
vecData->preview = textureEntry.preview;
return vecData;
}

View File

@ -28,13 +28,14 @@ class SampleTexture : public ShaderNode
std::shared_ptr<QtNodes::NodeData> outData(QtNodes::PortIndex port) override;
protected:
void ComputePreview(QPixmap& pixmap) const;
void UpdatePreview();
void UpdateTextureList();
void ComputePreview(QPixmap& pixmap) const;
NazaraSlot(ShaderGraph, OnTextureListUpdate, m_onTextureListUpdate);
NazaraSlot(ShaderGraph, OnTextureListUpdate, m_onTextureListUpdateSlot);
NazaraSlot(ShaderGraph, OnTexturePreviewUpdate, m_onTexturePreviewUpdateSlot);
std::size_t m_currentTextureIndex;
QComboBox* m_textureSelection;
QLabel* m_pixmapLabel;
QPixmap m_pixmap;

View File

@ -1 +1,5 @@
#include <DataModels/ShaderNode.hpp>
void ShaderNode::setInData(std::shared_ptr<QtNodes::NodeData>, int)
{
}

View File

@ -17,7 +17,7 @@ class ShaderNode : public QtNodes::NodeDataModel
inline ShaderGraph& GetGraph();
inline const ShaderGraph& GetGraph() const;
void setInData(std::shared_ptr<QtNodes::NodeData>, int) override {};
void setInData(std::shared_ptr<QtNodes::NodeData>, int) override;
private:
ShaderGraph& m_graph;

View File

@ -1 +1,23 @@
#include <DataModels/VecBinOp.hpp>
void Vec4Add::ApplyOp(const std::uint8_t* left, const std::uint8_t* right, std::uint8_t* output, std::size_t pixelCount)
{
for (std::size_t i = 0; i < pixelCount; ++i)
{
unsigned int lValue = left[i];
unsigned int rValue = right[i];
output[i] = static_cast<std::uint8_t>(std::min(lValue + rValue, 255U));
}
}
void Vec4Mul::ApplyOp(const std::uint8_t* left, const std::uint8_t* right, std::uint8_t* output, std::size_t pixelCount)
{
for (std::size_t i = 0; i < pixelCount; ++i)
{
unsigned int lValue = left[i];
unsigned int rValue = right[i];
output[i] = static_cast<std::uint8_t>(lValue * rValue / 255);
}
}

View File

@ -25,16 +25,14 @@ class VecBinOp : public ShaderNode
void setInData(std::shared_ptr<QtNodes::NodeData> value, int index) override;
private:
virtual void ApplyOp(const std::uint8_t* left, const std::uint8_t* right, std::uint8_t* output, std::size_t pixelCount) = 0;
void UpdatePreview();
using InternalType = typename Data::InternalType;
InternalType GetValue() const;
QLabel* m_pixmapLabel;
QPixmap m_preview;
std::shared_ptr<Vec4Data> m_lhs;
std::shared_ptr<Vec4Data> m_rhs;
std::shared_ptr<Vec4Data> m_output;
};
class Vec4Add : public VecBinOp<Vec4Data, Nz::ShaderAst::BinaryType::Add>
@ -44,6 +42,8 @@ class Vec4Add : public VecBinOp<Vec4Data, Nz::ShaderAst::BinaryType::Add>
QString caption() const override { return "Vec4 addition"; }
QString name() const override { return "Vec4Add"; }
void ApplyOp(const std::uint8_t* left, const std::uint8_t* right, std::uint8_t* output, std::size_t pixelCount) override;
};
class Vec4Mul : public VecBinOp<Vec4Data, Nz::ShaderAst::BinaryType::Multiply>
@ -53,6 +53,8 @@ class Vec4Mul : public VecBinOp<Vec4Data, Nz::ShaderAst::BinaryType::Multiply>
QString caption() const override { return "Vec4 multiplication"; }
QString name() const override { return "Vec4Mul"; }
void ApplyOp(const std::uint8_t* left, const std::uint8_t* right, std::uint8_t* output, std::size_t pixelCount) override;
};
#include <DataModels/VecBinOp.inl>

View File

@ -5,10 +5,11 @@ template<typename Data, Nz::ShaderAst::BinaryType BinOp>
VecBinOp<Data, BinOp>::VecBinOp(ShaderGraph& graph) :
ShaderNode(graph)
{
m_preview = QPixmap(64, 64);
m_output = std::make_shared<Data>();
m_pixmapLabel = new QLabel;
m_pixmapLabel->setPixmap(m_preview);
m_pixmapLabel->setStyleSheet("background-color: rgba(0,0,0,0)");
UpdatePreview();
}
template<typename Data, Nz::ShaderAst::BinaryType BinOp>
@ -50,17 +51,18 @@ template<typename Data, Nz::ShaderAst::BinaryType BinOp>
std::shared_ptr<QtNodes::NodeData> VecBinOp<Data, BinOp>::outData(QtNodes::PortIndex port)
{
assert(port == 0);
return std::make_shared<Data>(GetValue());
return m_output;
}
template<typename Data, Nz::ShaderAst::BinaryType BinOp>
void VecBinOp<Data, BinOp>::setInData(std::shared_ptr<QtNodes::NodeData> value, int index)
{
assert(index == 0 || index == 1);
std::shared_ptr<Data> castedValue;
if (value)
{
assert(dynamic_cast<Data*>(value.get()) != nullptr);
assert(index == 0 || index == 1);
castedValue = std::static_pointer_cast<Data>(value);
}
@ -78,16 +80,36 @@ void VecBinOp<Data, BinOp>::setInData(std::shared_ptr<QtNodes::NodeData> value,
template<typename Data, Nz::ShaderAst::BinaryType BinOp>
void VecBinOp<Data, BinOp>::UpdatePreview()
{
InternalType value = GetValue();
m_preview.fill(QColor::fromRgbF(value.x, value.y, value.z, value.w));
if (m_lhs && m_rhs)
{
const QImage& leftPreview = m_lhs->preview;
const QImage& rightPreview = m_rhs->preview;
int maxWidth = std::max(leftPreview.width(), rightPreview.width());
int maxHeight = std::max(leftPreview.height(), rightPreview.height());
// Exploit COW
QImage leftResized = leftPreview;
if (leftResized.width() != maxWidth || leftResized.height() != maxHeight)
leftResized = leftResized.scaled(maxWidth, maxHeight);
QImage rightResized = rightPreview;
if (rightResized.width() != maxWidth || rightResized.height() != maxHeight)
rightResized = rightResized.scaled(maxWidth, maxHeight);
int w = m_output->preview.width();
int h = m_output->preview.height();
m_output->preview = QImage(maxWidth, maxHeight, QImage::Format_RGBA8888);
ApplyOp(leftResized.constBits(), rightResized.constBits(), m_output->preview.bits(), maxWidth * maxHeight * 4);
m_output->preview = m_output->preview.scaled(w, h);
m_preview = QPixmap::fromImage(m_output->preview, Qt::AutoColor | Qt::NoOpaqueDetection);
}
else
{
m_preview = QPixmap(64, 64);
m_preview.fill(QColor::fromRgb(255, 255, 0, 0));
}
m_pixmapLabel->setPixmap(m_preview);
}
template<typename Data, Nz::ShaderAst::BinaryType BinOp>
auto VecBinOp<Data, BinOp>::GetValue() const -> InternalType
{
if (!m_lhs || !m_rhs)
return InternalType::Zero();
return m_lhs->value * m_rhs->value;
}

View File

@ -1,89 +1 @@
#include <DataModels/VecValue.hpp>
#include <Nazara/Renderer/ShaderBuilder.hpp>
Vec2Value::Vec2Value(ShaderGraph& graph) :
VecValue(graph)
{
UpdatePreview();
}
Nz::ShaderAst::ExpressionPtr Vec2Value::GetExpression(Nz::ShaderAst::ExpressionPtr* /*expressions*/, std::size_t count) const
{
assert(count == 0);
return Nz::ShaderBuilder::Constant(GetValue());
}
auto Vec2Value::dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const -> QtNodes::NodeDataType
{
assert(portType == QtNodes::PortType::Out);
assert(portIndex == 0);
return Vec2Data::Type();
}
std::shared_ptr<QtNodes::NodeData> Vec2Value::outData(QtNodes::PortIndex port)
{
assert(port == 0);
return std::make_shared<Vec2Data>(GetValue());
}
void Vec2Value::ComputePreview(QPixmap& pixmap) const
{
Nz::Vector4f value = GetValue();
pixmap.fill(QColor::fromRgbF(value.x, value.y, value.z, value.w));
}
Nz::Vector2f Vec2Value::GetValue() const
{
float x = float(m_values[0]->value());
float y = float(m_values[1]->value());
return Nz::Vector2f(x, y);
}
Vec4Value::Vec4Value(ShaderGraph& graph) :
VecValue(graph)
{
UpdatePreview();
}
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());
}
void Vec4Value::ComputePreview(QPixmap& pixmap) const
{
Nz::Vector4f value = GetValue();
pixmap.fill(QColor::fromRgbF(value.x, value.y, value.z, value.w));
}
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

@ -3,6 +3,7 @@
#ifndef NAZARA_SHADERNODES_VECVALUE_HPP
#define NAZARA_SHADERNODES_VECVALUE_HPP
#include <QtGui/QImage>
#include <QtWidgets/QDoubleSpinBox>
#include <QtWidgets/QFormLayout>
#include <QtWidgets/QLabel>
@ -10,34 +11,65 @@
#include <array>
template<std::size_t N>
struct VecTypeHelper;
template<>
struct VecTypeHelper<2>
{
using Type = Nz::Vector2f;
};
template<>
struct VecTypeHelper<3>
{
using Type = Nz::Vector3f;
};
template<>
struct VecTypeHelper<4>
{
using Type = Nz::Vector4f;
};
template<std::size_t N> using VecType = typename VecTypeHelper<N>::template Type;
template<std::size_t N, typename Data>
class VecValue : public ShaderNode
{
public:
VecValue(ShaderGraph& graph);
~VecValue() = default;
QtNodes::NodeDataType dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override;
QWidget* embeddedWidget() override;
unsigned int nPorts(QtNodes::PortType portType) const override;
protected:
void UpdatePreview();
std::shared_ptr<QtNodes::NodeData> outData(QtNodes::PortIndex port) override;
virtual void ComputePreview(QPixmap& pixmap) const = 0;
Nz::ShaderAst::ExpressionPtr GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const override;
protected:
QColor ToColor() const;
VecType<N> ToVector() const;
void UpdatePreview();
QLabel* m_pixmapLabel;
QPixmap m_pixmap;
QWidget* m_widget;
QFormLayout* m_layout;
std::array<QDoubleSpinBox*, N> m_values;
std::array<QDoubleSpinBox*, N> m_spinboxes;
};
class Vec2Data : public QtNodes::NodeData
struct VecData : public QtNodes::NodeData
{
public:
using InternalType = Nz::Vector2f;
inline VecData();
inline Vec2Data(const InternalType& vec);
QImage preview;
};
struct Vec2Data : public VecData
{
QtNodes::NodeDataType type() const override
{
return Type();
@ -47,17 +79,10 @@ class Vec2Data : public QtNodes::NodeData
{
return { "vec2", "Vec2" };
}
InternalType value;
};
class Vec4Data : public QtNodes::NodeData
struct Vec4Data : public VecData
{
public:
using InternalType = Nz::Vector4f;
inline Vec4Data(const InternalType& vec);
QtNodes::NodeDataType type() const override
{
return Type();
@ -67,48 +92,24 @@ class Vec4Data : public QtNodes::NodeData
{
return { "vec4", "Vec4" };
}
InternalType value;
};
class Vec2Value : public VecValue<2>
class Vec2Value : public VecValue<2, Vec2Data>
{
public:
Vec2Value(ShaderGraph& graph);
Nz::ShaderAst::ExpressionPtr GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const override;
using VecValue::VecValue;
QString caption() const override { return "Vec2 value"; }
QString name() const override { return "Vec2Value"; }
QtNodes::NodeDataType dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override;
std::shared_ptr<QtNodes::NodeData> outData(QtNodes::PortIndex port) override;
private:
void ComputePreview(QPixmap& pixmap) const override;
Nz::Vector2f GetValue() const;
};
class Vec4Value : public VecValue<4>
class Vec4Value : public VecValue<4, Vec4Data>
{
public:
Vec4Value(ShaderGraph& graph);
Nz::ShaderAst::ExpressionPtr GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const override;
using VecValue::VecValue;
QString caption() const override { return "Vec4 value"; }
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;
private:
void ComputePreview(QPixmap& pixmap) const override;
Nz::Vector4f GetValue() const;
};
#include <DataModels/VecValue.inl>

View File

@ -1,8 +1,10 @@
#include <Nazara/Core/Algorithm.hpp>
#include <Nazara/Renderer/ShaderBuilder.hpp>
#include <DataModels/VecValue.hpp>
#include <array>
template<std::size_t N>
VecValue<N>::VecValue(ShaderGraph& graph) :
template<std::size_t N, typename Data>
VecValue<N, Data>::VecValue(ShaderGraph& graph) :
ShaderNode(graph)
{
constexpr std::array<char, 4> componentName = { 'X', 'Y', 'Z', 'W' };
@ -11,21 +13,21 @@ ShaderNode(graph)
m_layout = new QFormLayout;
for (std::size_t i = 0; i < N; ++i)
{
m_values[i] = new QDoubleSpinBox;
m_values[i]->setDecimals(6);
m_values[i]->setValue(1.0);
m_values[i]->setStyleSheet("background-color: rgba(255,255,255,255)");
connect(m_values[i], qOverload<double>(&QDoubleSpinBox::valueChanged), [this](double)
m_spinboxes[i] = new QDoubleSpinBox;
m_spinboxes[i]->setDecimals(6);
m_spinboxes[i]->setValue(1.0);
m_spinboxes[i]->setStyleSheet("background-color: rgba(255,255,255,255)");
connect(m_spinboxes[i], qOverload<double>(&QDoubleSpinBox::valueChanged), [this](double)
{
UpdatePreview();
Q_EMIT dataUpdated(0);
});
m_layout->addRow(QString::fromUtf8(&componentName[i], 1), m_values[i]);
m_layout->addRow(QString::fromUtf8(&componentName[i], 1), m_spinboxes[i]);
}
m_pixmap = QPixmap(64, 64);
m_pixmap = QPixmap(32, 32);
m_pixmap.fill();
m_pixmapLabel = new QLabel;
@ -36,16 +38,27 @@ ShaderNode(graph)
m_widget = new QWidget;
m_widget->setStyleSheet("background-color: rgba(0,0,0,0)");
m_widget->setLayout(m_layout);
UpdatePreview();
}
template<std::size_t N>
QWidget* VecValue<N>::embeddedWidget()
template<std::size_t N, typename Data>
QtNodes::NodeDataType VecValue<N, Data>::dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const
{
assert(portType == QtNodes::PortType::Out);
assert(portIndex == 0);
return Data::Type();
}
template<std::size_t N, typename Data>
QWidget* VecValue<N, Data>::embeddedWidget()
{
return m_widget;
}
template<std::size_t N>
unsigned int VecValue<N>::nPorts(QtNodes::PortType portType) const
template<std::size_t N, typename Data>
unsigned int VecValue<N, Data>::nPorts(QtNodes::PortType portType) const
{
switch (portType)
{
@ -56,19 +69,64 @@ unsigned int VecValue<N>::nPorts(QtNodes::PortType portType) const
return 0;
}
template<std::size_t N>
void VecValue<N>::UpdatePreview()
template<std::size_t N, typename Data>
std::shared_ptr<QtNodes::NodeData> VecValue<N, Data>::outData(QtNodes::PortIndex port)
{
ComputePreview(m_pixmap);
assert(port == 0);
auto out = std::make_shared<Data>();
out->preview = QImage(1, 1, QImage::Format_RGBA8888);
out->preview.fill(ToColor());
return out;
}
template<std::size_t N, typename Data>
Nz::ShaderAst::ExpressionPtr VecValue<N, Data>::GetExpression(Nz::ShaderAst::ExpressionPtr* /*expressions*/, std::size_t count) const
{
assert(count == 0);
return Nz::ShaderBuilder::Constant(ToVector());
}
template<std::size_t N, typename Data>
QColor VecValue<N, Data>::ToColor() const
{
std::array<float, 4> values = { 0.f, 0.f, 0.f, 1.f };
for (std::size_t i = 0; i < N; ++i)
values[i] = std::clamp<float>(m_spinboxes[i]->value(), 0.0, 1.0);
return QColor::fromRgbF(values[0], values[1], values[2], values[3]);
}
template<std::size_t N, typename Data>
VecType<N> VecValue<N, Data>::ToVector() const
{
std::array<float, 4> values = { 0.f, 0.f, 0.f, 1.f };
for (std::size_t i = 0; i < N; ++i)
values[i] = std::clamp<float>(m_spinboxes[i]->value(), 0.0, 1.0);
if constexpr (N == 2)
return Nz::Vector2f(values[0], values[1]);
else if constexpr (N == 3)
return Nz::Vector3f(values[0], values[1], values[2]);
else if constexpr (N == 4)
return Nz::Vector4f(values[0], values[1], values[2], values[3]);
else
static_assert(Nz::AlwaysFalse<std::make_integer_sequence<int, N>>(), "Unhandled vector size");
}
template<std::size_t N, typename Data>
void VecValue<N, Data>::UpdatePreview()
{
m_pixmap.fill(ToColor());
m_pixmapLabel->setPixmap(m_pixmap);
}
Vec2Data::Vec2Data(const InternalType& vec) :
value(vec)
{
}
Vec4Data::Vec4Data(const InternalType& vec) :
value(vec)
inline VecData::VecData() :
preview(64, 64, QImage::Format_RGBA8888)
{
preview.fill(QColor::fromRgb(255, 255, 255, 0));
}

View File

@ -16,10 +16,10 @@
namespace
{
template<typename T>
void RegisterShaderNode(ShaderGraph& graph, std::shared_ptr<QtNodes::DataModelRegistry> registry)
void RegisterShaderNode(ShaderGraph& graph, std::shared_ptr<QtNodes::DataModelRegistry> registry, QString category = QString())
{
auto creator = [&] { return std::make_unique<T>(graph); };
registry->registerModel<T>(std::move(creator));
registry->registerModel<T>(category, std::move(creator));
}
}
@ -35,16 +35,19 @@ m_flowScene(BuildRegistry())
m_flowScene.createConnection(node2, 0, node1, 0);
}
void ShaderGraph::AddTexture(std::string name, Nz::ShaderAst::ExpressionType type)
std::size_t ShaderGraph::AddTexture(std::string name, Nz::ShaderAst::ExpressionType type)
{
std::size_t index = m_textures.size();
auto& textureEntry = m_textures.emplace_back();
textureEntry.name = std::move(name);
textureEntry.type = type;
OnTextureListUpdate(this);
return index;
}
Nz::ShaderAst::StatementPtr ShaderGraph::Generate()
Nz::ShaderAst::StatementPtr ShaderGraph::ToAst()
{
std::vector<Nz::ShaderAst::StatementPtr> statements;
@ -82,14 +85,25 @@ Nz::ShaderAst::StatementPtr ShaderGraph::Generate()
return std::make_shared<Nz::ShaderAst::StatementBlock>(std::move(statements));
}
void ShaderGraph::UpdateTexturePreview(std::size_t textureIndex, QImage preview)
{
assert(textureIndex < m_textures.size());
auto& textureEntry = m_textures[textureIndex];
textureEntry.preview = std::move(preview);
textureEntry.preview.convertTo(QImage::Format_RGBA8888);
OnTexturePreviewUpdate(this, textureIndex);
}
std::shared_ptr<QtNodes::DataModelRegistry> ShaderGraph::BuildRegistry()
{
auto registry = std::make_shared<QtNodes::DataModelRegistry>();
RegisterShaderNode<FragmentOutput>(*this, registry);
RegisterShaderNode<SampleTexture>(*this, registry);
RegisterShaderNode<Vec4Mul>(*this, registry);
RegisterShaderNode<Vec2Value>(*this, registry);
RegisterShaderNode<Vec4Value>(*this, registry);
RegisterShaderNode<FragmentOutput>(*this, registry, "Output");
RegisterShaderNode<SampleTexture>(*this, registry, "Texture");
RegisterShaderNode<Vec4Add>(*this, registry, "Vector operations");
RegisterShaderNode<Vec4Mul>(*this, registry, "Vector operations");
RegisterShaderNode<Vec2Value>(*this, registry, "Constants");
RegisterShaderNode<Vec4Value>(*this, registry, "Constants");
return registry;
}

View File

@ -18,21 +18,26 @@ class ShaderGraph
ShaderGraph();
~ShaderGraph() = default;
void AddTexture(std::string name, Nz::ShaderAst::ExpressionType type);
Nz::ShaderAst::StatementPtr Generate();
std::size_t AddTexture(std::string name, Nz::ShaderAst::ExpressionType type);
inline QtNodes::FlowScene& GetScene();
inline const TextureEntry& GetTexture(std::size_t textureIndex) const;
inline const std::vector<TextureEntry>& GetTextures();
NazaraSignal(OnTextureListUpdate, ShaderGraph*);
Nz::ShaderAst::StatementPtr ToAst();
void UpdateTexturePreview(std::size_t texture, QImage preview);
struct TextureEntry
{
std::string name;
Nz::ShaderAst::ExpressionType type;
QImage preview;
};
NazaraSignal(OnTextureListUpdate, ShaderGraph*);
NazaraSignal(OnTexturePreviewUpdate, ShaderGraph*, std::size_t /*textureIndex*/);
private:
std::shared_ptr<QtNodes::DataModelRegistry> BuildRegistry();

View File

@ -5,7 +5,14 @@ inline QtNodes::FlowScene& ShaderGraph::GetScene()
return m_flowScene;
}
inline auto ShaderGraph::GetTexture(std::size_t textureIndex) const -> const TextureEntry&
{
assert(textureIndex < m_textures.size());
return m_textures[textureIndex];
}
inline auto ShaderGraph::GetTextures() -> const std::vector<TextureEntry>&
{
return m_textures;
}

View File

@ -0,0 +1,50 @@
#include <Widgets/MainWindow.hpp>
#include <Nazara/Renderer/GlslWriter.hpp>
#include <ShaderGraph.hpp>
#include <Widgets/TextureEditor.hpp>
#include <nodes/FlowView>
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QTextEdit>
#include <iostream>
MainWindow::MainWindow(ShaderGraph& graph) :
m_shaderGraph(graph)
{
setWindowTitle("Nazara Shader nodes");
QtNodes::FlowScene* scene = &m_shaderGraph.GetScene();
QtNodes::FlowView* flowView = new QtNodes::FlowView(scene);
setCentralWidget(flowView);
QDockWidget* textureDock = new QDockWidget(tr("&Textures"));
TextureEditor* textureEditor = new TextureEditor(m_shaderGraph);
textureDock->setWidget(textureEditor);
addDockWidget(Qt::LeftDockWidgetArea, textureDock);
BuildMenu();
}
void MainWindow::BuildMenu()
{
QMenu* compileMenu = menuBar()->addMenu(tr("&Compilation"));
QAction* compileToGlsl = compileMenu->addAction(tr("GLSL"));
connect(compileToGlsl, &QAction::triggered, [&](bool) { OnCompileToGLSL(); });
}
void MainWindow::OnCompileToGLSL()
{
Nz::GlslWriter writer;
Nz::String glsl = writer.Generate(m_shaderGraph.ToAst());
std::cout << glsl << std::endl;
QTextEdit* output = new QTextEdit;
output->setReadOnly(true);
output->setText(QString::fromUtf8(glsl.GetConstBuffer(), int(glsl.GetSize())));
output->setAttribute(Qt::WA_DeleteOnClose, true);
output->setWindowTitle("GLSL Output");
output->show();
}

View File

@ -0,0 +1,26 @@
#pragma once
#ifndef NAZARA_SHADERNODES_MAINWINDOW_HPP
#define NAZARA_SHADERNODES_MAINWINDOW_HPP
#include <QtWidgets/QMainWindow>
#include <DataModels/ShaderNode.hpp>
class ShaderGraph;
class MainWindow : public QMainWindow
{
public:
MainWindow(ShaderGraph& graph);
~MainWindow() = default;
private:
void BuildMenu();
void OnCompileToGLSL();
ShaderGraph& m_shaderGraph;
};
#include <Widgets/MainWindow.inl>
#endif

View File

@ -0,0 +1 @@
#include <Widgets/MainWindow.hpp>

View File

@ -0,0 +1,81 @@
#include <Widgets/TextureEditor.hpp>
#include <ShaderGraph.hpp>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QListWidget>
#include <QtWidgets/QVBoxLayout>
TextureEditor::TextureEditor(ShaderGraph& graph) :
m_shaderGraph(graph)
{
m_textureList = new QListWidget(this);
connect(m_textureList, &QListWidget::currentRowChanged, this, &TextureEditor::OnTextureSelectionUpdate);
m_pixmapLabel = new QLabel;
QPushButton* updateTextureButton = new QPushButton(tr("Load texture..."));
connect(updateTextureButton, &QPushButton::released, this, &TextureEditor::OnLoadTexture);
m_layout = new QVBoxLayout;
m_layout->addWidget(m_textureList);
m_layout->addWidget(updateTextureButton);
m_layout->addWidget(m_pixmapLabel);
setLayout(m_layout);
m_onTextureListUpdateSlot.Connect(m_shaderGraph.OnTextureListUpdate, this, &TextureEditor::OnTextureListUpdate);
m_onTexturePreviewUpdateSlot.Connect(m_shaderGraph.OnTexturePreviewUpdate, this, &TextureEditor::OnTexturePreviewUpdate);
RefreshTextures();
}
void TextureEditor::OnLoadTexture()
{
if (!m_currentTextureIndex)
return;
QString fileName = QFileDialog::getOpenFileName(nullptr, tr("Open Image"), QDir::homePath(), tr("Image Files (*.png *.jpg *.bmp)"));
if (fileName.isEmpty())
return;
m_shaderGraph.UpdateTexturePreview(m_currentTextureIndex.value(), QImage(fileName));
}
void TextureEditor::OnTextureSelectionUpdate(int textureIndex)
{
if (textureIndex >= 0)
{
m_currentTextureIndex = textureIndex;
UpdateTexturePreview();
}
else
m_currentTextureIndex.reset();
}
void TextureEditor::OnTextureListUpdate(ShaderGraph* graph)
{
RefreshTextures();
}
void TextureEditor::OnTexturePreviewUpdate(ShaderGraph* /*graph*/, std::size_t textureIndex)
{
if (m_currentTextureIndex && *m_currentTextureIndex == textureIndex)
UpdateTexturePreview();
}
void TextureEditor::RefreshTextures()
{
m_textureList->clear();
m_textureList->setCurrentRow(-1);
for (const auto& textureEntry : m_shaderGraph.GetTextures())
m_textureList->addItem(QString::fromStdString(textureEntry.name));
}
void TextureEditor::UpdateTexturePreview()
{
assert(m_currentTextureIndex);
const auto& textureEntry = m_shaderGraph.GetTexture(*m_currentTextureIndex);
m_pixmapLabel->setPixmap(QPixmap::fromImage(textureEntry.preview).scaled(128, 128, Qt::KeepAspectRatio));
}

View File

@ -0,0 +1,40 @@
#pragma once
#ifndef NAZARA_SHADERNODES_TEXTUREEDITOR_HPP
#define NAZARA_SHADERNODES_TEXTUREEDITOR_HPP
#include <ShaderGraph.hpp>
#include <QtWidgets/QDockWidget>
#include <optional>
class QLabel;
class QListWidget;
class QVBoxLayout;
class TextureEditor : public QWidget
{
public:
TextureEditor(ShaderGraph& graph);
~TextureEditor() = default;
private:
void OnLoadTexture();
void OnTextureSelectionUpdate(int textureIndex);
void OnTextureListUpdate(ShaderGraph* graph);
void OnTexturePreviewUpdate(ShaderGraph* graph, std::size_t textureIndex);
void RefreshTextures();
void UpdateTexturePreview();
NazaraSlot(ShaderGraph, OnTextureListUpdate, m_onTextureListUpdateSlot);
NazaraSlot(ShaderGraph, OnTexturePreviewUpdate, m_onTexturePreviewUpdateSlot);
std::optional<std::size_t> m_currentTextureIndex;
ShaderGraph& m_shaderGraph;
QLabel* m_pixmapLabel;
QListWidget* m_textureList;
QVBoxLayout* m_layout;
};
#include <Widgets/TextureEditor.inl>
#endif

View File

@ -0,0 +1 @@
#include <Widgets/TextureEditor.hpp>

View File

@ -1,59 +1,17 @@
#include <Nazara/Renderer/ShaderAst.hpp>
#include <Nazara/Renderer/ShaderBuilder.hpp>
#include <Nazara/Renderer/GlslWriter.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 <ShaderGraph.hpp>
#include <Widgets/MainWindow.hpp>
#include <iostream>
void GenerateGLSL(ShaderGraph& graph)
{
Nz::GlslWriter writer;
Nz::String glsl = writer.Generate(graph.Generate());
std::cout << glsl << std::endl;
QTextEdit* output = new QTextEdit;
output->setReadOnly(true);
output->setText(QString::fromUtf8(glsl.GetConstBuffer(), glsl.GetSize()));
output->setAttribute(Qt::WA_DeleteOnClose, true);
output->setWindowTitle("GLSL Output");
output->show();
}
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
ShaderGraph shaderGraph;
shaderGraph.AddTexture("TextureMachin", Nz::ShaderAst::ExpressionType::Sampler2D);
shaderGraph.AddTexture("Potato", Nz::ShaderAst::ExpressionType::Sampler2D);
shaderGraph.AddTexture("Blackbird", Nz::ShaderAst::ExpressionType::Sampler2D);
QWidget mainWindow;
QVBoxLayout* layout = new QVBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0);
QtNodes::FlowScene* scene = &shaderGraph.GetScene();
QMenuBar* menuBar = new QMenuBar;
QAction* glslCode = menuBar->addAction("To GLSL");
QObject::connect(glslCode, &QAction::triggered, [&](bool) { GenerateGLSL(shaderGraph); });
layout->addWidget(new QtNodes::FlowView(scene));
layout->addWidget(menuBar);
mainWindow.setLayout(layout);
mainWindow.setWindowTitle("Nazara Shader nodes");
MainWindow mainWindow(shaderGraph);
mainWindow.resize(1280, 720);
mainWindow.show();