ShaderAst: Add node editor window

This commit is contained in:
Lynix 2020-05-26 19:22:31 +02:00
parent b1b9030359
commit 09e08255fb
24 changed files with 558 additions and 337 deletions

View File

@ -18,13 +18,14 @@ class CastVec : public ShaderNode
CastVec(ShaderGraph& graph);
~CastVec() = default;
void BuildNodeEdition(QVBoxLayout* layout) override;
Nz::ShaderAst::ExpressionPtr GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const override;
QString caption() const override;
QString name() const override;
QtNodes::NodeDataType dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override;
QWidget* embeddedWidget() override;
unsigned int nPorts(QtNodes::PortType portType) const override;
std::shared_ptr<QtNodes::NodeData> outData(QtNodes::PortIndex port) override;
@ -36,13 +37,10 @@ class CastVec : public ShaderNode
static constexpr std::size_t ToComponents = To::ComponentCount;
static constexpr std::size_t ComponentDiff = (ToComponents >= FromComponents) ? ToComponents - FromComponents : 0;
void ComputePreview(QPixmap& pixmap) const;
void UpdatePreview();
bool ComputePreview(QPixmap& pixmap) override;
void UpdateOutput();
QLabel* m_pixmapLabel;
QPixmap m_pixmap;
QWidget* m_widget;
std::array<QDoubleSpinBox*, ComponentDiff> m_spinboxes;
VecType<ComponentDiff> m_overflowComponents;
std::shared_ptr<From> m_input;
std::shared_ptr<To> m_output;
};

View File

