diff --git a/src/ShaderNode/DataModels/FragmentOutput.cpp b/src/ShaderNode/DataModels/FragmentOutput.cpp deleted file mode 100644 index b3938151b..000000000 --- a/src/ShaderNode/DataModels/FragmentOutput.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include -#include -#include - -Nz::ShaderAst::ExpressionPtr FragmentOutput::GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const -{ - using namespace Nz::ShaderAst; - using namespace Nz::ShaderBuilder; - - assert(count == 1); - - auto output = Nz::ShaderBuilder::Output("RenderTarget0", ExpressionType::Float4); - - return Nz::ShaderBuilder::Assign(output, *expressions); -} - -QtNodes::NodeDataType FragmentOutput::dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const -{ - assert(portType == QtNodes::PortType::In); - assert(portIndex == 0); - - return Vec4Data::Type(); -} - -unsigned int FragmentOutput::nPorts(QtNodes::PortType portType) const -{ - switch (portType) - { - case QtNodes::PortType::In: return 1; - case QtNodes::PortType::Out: return 0; - } - - return 0; -} - -std::shared_ptr FragmentOutput::outData(QtNodes::PortIndex /*port*/) -{ - return {}; -} - -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.inl b/src/ShaderNode/DataModels/FragmentOutput.inl deleted file mode 100644 index 5b93f6699..000000000 --- a/src/ShaderNode/DataModels/FragmentOutput.inl +++ /dev/null @@ -1,9 +0,0 @@ -#include - -inline FragmentOutput::FragmentOutput(ShaderGraph& graph) : -ShaderNode(graph) -{ - SetPreviewSize({ 128, 128 }); - DisableCustomVariableName(); - EnablePreview(true); -} diff --git a/src/ShaderNode/DataModels/InputValue.cpp b/src/ShaderNode/DataModels/InputValue.cpp index fb53ba0a8..9ff7fd207 100644 --- a/src/ShaderNode/DataModels/InputValue.cpp +++ b/src/ShaderNode/DataModels/InputValue.cpp @@ -101,11 +101,11 @@ Nz::ShaderAst::ExpressionPtr InputValue::GetExpression(Nz::ShaderAst::Expression { switch (inputEntry.type) { - case InputType::Bool: return Nz::ShaderAst::ExpressionType::Boolean; - case InputType::Float1: return Nz::ShaderAst::ExpressionType::Float1; - case InputType::Float2: return Nz::ShaderAst::ExpressionType::Float2; - case InputType::Float3: return Nz::ShaderAst::ExpressionType::Float3; - case InputType::Float4: return Nz::ShaderAst::ExpressionType::Float4; + case InOutType::Bool: return Nz::ShaderAst::ExpressionType::Boolean; + case InOutType::Float1: return Nz::ShaderAst::ExpressionType::Float1; + case InOutType::Float2: return Nz::ShaderAst::ExpressionType::Float2; + case InOutType::Float3: return Nz::ShaderAst::ExpressionType::Float3; + case InOutType::Float4: return Nz::ShaderAst::ExpressionType::Float4; } assert(false); @@ -117,20 +117,20 @@ 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); + if (!m_currentInputIndex) + return Vec4Data::Type(); + const auto& inputEntry = GetGraph().GetInput(*m_currentInputIndex); switch (inputEntry.type) { //case InputType::Bool: return Nz::ShaderAst::ExpressionType::Boolean; //case InputType::Float1: return Nz::ShaderAst::ExpressionType::Float1; - case InputType::Float2: return Vec2Data::Type(); - case InputType::Float3: return Vec3Data::Type(); - case InputType::Float4: return Vec4Data::Type(); + case InOutType::Float2: return Vec2Data::Type(); + case InOutType::Float3: return Vec3Data::Type(); + case InOutType::Float4: return Vec4Data::Type(); } assert(false); diff --git a/src/ShaderNode/DataModels/OutputValue.cpp b/src/ShaderNode/DataModels/OutputValue.cpp new file mode 100644 index 000000000..2d3a8dc14 --- /dev/null +++ b/src/ShaderNode/DataModels/OutputValue.cpp @@ -0,0 +1,160 @@ +#include +#include +#include +#include +#include +#include + +OutputValue::OutputValue(ShaderGraph& graph) : +ShaderNode(graph) +{ + m_onOutputListUpdateSlot.Connect(GetGraph().OnOutputListUpdate, [&](ShaderGraph*) { OnOutputListUpdate(); }); + m_onOutputUpdateSlot.Connect(GetGraph().OnOutputUpdate, [&](ShaderGraph*, std::size_t inputIndex) + { + if (m_currentOutputIndex == inputIndex) + UpdatePreview(); + }); + + if (graph.GetOutputCount() > 0) + { + auto& firstOutput = graph.GetOutput(0); + m_currentOutputIndex = 0; + m_currentOutputText = firstOutput.name; + } + + EnablePreview(); + SetPreviewSize({ 128, 128 }); + DisableCustomVariableName(); +} + +void OutputValue::BuildNodeEdition(QFormLayout* layout) +{ + ShaderNode::BuildNodeEdition(layout); + + QComboBox* outputSelection = new QComboBox; + + connect(outputSelection, qOverload(&QComboBox::currentIndexChanged), [&](int index) + { + if (index >= 0) + m_currentOutputIndex = static_cast(index); + else + m_currentOutputIndex.reset(); + + UpdatePreview(); + }); + + for (const auto& outputEntry : GetGraph().GetOutputs()) + outputSelection->addItem(QString::fromStdString(outputEntry.name)); + + layout->addRow(tr("Output"), outputSelection); +} + +Nz::ShaderAst::ExpressionPtr OutputValue::GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const +{ + using namespace Nz::ShaderAst; + using namespace Nz::ShaderBuilder; + + assert(count == 1); + + if (!m_currentOutputIndex) + throw std::runtime_error("no output"); + + const auto& outputEntry = GetGraph().GetOutput(*m_currentOutputIndex); + + Nz::ShaderAst::ExpressionType expression = [&] + { + switch (outputEntry.type) + { + case InOutType::Bool: return Nz::ShaderAst::ExpressionType::Boolean; + case InOutType::Float1: return Nz::ShaderAst::ExpressionType::Float1; + case InOutType::Float2: return Nz::ShaderAst::ExpressionType::Float2; + case InOutType::Float3: return Nz::ShaderAst::ExpressionType::Float3; + case InOutType::Float4: return Nz::ShaderAst::ExpressionType::Float4; + } + + assert(false); + throw std::runtime_error("Unhandled output type"); + }(); + + auto output = Nz::ShaderBuilder::Output(outputEntry.name, expression); + + return Nz::ShaderBuilder::Assign(std::move(output), *expressions); +} + +QtNodes::NodeDataType OutputValue::dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const +{ + assert(portType == QtNodes::PortType::In); + assert(portIndex == 0); + + if (!m_currentOutputIndex) + return Vec4Data::Type(); + + const auto& outputEntry = GetGraph().GetOutput(*m_currentOutputIndex); + switch (outputEntry.type) + { + //case InOutType::Bool: return Nz::ShaderAst::ExpressionType::Boolean; + //case InOutType::Float1: return Nz::ShaderAst::ExpressionType::Float1; + case InOutType::Float2: return Vec2Data::Type(); + case InOutType::Float3: return Vec3Data::Type(); + case InOutType::Float4: return Vec4Data::Type(); + } + + assert(false); + throw std::runtime_error("Unhandled output type"); +} + +unsigned int OutputValue::nPorts(QtNodes::PortType portType) const +{ + switch (portType) + { + case QtNodes::PortType::In: return 1; + case QtNodes::PortType::Out: return 0; + } + + return 0; +} + +std::shared_ptr OutputValue::outData(QtNodes::PortIndex /*port*/) +{ + return {}; +} + +void OutputValue::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 OutputValue::ComputePreview(QPixmap& pixmap) +{ + if (!m_input) + return false; + + pixmap = QPixmap::fromImage(m_input->preview); + return true; +} + +void OutputValue::OnOutputListUpdate() +{ + m_currentOutputIndex.reset(); + + std::size_t inputIndex = 0; + for (const auto& inputEntry : GetGraph().GetOutputs()) + { + if (inputEntry.name == m_currentOutputText) + { + m_currentOutputIndex = inputIndex; + break; + } + + inputIndex++; + } +} diff --git a/src/ShaderNode/DataModels/FragmentOutput.hpp b/src/ShaderNode/DataModels/OutputValue.hpp similarity index 54% rename from src/ShaderNode/DataModels/FragmentOutput.hpp rename to src/ShaderNode/DataModels/OutputValue.hpp index eb9b18bf9..8db611056 100644 --- a/src/ShaderNode/DataModels/FragmentOutput.hpp +++ b/src/ShaderNode/DataModels/OutputValue.hpp @@ -3,20 +3,23 @@ #ifndef NAZARA_SHADERNODES_FRAGMENTOUTPUT_HPP #define NAZARA_SHADERNODES_FRAGMENTOUTPUT_HPP +#include #include #include -#include -#include -class FragmentOutput : public ShaderNode +class QFormLayout; + +class OutputValue : public ShaderNode { public: - inline FragmentOutput(ShaderGraph& graph); + OutputValue(ShaderGraph& graph); + + void BuildNodeEdition(QFormLayout* layout) override; Nz::ShaderAst::ExpressionPtr GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const override; - QString caption() const override { return "Fragment shader output"; } - QString name() const override { return "FragmentShaderOutput"; } + QString caption() const override { return "Output"; } + QString name() const override { return "Output"; } QtNodes::NodeDataType dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; @@ -28,10 +31,16 @@ class FragmentOutput : public ShaderNode private: bool ComputePreview(QPixmap& pixmap) override; + void OnOutputListUpdate(); - std::shared_ptr m_input; + NazaraSlot(ShaderGraph, OnOutputListUpdate, m_onOutputListUpdateSlot); + NazaraSlot(ShaderGraph, OnOutputUpdate, m_onOutputUpdateSlot); + + std::optional m_currentOutputIndex; + std::shared_ptr m_input; + std::string m_currentOutputText; }; -#include +#include #endif diff --git a/src/ShaderNode/DataModels/OutputValue.inl b/src/ShaderNode/DataModels/OutputValue.inl new file mode 100644 index 000000000..585e40a7f --- /dev/null +++ b/src/ShaderNode/DataModels/OutputValue.inl @@ -0,0 +1 @@ +#include diff --git a/src/ShaderNode/DataModels/ShaderNode.hpp b/src/ShaderNode/DataModels/ShaderNode.hpp index 9d82851e2..91f6dc0d3 100644 --- a/src/ShaderNode/DataModels/ShaderNode.hpp +++ b/src/ShaderNode/DataModels/ShaderNode.hpp @@ -20,7 +20,8 @@ class ShaderNode : public QtNodes::NodeDataModel virtual void BuildNodeEdition(QFormLayout* layout); - void EnablePreview(bool enable); + inline void DisablePreview(); + void EnablePreview(bool enable = true); virtual Nz::ShaderAst::ExpressionPtr GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const = 0; inline ShaderGraph& GetGraph(); diff --git a/src/ShaderNode/DataModels/ShaderNode.inl b/src/ShaderNode/DataModels/ShaderNode.inl index 1fb9bf67a..558a86165 100644 --- a/src/ShaderNode/DataModels/ShaderNode.inl +++ b/src/ShaderNode/DataModels/ShaderNode.inl @@ -1,5 +1,10 @@ #include +inline void ShaderNode::DisablePreview() +{ + return EnablePreview(false); +} + inline ShaderGraph& ShaderNode::GetGraph() { return m_graph; diff --git a/src/ShaderNode/Enums.cpp b/src/ShaderNode/Enums.cpp index 22f21c972..fd99c5562 100644 --- a/src/ShaderNode/Enums.cpp +++ b/src/ShaderNode/Enums.cpp @@ -15,15 +15,15 @@ const char* EnumToString(InputRole role) return ""; } -const char* EnumToString(InputType input) +const char* EnumToString(InOutType input) { switch (input) { - case InputType::Bool: return "Bool"; - case InputType::Float1: return "Float"; - case InputType::Float2: return "Float2"; - case InputType::Float3: return "Float3"; - case InputType::Float4: return "Float4"; + case InOutType::Bool: return "Bool"; + case InOutType::Float1: return "Float"; + case InOutType::Float2: return "Float2"; + case InOutType::Float3: return "Float3"; + case InOutType::Float4: return "Float4"; } assert(false); diff --git a/src/ShaderNode/Enums.hpp b/src/ShaderNode/Enums.hpp index b8e944c5c..ba990fdcf 100644 --- a/src/ShaderNode/Enums.hpp +++ b/src/ShaderNode/Enums.hpp @@ -17,7 +17,7 @@ enum class InputRole constexpr std::size_t InputRoleCount = static_cast(InputRole::Max) + 1; -enum class InputType +enum class InOutType { Bool, Float1, @@ -28,7 +28,7 @@ enum class InputType Max = Float4 }; -constexpr std::size_t InputTypeCount = static_cast(InputType::Max) + 1; +constexpr std::size_t InOutTypeCount = static_cast(InOutType::Max) + 1; enum class TextureType { @@ -40,7 +40,7 @@ enum class TextureType constexpr std::size_t TextureTypeCount = static_cast(TextureType::Max) + 1; const char* EnumToString(InputRole role); -const char* EnumToString(InputType input); +const char* EnumToString(InOutType input); const char* EnumToString(TextureType textureType); #include diff --git a/src/ShaderNode/ShaderGraph.cpp b/src/ShaderNode/ShaderGraph.cpp index 1a80b9cbe..876b0f1a3 100644 --- a/src/ShaderNode/ShaderGraph.cpp +++ b/src/ShaderNode/ShaderGraph.cpp @@ -1,8 +1,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -43,7 +43,8 @@ m_flowScene(BuildRegistry()) }); // Test - AddInput("UV", InputType::Float2, InputRole::TexCoord, 0); + AddInput("UV", InOutType::Float2, InputRole::TexCoord, 0); + AddOutput("RenderTarget0", InOutType::Float4); AddTexture("Potato", TextureType::Sampler2D); UpdateTexturePreview(0, QImage(R"(C:\Users\Lynix\Pictures\potatavril.png)")); @@ -60,7 +61,7 @@ m_flowScene(BuildRegistry()) auto& node4 = m_flowScene.createNode(std::make_unique(*this)); node4.nodeGraphicsObject().setPos(400, 200); - auto& node5 = m_flowScene.createNode(std::make_unique(*this)); + auto& node5 = m_flowScene.createNode(std::make_unique(*this)); node5.nodeGraphicsObject().setPos(600, 300); m_flowScene.createConnection(node3, 0, node1, 0); @@ -75,7 +76,7 @@ ShaderGraph::~ShaderGraph() m_flowScene.clearScene(); } -std::size_t ShaderGraph::AddInput(std::string name, InputType type, InputRole role, std::size_t roleIndex) +std::size_t ShaderGraph::AddInput(std::string name, InOutType type, InputRole role, std::size_t roleIndex) { std::size_t index = m_inputs.size(); auto& inputEntry = m_inputs.emplace_back(); @@ -89,6 +90,18 @@ std::size_t ShaderGraph::AddInput(std::string name, InputType type, InputRole ro return index; } +std::size_t ShaderGraph::AddOutput(std::string name, InOutType type) +{ + std::size_t index = m_outputs.size(); + auto& outputEntry = m_outputs.emplace_back(); + outputEntry.name = std::move(name); + outputEntry.type = type; + + OnOutputListUpdate(this); + + return index; +} + std::size_t ShaderGraph::AddTexture(std::string name, TextureType type) { std::size_t index = m_textures.size(); @@ -198,18 +211,26 @@ Nz::ShaderAst::StatementPtr ShaderGraph::ToAst() return expression; }; - m_flowScene.iterateOverNodes([&](QtNodes::Node* node) + try { - if (node->nodeDataModel()->nPorts(QtNodes::PortType::Out) == 0) + m_flowScene.iterateOverNodes([&](QtNodes::Node* node) { - statements.emplace_back(Nz::ShaderBuilder::ExprStatement(HandleNode(node))); - } - }); + if (node->nodeDataModel()->nPorts(QtNodes::PortType::Out) == 0) + { + statements.emplace_back(Nz::ShaderBuilder::ExprStatement(HandleNode(node))); + } + }); + } + catch (const std::exception&) + { + + return nullptr; + } return std::make_shared(std::move(statements)); } -void ShaderGraph::UpdateInput(std::size_t inputIndex, std::string name, InputType type, InputRole role, std::size_t roleIndex) +void ShaderGraph::UpdateInput(std::size_t inputIndex, std::string name, InOutType type, InputRole role, std::size_t roleIndex) { assert(inputIndex < m_inputs.size()); auto& inputEntry = m_inputs[inputIndex]; @@ -221,6 +242,16 @@ void ShaderGraph::UpdateInput(std::size_t inputIndex, std::string name, InputTyp OnInputUpdate(this, inputIndex); } +void ShaderGraph::UpdateOutput(std::size_t outputIndex, std::string name, InOutType type) +{ + assert(outputIndex < m_outputs.size()); + auto& outputEntry = m_outputs[outputIndex]; + outputEntry.name = std::move(name); + outputEntry.type = type; + + OnOutputUpdate(this, outputIndex); +} + void ShaderGraph::UpdateTexturePreview(std::size_t textureIndex, QImage preview) { assert(textureIndex < m_textures.size()); @@ -240,8 +271,8 @@ std::shared_ptr ShaderGraph::BuildRegistry() RegisterShaderNode(*this, registry, "Casts"); RegisterShaderNode(*this, registry, "Casts"); RegisterShaderNode(*this, registry, "Casts"); - RegisterShaderNode(*this, registry, "Outputs"); RegisterShaderNode(*this, registry, "Inputs"); + RegisterShaderNode(*this, registry, "Outputs"); RegisterShaderNode(*this, registry, "Texture"); RegisterShaderNode(*this, registry, "Texture"); RegisterShaderNode(*this, registry, "Vector operations"); diff --git a/src/ShaderNode/ShaderGraph.hpp b/src/ShaderNode/ShaderGraph.hpp index ca29ff174..854d5cfb7 100644 --- a/src/ShaderNode/ShaderGraph.hpp +++ b/src/ShaderNode/ShaderGraph.hpp @@ -17,17 +17,22 @@ class ShaderGraph { public: struct InputEntry; + struct OutputEntry; struct TextureEntry; ShaderGraph(); ~ShaderGraph(); - std::size_t AddInput(std::string name, InputType type, InputRole role, std::size_t roleIndex); + std::size_t AddInput(std::string name, InOutType type, InputRole role, std::size_t roleIndex); + std::size_t AddOutput(std::string name, InOutType type); std::size_t AddTexture(std::string name, TextureType type); inline const InputEntry& GetInput(std::size_t inputIndex) const; inline std::size_t GetInputCount() const; inline const std::vector& GetInputs() const; + inline const OutputEntry& GetOutput(std::size_t outputIndex) const; + inline std::size_t GetOutputCount() const; + inline const std::vector& GetOutputs() const; inline const PreviewModel& GetPreviewModel() const; inline QtNodes::FlowScene& GetScene(); inline const TextureEntry& GetTexture(std::size_t textureIndex) const; @@ -36,7 +41,8 @@ class ShaderGraph Nz::ShaderAst::StatementPtr ToAst(); - void UpdateInput(std::size_t inputIndex, std::string name, InputType type, InputRole role, std::size_t roleIndex); + void UpdateInput(std::size_t inputIndex, std::string name, InOutType type, InputRole role, std::size_t roleIndex); + void UpdateOutput(std::size_t outputIndex, std::string name, InOutType type); void UpdateTexturePreview(std::size_t texture, QImage preview); struct InputEntry @@ -44,7 +50,13 @@ class ShaderGraph std::size_t roleIndex; std::string name; InputRole role; - InputType type; + InOutType type; + }; + + struct OutputEntry + { + std::string name; + InOutType type; }; struct TextureEntry @@ -56,6 +68,8 @@ class ShaderGraph NazaraSignal(OnInputListUpdate, ShaderGraph*); NazaraSignal(OnInputUpdate, ShaderGraph*, std::size_t /*inputIndex*/); + NazaraSignal(OnOutputListUpdate, ShaderGraph*); + NazaraSignal(OnOutputUpdate, ShaderGraph*, std::size_t /*outputIndex*/); NazaraSignal(OnSelectedNodeUpdate, ShaderGraph*, ShaderNode* /*node*/); NazaraSignal(OnTextureListUpdate, ShaderGraph*); NazaraSignal(OnTexturePreviewUpdate, ShaderGraph*, std::size_t /*textureIndex*/); @@ -65,6 +79,7 @@ class ShaderGraph QtNodes::FlowScene m_flowScene; std::vector m_inputs; + std::vector m_outputs; std::vector m_textures; std::unique_ptr m_previewModel; }; diff --git a/src/ShaderNode/ShaderGraph.inl b/src/ShaderNode/ShaderGraph.inl index 97184906e..3f839534c 100644 --- a/src/ShaderNode/ShaderGraph.inl +++ b/src/ShaderNode/ShaderGraph.inl @@ -16,6 +16,22 @@ inline auto ShaderGraph::GetInputs() const -> const std::vector& return m_inputs; } +inline auto ShaderGraph::GetOutput(std::size_t outputIndex) const -> const OutputEntry& +{ + assert(outputIndex < m_outputs.size()); + return m_outputs[outputIndex]; +} + +inline std::size_t ShaderGraph::GetOutputCount() const +{ + return m_outputs.size(); +} + +inline auto ShaderGraph::GetOutputs() const -> const std::vector& +{ + return m_outputs; +} + inline const PreviewModel& ShaderGraph::GetPreviewModel() const { return *m_previewModel; diff --git a/src/ShaderNode/Widgets/InputEditDialog.cpp b/src/ShaderNode/Widgets/InputEditDialog.cpp index 71e6b2298..09f711f3d 100644 --- a/src/ShaderNode/Widgets/InputEditDialog.cpp +++ b/src/ShaderNode/Widgets/InputEditDialog.cpp @@ -16,8 +16,8 @@ QDialog(parent) m_inputName = new QLineEdit; m_typeList = new QComboBox; - for (std::size_t i = 0; i < InputTypeCount; ++i) - m_typeList->addItem(EnumToString(static_cast(i))); + for (std::size_t i = 0; i < InOutTypeCount; ++i) + m_typeList->addItem(EnumToString(static_cast(i))); m_roleList = new QComboBox; for (std::size_t i = 0; i < InputRoleCount; ++i) @@ -57,7 +57,7 @@ InputInfo InputEditDialog::GetInputInfo() const inputInfo.name = m_inputName->text().toStdString(); inputInfo.role = static_cast(m_roleList->currentIndex()); inputInfo.roleIndex = static_cast(m_roleIndex->value()); - inputInfo.type = static_cast(m_typeList->currentIndex()); + inputInfo.type = static_cast(m_typeList->currentIndex()); return inputInfo; } diff --git a/src/ShaderNode/Widgets/InputEditDialog.hpp b/src/ShaderNode/Widgets/InputEditDialog.hpp index ef96d118c..633b9c99d 100644 --- a/src/ShaderNode/Widgets/InputEditDialog.hpp +++ b/src/ShaderNode/Widgets/InputEditDialog.hpp @@ -15,7 +15,7 @@ struct InputInfo std::size_t roleIndex; std::string name; InputRole role; - InputType type; + InOutType type; }; class InputEditDialog : public QDialog diff --git a/src/ShaderNode/Widgets/MainWindow.cpp b/src/ShaderNode/Widgets/MainWindow.cpp index 6f3d8a5f4..ca0bd4c0a 100644 --- a/src/ShaderNode/Widgets/MainWindow.cpp +++ b/src/ShaderNode/Widgets/MainWindow.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -20,6 +21,7 @@ m_shaderGraph(graph) QtNodes::FlowView* flowView = new QtNodes::FlowView(scene); setCentralWidget(flowView); + // Input editor InputEditor* inputEditor = new InputEditor(m_shaderGraph); QDockWidget* inputDock = new QDockWidget(tr("Inputs")); @@ -28,6 +30,16 @@ m_shaderGraph(graph) addDockWidget(Qt::LeftDockWidgetArea, inputDock); + // Output editor + OutputEditor* outputEditor = new OutputEditor(m_shaderGraph); + + QDockWidget* outputDock = new QDockWidget(tr("Outputs")); + outputDock->setFeatures(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable); + outputDock->setWidget(outputEditor); + + addDockWidget(Qt::LeftDockWidgetArea, outputDock); + + // Texture editor TextureEditor* textureEditor = new TextureEditor(m_shaderGraph); QDockWidget* textureDock = new QDockWidget(tr("Textures")); @@ -36,6 +48,7 @@ m_shaderGraph(graph) addDockWidget(Qt::LeftDockWidgetArea, textureDock); + // Node editor m_nodeEditor = new NodeEditor; QDockWidget* nodeEditorDock = new QDockWidget(tr("Node editor")); diff --git a/src/ShaderNode/Widgets/OutputEditDialog.cpp b/src/ShaderNode/Widgets/OutputEditDialog.cpp new file mode 100644 index 000000000..23f092f75 --- /dev/null +++ b/src/ShaderNode/Widgets/OutputEditDialog.cpp @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include +#include +#include + +OutputEditDialog::OutputEditDialog(QWidget* parent) : +QDialog(parent) +{ + setWindowTitle(tr("Output edit dialog")); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + m_outputName = new QLineEdit; + + m_typeList = new QComboBox; + for (std::size_t i = 0; i < InOutTypeCount; ++i) + m_typeList->addItem(EnumToString(static_cast(i))); + + QFormLayout* formLayout = new QFormLayout; + formLayout->addRow(tr("Name"), m_outputName); + formLayout->addRow(tr("Type"), m_typeList); + + QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + connect(buttonBox, &QDialogButtonBox::accepted, this, &OutputEditDialog::OnAccept); + connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + + QVBoxLayout* verticalLayout = new QVBoxLayout; + verticalLayout->addLayout(formLayout); + verticalLayout->addWidget(buttonBox); + + setLayout(verticalLayout); +} + +OutputEditDialog::OutputEditDialog(const OutputInfo& input, QWidget* parent) : +OutputEditDialog(parent) +{ + m_outputName->setText(QString::fromStdString(input.name)); + m_typeList->setCurrentText(EnumToString(input.type)); +} + +OutputInfo OutputEditDialog::GetOutputInfo() const +{ + OutputInfo inputInfo; + inputInfo.name = m_outputName->text().toStdString(); + inputInfo.type = static_cast(m_typeList->currentIndex()); + + return inputInfo; +} + +void OutputEditDialog::OnAccept() +{ + if (m_outputName->text().isEmpty()) + { + QMessageBox::critical(this, tr("Empty name"), tr("Output name must be set"), QMessageBox::Ok); + return; + } + + if (m_typeList->currentIndex() < 0) + { + QMessageBox::critical(this, tr("Invalid type"), tr("You must select a type"), QMessageBox::Ok); + return; + } + + accept(); +} diff --git a/src/ShaderNode/Widgets/OutputEditDialog.hpp b/src/ShaderNode/Widgets/OutputEditDialog.hpp new file mode 100644 index 000000000..3c06323dd --- /dev/null +++ b/src/ShaderNode/Widgets/OutputEditDialog.hpp @@ -0,0 +1,36 @@ +#pragma once + +#ifndef NAZARA_SHADERNODES_OUTPUTEDITDIALOG_HPP +#define NAZARA_SHADERNODES_OUTPUTEDITDIALOG_HPP + +#include +#include + +class QComboBox; +class QLineEdit; + +struct OutputInfo +{ + std::string name; + InOutType type; +}; + +class OutputEditDialog : public QDialog +{ + public: + OutputEditDialog(QWidget* parent = nullptr); + OutputEditDialog(const OutputInfo& input, QWidget* parent = nullptr); + ~OutputEditDialog() = default; + + OutputInfo GetOutputInfo() const; + + private: + void OnAccept(); + + QComboBox* m_typeList; + QLineEdit* m_outputName; +}; + +#include + +#endif diff --git a/src/ShaderNode/Widgets/OutputEditDialog.inl b/src/ShaderNode/Widgets/OutputEditDialog.inl new file mode 100644 index 000000000..9c2c2892b --- /dev/null +++ b/src/ShaderNode/Widgets/OutputEditDialog.inl @@ -0,0 +1 @@ +#include diff --git a/src/ShaderNode/Widgets/OutputEditor.cpp b/src/ShaderNode/Widgets/OutputEditor.cpp new file mode 100644 index 000000000..ba317de07 --- /dev/null +++ b/src/ShaderNode/Widgets/OutputEditor.cpp @@ -0,0 +1,95 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +OutputEditor::OutputEditor(ShaderGraph& graph) : +m_shaderGraph(graph) +{ + m_outputList = new QListWidget(this); + connect(m_outputList, &QListWidget::currentRowChanged, this, &OutputEditor::OnOutputSelectionUpdate); + connect(m_outputList, &QListWidget::itemDoubleClicked, [this](QListWidgetItem* item) + { + OnEditOutput(m_outputList->row(item)); + }); + + QPushButton* addOutputButton = new QPushButton(tr("Add output...")); + connect(addOutputButton, &QPushButton::released, this, &OutputEditor::OnAddOutput); + + m_layout = new QVBoxLayout; + m_layout->addWidget(m_outputList); + m_layout->addWidget(addOutputButton); + + setLayout(m_layout); + + m_onOutputListUpdateSlot.Connect(m_shaderGraph.OnOutputListUpdate, this, &OutputEditor::OnOutputListUpdate); + m_onOutputUpdateSlot.Connect(m_shaderGraph.OnOutputUpdate, this, &OutputEditor::OnOutputUpdate); + + RefreshOutputs(); +} + +void OutputEditor::OnAddOutput() +{ + OutputEditDialog* dialog = new OutputEditDialog(this); + dialog->setAttribute(Qt::WA_DeleteOnClose, true); + connect(dialog, &QDialog::accepted, [this, dialog] + { + OutputInfo inputInfo = dialog->GetOutputInfo(); + m_shaderGraph.AddOutput(std::move(inputInfo.name), inputInfo.type); + }); + + dialog->open(); +} + +void OutputEditor::OnEditOutput(int inputIndex) +{ + const auto& input = m_shaderGraph.GetOutput(inputIndex); + + OutputInfo info; + info.name = input.name; + info.type = input.type; + + OutputEditDialog* dialog = new OutputEditDialog(std::move(info), this); + dialog->setAttribute(Qt::WA_DeleteOnClose, true); + connect(dialog, &QDialog::accepted, [this, dialog, inputIndex] + { + OutputInfo inputInfo = dialog->GetOutputInfo(); + m_shaderGraph.UpdateOutput(inputIndex, std::move(inputInfo.name), inputInfo.type); + }); + + dialog->open(); +} + +void OutputEditor::OnOutputSelectionUpdate(int inputIndex) +{ + if (inputIndex >= 0) + { + m_currentOutputIndex = inputIndex; + } + else + m_currentOutputIndex.reset(); +} + +void OutputEditor::OnOutputListUpdate(ShaderGraph* /*graph*/) +{ + RefreshOutputs(); +} + +void OutputEditor::OnOutputUpdate(ShaderGraph* /*graph*/, std::size_t inputIndex) +{ + const auto& inputEntry = m_shaderGraph.GetOutput(inputIndex); + m_outputList->item(int(inputIndex))->setText(QString::fromStdString(inputEntry.name)); +} + +void OutputEditor::RefreshOutputs() +{ + m_outputList->clear(); + m_outputList->setCurrentRow(-1); + + for (const auto& inputEntry : m_shaderGraph.GetOutputs()) + m_outputList->addItem(QString::fromStdString(inputEntry.name)); +} diff --git a/src/ShaderNode/Widgets/OutputEditor.hpp b/src/ShaderNode/Widgets/OutputEditor.hpp new file mode 100644 index 000000000..6aa53eda0 --- /dev/null +++ b/src/ShaderNode/Widgets/OutputEditor.hpp @@ -0,0 +1,39 @@ +#pragma once + +#ifndef NAZARA_SHADERNODES_OUTPUTEDITOR_HPP +#define NAZARA_SHADERNODES_OUTPUTEDITOR_HPP + +#include +#include +#include + +class QLabel; +class QListWidget; +class QVBoxLayout; + +class OutputEditor : public QWidget +{ + public: + OutputEditor(ShaderGraph& graph); + ~OutputEditor() = default; + + private: + void OnAddOutput(); + void OnEditOutput(int inputIndex); + void OnOutputSelectionUpdate(int inputIndex); + void OnOutputListUpdate(ShaderGraph* graph); + void OnOutputUpdate(ShaderGraph* graph, std::size_t inputIndex); + void RefreshOutputs(); + + NazaraSlot(ShaderGraph, OnOutputListUpdate, m_onOutputListUpdateSlot); + NazaraSlot(ShaderGraph, OnOutputUpdate, m_onOutputUpdateSlot); + + std::optional m_currentOutputIndex; + ShaderGraph& m_shaderGraph; + QListWidget* m_outputList; + QVBoxLayout* m_layout; +}; + +#include + +#endif diff --git a/src/ShaderNode/Widgets/OutputEditor.inl b/src/ShaderNode/Widgets/OutputEditor.inl new file mode 100644 index 000000000..10ed1238b --- /dev/null +++ b/src/ShaderNode/Widgets/OutputEditor.inl @@ -0,0 +1 @@ +#include