ShaderNode: Add custom output support

This commit is contained in:
Lynix
2020-05-30 14:31:11 +02:00
parent 0a0dce4109
commit 2ecc624fe4
22 changed files with 537 additions and 118 deletions

View File

@@ -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;
}

View File

@@ -1,9 +0,0 @@
#include <ShaderNode/DataModels/FragmentOutput.hpp>
inline FragmentOutput::FragmentOutput(ShaderGraph& graph) :
ShaderNode(graph)
{
SetPreviewSize({ 128, 128 });
DisableCustomVariableName();
EnablePreview(true);
}

View File

@@ -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);

View File

@@ -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++;
}
}

View File

@@ -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

View File

@@ -0,0 +1 @@
#include <ShaderNode/DataModels/OutputValue.hpp>

View File

@@ -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();

View File

@@ -1,5 +1,10 @@
#include <ShaderNode/DataModels/ShaderNode.hpp>
inline void ShaderNode::DisablePreview()
{
return EnablePreview(false);
}
inline ShaderGraph& ShaderNode::GetGraph()
{
return m_graph;