@ -6,41 +6,35 @@ template<typename From, typename To>
CastVec<From, To>::CastVec(ShaderGraph& graph) :
ShaderNode(graph)
{
constexpr std::array<char, 4> componentName = { 'X', 'Y', 'Z', 'W' };
static_assert(ComponentDiff <= componentName.size());
static_assert(ComponentDiff <= s_vectorComponents.size());
}
QFormLayout* layout = new QFormLayout;
template<typename From, typename To>
void CastVec<From, To>::BuildNodeEdition(QVBoxLayout* layout)
{
ShaderNode::BuildNodeEdition(layout);
if constexpr (ComponentDiff > 0)
{
QFormLayout* formLayout = new QFormLayout;
for (std::size_t i = 0; i < ComponentDiff; ++i)
{
m_spinboxes[i] = new QDoubleSpinBox;
m_spinboxes[i]->setDecimals(6);
m_spinboxes[i]->setValue(1.0);
m_spinboxes[i]->setStyleSheet("background-color: rgba(255,255,255,255)");
connect(m_spinboxes[i], qOverload<double>(&QDoubleSpinBox::valueChanged), [this](double)
QDoubleSpinBox* spinbox = new QDoubleSpinBox;
spinbox->setDecimals(6);
spinbox->setValue(m_overflowComponents[i]);
connect(spinbox, qOverload<double>(&QDoubleSpinBox::valueChanged), [=](double)
{
UpdatePreview();
m_overflowComponents[i] = spinbox->value();
UpdateOutput();
});
layout->addRow(QString::fromUtf8(&componentName[FromComponents + i], 1), m_spinboxes[i]);
}
formLayout->addRow(QString::fromUtf8(&s_vectorComponents[FromComponents + i], 1), spinbox);
}
m_pixmap = QPixmap(64, 64);
m_pixmap.fill();
m_pixmapLabel = new QLabel;
m_pixmapLabel->setPixmap(m_pixmap);
layout->addWidget(m_pixmapLabel);
m_widget = new QWidget;
m_widget->setStyleSheet("background-color: rgba(0,0,0,0)");
m_widget->setLayout(layout);
m_output = std::make_shared<To>();
layout->addLayout(formLayout);
}
}
template<typename From, typename To>
@ -52,7 +46,7 @@ Nz::ShaderAst::ExpressionPtr CastVec<From, To>::GetExpression(Nz::ShaderAst::Exp
{
std::array<Nz::ShaderAst::ExpressionPtr, ComponentDiff> constants;
for (std::size_t i = 0; i < ComponentDiff; ++i)
constants[i] = Nz::ShaderBuilder::Constant(float(m_spinboxes[i]->value()));
constants[i] = Nz::ShaderBuilder::Constant(m_overflowComponents[i]);
return std::apply([&](auto&&... values)
{
@ -102,12 +96,6 @@ QtNodes::NodeDataType CastVec<From, To>::dataType(QtNodes::PortType portType, Qt
throw std::runtime_error("Invalid port type");
}
template<typename From, typename To>
QWidget* CastVec<From, To>::embeddedWidget()
{
return m_widget;
}
template<typename From, typename To>
unsigned int CastVec<From, To>::nPorts(QtNodes::PortType portType) const
{
@ -124,6 +112,10 @@ template<typename From, typename To>
std::shared_ptr<QtNodes::NodeData> CastVec<From, To>::outData(QtNodes::PortIndex port)
{
assert(port == 0);
if (!m_input)
return nullptr;
return m_output;
}
@ -139,13 +131,28 @@ void CastVec<From, To>::setInData(std::shared_ptr<QtNodes::NodeData> value, int
else
m_input.reset();
UpdatePreview();
UpdateOutput();
}
template<typename From, typename To>
void CastVec<From, To>::ComputePreview(QPixmap& pixmap) const
bool CastVec<From, To>::ComputePreview(QPixmap& pixmap)
{
assert(m_input);
if (!m_input)
return false;
pixmap = QPixmap::fromImage(m_output->preview);
return true;
}
template<typename From, typename To>
void CastVec<From, To>::UpdateOutput()
{
if (!m_input)
{
m_output->preview = QImage(1, 1, QImage::Format_RGBA8888);
m_output->preview.fill(QColor::fromRgb(0, 0, 0, 0));
return;
}
const QImage& input = m_input->preview;
@ -156,8 +163,11 @@ void CastVec<From, To>::ComputePreview(QPixmap& pixmap) const
output = QImage(inputWidth, inputHeight, QImage::Format_RGBA8888);
std::array<std::uint8_t, ComponentDiff> constants;
if constexpr (ComponentDiff > 0)
{
for (std::size_t i = 0; i < ComponentDiff; ++i)
constants[i] = static_cast<std::uint8_t>(std::clamp(int(m_spinboxes[i]->value() * 255), 0, 255));
constants[i] = static_cast<std::uint8_t>(std::clamp(int(m_overflowComponents[i] * 255), 0, 255));
}
std::uint8_t* outputPtr = output.bits();
const std::uint8_t* inputPtr = input.constBits();
@ -181,21 +191,7 @@ void CastVec<From, To>::ComputePreview(QPixmap& pixmap) const
}
}
pixmap = QPixmap::fromImage(output).scaled(64, 64);
}
template<typename From, typename To>
void CastVec<From, To>::UpdatePreview()
{
if (!m_input)
{
m_pixmap = QPixmap(64, 64);
m_pixmap.fill(QColor::fromRgb(255, 255, 255, 0));
}
else
ComputePreview(m_pixmap);
m_pixmapLabel->setPixmap(m_pixmap);
Q_EMIT dataUpdated(0);
UpdatePreview();
}

View File

@ -22,11 +22,6 @@ QtNodes::NodeDataType FragmentOutput::dataType(QtNodes::PortType portType, QtNod
return Vec4Data::Type();
}
QWidget* FragmentOutput::embeddedWidget()
{
return nullptr;
}
unsigned int FragmentOutput::nPorts(QtNodes::PortType portType) const
{
switch (portType)
@ -42,3 +37,26 @@ std::shared_ptr<QtNodes::NodeData> FragmentOutput::outData(QtNodes::PortIndex /*
{
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

@ -4,6 +4,7 @@
#define NAZARA_SHADERNODES_FRAGMENTOUTPUT_HPP
#include <ShaderNode/DataModels/ShaderNode.hpp>
#include <ShaderNode/DataModels/VecValue.hpp>
#include <QtWidgets/QDoubleSpinBox>
#include <QtWidgets/QFormLayout>
@ -19,13 +20,16 @@ class FragmentOutput : public ShaderNode
QtNodes::NodeDataType dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override;
QWidget* embeddedWidget() override;
unsigned int nPorts(QtNodes::PortType portType) const override;
std::shared_ptr<QtNodes::NodeData> outData(QtNodes::PortIndex port) override;
void setInData(std::shared_ptr<QtNodes::NodeData>, int) override {};
void setInData(std::shared_ptr<QtNodes::NodeData> value, int index) override;
private:
bool ComputePreview(QPixmap& pixmap) override;
std::shared_ptr<Vec4Data> m_input;
};
#include <ShaderNode/DataModels/FragmentOutput.inl>

View File

@ -3,4 +3,6 @@
inline FragmentOutput::FragmentOutput(ShaderGraph& graph) :
ShaderNode(graph)
{
SetPreviewSize({ 128, 128 });
EnablePreview(true);
}

View File

@ -4,48 +4,18 @@
#include <Nazara/Renderer/ShaderBuilder.hpp>
InputValue::InputValue(ShaderGraph& graph) :
ShaderNode(graph),
m_currentInputIndex(0)
ShaderNode(graph)
{
m_layout = new QVBoxLayout;
m_inputSelection = new QComboBox;
m_inputSelection->setStyleSheet("background-color: rgba(255,255,255,255)");
connect(m_inputSelection, qOverload<int>(&QComboBox::currentIndexChanged), [&](int index)
{
if (index < 0)
return;
m_currentInputIndex = static_cast<std::size_t>(index);
UpdatePreview();
});
m_layout->addWidget(m_inputSelection);
m_previewLabel = new QLabel;
m_layout->addWidget(m_previewLabel);
m_widget = new QWidget;
m_widget->setStyleSheet("background-color: rgba(0,0,0,0)");
m_widget->setLayout(m_layout);
m_onInputListUpdateSlot.Connect(GetGraph().OnInputListUpdate, [&](ShaderGraph*) { UpdateInputList(); });
m_onInputListUpdateSlot.Connect(GetGraph().OnInputListUpdate, [&](ShaderGraph*) { OnInputListUpdate(); });
m_onInputUpdateSlot.Connect(GetGraph().OnInputUpdate, [&](ShaderGraph*, std::size_t inputIndex)
{
if (m_currentInputIndex == inputIndex)
UpdatePreview();
});
UpdateInputList();
UpdatePreview();
}
QWidget* InputValue::embeddedWidget()
{
return m_widget;
}
unsigned int InputValue::nPorts(QtNodes::PortType portType) const
{
switch (portType)
@ -57,36 +27,66 @@ unsigned int InputValue::nPorts(QtNodes::PortType portType) const
return 0;
}
void InputValue::UpdatePreview()
bool InputValue::ComputePreview(QPixmap& pixmap)
{
if (m_inputSelection->count() == 0)
return;
if (!m_currentInputIndex)
return false;
const ShaderGraph& graph = GetGraph();
const auto& inputEntry = graph.GetInput(m_currentInputIndex);
const auto& inputEntry = graph.GetInput(*m_currentInputIndex);
const auto& preview = graph.GetPreviewModel();
m_previewLabel->setPixmap(QPixmap::fromImage(preview.GetImage(inputEntry.role, inputEntry.roleIndex)));
Q_EMIT dataUpdated(0);
pixmap = QPixmap::fromImage(preview.GetImage(inputEntry.role, inputEntry.roleIndex));
return true;
}
void InputValue::UpdateInputList()
void InputValue::OnInputListUpdate()
{
QString currentInput = m_inputSelection->currentText();
m_inputSelection->clear();
m_currentInputIndex.reset();
std::size_t inputIndex = 0;
for (const auto& inputEntry : GetGraph().GetInputs())
{
if (inputEntry.name == m_currentInputText)
{
m_currentInputIndex = inputIndex;
break;
}
inputIndex++;
}
}
void InputValue::BuildNodeEdition(QVBoxLayout* layout)
{
ShaderNode::BuildNodeEdition(layout);
QComboBox* inputSelection = new QComboBox;
connect(inputSelection, qOverload<int>(&QComboBox::currentIndexChanged), [&](int index)
{
if (index >= 0)
m_currentInputIndex = static_cast<std::size_t>(index);
else
m_currentInputIndex.reset();
UpdatePreview();
});
for (const auto& inputEntry : GetGraph().GetInputs())
m_inputSelection->addItem(QString::fromStdString(inputEntry.name));
inputSelection->addItem(QString::fromStdString(inputEntry.name));
m_inputSelection->setCurrentText(currentInput);
layout->addWidget(inputSelection);
}
Nz::ShaderAst::ExpressionPtr InputValue::GetExpression(Nz::ShaderAst::ExpressionPtr* /*expressions*/, std::size_t count) const
{
assert(count == 0);
const auto& inputEntry = GetGraph().GetInput(m_currentInputIndex);
if (!m_currentInputIndex)
throw std::runtime_error("no input");
const auto& inputEntry = GetGraph().GetInput(*m_currentInputIndex);
Nz::ShaderAst::ExpressionType expression = [&]
{
@ -108,10 +108,13 @@ Nz::ShaderAst::ExpressionPtr InputValue::GetExpression(Nz::ShaderAst::Expression
auto InputValue::dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const -> QtNodes::NodeDataType
{
if (!m_currentInputIndex)
return Vec4Data::Type();
assert(portType == QtNodes::PortType::Out);
assert(portIndex == 0);
const auto& inputEntry = GetGraph().GetInput(m_currentInputIndex);
const auto& inputEntry = GetGraph().GetInput(*m_currentInputIndex);
switch (inputEntry.type)
{
//case InputType::Bool: return Nz::ShaderAst::ExpressionType::Boolean;
@ -127,10 +130,13 @@ auto InputValue::dataType(QtNodes::PortType portType, QtNodes::PortIndex portInd
std::shared_ptr<QtNodes::NodeData> InputValue::outData(QtNodes::PortIndex port)
{
if (!m_currentInputIndex)
return nullptr;
assert(port == 0);
const ShaderGraph& graph = GetGraph();
const auto& inputEntry = graph.GetInput(m_currentInputIndex);
const auto& inputEntry = graph.GetInput(*m_currentInputIndex);
const auto& preview = graph.GetPreviewModel();
auto vecData = std::make_shared<Vec2Data>();

View File

@ -9,6 +9,7 @@
#include <ShaderNode/ShaderGraph.hpp>
#include <ShaderNode/DataModels/ShaderNode.hpp>
#include <array>
#include <optional>
class InputValue : public ShaderNode
{
@ -16,30 +17,28 @@ class InputValue : public ShaderNode
InputValue(ShaderGraph& graph);
~InputValue() = default;
void BuildNodeEdition(QVBoxLayout* layout) override;
Nz::ShaderAst::ExpressionPtr GetExpression(Nz::ShaderAst::ExpressionPtr* /*expressions*/, std::size_t count) const override;
QString caption() const override { return "Input"; }
QString name() const override { return "Input"; }
QWidget* embeddedWidget() override;
unsigned int nPorts(QtNodes::PortType portType) const override;
QtNodes::NodeDataType dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override;
std::shared_ptr<QtNodes::NodeData> outData(QtNodes::PortIndex port) override;
protected:
void UpdatePreview();
void UpdateInputList();
private:
bool ComputePreview(QPixmap& pixmap) override;
void OnInputListUpdate();
NazaraSlot(ShaderGraph, OnInputListUpdate, m_onInputListUpdateSlot);
NazaraSlot(ShaderGraph, OnInputUpdate, m_onInputUpdateSlot);
std::size_t m_currentInputIndex;
QComboBox* m_inputSelection;
QLabel* m_previewLabel;
QWidget* m_widget;
QVBoxLayout* m_layout;
std::optional<std::size_t> m_currentInputIndex;
std::string m_currentInputText;
};
#include <ShaderNode/DataModels/InputValue.inl>

View File

@ -4,52 +4,16 @@
#include <Nazara/Renderer/ShaderBuilder.hpp>
SampleTexture::SampleTexture(ShaderGraph& graph) :
ShaderNode(graph),
m_currentTextureIndex(0)
ShaderNode(graph)
{
m_output = std::make_shared<Vec4Data>();
m_layout = new QVBoxLayout;
m_textureSelection = new QComboBox;
m_textureSelection->setStyleSheet("background-color: rgba(255,255,255,255)");
connect(m_textureSelection, qOverload<int>(&QComboBox::currentIndexChanged), [&](int index)
{
if (index < 0)
return;
m_currentTextureIndex = static_cast<std::size_t>(index);
UpdatePreview();
});
m_layout->addWidget(m_textureSelection);
m_pixmap = QPixmap(64, 64);
m_pixmap.fill();
m_pixmapLabel = new QLabel;
m_pixmapLabel->setPixmap(m_pixmap);
m_layout->addWidget(m_pixmapLabel);
m_widget = new QWidget;
m_widget->setStyleSheet("background-color: rgba(0,0,0,0)");
m_widget->setLayout(m_layout);
m_onTextureListUpdateSlot.Connect(GetGraph().OnTextureListUpdate, [&](ShaderGraph*) { UpdateTextureList(); });
m_onTextureListUpdateSlot.Connect(GetGraph().OnTextureListUpdate, [&](ShaderGraph*) { OnTextureListUpdate(); });
m_onTexturePreviewUpdateSlot.Connect(GetGraph().OnTexturePreviewUpdate, [&](ShaderGraph*, std::size_t textureIndex)
{
if (m_currentTextureIndex == textureIndex)
UpdatePreview();
});
UpdateTextureList();
UpdatePreview();
}
QWidget* SampleTexture::embeddedWidget()
{
return m_widget;
}
unsigned int SampleTexture::nPorts(QtNodes::PortType portType) const
@ -63,39 +27,39 @@ unsigned int SampleTexture::nPorts(QtNodes::PortType portType) const
return 0;
}
void SampleTexture::UpdatePreview()
void SampleTexture::OnTextureListUpdate()
{
if (m_textureSelection->count() == 0)
return;
ComputePreview(m_pixmap);
m_pixmapLabel->setPixmap(m_pixmap);
Q_EMIT dataUpdated(0);
}
void SampleTexture::UpdateTextureList()
{
QString currentTexture = m_textureSelection->currentText();
m_textureSelection->clear();
m_currentTextureIndex.reset();
std::size_t inputIndex = 0;
for (const auto& textureEntry : GetGraph().GetTextures())
m_textureSelection->addItem(QString::fromStdString(textureEntry.name));
m_textureSelection->setCurrentText(currentTexture);
{
if (textureEntry.name == m_currentTextureText)
{
m_currentTextureIndex = inputIndex;
break;
}
void SampleTexture::ComputePreview(QPixmap& pixmap) const
{
if (!m_uv)
return;
inputIndex++;
}
}
const auto& textureEntry = GetGraph().GetTexture(m_currentTextureIndex);
void SampleTexture::UpdateOutput()
{
QImage& output = m_output->preview;
if (!m_currentTextureIndex || !m_uv)
{
output = QImage(1, 1, QImage::Format_RGBA8888);
output.fill(QColor::fromRgb(0, 0, 0, 0));
return;
}
const auto& textureEntry = GetGraph().GetTexture(*m_currentTextureIndex);
int textureWidth = textureEntry.preview.width();
int textureHeight = textureEntry.preview.height();
QImage& output = m_output->preview;
const QImage& uv = m_uv->preview;
int uvWidth = uv.width();
@ -125,14 +89,49 @@ void SampleTexture::ComputePreview(QPixmap& pixmap) const
}
}
pixmap = QPixmap::fromImage(output).scaled(128, 128, Qt::KeepAspectRatio);
Q_EMIT dataUpdated(0);
UpdatePreview();
}
bool SampleTexture::ComputePreview(QPixmap& pixmap)
{
if (!m_currentTextureIndex || !m_uv)
return false;
pixmap = QPixmap::fromImage(m_output->preview);
return true;
}
void SampleTexture::BuildNodeEdition(QVBoxLayout* layout)
{
ShaderNode::BuildNodeEdition(layout);
QComboBox* textureSelection = new QComboBox;
connect(textureSelection, qOverload<int>(&QComboBox::currentIndexChanged), [&](int index)
{
if (index >= 0)
m_currentTextureIndex = static_cast<std::size_t>(index);
else
m_currentTextureIndex.reset();
UpdateOutput();
});
for (const auto& textureEntry : GetGraph().GetTextures())
textureSelection->addItem(QString::fromStdString(textureEntry.name));
layout->addWidget(textureSelection);
}
Nz::ShaderAst::ExpressionPtr SampleTexture::GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const
{
if (!m_currentTextureIndex || !m_uv)
throw std::runtime_error("invalid inputs");
assert(count == 1);
const auto& textureEntry = GetGraph().GetTexture(m_currentTextureIndex);
const auto& textureEntry = GetGraph().GetTexture(*m_currentTextureIndex);
Nz::ShaderAst::ExpressionType expression = [&]
{
@ -203,6 +202,9 @@ std::shared_ptr<QtNodes::NodeData> SampleTexture::outData(QtNodes::PortIndex por
{
assert(port == 0);
if (!m_currentTextureIndex)
return nullptr;
return m_output;
}
@ -219,5 +221,5 @@ void SampleTexture::setInData(std::shared_ptr<QtNodes::NodeData> value, int inde
else
m_uv.reset();
UpdatePreview();
UpdateOutput();
}

View File

@ -17,12 +17,13 @@ class SampleTexture : public ShaderNode
SampleTexture(ShaderGraph& graph);
~SampleTexture() = default;
void BuildNodeEdition(QVBoxLayout* layout) override;
Nz::ShaderAst::ExpressionPtr GetExpression(Nz::ShaderAst::ExpressionPtr* /*expressions*/, std::size_t count) const override;
QString caption() const override { return "Sample texture"; }
QString name() const override { return "SampleTexture"; }
QWidget* embeddedWidget() override;
unsigned int nPorts(QtNodes::PortType portType) const override;
QtNodes::NodeDataType dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override;
@ -36,21 +37,17 @@ class SampleTexture : public ShaderNode
void setInData(std::shared_ptr<QtNodes::NodeData> value, int index) override;
protected:
void ComputePreview(QPixmap& pixmap) const;
void UpdatePreview();
void UpdateTextureList();
bool ComputePreview(QPixmap& pixmap) override;
void OnTextureListUpdate();
void UpdateOutput();
NazaraSlot(ShaderGraph, OnTextureListUpdate, m_onTextureListUpdateSlot);
NazaraSlot(ShaderGraph, OnTexturePreviewUpdate, m_onTexturePreviewUpdateSlot);
std::size_t m_currentTextureIndex;
std::optional<std::size_t> m_currentTextureIndex;
std::shared_ptr<Vec2Data> m_uv;
std::shared_ptr<Vec4Data> m_output;
QComboBox* m_textureSelection;
QLabel* m_pixmapLabel;
QPixmap m_pixmap;
QWidget* m_widget;
QVBoxLayout* m_layout;
std::string m_currentTextureText;
};
#include <ShaderNode/DataModels/SampleTexture.inl>

View File

@ -1,5 +1,81 @@
#include <ShaderNode/DataModels/ShaderNode.hpp>
#include <QtWidgets/QCheckBox>
#include <QtWidgets/QLabel>
#include <QtWidgets/QVBoxLayout>
ShaderNode::ShaderNode(ShaderGraph& graph) :
m_previewSize(64, 64),
m_pixmapLabel(nullptr),
m_graph(graph),
m_isPreviewEnabled(false)
{
m_pixmapLabel = new QLabel;
m_pixmapLabel->setStyleSheet("background-color: rgba(0,0,0,0)");
}
void ShaderNode::BuildNodeEdition(QVBoxLayout* layout)
{
QCheckBox* checkbox = new QCheckBox(tr("Enable preview"));
checkbox->setCheckState((m_isPreviewEnabled) ? Qt::Checked : Qt::Unchecked);
connect(checkbox, &QCheckBox::stateChanged, [&](int state)
{
EnablePreview(state == Qt::Checked);
});
layout->addWidget(checkbox);
}
void ShaderNode::EnablePreview(bool enable)
{
if (m_isPreviewEnabled != enable)
{
m_isPreviewEnabled = enable;
if (m_isPreviewEnabled)
{
m_pixmap.emplace(m_previewSize.x, m_previewSize.y);
UpdatePreview();
}
else
{
m_pixmapLabel->clear();
m_pixmap.reset();
}
embeddedWidgetSizeUpdated();
}
}
QWidget* ShaderNode::embeddedWidget()
{
return m_pixmapLabel;
}
void ShaderNode::setInData(std::shared_ptr<QtNodes::NodeData>, int)
{
}
bool ShaderNode::ComputePreview(QPixmap& /*pixmap*/)
{
return false;
}
void ShaderNode::UpdatePreview()
{
if (!m_pixmap)
return;
QPixmap& pixmap = *m_pixmap;
if (!ComputePreview(pixmap))
{
pixmap = QPixmap(m_previewSize.x, m_previewSize.y);
pixmap.fill(QColor::fromRgb(255, 255, 255, 0));
}
else
pixmap = pixmap.scaled(m_previewSize.x, m_previewSize.y);
m_pixmapLabel->setPixmap(pixmap);
}

View File

@ -3,24 +3,46 @@
#ifndef NAZARA_SHADERNODES_SHADERNODE_HPP
#define NAZARA_SHADERNODES_SHADERNODE_HPP
#include <Nazara/Math/Vector2.hpp>
#include <Nazara/Renderer/ShaderAst.hpp>
#include <nodes/NodeDataModel>
#include <QtGui/QPixmap>
#include <optional>
class QLabel;
class QVBoxLayout;
class ShaderGraph;
class ShaderNode : public QtNodes::NodeDataModel
{
public:
inline ShaderNode(ShaderGraph& graph);
ShaderNode(ShaderGraph& graph);
virtual void BuildNodeEdition(QVBoxLayout* layout);
void EnablePreview(bool enable);
virtual Nz::ShaderAst::ExpressionPtr GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const = 0;
inline ShaderGraph& GetGraph();
inline const ShaderGraph& GetGraph() const;
void SetPreviewSize(const Nz::Vector2i& size);
QWidget* embeddedWidget() final;
void setInData(std::shared_ptr<QtNodes::NodeData>, int) override;
protected:
void UpdatePreview();
private:
virtual bool ComputePreview(QPixmap& pixmap);
Nz::Vector2i m_previewSize;
QLabel* m_pixmapLabel;
std::optional<QPixmap> m_pixmap;
ShaderGraph& m_graph;
bool m_isPreviewEnabled;
};
#include <ShaderNode/DataModels/ShaderNode.inl>

View File

@ -1,9 +1,5 @@
#include <ShaderNode/DataModels/ShaderNode.hpp>
inline ShaderNode::ShaderNode(ShaderGraph& graph) :
m_graph(graph)
{
}
inline ShaderGraph& ShaderNode::GetGraph()
{
@ -14,3 +10,10 @@ inline const ShaderGraph& ShaderNode::GetGraph() const
{
return m_graph;
}
inline void ShaderNode::SetPreviewSize(const Nz::Vector2i& size)
{
m_previewSize = size;
if (m_isPreviewEnabled)
UpdatePreview();
}

View File

@ -15,7 +15,6 @@ class VecBinOp : public ShaderNode
Nz::ShaderAst::ExpressionPtr GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const override;
QWidget* embeddedWidget() override;
unsigned int nPorts(QtNodes::PortType portType) const override;
QtNodes::NodeDataType dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override;
@ -26,10 +25,10 @@ class VecBinOp : public ShaderNode
private:
virtual void ApplyOp(const std::uint8_t* left, const std::uint8_t* right, std::uint8_t* output, std::size_t pixelCount) = 0;
void UpdatePreview();
QLabel* m_pixmapLabel;
QPixmap m_preview;
bool ComputePreview(QPixmap& pixmap) override;
void UpdateOutput();
std::shared_ptr<Data> m_lhs;
std::shared_ptr<Data> m_rhs;
std::shared_ptr<Data> m_output;

View File

@ -7,9 +7,7 @@ ShaderNode(graph)
{
m_output = std::make_shared<Data>();
m_pixmapLabel = new QLabel;
m_pixmapLabel->setStyleSheet("background-color: rgba(0,0,0,0)");
UpdatePreview();
UpdateOutput();
}
template<typename Data, Nz::ShaderAst::BinaryType BinOp>
@ -21,12 +19,6 @@ Nz::ShaderAst::ExpressionPtr VecBinOp<Data, BinOp>::GetExpression(Nz::ShaderAst:
return builder(expressions[0], expressions[1]);
}
template<typename Data, Nz::ShaderAst::BinaryType BinOp>
QWidget* VecBinOp<Data, BinOp>::embeddedWidget()
{
return m_pixmapLabel;
}
template<typename Data, Nz::ShaderAst::BinaryType BinOp>
QtNodes::NodeDataType VecBinOp<Data, BinOp>::dataType(QtNodes::PortType /*portType*/, QtNodes::PortIndex portIndex) const
{
@ -72,16 +64,29 @@ void VecBinOp<Data, BinOp>::setInData(std::shared_ptr<QtNodes::NodeData> value,
else
m_rhs = std::move(castedValue);
UpdatePreview();
Q_EMIT dataUpdated(0);
UpdateOutput();
}
template<typename Data, Nz::ShaderAst::BinaryType BinOp>
void VecBinOp<Data, BinOp>::UpdatePreview()
bool VecBinOp<Data, BinOp>::ComputePreview(QPixmap& pixmap)
{
if (m_lhs && m_rhs)
if (!m_lhs || !m_rhs)
return false;
pixmap = QPixmap::fromImage(m_output->preview);
return true;
}
template<typename Data, Nz::ShaderAst::BinaryType BinOp>
void VecBinOp<Data, BinOp>::UpdateOutput()
{
if (!m_lhs || !m_rhs)
{
m_output->preview = QImage(1, 1, QImage::Format_RGBA8888);
m_output->preview.fill(QColor::fromRgb(0, 0, 0, 0));
return;
}
const QImage& leftPreview = m_lhs->preview;
const QImage& rightPreview = m_rhs->preview;
int maxWidth = std::max(leftPreview.width(), rightPreview.width());
@ -99,15 +104,9 @@ void VecBinOp<Data, BinOp>::UpdatePreview()
m_output->preview = QImage(maxWidth, maxHeight, QImage::Format_RGBA8888);
ApplyOp(leftResized.constBits(), rightResized.constBits(), m_output->preview.bits(), maxWidth * maxHeight * 4);
m_preview = QPixmap::fromImage(m_output->preview).scaled(64, 64);
}
else
{
m_preview = QPixmap(64, 64);
m_preview.fill(QColor::fromRgb(255, 255, 0, 0));
}
Q_EMIT dataUpdated(0);
m_pixmapLabel->setPixmap(m_preview);
UpdatePreview();
}
template<typename Data>

View File

@ -62,6 +62,45 @@ struct Vec4Data : public VecData
}
};
struct VecTypeDummy {};
template<std::size_t N>
struct VecTypeHelper;
template<>
struct VecTypeHelper<0>
{
using Type = VecTypeDummy;
};
template<>
struct VecTypeHelper<1>
{
using Type = std::array<float, 1>;
};
template<>
struct VecTypeHelper<2>
{
using Type = Nz::Vector2f;
};
template<>
struct VecTypeHelper<3>
{
using Type = Nz::Vector3f;
};
template<>
struct VecTypeHelper<4>
{
using Type = Nz::Vector4f;
};
template<std::size_t N> using VecType = typename VecTypeHelper<N>::template Type;
constexpr std::array<char, 4> s_vectorComponents = { 'X', 'Y', 'Z', 'W' };
#include <ShaderNode/DataModels/VecData.inl>
#endif

View File

@ -11,29 +11,6 @@
#include <ShaderNode/DataModels/VecData.hpp>
#include <array>
template<std::size_t N>
struct VecTypeHelper;
template<>
struct VecTypeHelper<2>
{
using Type = Nz::Vector2f;
};
template<>
struct VecTypeHelper<3>
{
using Type = Nz::Vector3f;
};
template<>
struct VecTypeHelper<4>
{
using Type = Nz::Vector4f;
};
template<std::size_t N> using VecType = typename VecTypeHelper<N>::template Type;
template<typename Data>
class VecValue : public ShaderNode
{
@ -46,25 +23,22 @@ class VecValue : public ShaderNode
QtNodes::NodeDataType dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override;
QWidget* embeddedWidget() override;
unsigned int nPorts(QtNodes::PortType portType) const override;
std::shared_ptr<QtNodes::NodeData> outData(QtNodes::PortIndex port) override;
void BuildNodeEdition(QVBoxLayout* layout) override;
Nz::ShaderAst::ExpressionPtr GetExpression(Nz::ShaderAst::ExpressionPtr* expressions, std::size_t count) const override;
protected:
private:
bool ComputePreview(QPixmap& pixmap) override;
static constexpr std::size_t ComponentCount = Data::ComponentCount;
QColor ToColor() const;
VecType<ComponentCount> ToVector() const;
void UpdatePreview();
QLabel* m_pixmapLabel;
QPixmap m_pixmap;
QWidget* m_widget;
QFormLayout* m_layout;
std::array<QDoubleSpinBox*, ComponentCount> m_spinboxes;
VecType<ComponentCount> m_value;
};
using Vec2Value = VecValue<Vec2Data>;

View File

@ -8,37 +8,14 @@ template<typename Data>
VecValue<Data>::VecValue(ShaderGraph& graph) :
ShaderNode(graph)
{
constexpr std::array<char, 4> componentName = { 'X', 'Y', 'Z', 'W' };
static_assert(ComponentCount <= componentName.size());
static_assert(ComponentCount <= s_vectorComponents.size());
std::array<float, ComponentCount> defaultValues;
m_layout = new QFormLayout;
for (std::size_t i = 0; i < ComponentCount; ++i)
{
m_spinboxes[i] = new QDoubleSpinBox;
m_spinboxes[i]->setDecimals(6);
m_spinboxes[i]->setValue(1.0);
m_spinboxes[i]->setStyleSheet("background-color: rgba(255,255,255,255)");
connect(m_spinboxes[i], qOverload<double>(&QDoubleSpinBox::valueChanged), [this](double)
{
UpdatePreview();
defaultValues[i] = (i == 3) ? 1.f : 0.f;
Q_EMIT dataUpdated(0);
});
m_layout->addRow(QString::fromUtf8(&componentName[i], 1), m_spinboxes[i]);
}
m_pixmap = QPixmap(32, 32);
m_pixmap.fill();
m_pixmapLabel = new QLabel;
m_pixmapLabel->setPixmap(m_pixmap);
m_layout->addWidget(m_pixmapLabel);
m_widget = new QWidget;
m_widget->setStyleSheet("background-color: rgba(0,0,0,0)");
m_widget->setLayout(m_layout);
m_value.Set(defaultValues.data());
UpdatePreview();
}
@ -66,12 +43,6 @@ QtNodes::NodeDataType VecValue<Data>::dataType(QtNodes::PortType portType, QtNod
return Data::Type();
}
template<typename Data>
QWidget* VecValue<Data>::embeddedWidget()
{
return m_widget;
}
template<typename Data>
unsigned int VecValue<Data>::nPorts(QtNodes::PortType portType) const
{
@ -96,12 +67,45 @@ std::shared_ptr<QtNodes::NodeData> VecValue<Data>::outData(QtNodes::PortIndex po
return out;
}
template<typename Data>
void VecValue<Data>::BuildNodeEdition(QVBoxLayout* layout)
{
ShaderNode::BuildNodeEdition(layout);
QFormLayout* formLayout = new QFormLayout;
for (std::size_t i = 0; i < ComponentCount; ++i)
{
QDoubleSpinBox* spinbox = new QDoubleSpinBox;
spinbox->setDecimals(6);
spinbox->setValue(m_value[i]);
connect(spinbox, qOverload<double>(&QDoubleSpinBox::valueChanged), [=](double)
{
m_value[i] = spinbox->value();
Q_EMIT dataUpdated(0);
UpdatePreview();
});
formLayout->addRow(QString::fromUtf8(&s_vectorComponents[i], 1), spinbox);
}
layout->addLayout(formLayout);
}
template<typename Data>
Nz::ShaderAst::ExpressionPtr VecValue<Data>::GetExpression(Nz::ShaderAst::ExpressionPtr* /*expressions*/, std::size_t count) const
{
assert(count == 0);
return Nz::ShaderBuilder::Constant(ToVector());
return Nz::ShaderBuilder::Constant(m_value);
}
template<typename Data>
bool VecValue<Data>::ComputePreview(QPixmap& pixmap)
{
pixmap.fill(ToColor());
return true;
}
template<typename Data>
@ -110,28 +114,7 @@ QColor VecValue<Data>::ToColor() const
std::array<float, 4> values = { 0.f, 0.f, 0.f, 1.f };
for (std::size_t i = 0; i < ComponentCount; ++i)
values[i] = std::clamp(float(m_spinboxes[i]->value()), 0.f, 1.f);
values[i] = std::clamp(m_value[i], 0.f, 1.f);
return QColor::fromRgbF(values[0], values[1], values[2], values[3]);
}
template<typename Data>
auto VecValue<Data>::ToVector() const -> VecType<ComponentCount>
{
std::array<float, ComponentCount> values;
for (std::size_t i = 0; i < ComponentCount; ++i)
values[i] = std::clamp(float(m_spinboxes[i]->value()), 0.f, 1.f);
return std::apply([](auto... values)
{
return VecType<ComponentCount>(values...);
}, values);
}
template<typename Data>
void VecValue<Data>::UpdatePreview()
{
m_pixmap.fill(ToColor());
m_pixmapLabel->setPixmap(m_pixmap);
}

View File

@ -29,6 +29,17 @@ namespace
ShaderGraph::ShaderGraph() :
m_flowScene(BuildRegistry())
{
m_previewModel = std::make_unique<QuadPreview>();
QObject::connect(&m_flowScene, &QGraphicsScene::selectionChanged, [&]
{
auto selectedNodes = m_flowScene.selectedNodes();
if (selectedNodes.size() == 1)
OnSelectedNodeUpdate(this, static_cast<ShaderNode*>(selectedNodes.front()->nodeDataModel()));
else
OnSelectedNodeUpdate(this, nullptr);
});
auto& node1 = m_flowScene.createNode(std::make_unique<Vec4Value>(*this));
node1.nodeGraphicsObject().setPos(200, 200);
@ -36,8 +47,6 @@ m_flowScene(BuildRegistry())
node2.nodeGraphicsObject().setPos(500, 300);
m_flowScene.createConnection(node2, 0, node1, 0);
m_previewModel = std::make_unique<QuadPreview>();
}
ShaderGraph::~ShaderGraph()

View File

@ -11,6 +11,8 @@
#include <string>
#include <vector>
class ShaderNode;
class ShaderGraph
{
public:
@ -52,6 +54,7 @@ class ShaderGraph
NazaraSignal(OnInputListUpdate, ShaderGraph*);
NazaraSignal(OnInputUpdate, ShaderGraph*, std::size_t /*inputIndex*/);
NazaraSignal(OnSelectedNodeUpdate, ShaderGraph*, ShaderNode* /*node*/);
NazaraSignal(OnTextureListUpdate, ShaderGraph*);
NazaraSignal(OnTexturePreviewUpdate, ShaderGraph*, std::size_t /*textureIndex*/);

View File

@ -2,6 +2,7 @@
#include <Nazara/Renderer/GlslWriter.hpp>
#include <ShaderNode/ShaderGraph.hpp>
#include <ShaderNode/Widgets/InputEditor.hpp>
#include <ShaderNode/Widgets/NodeEditor.hpp>
#include <ShaderNode/Widgets/TextureEditor.hpp>
#include <nodes/FlowView>
#include <QtWidgets/QDockWidget>
@ -19,20 +20,44 @@ m_shaderGraph(graph)
QtNodes::FlowView* flowView = new QtNodes::FlowView(scene);
setCentralWidget(flowView);
QDockWidget* inputDock = new QDockWidget(tr("&Inputs"));
InputEditor* inputEditor = new InputEditor(m_shaderGraph);
QDockWidget* inputDock = new QDockWidget(tr("Inputs"));
inputDock->setFeatures(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable);
inputDock->setWidget(inputEditor);
addDockWidget(Qt::LeftDockWidgetArea, inputDock);
QDockWidget* textureDock = new QDockWidget(tr("&Textures"));
TextureEditor* textureEditor = new TextureEditor(m_shaderGraph);
QDockWidget* textureDock = new QDockWidget(tr("Textures"));
textureDock->setFeatures(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable);
textureDock->setWidget(textureEditor);
addDockWidget(Qt::LeftDockWidgetArea, textureDock);
m_nodeEditor = new NodeEditor;
QDockWidget* nodeEditorDock = new QDockWidget(tr("Node editor"));
nodeEditorDock->setFeatures(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable);
nodeEditorDock->setWidget(m_nodeEditor);
addDockWidget(Qt::RightDockWidgetArea, nodeEditorDock);
m_onSelectedNodeUpdate.Connect(m_shaderGraph.OnSelectedNodeUpdate, [&](ShaderGraph*, ShaderNode* node)
{
if (node)
{
m_nodeEditor->UpdateContent(node->caption(), [node](QVBoxLayout* layout)
{
node->BuildNodeEdition(layout);
});
}
else
m_nodeEditor->Clear();
});
BuildMenu();
}

View File

@ -4,9 +4,10 @@
#define NAZARA_SHADERNODES_MAINWINDOW_HPP
#include <QtWidgets/QMainWindow>
#include <ShaderNode/ShaderGraph.hpp>
#include <ShaderNode/DataModels/ShaderNode.hpp>
class ShaderGraph;
class NodeEditor;
class MainWindow : public QMainWindow
{
@ -18,6 +19,9 @@ class MainWindow : public QMainWindow
void BuildMenu();
void OnCompileToGLSL();
NazaraSlot(ShaderGraph, OnSelectedNodeUpdate, m_onSelectedNodeUpdate);
NodeEditor* m_nodeEditor;
ShaderGraph& m_shaderGraph;
};

View File

@ -0,0 +1,20 @@
#include <ShaderNode/Widgets/NodeEditor.hpp>
#include <QtWidgets/QLabel>
#include <QtWidgets/QVBoxLayout>
NodeEditor::NodeEditor() :
m_layout(nullptr)
{
}
void NodeEditor::Clear()
{
if (m_layout)
{
while (QWidget* w = findChild<QWidget*>())
delete w;
delete m_layout;
m_layout = nullptr;
}
}

View File

@ -0,0 +1,27 @@
#pragma once
#ifndef NAZARA_SHADERNODES_NODEEDITOR_HPP
#define NAZARA_SHADERNODES_NODEEDITOR_HPP
#include <ShaderNode/ShaderGraph.hpp>
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QWidget>
#include <optional>
class NodeEditor : public QWidget
{
public:
NodeEditor();
~NodeEditor() = default;
void Clear();
template<typename F> void UpdateContent(QString nodeName, F&& callback);
private:
QVBoxLayout* m_layout;
};
#include <ShaderNode/Widgets/NodeEditor.inl>
#endif

View File

@ -0,0 +1,16 @@
#include <ShaderNode/Widgets/NodeEditor.hpp>
#include <QtWidgets/QLabel>
template<typename F>
void NodeEditor::UpdateContent(QString nodeName, F&& callback)
{
Clear();
m_layout = new QVBoxLayout;
setLayout(m_layout);
QLabel* label = new QLabel(nodeName);
m_layout->addWidget(label);
callback(m_layout);
}