ShaderNode: Add custom output support
This commit is contained in:
parent
0a0dce4109
commit
2ecc624fe4
|
|
@ -1,62 +0,0 @@
|
|||
#include <ShaderNode/DataModels/FragmentOutput.hpp>
|
||||
#include <Nazara/Renderer/ShaderBuilder.hpp>
|
||||
#include <ShaderNode/DataTypes/VecData.hpp>
|
||||
|
||||
Nz::ShaderAst::ExpressionPtr FragmentOutput::GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const
|
||||
{
|
||||
using namespace Nz::ShaderAst;
|
||||
using namespace Nz::ShaderBuilder;
|
||||
|
||||
assert(count == 1);
|
||||
|
||||
auto output = Nz::ShaderBuilder::Output("RenderTarget0", ExpressionType::Float4);
|
||||
|
||||
return Nz::ShaderBuilder::Assign(output, *expressions);
|
||||
}
|
||||
|
||||
QtNodes::NodeDataType FragmentOutput::dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const
|
||||
{
|
||||
assert(portType == QtNodes::PortType::In);
|
||||
assert(portIndex == 0);
|
||||
|
||||
return Vec4Data::Type();
|
||||
}
|
||||
|
||||
unsigned int FragmentOutput::nPorts(QtNodes::PortType portType) const
|
||||
{
|
||||
switch (portType)
|
||||
{
|
||||
case QtNodes::PortType::In: return 1;
|
||||
case QtNodes::PortType::Out: return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::shared_ptr<QtNodes::NodeData> FragmentOutput::outData(QtNodes::PortIndex /*port*/)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
void FragmentOutput::setInData(std::shared_ptr<QtNodes::NodeData> value, int index)
|
||||
{
|
||||
assert(index == 0);
|
||||
if (value)
|
||||
{
|
||||
assert(dynamic_cast<Vec4Data*>(value.get()) != nullptr);
|
||||
m_input = std::static_pointer_cast<Vec4Data>(value);
|
||||
}
|
||||
else
|
||||
m_input.reset();
|
||||
|
||||
UpdatePreview();
|
||||
}
|
||||
|
||||
bool FragmentOutput::ComputePreview(QPixmap& pixmap)
|
||||
{
|
||||
if (!m_input)
|
||||
return false;
|
||||
|
||||
pixmap = QPixmap::fromImage(m_input->preview);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
#include <ShaderNode/DataModels/FragmentOutput.hpp>
|
||||
|
||||
inline FragmentOutput::FragmentOutput(ShaderGraph& graph) :
|
||||
ShaderNode(graph)
|
||||
{
|
||||
SetPreviewSize({ 128, 128 });
|
||||
DisableCustomVariableName();
|
||||
EnablePreview(true);
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,160 @@
|
|||
#include <ShaderNode/DataModels/OutputValue.hpp>
|
||||
#include <Nazara/Renderer/ShaderBuilder.hpp>
|
||||
#include <ShaderNode/ShaderGraph.hpp>
|
||||
#include <ShaderNode/DataTypes/VecData.hpp>
|
||||
#include <QtWidgets/QComboBox>
|
||||
#include <QtWidgets/QFormLayout>
|
||||
|
||||
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<int>(&QComboBox::currentIndexChanged), [&](int index)
|
||||
{
|
||||
if (index >= 0)
|
||||
m_currentOutputIndex = static_cast<std::size_t>(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<QtNodes::NodeData> OutputValue::outData(QtNodes::PortIndex /*port*/)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
void OutputValue::setInData(std::shared_ptr<QtNodes::NodeData> value, int index)
|
||||
{
|
||||
assert(index == 0);
|
||||
if (value)
|
||||
{
|
||||
assert(dynamic_cast<Vec4Data*>(value.get()) != nullptr);
|
||||
m_input = std::static_pointer_cast<Vec4Data>(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++;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,20 +3,23 @@
|
|||
#ifndef NAZARA_SHADERNODES_FRAGMENTOUTPUT_HPP
|
||||
#define NAZARA_SHADERNODES_FRAGMENTOUTPUT_HPP
|
||||
|
||||
#include <ShaderNode/ShaderGraph.hpp>
|
||||
#include <ShaderNode/DataModels/ShaderNode.hpp>
|
||||
#include <ShaderNode/DataTypes/VecData.hpp>
|
||||
#include <QtWidgets/QDoubleSpinBox>
|
||||
#include <QtWidgets/QFormLayout>
|
||||
|
||||
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<Vec4Data> m_input;
|
||||
NazaraSlot(ShaderGraph, OnOutputListUpdate, m_onOutputListUpdateSlot);
|
||||
NazaraSlot(ShaderGraph, OnOutputUpdate, m_onOutputUpdateSlot);
|
||||
|
||||
std::optional<std::size_t> m_currentOutputIndex;
|
||||
std::shared_ptr<VecData> m_input;
|
||||
std::string m_currentOutputText;
|
||||
};
|
||||
|
||||
#include <ShaderNode/DataModels/FragmentOutput.inl>
|
||||
#include <ShaderNode/DataModels/OutputValue.inl>
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1 @@
|
|||
#include <ShaderNode/DataModels/OutputValue.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();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
#include <ShaderNode/DataModels/ShaderNode.hpp>
|
||||
|
||||
inline void ShaderNode::DisablePreview()
|
||||
{
|
||||
return EnablePreview(false);
|
||||
}
|
||||
|
||||
inline ShaderGraph& ShaderNode::GetGraph()
|
||||
{
|
||||
return m_graph;
|
||||
|
|
|
|||
|
|
@ -15,15 +15,15 @@ const char* EnumToString(InputRole role)
|
|||
return "<Unhandled>";
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ enum class InputRole
|
|||
|
||||
constexpr std::size_t InputRoleCount = static_cast<std::size_t>(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<std::size_t>(InputType::Max) + 1;
|
||||
constexpr std::size_t InOutTypeCount = static_cast<std::size_t>(InOutType::Max) + 1;
|
||||
|
||||
enum class TextureType
|
||||
{
|
||||
|
|
@ -40,7 +40,7 @@ enum class TextureType
|
|||
constexpr std::size_t TextureTypeCount = static_cast<std::size_t>(TextureType::Max) + 1;
|
||||
|
||||
const char* EnumToString(InputRole role);
|
||||
const char* EnumToString(InputType input);
|
||||
const char* EnumToString(InOutType input);
|
||||
const char* EnumToString(TextureType textureType);
|
||||
|
||||
#include <ShaderNode/Enums.inl>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#include <ShaderNode/ShaderGraph.hpp>
|
||||
#include <Nazara/Core/StackArray.hpp>
|
||||
#include <ShaderNode/DataModels/Cast.hpp>
|
||||
#include <ShaderNode/DataModels/FragmentOutput.hpp>
|
||||
#include <ShaderNode/DataModels/InputValue.hpp>
|
||||
#include <ShaderNode/DataModels/OutputValue.hpp>
|
||||
#include <ShaderNode/DataModels/SampleTexture.hpp>
|
||||
#include <ShaderNode/DataModels/ShaderNode.hpp>
|
||||
#include <ShaderNode/DataModels/TextureValue.hpp>
|
||||
|
|
@ -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<Vec4Mul>(*this));
|
||||
node4.nodeGraphicsObject().setPos(400, 200);
|
||||
|
||||
auto& node5 = m_flowScene.createNode(std::make_unique<FragmentOutput>(*this));
|
||||
auto& node5 = m_flowScene.createNode(std::make_unique<OutputValue>(*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<Nz::ShaderAst::StatementBlock>(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<QtNodes::DataModelRegistry> ShaderGraph::BuildRegistry()
|
|||
RegisterShaderNode<CastVec3ToVec4>(*this, registry, "Casts");
|
||||
RegisterShaderNode<CastVec4ToVec2>(*this, registry, "Casts");
|
||||
RegisterShaderNode<CastVec4ToVec3>(*this, registry, "Casts");
|
||||
RegisterShaderNode<FragmentOutput>(*this, registry, "Outputs");
|
||||
RegisterShaderNode<InputValue>(*this, registry, "Inputs");
|
||||
RegisterShaderNode<OutputValue>(*this, registry, "Outputs");
|
||||
RegisterShaderNode<SampleTexture>(*this, registry, "Texture");
|
||||
RegisterShaderNode<TextureValue>(*this, registry, "Texture");
|
||||
RegisterShaderNode<Vec2Add>(*this, registry, "Vector operations");
|
||||
|
|
|
|||
|
|
@ -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<InputEntry>& GetInputs() const;
|
||||
inline const OutputEntry& GetOutput(std::size_t outputIndex) const;
|
||||
inline std::size_t GetOutputCount() const;
|
||||
inline const std::vector<OutputEntry>& 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<InputEntry> m_inputs;
|
||||
std::vector<OutputEntry> m_outputs;
|
||||
std::vector<TextureEntry> m_textures;
|
||||
std::unique_ptr<PreviewModel> m_previewModel;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -16,6 +16,22 @@ inline auto ShaderGraph::GetInputs() const -> const std::vector<InputEntry>&
|
|||
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<OutputEntry>&
|
||||
{
|
||||
return m_outputs;
|
||||
}
|
||||
|
||||
inline const PreviewModel& ShaderGraph::GetPreviewModel() const
|
||||
{
|
||||
return *m_previewModel;
|
||||
|
|
|
|||
|
|
@ -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<InputType>(i)));
|
||||
for (std::size_t i = 0; i < InOutTypeCount; ++i)
|
||||
m_typeList->addItem(EnumToString(static_cast<InOutType>(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<InputRole>(m_roleList->currentIndex());
|
||||
inputInfo.roleIndex = static_cast<std::size_t>(m_roleIndex->value());
|
||||
inputInfo.type = static_cast<InputType>(m_typeList->currentIndex());
|
||||
inputInfo.type = static_cast<InOutType>(m_typeList->currentIndex());
|
||||
|
||||
return inputInfo;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ struct InputInfo
|
|||
std::size_t roleIndex;
|
||||
std::string name;
|
||||
InputRole role;
|
||||
InputType type;
|
||||
InOutType type;
|
||||
};
|
||||
|
||||
class InputEditDialog : public QDialog
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include <Nazara/Renderer/GlslWriter.hpp>
|
||||
#include <ShaderNode/ShaderGraph.hpp>
|
||||
#include <ShaderNode/Widgets/InputEditor.hpp>
|
||||
#include <ShaderNode/Widgets/OutputEditor.hpp>
|
||||
#include <ShaderNode/Widgets/NodeEditor.hpp>
|
||||
#include <ShaderNode/Widgets/TextureEditor.hpp>
|
||||
#include <nodes/FlowView>
|
||||
|
|
@ -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"));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
#include <ShaderNode/Widgets/OutputEditDialog.hpp>
|
||||
#include <QtWidgets/QComboBox>
|
||||
#include <QtWidgets/QDialogButtonBox>
|
||||
#include <QtWidgets/QFormLayout>
|
||||
#include <QtWidgets/QLineEdit>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include <QtWidgets/QVBoxLayout>
|
||||
|
||||
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<InOutType>(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<InOutType>(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();
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef NAZARA_SHADERNODES_OUTPUTEDITDIALOG_HPP
|
||||
#define NAZARA_SHADERNODES_OUTPUTEDITDIALOG_HPP
|
||||
|
||||
#include <ShaderNode/Enums.hpp>
|
||||
#include <QtWidgets/QDialog>
|
||||
|
||||
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 <ShaderNode/Widgets/OutputEditDialog.inl>
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1 @@
|
|||
#include <ShaderNode/Widgets/OutputEditDialog.hpp>
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
#include <ShaderNode/Widgets/OutputEditor.hpp>
|
||||
#include <ShaderNode/Widgets/OutputEditDialog.hpp>
|
||||
#include <ShaderNode/ShaderGraph.hpp>
|
||||
#include <QtWidgets/QFileDialog>
|
||||
#include <QtWidgets/QLabel>
|
||||
#include <QtWidgets/QPushButton>
|
||||
#include <QtWidgets/QListWidget>
|
||||
#include <QtWidgets/QVBoxLayout>
|
||||
|
||||
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));
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef NAZARA_SHADERNODES_OUTPUTEDITOR_HPP
|
||||
#define NAZARA_SHADERNODES_OUTPUTEDITOR_HPP
|
||||
|
||||
#include <ShaderNode/ShaderGraph.hpp>
|
||||
#include <QtWidgets/QWidget>
|
||||
#include <optional>
|
||||
|
||||
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<std::size_t> m_currentOutputIndex;
|
||||
ShaderGraph& m_shaderGraph;
|
||||
QListWidget* m_outputList;
|
||||
QVBoxLayout* m_layout;
|
||||
};
|
||||
|
||||
#include <ShaderNode/Widgets/OutputEditor.inl>
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1 @@
|
|||
#include <ShaderNode/Widgets/OutputEditor.hpp>
|
||||
Loading…
Reference in New Issue