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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -15,7 +15,7 @@ struct InputInfo
std::size_t roleIndex;
std::string name;
InputRole role;
InputType type;
InOutType type;
};
class InputEditDialog : public QDialog

View File

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

View File

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

View File

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

View File

@ -0,0 +1 @@
#include <ShaderNode/Widgets/OutputEditDialog.hpp>

View File

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

View File

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

View File

@ -0,0 +1 @@
#include <ShaderNode/Widgets/OutputEditor.hpp>