Add conditional expression/statement support for shaders
This commit is contained in:
240
src/ShaderNode/DataModels/ConditionalExpression.cpp
Normal file
240
src/ShaderNode/DataModels/ConditionalExpression.cpp
Normal file
@@ -0,0 +1,240 @@
|
||||
#include <ShaderNode/DataModels/ConditionalExpression.hpp>
|
||||
#include <Nazara/Core/CallOnExit.hpp>
|
||||
#include <ShaderNode/ShaderGraph.hpp>
|
||||
#include <ShaderNode/DataTypes/BoolData.hpp>
|
||||
#include <ShaderNode/DataTypes/FloatData.hpp>
|
||||
#include <ShaderNode/DataTypes/Matrix4Data.hpp>
|
||||
#include <ShaderNode/DataTypes/VecData.hpp>
|
||||
#include <Nazara/Shader/ShaderBuilder.hpp>
|
||||
#include <QtWidgets/QComboBox>
|
||||
#include <QtWidgets/QFormLayout>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
ConditionalExpression::ConditionalExpression(ShaderGraph& graph) :
|
||||
ShaderNode(graph)
|
||||
{
|
||||
m_onConditionListUpdateSlot.Connect(GetGraph().OnConditionListUpdate, [&](ShaderGraph*) { OnConditionListUpdate(); });
|
||||
m_onConditionUpdateSlot.Connect(GetGraph().OnConditionUpdate, [&](ShaderGraph*, std::size_t conditionIndex)
|
||||
{
|
||||
if (m_currentConditionIndex == conditionIndex)
|
||||
{
|
||||
UpdatePreview();
|
||||
Q_EMIT dataUpdated(0);
|
||||
}
|
||||
});
|
||||
|
||||
if (graph.GetConditionCount() > 0)
|
||||
{
|
||||
m_currentConditionIndex = 0;
|
||||
UpdateConditionText();
|
||||
}
|
||||
|
||||
EnablePreview();
|
||||
SetPreviewSize({ 128, 128 });
|
||||
UpdatePreview();
|
||||
}
|
||||
|
||||
Nz::ShaderNodes::ExpressionPtr ConditionalExpression::GetExpression(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count) const
|
||||
{
|
||||
assert(count == 2);
|
||||
|
||||
if (!m_currentConditionIndex)
|
||||
throw std::runtime_error("no condition");
|
||||
|
||||
const ShaderGraph& graph = GetGraph();
|
||||
|
||||
const auto& conditionEntry = graph.GetCondition(*m_currentConditionIndex);
|
||||
return Nz::ShaderBuilder::ConditionalExpression(conditionEntry.name, expressions[0], expressions[1]);
|
||||
}
|
||||
|
||||
QString ConditionalExpression::caption() const
|
||||
{
|
||||
return "ConditionalExpression (" + QString::fromStdString(m_currentConditionText) + ")";
|
||||
}
|
||||
|
||||
QString ConditionalExpression::name() const
|
||||
{
|
||||
return "ConditionalExpression";
|
||||
}
|
||||
|
||||
unsigned int ConditionalExpression::nPorts(QtNodes::PortType portType) const
|
||||
{
|
||||
switch (portType)
|
||||
{
|
||||
case QtNodes::PortType::In: return 2;
|
||||
case QtNodes::PortType::Out: return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ConditionalExpression::BuildNodeEdition(QFormLayout* layout)
|
||||
{
|
||||
ShaderNode::BuildNodeEdition(layout);
|
||||
|
||||
QComboBox* conditionSelection = new QComboBox;
|
||||
for (const auto& conditionEntry : GetGraph().GetConditions())
|
||||
conditionSelection->addItem(QString::fromStdString(conditionEntry.name));
|
||||
|
||||
if (m_currentConditionIndex)
|
||||
conditionSelection->setCurrentIndex(int(*m_currentConditionIndex));
|
||||
else
|
||||
conditionSelection->setCurrentIndex(-1);
|
||||
|
||||
connect(conditionSelection, qOverload<int>(&QComboBox::currentIndexChanged), [&](int index)
|
||||
{
|
||||
if (index >= 0)
|
||||
m_currentConditionIndex = static_cast<std::size_t>(index);
|
||||
else
|
||||
m_currentConditionIndex.reset();
|
||||
|
||||
UpdateConditionText();
|
||||
UpdatePreview();
|
||||
|
||||
Q_EMIT dataUpdated(0);
|
||||
});
|
||||
|
||||
layout->addRow(tr("Condition"), conditionSelection);
|
||||
}
|
||||
|
||||
auto ConditionalExpression::dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const -> QtNodes::NodeDataType
|
||||
{
|
||||
return VecData::Type();
|
||||
|
||||
assert(portType == QtNodes::PortType::Out);
|
||||
assert(portIndex == 0);
|
||||
|
||||
if (!m_truePath && !m_falsePath)
|
||||
return VecData::Type();
|
||||
|
||||
return (m_truePath) ? m_truePath->type() : m_falsePath->type();
|
||||
}
|
||||
|
||||
QString ConditionalExpression::portCaption(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const
|
||||
{
|
||||
switch (portType)
|
||||
{
|
||||
case QtNodes::PortType::In:
|
||||
{
|
||||
switch (portIndex)
|
||||
{
|
||||
case 0:
|
||||
return "True path";
|
||||
|
||||
case 1:
|
||||
return "False path";
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return QString{};
|
||||
}
|
||||
|
||||
bool ConditionalExpression::portCaptionVisible(QtNodes::PortType portType, QtNodes::PortIndex /*portIndex*/) const
|
||||
{
|
||||
return portType == QtNodes::PortType::In;
|
||||
}
|
||||
|
||||
std::shared_ptr<QtNodes::NodeData> ConditionalExpression::outData(QtNodes::PortIndex port)
|
||||
{
|
||||
if (!m_currentConditionIndex)
|
||||
return nullptr;
|
||||
|
||||
assert(port == 0);
|
||||
return (GetGraph().IsConditionEnabled(*m_currentConditionIndex)) ? m_truePath : m_falsePath;
|
||||
}
|
||||
|
||||
void ConditionalExpression::restore(const QJsonObject& data)
|
||||
{
|
||||
m_currentConditionText = data["condition_name"].toString().toStdString();
|
||||
OnConditionListUpdate();
|
||||
|
||||
ShaderNode::restore(data);
|
||||
}
|
||||
|
||||
QJsonObject ConditionalExpression::save() const
|
||||
{
|
||||
QJsonObject data = ShaderNode::save();
|
||||
data["condition_name"] = QString::fromStdString(m_currentConditionText);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void ConditionalExpression::setInData(std::shared_ptr<QtNodes::NodeData> value, int index)
|
||||
{
|
||||
assert(index == 0 || index == 1);
|
||||
|
||||
if (index == 0)
|
||||
m_truePath = std::move(value);
|
||||
else
|
||||
m_falsePath = std::move(value);
|
||||
|
||||
UpdatePreview();
|
||||
}
|
||||
|
||||
QtNodes::NodeValidationState ConditionalExpression::validationState() const
|
||||
{
|
||||
if (!m_truePath || !m_falsePath)
|
||||
return QtNodes::NodeValidationState::Error;
|
||||
|
||||
return QtNodes::NodeValidationState::Valid;
|
||||
}
|
||||
|
||||
QString ConditionalExpression::validationMessage() const
|
||||
{
|
||||
if (!m_truePath || !m_falsePath)
|
||||
return "Missing input";
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
bool ConditionalExpression::ComputePreview(QPixmap& pixmap)
|
||||
{
|
||||
if (!m_currentConditionIndex)
|
||||
return false;
|
||||
|
||||
auto input = outData(0);
|
||||
if (!input || input->type().id != VecData::Type().id)
|
||||
return false;
|
||||
|
||||
assert(dynamic_cast<VecData*>(input.get()) != nullptr);
|
||||
const VecData& data = static_cast<const VecData&>(*input);
|
||||
|
||||
pixmap = QPixmap::fromImage(data.preview.GenerateImage());
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConditionalExpression::OnConditionListUpdate()
|
||||
{
|
||||
m_currentConditionIndex.reset();
|
||||
|
||||
std::size_t conditionIndex = 0;
|
||||
for (const auto& conditionEntry : GetGraph().GetConditions())
|
||||
{
|
||||
if (conditionEntry.name == m_currentConditionText)
|
||||
{
|
||||
m_currentConditionIndex = conditionIndex;
|
||||
break;
|
||||
}
|
||||
|
||||
conditionIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
void ConditionalExpression::UpdateConditionText()
|
||||
{
|
||||
if (m_currentConditionIndex)
|
||||
{
|
||||
auto& condition = GetGraph().GetCondition(*m_currentConditionIndex);
|
||||
m_currentConditionText = condition.name;
|
||||
}
|
||||
else
|
||||
m_currentConditionText.clear();
|
||||
}
|
||||
58
src/ShaderNode/DataModels/ConditionalExpression.hpp
Normal file
58
src/ShaderNode/DataModels/ConditionalExpression.hpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef NAZARA_SHADERNODES_CONDITIONALEXPRESSION_HPP
|
||||
#define NAZARA_SHADERNODES_CONDITIONALEXPRESSION_HPP
|
||||
|
||||
#include <ShaderNode/ShaderGraph.hpp>
|
||||
#include <ShaderNode/DataModels/ShaderNode.hpp>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class ConditionalExpression : public ShaderNode
|
||||
{
|
||||
public:
|
||||
ConditionalExpression(ShaderGraph& graph);
|
||||
~ConditionalExpression() = default;
|
||||
|
||||
void BuildNodeEdition(QFormLayout* layout) override;
|
||||
|
||||
Nz::ShaderNodes::ExpressionPtr GetExpression(Nz::ShaderNodes::ExpressionPtr* /*expressions*/, std::size_t count) const override;
|
||||
|
||||
QString caption() const override;
|
||||
QString name() const override;
|
||||
|
||||
unsigned int nPorts(QtNodes::PortType portType) const override;
|
||||
|
||||
QtNodes::NodeDataType dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override;
|
||||
|
||||
QString portCaption(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override;
|
||||
bool portCaptionVisible(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override;
|
||||
|
||||
std::shared_ptr<QtNodes::NodeData> outData(QtNodes::PortIndex port) override;
|
||||
|
||||
void restore(const QJsonObject& data) override;
|
||||
QJsonObject save() const override;
|
||||
|
||||
void setInData(std::shared_ptr<QtNodes::NodeData> value, int index) override;
|
||||
|
||||
QtNodes::NodeValidationState validationState() const override;
|
||||
QString validationMessage() const override;
|
||||
|
||||
private:
|
||||
bool ComputePreview(QPixmap& pixmap) override;
|
||||
void OnConditionListUpdate();
|
||||
void UpdateConditionText();
|
||||
|
||||
NazaraSlot(ShaderGraph, OnConditionListUpdate, m_onConditionListUpdateSlot);
|
||||
NazaraSlot(ShaderGraph, OnConditionUpdate, m_onConditionUpdateSlot);
|
||||
|
||||
std::optional<std::size_t> m_currentConditionIndex;
|
||||
std::shared_ptr<QtNodes::NodeData> m_falsePath;
|
||||
std::shared_ptr<QtNodes::NodeData> m_truePath;
|
||||
std::string m_currentConditionText;
|
||||
};
|
||||
|
||||
#include <ShaderNode/DataModels/BufferField.inl>
|
||||
|
||||
#endif
|
||||
2
src/ShaderNode/DataModels/ConditionalExpression.inl
Normal file
2
src/ShaderNode/DataModels/ConditionalExpression.inl
Normal file
@@ -0,0 +1,2 @@
|
||||
#include <ShaderNode/DataModels/BufferField.hpp>
|
||||
#include <Nazara/Core/Algorithm.hpp>
|
||||
@@ -238,16 +238,16 @@ void OutputValue::OnOutputListUpdate()
|
||||
{
|
||||
m_currentOutputIndex.reset();
|
||||
|
||||
std::size_t inputIndex = 0;
|
||||
for (const auto& inputEntry : GetGraph().GetOutputs())
|
||||
std::size_t outputIndex = 0;
|
||||
for (const auto& outputEntry : GetGraph().GetOutputs())
|
||||
{
|
||||
if (inputEntry.name == m_currentOutputText)
|
||||
if (outputEntry.name == m_currentOutputText)
|
||||
{
|
||||
m_currentOutputIndex = inputIndex;
|
||||
m_currentOutputIndex = outputIndex;
|
||||
break;
|
||||
}
|
||||
|
||||
inputIndex++;
|
||||
outputIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
ShaderNode::ShaderNode(ShaderGraph& graph) :
|
||||
m_previewSize(64, 64),
|
||||
m_pixmapLabel(nullptr),
|
||||
m_embeddedWidget(nullptr),
|
||||
m_graph(graph),
|
||||
m_enableCustomVariableName(true),
|
||||
m_isPreviewEnabled(false)
|
||||
@@ -86,7 +87,24 @@ void ShaderNode::EnablePreview(bool enable)
|
||||
|
||||
QWidget* ShaderNode::embeddedWidget()
|
||||
{
|
||||
return m_pixmapLabel;
|
||||
if (!m_embeddedWidget)
|
||||
{
|
||||
QWidget* embedded = EmbeddedWidget();
|
||||
if (embedded)
|
||||
{
|
||||
QVBoxLayout* layout = new QVBoxLayout;
|
||||
layout->addWidget(embedded);
|
||||
layout->addWidget(m_pixmapLabel);
|
||||
|
||||
m_embeddedWidget = new QWidget;
|
||||
m_embeddedWidget->setStyleSheet("background-color: rgba(0,0,0,0)");
|
||||
m_embeddedWidget->setLayout(layout);
|
||||
}
|
||||
else
|
||||
m_embeddedWidget = m_pixmapLabel;
|
||||
}
|
||||
|
||||
return m_embeddedWidget;
|
||||
}
|
||||
|
||||
void ShaderNode::restore(const QJsonObject& data)
|
||||
@@ -121,6 +139,11 @@ bool ShaderNode::ComputePreview(QPixmap& /*pixmap*/)
|
||||
return false;
|
||||
}
|
||||
|
||||
QWidget* ShaderNode::EmbeddedWidget()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ShaderNode::UpdatePreview()
|
||||
{
|
||||
if (!m_pixmap)
|
||||
|
||||
@@ -41,6 +41,7 @@ class ShaderNode : public QtNodes::NodeDataModel
|
||||
protected:
|
||||
inline void DisableCustomVariableName();
|
||||
inline void EnableCustomVariableName(bool enable = true);
|
||||
virtual QWidget* EmbeddedWidget();
|
||||
void UpdatePreview();
|
||||
|
||||
private:
|
||||
@@ -48,6 +49,7 @@ class ShaderNode : public QtNodes::NodeDataModel
|
||||
|
||||
Nz::Vector2i m_previewSize;
|
||||
QLabel* m_pixmapLabel;
|
||||
QWidget* m_embeddedWidget;
|
||||
std::optional<QPixmap> m_pixmap;
|
||||
std::string m_variableName;
|
||||
ShaderGraph& m_graph;
|
||||
|
||||
Reference in New Issue
Block a user