diff --git a/src/ShaderNode/DataModels/SampleTexture.cpp b/src/ShaderNode/DataModels/SampleTexture.cpp index 1f4bd4f80..6d222abb6 100644 --- a/src/ShaderNode/DataModels/SampleTexture.cpp +++ b/src/ShaderNode/DataModels/SampleTexture.cpp @@ -4,12 +4,21 @@ #include 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(&QComboBox::currentIndexChanged), [&](int index) + { + if (index < 0) + return; + + m_currentTextureIndex = static_cast(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 SampleTexture::outData(QtNodes::PortIndex por { assert(port == 0); - return std::make_shared(Nz::Vector4f::Zero()); + const auto& textureEntry = GetGraph().GetTexture(m_currentTextureIndex); + + auto vecData = std::make_shared(); + vecData->preview = textureEntry.preview; + + return vecData; } diff --git a/src/ShaderNode/DataModels/SampleTexture.hpp b/src/ShaderNode/DataModels/SampleTexture.hpp index df107169c..f717797f2 100644 --- a/src/ShaderNode/DataModels/SampleTexture.hpp +++ b/src/ShaderNode/DataModels/SampleTexture.hpp @@ -28,13 +28,14 @@ class SampleTexture : public ShaderNode std::shared_ptr 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; diff --git a/src/ShaderNode/DataModels/ShaderNode.cpp b/src/ShaderNode/DataModels/ShaderNode.cpp index fba232649..1f45d3ca5 100644 --- a/src/ShaderNode/DataModels/ShaderNode.cpp +++ b/src/ShaderNode/DataModels/ShaderNode.cpp @@ -1 +1,5 @@ #include + +void ShaderNode::setInData(std::shared_ptr, int) +{ +} diff --git a/src/ShaderNode/DataModels/ShaderNode.hpp b/src/ShaderNode/DataModels/ShaderNode.hpp index 71a279120..d33fc632d 100644 --- a/src/ShaderNode/DataModels/ShaderNode.hpp +++ b/src/ShaderNode/DataModels/ShaderNode.hpp @@ -17,7 +17,7 @@ class ShaderNode : public QtNodes::NodeDataModel inline ShaderGraph& GetGraph(); inline const ShaderGraph& GetGraph() const; - void setInData(std::shared_ptr, int) override {}; + void setInData(std::shared_ptr, int) override; private: ShaderGraph& m_graph; diff --git a/src/ShaderNode/DataModels/VecBinOp.cpp b/src/ShaderNode/DataModels/VecBinOp.cpp index dd13b5b56..7349b708f 100644 --- a/src/ShaderNode/DataModels/VecBinOp.cpp +++ b/src/ShaderNode/DataModels/VecBinOp.cpp @@ -1 +1,23 @@ #include + +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::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(lValue * rValue / 255); + } +} diff --git a/src/ShaderNode/DataModels/VecBinOp.hpp b/src/ShaderNode/DataModels/VecBinOp.hpp index 131a05f6d..97f433048 100644 --- a/src/ShaderNode/DataModels/VecBinOp.hpp +++ b/src/ShaderNode/DataModels/VecBinOp.hpp @@ -25,16 +25,14 @@ class VecBinOp : public ShaderNode void setInData(std::shared_ptr 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 m_lhs; std::shared_ptr m_rhs; + std::shared_ptr m_output; }; class Vec4Add : public VecBinOp @@ -44,6 +42,8 @@ class Vec4Add : public VecBinOp 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 @@ -53,6 +53,8 @@ class Vec4Mul : public VecBinOp 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 diff --git a/src/ShaderNode/DataModels/VecBinOp.inl b/src/ShaderNode/DataModels/VecBinOp.inl index 371905221..fea7837a7 100644 --- a/src/ShaderNode/DataModels/VecBinOp.inl +++ b/src/ShaderNode/DataModels/VecBinOp.inl @@ -5,10 +5,11 @@ template VecBinOp::VecBinOp(ShaderGraph& graph) : ShaderNode(graph) { - m_preview = QPixmap(64, 64); + m_output = std::make_shared(); m_pixmapLabel = new QLabel; - m_pixmapLabel->setPixmap(m_preview); + m_pixmapLabel->setStyleSheet("background-color: rgba(0,0,0,0)"); + UpdatePreview(); } template @@ -39,7 +40,7 @@ unsigned int VecBinOp::nPorts(QtNodes::PortType portType) const { switch (portType) { - case QtNodes::PortType::In: return 2; + case QtNodes::PortType::In: return 2; case QtNodes::PortType::Out: return 1; } @@ -50,17 +51,18 @@ template std::shared_ptr VecBinOp::outData(QtNodes::PortIndex port) { assert(port == 0); - return std::make_shared(GetValue()); + return m_output; } template void VecBinOp::setInData(std::shared_ptr value, int index) { + assert(index == 0 || index == 1); + std::shared_ptr castedValue; if (value) { assert(dynamic_cast(value.get()) != nullptr); - assert(index == 0 || index == 1); castedValue = std::static_pointer_cast(value); } @@ -78,16 +80,36 @@ void VecBinOp::setInData(std::shared_ptr value, template void VecBinOp::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 -auto VecBinOp::GetValue() const -> InternalType -{ - if (!m_lhs || !m_rhs) - return InternalType::Zero(); - - return m_lhs->value * m_rhs->value; -} diff --git a/src/ShaderNode/DataModels/VecValue.cpp b/src/ShaderNode/DataModels/VecValue.cpp index 84f1f4ea8..807df2955 100644 --- a/src/ShaderNode/DataModels/VecValue.cpp +++ b/src/ShaderNode/DataModels/VecValue.cpp @@ -1,89 +1 @@ #include -#include - -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 Vec2Value::outData(QtNodes::PortIndex port) -{ - assert(port == 0); - - return std::make_shared(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 Vec4Value::outData(QtNodes::PortIndex port) -{ - assert(port == 0); - - return std::make_shared(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); -} diff --git a/src/ShaderNode/DataModels/VecValue.hpp b/src/ShaderNode/DataModels/VecValue.hpp index 10e215971..ba7a61f08 100644 --- a/src/ShaderNode/DataModels/VecValue.hpp +++ b/src/ShaderNode/DataModels/VecValue.hpp @@ -3,6 +3,7 @@ #ifndef NAZARA_SHADERNODES_VECVALUE_HPP #define NAZARA_SHADERNODES_VECVALUE_HPP +#include #include #include #include @@ -10,105 +11,105 @@ #include template +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 using VecType = typename VecTypeHelper::template Type; + +template 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 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 ToVector() const; + void UpdatePreview(); QLabel* m_pixmapLabel; QPixmap m_pixmap; QWidget* m_widget; QFormLayout* m_layout; - std::array m_values; + std::array m_spinboxes; }; -class Vec2Data : public QtNodes::NodeData +struct VecData : public QtNodes::NodeData { - public: - using InternalType = Nz::Vector2f; + inline VecData(); - inline Vec2Data(const InternalType& vec); - - QtNodes::NodeDataType type() const override - { - return Type(); - } - - static QtNodes::NodeDataType Type() - { - return { "vec2", "Vec2" }; - } - - InternalType value; + QImage preview; }; -class Vec4Data : public QtNodes::NodeData +struct Vec2Data : public VecData { - public: - using InternalType = Nz::Vector4f; + QtNodes::NodeDataType type() const override + { + return Type(); + } - inline Vec4Data(const InternalType& vec); - - QtNodes::NodeDataType type() const override - { - return Type(); - } - - static QtNodes::NodeDataType Type() - { - return { "vec4", "Vec4" }; - } - - InternalType value; + static QtNodes::NodeDataType Type() + { + return { "vec2", "Vec2" }; + } }; -class Vec2Value : public VecValue<2> +struct Vec4Data : public VecData +{ + QtNodes::NodeDataType type() const override + { + return Type(); + } + + static QtNodes::NodeDataType Type() + { + return { "vec4", "Vec4" }; + } +}; + +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 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 outData(QtNodes::PortIndex port) override; - - private: - void ComputePreview(QPixmap& pixmap) const override; - - Nz::Vector4f GetValue() const; }; #include diff --git a/src/ShaderNode/DataModels/VecValue.inl b/src/ShaderNode/DataModels/VecValue.inl index e79eef3dc..8f8a53fc7 100644 --- a/src/ShaderNode/DataModels/VecValue.inl +++ b/src/ShaderNode/DataModels/VecValue.inl @@ -1,8 +1,10 @@ +#include +#include #include #include -template -VecValue::VecValue(ShaderGraph& graph) : +template +VecValue::VecValue(ShaderGraph& graph) : ShaderNode(graph) { constexpr std::array 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(&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(&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 -QWidget* VecValue::embeddedWidget() +template +QtNodes::NodeDataType VecValue::dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const +{ + assert(portType == QtNodes::PortType::Out); + assert(portIndex == 0); + + return Data::Type(); +} + +template +QWidget* VecValue::embeddedWidget() { return m_widget; } -template -unsigned int VecValue::nPorts(QtNodes::PortType portType) const +template +unsigned int VecValue::nPorts(QtNodes::PortType portType) const { switch (portType) { @@ -56,19 +69,64 @@ unsigned int VecValue::nPorts(QtNodes::PortType portType) const return 0; } -template -void VecValue::UpdatePreview() +template +std::shared_ptr VecValue::outData(QtNodes::PortIndex port) { - ComputePreview(m_pixmap); + assert(port == 0); + + auto out = std::make_shared(); + out->preview = QImage(1, 1, QImage::Format_RGBA8888); + out->preview.fill(ToColor()); + + return out; +} + +template +Nz::ShaderAst::ExpressionPtr VecValue::GetExpression(Nz::ShaderAst::ExpressionPtr* /*expressions*/, std::size_t count) const +{ + assert(count == 0); + + return Nz::ShaderBuilder::Constant(ToVector()); +} + +template +QColor VecValue::ToColor() const +{ + std::array values = { 0.f, 0.f, 0.f, 1.f }; + + for (std::size_t i = 0; i < N; ++i) + values[i] = std::clamp(m_spinboxes[i]->value(), 0.0, 1.0); + + return QColor::fromRgbF(values[0], values[1], values[2], values[3]); +} + +template +VecType VecValue::ToVector() const +{ + std::array values = { 0.f, 0.f, 0.f, 1.f }; + + for (std::size_t i = 0; i < N; ++i) + values[i] = std::clamp(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>(), "Unhandled vector size"); +} + +template +void VecValue::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)); } diff --git a/src/ShaderNode/ShaderGraph.cpp b/src/ShaderNode/ShaderGraph.cpp index 478e99408..ee02e6c11 100644 --- a/src/ShaderNode/ShaderGraph.cpp +++ b/src/ShaderNode/ShaderGraph.cpp @@ -16,10 +16,10 @@ namespace { template - void RegisterShaderNode(ShaderGraph& graph, std::shared_ptr registry) + void RegisterShaderNode(ShaderGraph& graph, std::shared_ptr registry, QString category = QString()) { auto creator = [&] { return std::make_unique(graph); }; - registry->registerModel(std::move(creator)); + registry->registerModel(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 statements; @@ -82,14 +85,25 @@ Nz::ShaderAst::StatementPtr ShaderGraph::Generate() return std::make_shared(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 ShaderGraph::BuildRegistry() { auto registry = std::make_shared(); - RegisterShaderNode(*this, registry); - RegisterShaderNode(*this, registry); - RegisterShaderNode(*this, registry); - RegisterShaderNode(*this, registry); - RegisterShaderNode(*this, registry); + RegisterShaderNode(*this, registry, "Output"); + RegisterShaderNode(*this, registry, "Texture"); + RegisterShaderNode(*this, registry, "Vector operations"); + RegisterShaderNode(*this, registry, "Vector operations"); + RegisterShaderNode(*this, registry, "Constants"); + RegisterShaderNode(*this, registry, "Constants"); return registry; } diff --git a/src/ShaderNode/ShaderGraph.hpp b/src/ShaderNode/ShaderGraph.hpp index 8e61175f1..cac252802 100644 --- a/src/ShaderNode/ShaderGraph.hpp +++ b/src/ShaderNode/ShaderGraph.hpp @@ -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& 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 BuildRegistry(); diff --git a/src/ShaderNode/ShaderGraph.inl b/src/ShaderNode/ShaderGraph.inl index 07c20a134..1f0b463e9 100644 --- a/src/ShaderNode/ShaderGraph.inl +++ b/src/ShaderNode/ShaderGraph.inl @@ -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& { return m_textures; } + diff --git a/src/ShaderNode/Widgets/MainWindow.cpp b/src/ShaderNode/Widgets/MainWindow.cpp new file mode 100644 index 000000000..d4a661790 --- /dev/null +++ b/src/ShaderNode/Widgets/MainWindow.cpp @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +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(); +} diff --git a/src/ShaderNode/Widgets/MainWindow.hpp b/src/ShaderNode/Widgets/MainWindow.hpp new file mode 100644 index 000000000..fa8257bcf --- /dev/null +++ b/src/ShaderNode/Widgets/MainWindow.hpp @@ -0,0 +1,26 @@ +#pragma once + +#ifndef NAZARA_SHADERNODES_MAINWINDOW_HPP +#define NAZARA_SHADERNODES_MAINWINDOW_HPP + +#include +#include + +class ShaderGraph; + +class MainWindow : public QMainWindow +{ + public: + MainWindow(ShaderGraph& graph); + ~MainWindow() = default; + + private: + void BuildMenu(); + void OnCompileToGLSL(); + + ShaderGraph& m_shaderGraph; +}; + +#include + +#endif diff --git a/src/ShaderNode/Widgets/MainWindow.inl b/src/ShaderNode/Widgets/MainWindow.inl new file mode 100644 index 000000000..48dbe409e --- /dev/null +++ b/src/ShaderNode/Widgets/MainWindow.inl @@ -0,0 +1 @@ +#include diff --git a/src/ShaderNode/Widgets/TextureEditor.cpp b/src/ShaderNode/Widgets/TextureEditor.cpp new file mode 100644 index 000000000..594262c1a --- /dev/null +++ b/src/ShaderNode/Widgets/TextureEditor.cpp @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include +#include +#include + +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)); +} diff --git a/src/ShaderNode/Widgets/TextureEditor.hpp b/src/ShaderNode/Widgets/TextureEditor.hpp new file mode 100644 index 000000000..fcdbf3f7c --- /dev/null +++ b/src/ShaderNode/Widgets/TextureEditor.hpp @@ -0,0 +1,40 @@ +#pragma once + +#ifndef NAZARA_SHADERNODES_TEXTUREEDITOR_HPP +#define NAZARA_SHADERNODES_TEXTUREEDITOR_HPP + +#include +#include +#include + +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 m_currentTextureIndex; + ShaderGraph& m_shaderGraph; + QLabel* m_pixmapLabel; + QListWidget* m_textureList; + QVBoxLayout* m_layout; +}; + +#include + +#endif diff --git a/src/ShaderNode/Widgets/TextureEditor.inl b/src/ShaderNode/Widgets/TextureEditor.inl new file mode 100644 index 000000000..d11298db1 --- /dev/null +++ b/src/ShaderNode/Widgets/TextureEditor.inl @@ -0,0 +1 @@ +#include diff --git a/src/ShaderNode/main.cpp b/src/ShaderNode/main.cpp index 7878bec22..648cbbbbc 100644 --- a/src/ShaderNode/main.cpp +++ b/src/ShaderNode/main.cpp @@ -1,59 +1,17 @@ -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include #include -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();