diff --git a/src/ShaderNode/DataModels/Cast.hpp b/src/ShaderNode/DataModels/Cast.hpp index a6709bd2d..19d6363af 100644 --- a/src/ShaderNode/DataModels/Cast.hpp +++ b/src/ShaderNode/DataModels/Cast.hpp @@ -18,13 +18,14 @@ class CastVec : public ShaderNode CastVec(ShaderGraph& graph); ~CastVec() = default; + void BuildNodeEdition(QVBoxLayout* layout) override; + Nz::ShaderAst::ExpressionPtr GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const override; QString caption() const override; QString name() const override; 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; @@ -36,13 +37,10 @@ class CastVec : public ShaderNode static constexpr std::size_t ToComponents = To::ComponentCount; static constexpr std::size_t ComponentDiff = (ToComponents >= FromComponents) ? ToComponents - FromComponents : 0; - void ComputePreview(QPixmap& pixmap) const; - void UpdatePreview(); + bool ComputePreview(QPixmap& pixmap) override; + void UpdateOutput(); - QLabel* m_pixmapLabel; - QPixmap m_pixmap; - QWidget* m_widget; - std::array m_spinboxes; + VecType m_overflowComponents; std::shared_ptr m_input; std::shared_ptr m_output; }; diff --git a/src/ShaderNode/DataModels/Cast.inl b/src/ShaderNode/DataModels/Cast.inl index 0133d6cc7..5c0600833 100644 --- a/src/ShaderNode/DataModels/Cast.inl +++ b/src/ShaderNode/DataModels/Cast.inl @@ -6,41 +6,35 @@ template CastVec::CastVec(ShaderGraph& graph) : ShaderNode(graph) { - constexpr std::array componentName = { 'X', 'Y', 'Z', 'W' }; - static_assert(ComponentDiff <= componentName.size()); + static_assert(ComponentDiff <= s_vectorComponents.size()); +} - QFormLayout* layout = new QFormLayout; +template +void CastVec::BuildNodeEdition(QVBoxLayout* layout) +{ + ShaderNode::BuildNodeEdition(layout); if constexpr (ComponentDiff > 0) { + QFormLayout* formLayout = new QFormLayout; + for (std::size_t i = 0; i < ComponentDiff; ++i) { - 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) + QDoubleSpinBox* spinbox = new QDoubleSpinBox; + spinbox->setDecimals(6); + spinbox->setValue(m_overflowComponents[i]); + + connect(spinbox, qOverload(&QDoubleSpinBox::valueChanged), [=](double) { - UpdatePreview(); + m_overflowComponents[i] = spinbox->value(); + UpdateOutput(); }); - layout->addRow(QString::fromUtf8(&componentName[FromComponents + i], 1), m_spinboxes[i]); + formLayout->addRow(QString::fromUtf8(&s_vectorComponents[FromComponents + i], 1), spinbox); } + + layout->addLayout(formLayout); } - - m_pixmap = QPixmap(64, 64); - m_pixmap.fill(); - - m_pixmapLabel = new QLabel; - m_pixmapLabel->setPixmap(m_pixmap); - - layout->addWidget(m_pixmapLabel); - - m_widget = new QWidget; - m_widget->setStyleSheet("background-color: rgba(0,0,0,0)"); - m_widget->setLayout(layout); - - m_output = std::make_shared(); } template @@ -52,7 +46,7 @@ Nz::ShaderAst::ExpressionPtr CastVec::GetExpression(Nz::ShaderAst::Exp { std::array constants; for (std::size_t i = 0; i < ComponentDiff; ++i) - constants[i] = Nz::ShaderBuilder::Constant(float(m_spinboxes[i]->value())); + constants[i] = Nz::ShaderBuilder::Constant(m_overflowComponents[i]); return std::apply([&](auto&&... values) { @@ -102,12 +96,6 @@ QtNodes::NodeDataType CastVec::dataType(QtNodes::PortType portType, Qt throw std::runtime_error("Invalid port type"); } -template -QWidget* CastVec::embeddedWidget() -{ - return m_widget; -} - template unsigned int CastVec::nPorts(QtNodes::PortType portType) const { @@ -124,6 +112,10 @@ template std::shared_ptr CastVec::outData(QtNodes::PortIndex port) { assert(port == 0); + + if (!m_input) + return nullptr; + return m_output; } @@ -139,13 +131,28 @@ void CastVec::setInData(std::shared_ptr value, int else m_input.reset(); - UpdatePreview(); + UpdateOutput(); } template -void CastVec::ComputePreview(QPixmap& pixmap) const +bool CastVec::ComputePreview(QPixmap& pixmap) { - assert(m_input); + if (!m_input) + return false; + + pixmap = QPixmap::fromImage(m_output->preview); + return true; +} + +template +void CastVec::UpdateOutput() +{ + if (!m_input) + { + m_output->preview = QImage(1, 1, QImage::Format_RGBA8888); + m_output->preview.fill(QColor::fromRgb(0, 0, 0, 0)); + return; + } const QImage& input = m_input->preview; @@ -156,8 +163,11 @@ void CastVec::ComputePreview(QPixmap& pixmap) const output = QImage(inputWidth, inputHeight, QImage::Format_RGBA8888); std::array constants; - for (std::size_t i = 0; i < ComponentDiff; ++i) - constants[i] = static_cast(std::clamp(int(m_spinboxes[i]->value() * 255), 0, 255)); + if constexpr (ComponentDiff > 0) + { + for (std::size_t i = 0; i < ComponentDiff; ++i) + constants[i] = static_cast(std::clamp(int(m_overflowComponents[i] * 255), 0, 255)); + } std::uint8_t* outputPtr = output.bits(); const std::uint8_t* inputPtr = input.constBits(); @@ -181,21 +191,7 @@ void CastVec::ComputePreview(QPixmap& pixmap) const } } - pixmap = QPixmap::fromImage(output).scaled(64, 64); -} - -template -void CastVec::UpdatePreview() -{ - if (!m_input) - { - m_pixmap = QPixmap(64, 64); - m_pixmap.fill(QColor::fromRgb(255, 255, 255, 0)); - } - else - ComputePreview(m_pixmap); - - m_pixmapLabel->setPixmap(m_pixmap); - Q_EMIT dataUpdated(0); + + UpdatePreview(); } diff --git a/src/ShaderNode/DataModels/FragmentOutput.cpp b/src/ShaderNode/DataModels/FragmentOutput.cpp index 86731a0f4..bfac47bee 100644 --- a/src/ShaderNode/DataModels/FragmentOutput.cpp +++ b/src/ShaderNode/DataModels/FragmentOutput.cpp @@ -22,11 +22,6 @@ QtNodes::NodeDataType FragmentOutput::dataType(QtNodes::PortType portType, QtNod return Vec4Data::Type(); } -QWidget* FragmentOutput::embeddedWidget() -{ - return nullptr; -} - unsigned int FragmentOutput::nPorts(QtNodes::PortType portType) const { switch (portType) @@ -42,3 +37,26 @@ std::shared_ptr FragmentOutput::outData(QtNodes::PortIndex /* { return {}; } + +void FragmentOutput::setInData(std::shared_ptr value, int index) +{ + assert(index == 0); + if (value) + { + assert(dynamic_cast(value.get()) != nullptr); + m_input = std::static_pointer_cast(value); + } + else + m_input.reset(); + + UpdatePreview(); +} + +bool FragmentOutput::ComputePreview(QPixmap& pixmap) +{ + if (!m_input) + return false; + + pixmap = QPixmap::fromImage(m_input->preview); + return true; +} diff --git a/src/ShaderNode/DataModels/FragmentOutput.hpp b/src/ShaderNode/DataModels/FragmentOutput.hpp index ab89ae419..c70f39ff6 100644 --- a/src/ShaderNode/DataModels/FragmentOutput.hpp +++ b/src/ShaderNode/DataModels/FragmentOutput.hpp @@ -4,6 +4,7 @@ #define NAZARA_SHADERNODES_FRAGMENTOUTPUT_HPP #include +#include #include #include @@ -19,13 +20,16 @@ class FragmentOutput : public ShaderNode 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 {}; + void setInData(std::shared_ptr value, int index) override; + private: + bool ComputePreview(QPixmap& pixmap) override; + + std::shared_ptr m_input; }; #include diff --git a/src/ShaderNode/DataModels/FragmentOutput.inl b/src/ShaderNode/DataModels/FragmentOutput.inl index c809a587d..24fc6cbf9 100644 --- a/src/ShaderNode/DataModels/FragmentOutput.inl +++ b/src/ShaderNode/DataModels/FragmentOutput.inl @@ -3,4 +3,6 @@ inline FragmentOutput::FragmentOutput(ShaderGraph& graph) : ShaderNode(graph) { + SetPreviewSize({ 128, 128 }); + EnablePreview(true); } diff --git a/src/ShaderNode/DataModels/InputValue.cpp b/src/ShaderNode/DataModels/InputValue.cpp index f2ae0eaa8..a6044a229 100644 --- a/src/ShaderNode/DataModels/InputValue.cpp +++ b/src/ShaderNode/DataModels/InputValue.cpp @@ -4,48 +4,18 @@ #include InputValue::InputValue(ShaderGraph& graph) : -ShaderNode(graph), -m_currentInputIndex(0) +ShaderNode(graph) { - m_layout = new QVBoxLayout; - - m_inputSelection = new QComboBox; - m_inputSelection->setStyleSheet("background-color: rgba(255,255,255,255)"); - connect(m_inputSelection, qOverload(&QComboBox::currentIndexChanged), [&](int index) - { - if (index < 0) - return; - - m_currentInputIndex = static_cast(index); - UpdatePreview(); - }); - - m_layout->addWidget(m_inputSelection); - - m_previewLabel = new QLabel; - - m_layout->addWidget(m_previewLabel); - - m_widget = new QWidget; - m_widget->setStyleSheet("background-color: rgba(0,0,0,0)"); - m_widget->setLayout(m_layout); - - m_onInputListUpdateSlot.Connect(GetGraph().OnInputListUpdate, [&](ShaderGraph*) { UpdateInputList(); }); + m_onInputListUpdateSlot.Connect(GetGraph().OnInputListUpdate, [&](ShaderGraph*) { OnInputListUpdate(); }); m_onInputUpdateSlot.Connect(GetGraph().OnInputUpdate, [&](ShaderGraph*, std::size_t inputIndex) { if (m_currentInputIndex == inputIndex) UpdatePreview(); }); - UpdateInputList(); UpdatePreview(); } -QWidget* InputValue::embeddedWidget() -{ - return m_widget; -} - unsigned int InputValue::nPorts(QtNodes::PortType portType) const { switch (portType) @@ -57,36 +27,66 @@ unsigned int InputValue::nPorts(QtNodes::PortType portType) const return 0; } -void InputValue::UpdatePreview() +bool InputValue::ComputePreview(QPixmap& pixmap) { - if (m_inputSelection->count() == 0) - return; + if (!m_currentInputIndex) + return false; const ShaderGraph& graph = GetGraph(); - const auto& inputEntry = graph.GetInput(m_currentInputIndex); + const auto& inputEntry = graph.GetInput(*m_currentInputIndex); const auto& preview = graph.GetPreviewModel(); - m_previewLabel->setPixmap(QPixmap::fromImage(preview.GetImage(inputEntry.role, inputEntry.roleIndex))); - - Q_EMIT dataUpdated(0); + pixmap = QPixmap::fromImage(preview.GetImage(inputEntry.role, inputEntry.roleIndex)); + return true; } -void InputValue::UpdateInputList() +void InputValue::OnInputListUpdate() { - QString currentInput = m_inputSelection->currentText(); - m_inputSelection->clear(); + m_currentInputIndex.reset(); + + std::size_t inputIndex = 0; + for (const auto& inputEntry : GetGraph().GetInputs()) + { + if (inputEntry.name == m_currentInputText) + { + m_currentInputIndex = inputIndex; + break; + } + + inputIndex++; + } +} + +void InputValue::BuildNodeEdition(QVBoxLayout* layout) +{ + ShaderNode::BuildNodeEdition(layout); + + QComboBox* inputSelection = new QComboBox; + + connect(inputSelection, qOverload(&QComboBox::currentIndexChanged), [&](int index) + { + if (index >= 0) + m_currentInputIndex = static_cast(index); + else + m_currentInputIndex.reset(); + + UpdatePreview(); + }); for (const auto& inputEntry : GetGraph().GetInputs()) - m_inputSelection->addItem(QString::fromStdString(inputEntry.name)); + inputSelection->addItem(QString::fromStdString(inputEntry.name)); - m_inputSelection->setCurrentText(currentInput); + layout->addWidget(inputSelection); } Nz::ShaderAst::ExpressionPtr InputValue::GetExpression(Nz::ShaderAst::ExpressionPtr* /*expressions*/, std::size_t count) const { assert(count == 0); - const auto& inputEntry = GetGraph().GetInput(m_currentInputIndex); + if (!m_currentInputIndex) + throw std::runtime_error("no input"); + + const auto& inputEntry = GetGraph().GetInput(*m_currentInputIndex); Nz::ShaderAst::ExpressionType expression = [&] { @@ -108,10 +108,13 @@ Nz::ShaderAst::ExpressionPtr InputValue::GetExpression(Nz::ShaderAst::Expression auto InputValue::dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const -> QtNodes::NodeDataType { + if (!m_currentInputIndex) + return Vec4Data::Type(); + assert(portType == QtNodes::PortType::Out); assert(portIndex == 0); - const auto& inputEntry = GetGraph().GetInput(m_currentInputIndex); + const auto& inputEntry = GetGraph().GetInput(*m_currentInputIndex); switch (inputEntry.type) { //case InputType::Bool: return Nz::ShaderAst::ExpressionType::Boolean; @@ -127,10 +130,13 @@ auto InputValue::dataType(QtNodes::PortType portType, QtNodes::PortIndex portInd std::shared_ptr InputValue::outData(QtNodes::PortIndex port) { + if (!m_currentInputIndex) + return nullptr; + assert(port == 0); const ShaderGraph& graph = GetGraph(); - const auto& inputEntry = graph.GetInput(m_currentInputIndex); + const auto& inputEntry = graph.GetInput(*m_currentInputIndex); const auto& preview = graph.GetPreviewModel(); auto vecData = std::make_shared(); diff --git a/src/ShaderNode/DataModels/InputValue.hpp b/src/ShaderNode/DataModels/InputValue.hpp index 273155cc4..84b7d6393 100644 --- a/src/ShaderNode/DataModels/InputValue.hpp +++ b/src/ShaderNode/DataModels/InputValue.hpp @@ -9,6 +9,7 @@ #include #include #include +#include class InputValue : public ShaderNode { @@ -16,30 +17,28 @@ class InputValue : public ShaderNode InputValue(ShaderGraph& graph); ~InputValue() = default; + void BuildNodeEdition(QVBoxLayout* layout) override; + Nz::ShaderAst::ExpressionPtr GetExpression(Nz::ShaderAst::ExpressionPtr* /*expressions*/, std::size_t count) const override; QString caption() const override { return "Input"; } QString name() const override { return "Input"; } - QWidget* embeddedWidget() override; unsigned int nPorts(QtNodes::PortType portType) const override; QtNodes::NodeDataType dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; std::shared_ptr outData(QtNodes::PortIndex port) override; - protected: - void UpdatePreview(); - void UpdateInputList(); + private: + bool ComputePreview(QPixmap& pixmap) override; + void OnInputListUpdate(); NazaraSlot(ShaderGraph, OnInputListUpdate, m_onInputListUpdateSlot); NazaraSlot(ShaderGraph, OnInputUpdate, m_onInputUpdateSlot); - std::size_t m_currentInputIndex; - QComboBox* m_inputSelection; - QLabel* m_previewLabel; - QWidget* m_widget; - QVBoxLayout* m_layout; + std::optional m_currentInputIndex; + std::string m_currentInputText; }; #include diff --git a/src/ShaderNode/DataModels/SampleTexture.cpp b/src/ShaderNode/DataModels/SampleTexture.cpp index 81982c83a..f299b6009 100644 --- a/src/ShaderNode/DataModels/SampleTexture.cpp +++ b/src/ShaderNode/DataModels/SampleTexture.cpp @@ -4,52 +4,16 @@ #include SampleTexture::SampleTexture(ShaderGraph& graph) : -ShaderNode(graph), -m_currentTextureIndex(0) +ShaderNode(graph) { m_output = std::make_shared(); - 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); - - m_pixmap = QPixmap(64, 64); - m_pixmap.fill(); - - m_pixmapLabel = new QLabel; - m_pixmapLabel->setPixmap(m_pixmap); - - m_layout->addWidget(m_pixmapLabel); - - m_widget = new QWidget; - m_widget->setStyleSheet("background-color: rgba(0,0,0,0)"); - m_widget->setLayout(m_layout); - - m_onTextureListUpdateSlot.Connect(GetGraph().OnTextureListUpdate, [&](ShaderGraph*) { UpdateTextureList(); }); + m_onTextureListUpdateSlot.Connect(GetGraph().OnTextureListUpdate, [&](ShaderGraph*) { OnTextureListUpdate(); }); m_onTexturePreviewUpdateSlot.Connect(GetGraph().OnTexturePreviewUpdate, [&](ShaderGraph*, std::size_t textureIndex) { if (m_currentTextureIndex == textureIndex) UpdatePreview(); }); - - UpdateTextureList(); - UpdatePreview(); -} - -QWidget* SampleTexture::embeddedWidget() -{ - return m_widget; } unsigned int SampleTexture::nPorts(QtNodes::PortType portType) const @@ -63,39 +27,39 @@ unsigned int SampleTexture::nPorts(QtNodes::PortType portType) const return 0; } -void SampleTexture::UpdatePreview() +void SampleTexture::OnTextureListUpdate() { - if (m_textureSelection->count() == 0) - return; - - ComputePreview(m_pixmap); - m_pixmapLabel->setPixmap(m_pixmap); - - Q_EMIT dataUpdated(0); -} - -void SampleTexture::UpdateTextureList() -{ - QString currentTexture = m_textureSelection->currentText(); - m_textureSelection->clear(); + m_currentTextureIndex.reset(); + std::size_t inputIndex = 0; for (const auto& textureEntry : GetGraph().GetTextures()) - m_textureSelection->addItem(QString::fromStdString(textureEntry.name)); + { + if (textureEntry.name == m_currentTextureText) + { + m_currentTextureIndex = inputIndex; + break; + } - m_textureSelection->setCurrentText(currentTexture); + inputIndex++; + } } -void SampleTexture::ComputePreview(QPixmap& pixmap) const +void SampleTexture::UpdateOutput() { - if (!m_uv) - return; + QImage& output = m_output->preview; - const auto& textureEntry = GetGraph().GetTexture(m_currentTextureIndex); + if (!m_currentTextureIndex || !m_uv) + { + output = QImage(1, 1, QImage::Format_RGBA8888); + output.fill(QColor::fromRgb(0, 0, 0, 0)); + return; + } + + const auto& textureEntry = GetGraph().GetTexture(*m_currentTextureIndex); int textureWidth = textureEntry.preview.width(); int textureHeight = textureEntry.preview.height(); - QImage& output = m_output->preview; const QImage& uv = m_uv->preview; int uvWidth = uv.width(); @@ -125,14 +89,49 @@ void SampleTexture::ComputePreview(QPixmap& pixmap) const } } - pixmap = QPixmap::fromImage(output).scaled(128, 128, Qt::KeepAspectRatio); + Q_EMIT dataUpdated(0); + + UpdatePreview(); +} + +bool SampleTexture::ComputePreview(QPixmap& pixmap) +{ + if (!m_currentTextureIndex || !m_uv) + return false; + + pixmap = QPixmap::fromImage(m_output->preview); + return true; +} + +void SampleTexture::BuildNodeEdition(QVBoxLayout* layout) +{ + ShaderNode::BuildNodeEdition(layout); + + QComboBox* textureSelection = new QComboBox; + connect(textureSelection, qOverload(&QComboBox::currentIndexChanged), [&](int index) + { + if (index >= 0) + m_currentTextureIndex = static_cast(index); + else + m_currentTextureIndex.reset(); + + UpdateOutput(); + }); + + for (const auto& textureEntry : GetGraph().GetTextures()) + textureSelection->addItem(QString::fromStdString(textureEntry.name)); + + layout->addWidget(textureSelection); } Nz::ShaderAst::ExpressionPtr SampleTexture::GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const { + if (!m_currentTextureIndex || !m_uv) + throw std::runtime_error("invalid inputs"); + assert(count == 1); - const auto& textureEntry = GetGraph().GetTexture(m_currentTextureIndex); + const auto& textureEntry = GetGraph().GetTexture(*m_currentTextureIndex); Nz::ShaderAst::ExpressionType expression = [&] { @@ -203,6 +202,9 @@ std::shared_ptr SampleTexture::outData(QtNodes::PortIndex por { assert(port == 0); + if (!m_currentTextureIndex) + return nullptr; + return m_output; } @@ -219,5 +221,5 @@ void SampleTexture::setInData(std::shared_ptr value, int inde else m_uv.reset(); - UpdatePreview(); + UpdateOutput(); } diff --git a/src/ShaderNode/DataModels/SampleTexture.hpp b/src/ShaderNode/DataModels/SampleTexture.hpp index 7697d296f..738047712 100644 --- a/src/ShaderNode/DataModels/SampleTexture.hpp +++ b/src/ShaderNode/DataModels/SampleTexture.hpp @@ -17,12 +17,13 @@ class SampleTexture : public ShaderNode SampleTexture(ShaderGraph& graph); ~SampleTexture() = default; + void BuildNodeEdition(QVBoxLayout* layout) override; + Nz::ShaderAst::ExpressionPtr GetExpression(Nz::ShaderAst::ExpressionPtr* /*expressions*/, std::size_t count) const override; QString caption() const override { return "Sample texture"; } QString name() const override { return "SampleTexture"; } - QWidget* embeddedWidget() override; unsigned int nPorts(QtNodes::PortType portType) const override; QtNodes::NodeDataType dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; @@ -36,21 +37,17 @@ class SampleTexture : public ShaderNode void setInData(std::shared_ptr value, int index) override; protected: - void ComputePreview(QPixmap& pixmap) const; - void UpdatePreview(); - void UpdateTextureList(); + bool ComputePreview(QPixmap& pixmap) override; + void OnTextureListUpdate(); + void UpdateOutput(); NazaraSlot(ShaderGraph, OnTextureListUpdate, m_onTextureListUpdateSlot); NazaraSlot(ShaderGraph, OnTexturePreviewUpdate, m_onTexturePreviewUpdateSlot); - std::size_t m_currentTextureIndex; + std::optional m_currentTextureIndex; std::shared_ptr m_uv; std::shared_ptr m_output; - QComboBox* m_textureSelection; - QLabel* m_pixmapLabel; - QPixmap m_pixmap; - QWidget* m_widget; - QVBoxLayout* m_layout; + std::string m_currentTextureText; }; #include diff --git a/src/ShaderNode/DataModels/ShaderNode.cpp b/src/ShaderNode/DataModels/ShaderNode.cpp index aeb143452..1221d1df6 100644 --- a/src/ShaderNode/DataModels/ShaderNode.cpp +++ b/src/ShaderNode/DataModels/ShaderNode.cpp @@ -1,5 +1,81 @@ #include +#include +#include +#include + +ShaderNode::ShaderNode(ShaderGraph& graph) : +m_previewSize(64, 64), +m_pixmapLabel(nullptr), +m_graph(graph), +m_isPreviewEnabled(false) +{ + m_pixmapLabel = new QLabel; + m_pixmapLabel->setStyleSheet("background-color: rgba(0,0,0,0)"); +} + +void ShaderNode::BuildNodeEdition(QVBoxLayout* layout) +{ + QCheckBox* checkbox = new QCheckBox(tr("Enable preview")); + checkbox->setCheckState((m_isPreviewEnabled) ? Qt::Checked : Qt::Unchecked); + + connect(checkbox, &QCheckBox::stateChanged, [&](int state) + { + EnablePreview(state == Qt::Checked); + }); + + layout->addWidget(checkbox); +} + +void ShaderNode::EnablePreview(bool enable) +{ + if (m_isPreviewEnabled != enable) + { + m_isPreviewEnabled = enable; + + if (m_isPreviewEnabled) + { + m_pixmap.emplace(m_previewSize.x, m_previewSize.y); + + UpdatePreview(); + } + else + { + m_pixmapLabel->clear(); + m_pixmap.reset(); + } + + embeddedWidgetSizeUpdated(); + } +} + +QWidget* ShaderNode::embeddedWidget() +{ + return m_pixmapLabel; +} void ShaderNode::setInData(std::shared_ptr, int) { } + +bool ShaderNode::ComputePreview(QPixmap& /*pixmap*/) +{ + return false; +} + +void ShaderNode::UpdatePreview() +{ + if (!m_pixmap) + return; + + QPixmap& pixmap = *m_pixmap; + + if (!ComputePreview(pixmap)) + { + pixmap = QPixmap(m_previewSize.x, m_previewSize.y); + pixmap.fill(QColor::fromRgb(255, 255, 255, 0)); + } + else + pixmap = pixmap.scaled(m_previewSize.x, m_previewSize.y); + + m_pixmapLabel->setPixmap(pixmap); +} diff --git a/src/ShaderNode/DataModels/ShaderNode.hpp b/src/ShaderNode/DataModels/ShaderNode.hpp index 3d73d7330..3cf16ea06 100644 --- a/src/ShaderNode/DataModels/ShaderNode.hpp +++ b/src/ShaderNode/DataModels/ShaderNode.hpp @@ -3,24 +3,46 @@ #ifndef NAZARA_SHADERNODES_SHADERNODE_HPP #define NAZARA_SHADERNODES_SHADERNODE_HPP +#include #include #include +#include +#include +class QLabel; +class QVBoxLayout; class ShaderGraph; class ShaderNode : public QtNodes::NodeDataModel { public: - inline ShaderNode(ShaderGraph& graph); + ShaderNode(ShaderGraph& graph); + + virtual void BuildNodeEdition(QVBoxLayout* layout); + + void EnablePreview(bool enable); virtual Nz::ShaderAst::ExpressionPtr GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const = 0; inline ShaderGraph& GetGraph(); inline const ShaderGraph& GetGraph() const; + void SetPreviewSize(const Nz::Vector2i& size); + + QWidget* embeddedWidget() final; + void setInData(std::shared_ptr, int) override; + protected: + void UpdatePreview(); + private: + virtual bool ComputePreview(QPixmap& pixmap); + + Nz::Vector2i m_previewSize; + QLabel* m_pixmapLabel; + std::optional m_pixmap; ShaderGraph& m_graph; + bool m_isPreviewEnabled; }; #include diff --git a/src/ShaderNode/DataModels/ShaderNode.inl b/src/ShaderNode/DataModels/ShaderNode.inl index defa5aea1..7f80d00ff 100644 --- a/src/ShaderNode/DataModels/ShaderNode.inl +++ b/src/ShaderNode/DataModels/ShaderNode.inl @@ -1,9 +1,5 @@ #include -inline ShaderNode::ShaderNode(ShaderGraph& graph) : -m_graph(graph) -{ -} inline ShaderGraph& ShaderNode::GetGraph() { @@ -14,3 +10,10 @@ inline const ShaderGraph& ShaderNode::GetGraph() const { return m_graph; } + +inline void ShaderNode::SetPreviewSize(const Nz::Vector2i& size) +{ + m_previewSize = size; + if (m_isPreviewEnabled) + UpdatePreview(); +} diff --git a/src/ShaderNode/DataModels/VecBinOp.hpp b/src/ShaderNode/DataModels/VecBinOp.hpp index ef3ee4bca..e56c13461 100644 --- a/src/ShaderNode/DataModels/VecBinOp.hpp +++ b/src/ShaderNode/DataModels/VecBinOp.hpp @@ -15,7 +15,6 @@ class VecBinOp : public ShaderNode Nz::ShaderAst::ExpressionPtr GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const override; - QWidget* embeddedWidget() override; unsigned int nPorts(QtNodes::PortType portType) const override; QtNodes::NodeDataType dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; @@ -26,10 +25,10 @@ class VecBinOp : public ShaderNode 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(); - QLabel* m_pixmapLabel; - QPixmap m_preview; + bool ComputePreview(QPixmap& pixmap) override; + void UpdateOutput(); + std::shared_ptr m_lhs; std::shared_ptr m_rhs; std::shared_ptr m_output; diff --git a/src/ShaderNode/DataModels/VecBinOp.inl b/src/ShaderNode/DataModels/VecBinOp.inl index 4159193fb..2d33f65c6 100644 --- a/src/ShaderNode/DataModels/VecBinOp.inl +++ b/src/ShaderNode/DataModels/VecBinOp.inl @@ -7,9 +7,7 @@ ShaderNode(graph) { m_output = std::make_shared(); - m_pixmapLabel = new QLabel; - m_pixmapLabel->setStyleSheet("background-color: rgba(0,0,0,0)"); - UpdatePreview(); + UpdateOutput(); } template @@ -21,12 +19,6 @@ Nz::ShaderAst::ExpressionPtr VecBinOp::GetExpression(Nz::ShaderAst: return builder(expressions[0], expressions[1]); } -template -QWidget* VecBinOp::embeddedWidget() -{ - return m_pixmapLabel; -} - template QtNodes::NodeDataType VecBinOp::dataType(QtNodes::PortType /*portType*/, QtNodes::PortIndex portIndex) const { @@ -72,42 +64,49 @@ void VecBinOp::setInData(std::shared_ptr value, else m_rhs = std::move(castedValue); - UpdatePreview(); - - Q_EMIT dataUpdated(0); + UpdateOutput(); } template -void VecBinOp::UpdatePreview() +bool VecBinOp::ComputePreview(QPixmap& pixmap) { - if (m_lhs && m_rhs) + if (!m_lhs || !m_rhs) + return false; + + pixmap = QPixmap::fromImage(m_output->preview); + return true; +} + +template +void VecBinOp::UpdateOutput() +{ + 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); - - m_output->preview = QImage(maxWidth, maxHeight, QImage::Format_RGBA8888); - ApplyOp(leftResized.constBits(), rightResized.constBits(), m_output->preview.bits(), maxWidth * maxHeight * 4); - - m_preview = QPixmap::fromImage(m_output->preview).scaled(64, 64); - } - else - { - m_preview = QPixmap(64, 64); - m_preview.fill(QColor::fromRgb(255, 255, 0, 0)); + m_output->preview = QImage(1, 1, QImage::Format_RGBA8888); + m_output->preview.fill(QColor::fromRgb(0, 0, 0, 0)); + return; } - m_pixmapLabel->setPixmap(m_preview); + 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); + + m_output->preview = QImage(maxWidth, maxHeight, QImage::Format_RGBA8888); + ApplyOp(leftResized.constBits(), rightResized.constBits(), m_output->preview.bits(), maxWidth * maxHeight * 4); + + Q_EMIT dataUpdated(0); + + UpdatePreview(); } template diff --git a/src/ShaderNode/DataModels/VecData.hpp b/src/ShaderNode/DataModels/VecData.hpp index 8a1b152f5..3882f20f1 100644 --- a/src/ShaderNode/DataModels/VecData.hpp +++ b/src/ShaderNode/DataModels/VecData.hpp @@ -62,6 +62,45 @@ struct Vec4Data : public VecData } }; +struct VecTypeDummy {}; + +template +struct VecTypeHelper; + +template<> +struct VecTypeHelper<0> +{ + using Type = VecTypeDummy; +}; + +template<> +struct VecTypeHelper<1> +{ + using Type = std::array; +}; + +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; + +constexpr std::array s_vectorComponents = { 'X', 'Y', 'Z', 'W' }; + #include #endif diff --git a/src/ShaderNode/DataModels/VecValue.hpp b/src/ShaderNode/DataModels/VecValue.hpp index 7e8db4d88..27df2757f 100644 --- a/src/ShaderNode/DataModels/VecValue.hpp +++ b/src/ShaderNode/DataModels/VecValue.hpp @@ -11,29 +11,6 @@ #include #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 { @@ -46,25 +23,22 @@ class VecValue : public ShaderNode 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 BuildNodeEdition(QVBoxLayout* layout) override; + Nz::ShaderAst::ExpressionPtr GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const override; - protected: + private: + bool ComputePreview(QPixmap& pixmap) override; + static constexpr std::size_t ComponentCount = Data::ComponentCount; QColor ToColor() const; - VecType ToVector() const; - void UpdatePreview(); - QLabel* m_pixmapLabel; - QPixmap m_pixmap; - QWidget* m_widget; - QFormLayout* m_layout; - std::array m_spinboxes; + VecType m_value; }; using Vec2Value = VecValue; diff --git a/src/ShaderNode/DataModels/VecValue.inl b/src/ShaderNode/DataModels/VecValue.inl index 45138b83f..8f9212631 100644 --- a/src/ShaderNode/DataModels/VecValue.inl +++ b/src/ShaderNode/DataModels/VecValue.inl @@ -8,37 +8,14 @@ template VecValue::VecValue(ShaderGraph& graph) : ShaderNode(graph) { - constexpr std::array componentName = { 'X', 'Y', 'Z', 'W' }; - static_assert(ComponentCount <= componentName.size()); + static_assert(ComponentCount <= s_vectorComponents.size()); + + std::array defaultValues; - m_layout = new QFormLayout; for (std::size_t i = 0; i < ComponentCount; ++i) - { - 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(); + defaultValues[i] = (i == 3) ? 1.f : 0.f; - Q_EMIT dataUpdated(0); - }); - - m_layout->addRow(QString::fromUtf8(&componentName[i], 1), m_spinboxes[i]); - } - - m_pixmap = QPixmap(32, 32); - m_pixmap.fill(); - - m_pixmapLabel = new QLabel; - m_pixmapLabel->setPixmap(m_pixmap); - - m_layout->addWidget(m_pixmapLabel); - - m_widget = new QWidget; - m_widget->setStyleSheet("background-color: rgba(0,0,0,0)"); - m_widget->setLayout(m_layout); + m_value.Set(defaultValues.data()); UpdatePreview(); } @@ -66,12 +43,6 @@ QtNodes::NodeDataType VecValue::dataType(QtNodes::PortType portType, QtNod return Data::Type(); } -template -QWidget* VecValue::embeddedWidget() -{ - return m_widget; -} - template unsigned int VecValue::nPorts(QtNodes::PortType portType) const { @@ -96,12 +67,45 @@ std::shared_ptr VecValue::outData(QtNodes::PortIndex po return out; } +template +void VecValue::BuildNodeEdition(QVBoxLayout* layout) +{ + ShaderNode::BuildNodeEdition(layout); + + QFormLayout* formLayout = new QFormLayout; + for (std::size_t i = 0; i < ComponentCount; ++i) + { + QDoubleSpinBox* spinbox = new QDoubleSpinBox; + spinbox->setDecimals(6); + spinbox->setValue(m_value[i]); + + connect(spinbox, qOverload(&QDoubleSpinBox::valueChanged), [=](double) + { + m_value[i] = spinbox->value(); + Q_EMIT dataUpdated(0); + + UpdatePreview(); + }); + + formLayout->addRow(QString::fromUtf8(&s_vectorComponents[i], 1), spinbox); + } + + layout->addLayout(formLayout); +} + template Nz::ShaderAst::ExpressionPtr VecValue::GetExpression(Nz::ShaderAst::ExpressionPtr* /*expressions*/, std::size_t count) const { assert(count == 0); - return Nz::ShaderBuilder::Constant(ToVector()); + return Nz::ShaderBuilder::Constant(m_value); +} + +template +bool VecValue::ComputePreview(QPixmap& pixmap) +{ + pixmap.fill(ToColor()); + return true; } template @@ -110,28 +114,7 @@ QColor VecValue::ToColor() const std::array values = { 0.f, 0.f, 0.f, 1.f }; for (std::size_t i = 0; i < ComponentCount; ++i) - values[i] = std::clamp(float(m_spinboxes[i]->value()), 0.f, 1.f); + values[i] = std::clamp(m_value[i], 0.f, 1.f); return QColor::fromRgbF(values[0], values[1], values[2], values[3]); } - -template -auto VecValue::ToVector() const -> VecType -{ - std::array values; - - for (std::size_t i = 0; i < ComponentCount; ++i) - values[i] = std::clamp(float(m_spinboxes[i]->value()), 0.f, 1.f); - - return std::apply([](auto... values) - { - return VecType(values...); - }, values); -} - -template -void VecValue::UpdatePreview() -{ - m_pixmap.fill(ToColor()); - m_pixmapLabel->setPixmap(m_pixmap); -} diff --git a/src/ShaderNode/ShaderGraph.cpp b/src/ShaderNode/ShaderGraph.cpp index e29c0129f..bc9d643fd 100644 --- a/src/ShaderNode/ShaderGraph.cpp +++ b/src/ShaderNode/ShaderGraph.cpp @@ -29,6 +29,17 @@ namespace ShaderGraph::ShaderGraph() : m_flowScene(BuildRegistry()) { + m_previewModel = std::make_unique(); + + QObject::connect(&m_flowScene, &QGraphicsScene::selectionChanged, [&] + { + auto selectedNodes = m_flowScene.selectedNodes(); + if (selectedNodes.size() == 1) + OnSelectedNodeUpdate(this, static_cast(selectedNodes.front()->nodeDataModel())); + else + OnSelectedNodeUpdate(this, nullptr); + }); + auto& node1 = m_flowScene.createNode(std::make_unique(*this)); node1.nodeGraphicsObject().setPos(200, 200); @@ -36,8 +47,6 @@ m_flowScene(BuildRegistry()) node2.nodeGraphicsObject().setPos(500, 300); m_flowScene.createConnection(node2, 0, node1, 0); - - m_previewModel = std::make_unique(); } ShaderGraph::~ShaderGraph() diff --git a/src/ShaderNode/ShaderGraph.hpp b/src/ShaderNode/ShaderGraph.hpp index aa2188f16..3ed906edd 100644 --- a/src/ShaderNode/ShaderGraph.hpp +++ b/src/ShaderNode/ShaderGraph.hpp @@ -11,6 +11,8 @@ #include #include +class ShaderNode; + class ShaderGraph { public: @@ -52,6 +54,7 @@ class ShaderGraph NazaraSignal(OnInputListUpdate, ShaderGraph*); NazaraSignal(OnInputUpdate, ShaderGraph*, std::size_t /*inputIndex*/); + NazaraSignal(OnSelectedNodeUpdate, ShaderGraph*, ShaderNode* /*node*/); NazaraSignal(OnTextureListUpdate, ShaderGraph*); NazaraSignal(OnTexturePreviewUpdate, ShaderGraph*, std::size_t /*textureIndex*/); diff --git a/src/ShaderNode/Widgets/MainWindow.cpp b/src/ShaderNode/Widgets/MainWindow.cpp index 8aa48c353..580e5b506 100644 --- a/src/ShaderNode/Widgets/MainWindow.cpp +++ b/src/ShaderNode/Widgets/MainWindow.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -19,20 +20,44 @@ m_shaderGraph(graph) QtNodes::FlowView* flowView = new QtNodes::FlowView(scene); setCentralWidget(flowView); - QDockWidget* inputDock = new QDockWidget(tr("&Inputs")); - InputEditor* inputEditor = new InputEditor(m_shaderGraph); + + QDockWidget* inputDock = new QDockWidget(tr("Inputs")); + inputDock->setFeatures(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable); inputDock->setWidget(inputEditor); addDockWidget(Qt::LeftDockWidgetArea, inputDock); - QDockWidget* textureDock = new QDockWidget(tr("&Textures")); - TextureEditor* textureEditor = new TextureEditor(m_shaderGraph); + + QDockWidget* textureDock = new QDockWidget(tr("Textures")); + textureDock->setFeatures(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable); textureDock->setWidget(textureEditor); addDockWidget(Qt::LeftDockWidgetArea, textureDock); + m_nodeEditor = new NodeEditor; + + QDockWidget* nodeEditorDock = new QDockWidget(tr("Node editor")); + nodeEditorDock->setFeatures(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable); + nodeEditorDock->setWidget(m_nodeEditor); + + addDockWidget(Qt::RightDockWidgetArea, nodeEditorDock); + + m_onSelectedNodeUpdate.Connect(m_shaderGraph.OnSelectedNodeUpdate, [&](ShaderGraph*, ShaderNode* node) + { + if (node) + { + m_nodeEditor->UpdateContent(node->caption(), [node](QVBoxLayout* layout) + { + node->BuildNodeEdition(layout); + }); + } + else + m_nodeEditor->Clear(); + }); + + BuildMenu(); } diff --git a/src/ShaderNode/Widgets/MainWindow.hpp b/src/ShaderNode/Widgets/MainWindow.hpp index 3e68ec267..777c3b92b 100644 --- a/src/ShaderNode/Widgets/MainWindow.hpp +++ b/src/ShaderNode/Widgets/MainWindow.hpp @@ -4,9 +4,10 @@ #define NAZARA_SHADERNODES_MAINWINDOW_HPP #include +#include #include -class ShaderGraph; +class NodeEditor; class MainWindow : public QMainWindow { @@ -18,6 +19,9 @@ class MainWindow : public QMainWindow void BuildMenu(); void OnCompileToGLSL(); + NazaraSlot(ShaderGraph, OnSelectedNodeUpdate, m_onSelectedNodeUpdate); + + NodeEditor* m_nodeEditor; ShaderGraph& m_shaderGraph; }; diff --git a/src/ShaderNode/Widgets/NodeEditor.cpp b/src/ShaderNode/Widgets/NodeEditor.cpp new file mode 100644 index 000000000..ee6b63e5a --- /dev/null +++ b/src/ShaderNode/Widgets/NodeEditor.cpp @@ -0,0 +1,20 @@ +#include +#include +#include + +NodeEditor::NodeEditor() : +m_layout(nullptr) +{ +} + +void NodeEditor::Clear() +{ + if (m_layout) + { + while (QWidget* w = findChild()) + delete w; + + delete m_layout; + m_layout = nullptr; + } +} diff --git a/src/ShaderNode/Widgets/NodeEditor.hpp b/src/ShaderNode/Widgets/NodeEditor.hpp new file mode 100644 index 000000000..65e73c107 --- /dev/null +++ b/src/ShaderNode/Widgets/NodeEditor.hpp @@ -0,0 +1,27 @@ +#pragma once + +#ifndef NAZARA_SHADERNODES_NODEEDITOR_HPP +#define NAZARA_SHADERNODES_NODEEDITOR_HPP + +#include +#include +#include +#include + +class NodeEditor : public QWidget +{ + public: + NodeEditor(); + ~NodeEditor() = default; + + void Clear(); + + template void UpdateContent(QString nodeName, F&& callback); + + private: + QVBoxLayout* m_layout; +}; + +#include + +#endif diff --git a/src/ShaderNode/Widgets/NodeEditor.inl b/src/ShaderNode/Widgets/NodeEditor.inl new file mode 100644 index 000000000..a7f916889 --- /dev/null +++ b/src/ShaderNode/Widgets/NodeEditor.inl @@ -0,0 +1,16 @@ +#include +#include + +template +void NodeEditor::UpdateContent(QString nodeName, F&& callback) +{ + Clear(); + + m_layout = new QVBoxLayout; + setLayout(m_layout); + + QLabel* label = new QLabel(nodeName); + m_layout->addWidget(label); + + callback(m_layout); +}