Add conditional expression/statement support for shaders

This commit is contained in:
Jérôme Leclercq
2020-11-19 13:56:54 +01:00
parent ad88561245
commit 960817a1f1
45 changed files with 996 additions and 56 deletions

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

View 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

View File

@@ -0,0 +1,2 @@
#include <ShaderNode/DataModels/BufferField.hpp>
#include <Nazara/Core/Algorithm.hpp>

View File

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

View File

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

View File

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

View File

@@ -2,6 +2,7 @@
#include <Nazara/Core/StackArray.hpp>
#include <ShaderNode/DataModels/BufferField.hpp>
#include <ShaderNode/DataModels/Cast.hpp>
#include <ShaderNode/DataModels/ConditionalExpression.hpp>
#include <ShaderNode/DataModels/FloatValue.hpp>
#include <ShaderNode/DataModels/InputValue.hpp>
#include <ShaderNode/DataModels/OutputValue.hpp>
@@ -124,6 +125,17 @@ std::size_t ShaderGraph::AddBuffer(std::string name, BufferType bufferType, std:
return index;
}
std::size_t ShaderGraph::AddCondition(std::string name)
{
std::size_t index = m_conditions.size();
auto& conditionEntry = m_conditions.emplace_back();
conditionEntry.name = std::move(name);
OnConditionListUpdate(this);
return index;
}
std::size_t ShaderGraph::AddInput(std::string name, PrimitiveType type, InputRole role, std::size_t roleIndex, std::size_t locationIndex)
{
std::size_t index = m_inputs.size();
@@ -185,6 +197,7 @@ void ShaderGraph::Clear()
m_flowScene.clear();
m_buffers.clear();
m_conditions.clear();
m_inputs.clear();
m_structs.clear();
m_outputs.clear();
@@ -197,6 +210,15 @@ void ShaderGraph::Clear()
OnTextureListUpdate(this);
}
void ShaderGraph::EnableCondition(std::size_t conditionIndex, bool enable)
{
assert(conditionIndex < m_conditions.size());
auto& conditionEntry = m_conditions[conditionIndex];
conditionEntry.enabled = enable;
OnConditionUpdate(this, conditionIndex);
}
void ShaderGraph::Load(const QJsonObject& data)
{
Clear();
@@ -218,6 +240,17 @@ void ShaderGraph::Load(const QJsonObject& data)
OnBufferListUpdate(this);
QJsonArray conditionArray = data["conditions"].toArray();
for (const auto& conditionDocRef : conditionArray)
{
QJsonObject conditionDoc = conditionDocRef.toObject();
ConditionEntry& condition = m_conditions.emplace_back();
condition.name = conditionDoc["name"].toString().toStdString();
}
OnConditionListUpdate(this);
QJsonArray inputArray = data["inputs"].toArray();
for (const auto& inputDocRef : inputArray)
{
@@ -312,6 +345,18 @@ QJsonObject ShaderGraph::Save()
}
sceneJson["buffers"] = bufferArray;
QJsonArray conditionArray;
{
for (const auto& condition : m_conditions)
{
QJsonObject inputDoc;
inputDoc["name"] = QString::fromStdString(condition.name);
conditionArray.append(inputDoc);
}
}
sceneJson["conditions"] = conditionArray;
QJsonArray inputArray;
{
for (const auto& input : m_inputs)
@@ -436,10 +481,15 @@ Nz::ShaderNodes::StatementPtr ShaderGraph::ToAst()
(*it)++;
};
std::vector<QtNodes::Node*> outputNodes;
m_flowScene.iterateOverNodes([&](QtNodes::Node* node)
{
if (node->nodeDataModel()->nPorts(QtNodes::PortType::Out) == 0)
{
DetectVariables(node);
outputNodes.push_back(node);
}
});
QHash<QUuid, Nz::ShaderNodes::ExpressionPtr> variableExpressions;
@@ -510,13 +560,8 @@ Nz::ShaderNodes::StatementPtr ShaderGraph::ToAst()
return expression;
};
m_flowScene.iterateOverNodes([&](QtNodes::Node* node)
{
if (node->nodeDataModel()->nPorts(QtNodes::PortType::Out) == 0)
{
statements.emplace_back(Nz::ShaderBuilder::ExprStatement(HandleNode(node)));
}
});
for (QtNodes::Node* node : outputNodes)
statements.emplace_back(Nz::ShaderBuilder::ExprStatement(HandleNode(node)));
return Nz::ShaderNodes::StatementBlock::Build(std::move(statements));
}
@@ -551,6 +596,15 @@ void ShaderGraph::UpdateBuffer(std::size_t bufferIndex, std::string name, Buffer
OnBufferUpdate(this, bufferIndex);
}
void ShaderGraph::UpdateCondition(std::size_t conditionIndex, std::string condition)
{
assert(conditionIndex < m_conditions.size());
auto& conditionEntry = m_conditions[conditionIndex];
conditionEntry.name = std::move(condition);
OnConditionUpdate(this, conditionIndex);
}
void ShaderGraph::UpdateInput(std::size_t inputIndex, std::string name, PrimitiveType type, InputRole role, std::size_t roleIndex, std::size_t locationIndex)
{
assert(inputIndex < m_inputs.size());
@@ -687,6 +741,7 @@ std::shared_ptr<QtNodes::DataModelRegistry> ShaderGraph::BuildRegistry()
RegisterShaderNode<CastToVec2>(*this, registry, "Casts");
RegisterShaderNode<CastToVec3>(*this, registry, "Casts");
RegisterShaderNode<CastToVec4>(*this, registry, "Casts");
RegisterShaderNode<ConditionalExpression>(*this, registry, "Shader");
RegisterShaderNode<FloatValue>(*this, registry, "Constants");
RegisterShaderNode<InputValue>(*this, registry, "Inputs");
RegisterShaderNode<PositionOutputValue>(*this, registry, "Outputs");

View File

@@ -18,6 +18,7 @@ class ShaderGraph
{
public:
struct BufferEntry;
struct ConditionEntry;
struct InputEntry;
struct OutputEntry;
struct StructEntry;
@@ -28,6 +29,7 @@ class ShaderGraph
~ShaderGraph();
std::size_t AddBuffer(std::string name, BufferType bufferType, std::size_t structIndex, std::size_t bindingIndex);
std::size_t AddCondition(std::string name);
std::size_t AddInput(std::string name, PrimitiveType type, InputRole role, std::size_t roleIndex, std::size_t locationIndex);
std::size_t AddOutput(std::string name, PrimitiveType type, std::size_t locationIndex);
std::size_t AddStruct(std::string name, std::vector<StructMemberEntry> members);
@@ -35,9 +37,14 @@ class ShaderGraph
void Clear();
void EnableCondition(std::size_t conditionIndex, bool enable);
inline const BufferEntry& GetBuffer(std::size_t bufferIndex) const;
inline std::size_t GetBufferCount() const;
inline const std::vector<BufferEntry>& GetBuffers() const;
inline const ConditionEntry& GetCondition(std::size_t conditionIndex) const;
inline std::size_t GetConditionCount() const;
inline const std::vector<ConditionEntry>& GetConditions() const;
inline const InputEntry& GetInput(std::size_t bufferIndex) const;
inline std::size_t GetInputCount() const;
inline const std::vector<InputEntry>& GetInputs() const;
@@ -54,6 +61,8 @@ class ShaderGraph
inline const std::vector<TextureEntry>& GetTextures() const;
inline ShaderType GetType() const;
inline bool IsConditionEnabled(std::size_t conditionIndex) const;
void Load(const QJsonObject& data);
QJsonObject Save();
@@ -61,6 +70,7 @@ class ShaderGraph
Nz::ShaderExpressionType ToShaderExpressionType(const std::variant<PrimitiveType, std::size_t>& type) const;
void UpdateBuffer(std::size_t bufferIndex, std::string name, BufferType bufferType, std::size_t structIndex, std::size_t bindingIndex);
void UpdateCondition(std::size_t conditionIndex, std::string condition);
void UpdateInput(std::size_t inputIndex, std::string name, PrimitiveType type, InputRole role, std::size_t roleIndex, std::size_t locationIndex);
void UpdateOutput(std::size_t outputIndex, std::string name, PrimitiveType type, std::size_t locationIndex);
void UpdateStruct(std::size_t structIndex, std::string name, std::vector<StructMemberEntry> members);
@@ -76,6 +86,12 @@ class ShaderGraph
BufferType type;
};
struct ConditionEntry
{
std::string name;
bool enabled = false;
};
struct InputEntry
{
std::size_t locationIndex;
@@ -113,7 +129,9 @@ class ShaderGraph
};
NazaraSignal(OnBufferListUpdate, ShaderGraph*);
NazaraSignal(OnBufferUpdate, ShaderGraph*, std::size_t /*outputIndex*/);
NazaraSignal(OnBufferUpdate, ShaderGraph*, std::size_t /*bufferIndex*/);
NazaraSignal(OnConditionListUpdate, ShaderGraph*);
NazaraSignal(OnConditionUpdate, ShaderGraph*, std::size_t /*conditionIndex*/);
NazaraSignal(OnInputListUpdate, ShaderGraph*);
NazaraSignal(OnInputUpdate, ShaderGraph*, std::size_t /*inputIndex*/);
NazaraSignal(OnOutputListUpdate, ShaderGraph*);
@@ -136,6 +154,7 @@ class ShaderGraph
QtNodes::FlowScene m_flowScene;
std::vector<BufferEntry> m_buffers;
std::vector<ConditionEntry> m_conditions;
std::vector<InputEntry> m_inputs;
std::vector<OutputEntry> m_outputs;
std::vector<StructEntry> m_structs;

View File

@@ -16,6 +16,22 @@ inline auto ShaderGraph::GetBuffers() const -> const std::vector<BufferEntry>&
return m_buffers;
}
inline auto ShaderGraph::GetCondition(std::size_t conditionIndex) const -> const ConditionEntry&
{
assert(conditionIndex < m_conditions.size());
return m_conditions[conditionIndex];
}
inline std::size_t ShaderGraph::GetConditionCount() const
{
return m_conditions.size();
}
inline auto ShaderGraph::GetConditions() const -> const std::vector<ConditionEntry>&
{
return m_conditions;
}
inline auto ShaderGraph::GetInput(std::size_t inputIndex) const -> const InputEntry&
{
assert(inputIndex < m_inputs.size());
@@ -95,3 +111,9 @@ inline ShaderType ShaderGraph::GetType() const
return m_type;
}
inline bool ShaderGraph::IsConditionEnabled(std::size_t conditionIndex) const
{
assert(conditionIndex < m_conditions.size());
return m_conditions[conditionIndex].enabled;
}

View File

@@ -0,0 +1,55 @@
#include <ShaderNode/Widgets/ConditionEditDialog.hpp>
#include <QtWidgets/QComboBox>
#include <QtWidgets/QDialogButtonBox>
#include <QtWidgets/QFormLayout>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QSpinBox>
#include <QtWidgets/QVBoxLayout>
ConditionEditDialog::ConditionEditDialog(QWidget* parent) :
QDialog(parent)
{
setWindowTitle(tr("Condition edit dialog"));
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
m_conditionName = new QLineEdit;
QFormLayout* formLayout = new QFormLayout;
formLayout->addRow(tr("Name"), m_conditionName);
QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttonBox, &QDialogButtonBox::accepted, this, &ConditionEditDialog::OnAccept);
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
QVBoxLayout* verticalLayout = new QVBoxLayout;
verticalLayout->addLayout(formLayout);
verticalLayout->addWidget(buttonBox);
setLayout(verticalLayout);
}
ConditionEditDialog::ConditionEditDialog(const ConditionInfo& condition, QWidget* parent) :
ConditionEditDialog(parent)
{
m_conditionName->setText(QString::fromStdString(condition.name));
}
ConditionInfo ConditionEditDialog::GetConditionInfo() const
{
ConditionInfo inputInfo;
inputInfo.name = m_conditionName->text().toStdString();
return inputInfo;
}
void ConditionEditDialog::OnAccept()
{
if (m_conditionName->text().isEmpty())
{
QMessageBox::critical(this, tr("Empty name"), tr("Condition name must be set"), QMessageBox::Ok);
return;
}
accept();
}

View File

@@ -0,0 +1,35 @@
#pragma once
#ifndef NAZARA_SHADERNODES_CONDITIONEDITDIALOG_HPP
#define NAZARA_SHADERNODES_CONDITIONEDITDIALOG_HPP
#include <ShaderNode/Enums.hpp>
#include <QtWidgets/QDialog>
class QComboBox;
class QLineEdit;
class QSpinBox;
struct ConditionInfo
{
std::string name;
};
class ConditionEditDialog : public QDialog
{
public:
ConditionEditDialog(QWidget* parent = nullptr);
ConditionEditDialog(const ConditionInfo& input, QWidget* parent = nullptr);
~ConditionEditDialog() = default;
ConditionInfo GetConditionInfo() const;
private:
void OnAccept();
QLineEdit* m_conditionName;
};
#include <ShaderNode/Widgets/ConditionEditDialog.inl>
#endif

View File

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

View File

@@ -0,0 +1,117 @@
#include <ShaderNode/Widgets/ConditionEditor.hpp>
#include <ShaderNode/Widgets/ConditionEditDialog.hpp>
#include <ShaderNode/ShaderGraph.hpp>
#include <QtGui/QStandardItemModel>
#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QListWidget>
#include <QtWidgets/QTableView>
#include <QtWidgets/QVBoxLayout>
ConditionEditor::ConditionEditor(ShaderGraph& graph) :
m_shaderGraph(graph)
{
QTableView* tableView = new QTableView;
m_model = new QStandardItemModel(0, 2, tableView);
tableView->setModel(m_model);
m_model->setHorizontalHeaderLabels({ tr("Condition"), tr("Enabled") });
connect(tableView, &QTableView::doubleClicked, [this](const QModelIndex& index)
{
if (index.column() == 0)
OnEditCondition(index.row());
});
connect(m_model, &QStandardItemModel::itemChanged, [this](QStandardItem* item)
{
if (item->column() == 1)
{
std::size_t conditionIndex = static_cast<std::size_t>(item->row());
bool value = item->checkState() == Qt::Checked;
m_shaderGraph.EnableCondition(conditionIndex, value);
}
});
QPushButton* addStructButton = new QPushButton(tr("Add condition..."));
connect(addStructButton, &QPushButton::released, this, &ConditionEditor::OnAddCondition);
m_layout = new QVBoxLayout;
m_layout->addWidget(tableView);
m_layout->addWidget(addStructButton);
setLayout(m_layout);
m_onConditionListUpdateSlot.Connect(m_shaderGraph.OnConditionListUpdate, this, &ConditionEditor::OnConditionListUpdate);
m_onConditionUpdateSlot.Connect(m_shaderGraph.OnConditionUpdate, this, &ConditionEditor::OnConditionUpdate);
RefreshConditions();
}
void ConditionEditor::OnAddCondition()
{
ConditionEditDialog* dialog = new ConditionEditDialog(this);
dialog->setAttribute(Qt::WA_DeleteOnClose, true);
connect(dialog, &QDialog::accepted, [this, dialog]
{
ConditionInfo conditionInfo = dialog->GetConditionInfo();
m_shaderGraph.AddCondition(std::move(conditionInfo.name));
});
dialog->open();
}
void ConditionEditor::OnEditCondition(int conditionIndex)
{
const auto& conditionInfo = m_shaderGraph.GetCondition(conditionIndex);
ConditionInfo info;
info.name = conditionInfo.name;
ConditionEditDialog* dialog = new ConditionEditDialog(info, this);
dialog->setAttribute(Qt::WA_DeleteOnClose, true);
connect(dialog, &QDialog::accepted, [this, dialog, conditionIndex]
{
ConditionInfo conditionInfo = dialog->GetConditionInfo();
m_shaderGraph.UpdateCondition(conditionIndex, std::move(conditionInfo.name));
});
dialog->open();
}
void ConditionEditor::OnConditionListUpdate(ShaderGraph* /*graph*/)
{
RefreshConditions();
}
void ConditionEditor::OnConditionUpdate(ShaderGraph* /*graph*/, std::size_t conditionIndex)
{
const auto& conditionEntry = m_shaderGraph.GetCondition(conditionIndex);
int row = int(conditionIndex);
m_model->item(row, 0)->setText(QString::fromStdString(conditionEntry.name));
m_model->item(row, 1)->setCheckState((conditionEntry.enabled) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
}
void ConditionEditor::RefreshConditions()
{
m_model->setRowCount(int(m_shaderGraph.GetConditionCount()));
int rowIndex = 0;
for (const auto& conditionEntry : m_shaderGraph.GetConditions())
{
QStandardItem* label = new QStandardItem(1);
label->setEditable(false);
label->setText(QString::fromStdString(conditionEntry.name));
m_model->setItem(rowIndex, 0, label);
QStandardItem* checkbox = new QStandardItem(1);
checkbox->setCheckable(true);
checkbox->setCheckState((conditionEntry.enabled) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
m_model->setItem(rowIndex, 1, checkbox);
}

View File

@@ -0,0 +1,36 @@
#pragma once
#ifndef NAZARA_SHADERNODES_CONDITIONEDITOR_HPP
#define NAZARA_SHADERNODES_CONDITIONEDITOR_HPP
#include <ShaderNode/ShaderGraph.hpp>
#include <QtWidgets/QWidget>
#include <optional>
class QStandardItemModel;
class QVBoxLayout;
class ConditionEditor : public QWidget
{
public:
ConditionEditor(ShaderGraph& graph);
~ConditionEditor() = default;
private:
void OnAddCondition();
void OnConditionListUpdate(ShaderGraph* graph);
void OnConditionUpdate(ShaderGraph* graph, std::size_t conditionIndex);
void OnEditCondition(int inputIndex);
void RefreshConditions();
NazaraSlot(ShaderGraph, OnStructListUpdate, m_onConditionListUpdateSlot);
NazaraSlot(ShaderGraph, OnStructUpdate, m_onConditionUpdateSlot);
ShaderGraph& m_shaderGraph;
QStandardItemModel* m_model;
QVBoxLayout* m_layout;
};
#include <ShaderNode/Widgets/ConditionEditor.inl>
#endif

View File

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

View File

@@ -4,6 +4,7 @@
#include <Nazara/Shader/ShaderAstSerializer.hpp>
#include <ShaderNode/ShaderGraph.hpp>
#include <ShaderNode/Widgets/BufferEditor.hpp>
#include <ShaderNode/Widgets/ConditionEditor.hpp>
#include <ShaderNode/Widgets/InputEditor.hpp>
#include <ShaderNode/Widgets/OutputEditor.hpp>
#include <ShaderNode/Widgets/NodeEditor.hpp>
@@ -84,6 +85,15 @@ m_shaderGraph(graph)
addDockWidget(Qt::RightDockWidgetArea, structDock);
// Condition editor
ConditionEditor* conditionEditor = new ConditionEditor(m_shaderGraph);
QDockWidget* conditionDock = new QDockWidget(tr("Conditions"));
conditionDock->setFeatures(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable);
conditionDock->setWidget(conditionEditor);
addDockWidget(Qt::RightDockWidgetArea, conditionDock);
m_onSelectedNodeUpdate.Connect(m_shaderGraph.OnSelectedNodeUpdate, [&](ShaderGraph*, ShaderNode* node)
{
if (node)
@@ -99,6 +109,21 @@ m_shaderGraph(graph)
BuildMenu();
m_codeOutput = new QTextEdit;
m_codeOutput->setReadOnly(true);
m_codeOutput->setWindowTitle("GLSL Output");
m_onConditionUpdate.Connect(m_shaderGraph.OnConditionUpdate, [&](ShaderGraph*, std::size_t conditionIndex)
{
if (m_codeOutput->isVisible())
OnGenerateGLSL();
});
}
MainWindow::~MainWindow()
{
delete m_codeOutput;
}
void MainWindow::BuildMenu()
@@ -109,6 +134,7 @@ void MainWindow::BuildMenu()
{
QAction* loadShader = file->addAction(tr("Load..."));
QObject::connect(loadShader, &QAction::triggered, this, &MainWindow::OnLoad);
QAction* saveShader = file->addAction(tr("Save..."));
QObject::connect(saveShader, &QAction::triggered, this, &MainWindow::OnSave);
}
@@ -117,6 +143,7 @@ void MainWindow::BuildMenu()
{
QAction* settings = shader->addAction(tr("Settings..."));
QObject::connect(settings, &QAction::triggered, this, &MainWindow::OnUpdateInfo);
QAction* compileShader = shader->addAction(tr("Compile..."));
QObject::connect(compileShader, &QAction::triggered, this, &MainWindow::OnCompile);
}
@@ -155,16 +182,20 @@ void MainWindow::OnGenerateGLSL()
try
{
Nz::GlslWriter writer;
std::string glsl = writer.Generate(ToShader());
Nz::GlslWriter::States states;
for (const auto& condition : m_shaderGraph.GetConditions())
{
if (condition.enabled)
states.enabledConditions.insert(condition.name);
}
std::string glsl = writer.Generate(ToShader(), states);
std::cout << glsl << std::endl;
QTextEdit* output = new QTextEdit;
output->setReadOnly(true);
output->setText(QString::fromStdString(glsl));
output->setAttribute(Qt::WA_DeleteOnClose, true);
output->setWindowTitle("GLSL Output");
output->show();
m_codeOutput->setText(QString::fromStdString(glsl));
m_codeOutput->show();
}
catch (const std::exception& e)
{
@@ -238,6 +269,9 @@ Nz::ShaderAst MainWindow::ToShader()
Nz::ShaderNodes::StatementPtr shaderAst = m_shaderGraph.ToAst();
Nz::ShaderAst shader(ShaderGraph::ToShaderStageType(m_shaderGraph.GetType())); //< FIXME
for (const auto& condition : m_shaderGraph.GetConditions())
shader.AddCondition(condition.name);
for (const auto& input : m_shaderGraph.GetInputs())
shader.AddInput(input.name, m_shaderGraph.ToShaderExpressionType(input.type), input.locationIndex);

View File

@@ -8,6 +8,7 @@
#include <ShaderNode/DataModels/ShaderNode.hpp>
class NodeEditor;
class QTextEdit;
namespace Nz
{
@@ -18,7 +19,7 @@ class MainWindow : public QMainWindow
{
public:
MainWindow(ShaderGraph& graph);
~MainWindow() = default;
~MainWindow();
private:
void BuildMenu();
@@ -29,10 +30,12 @@ class MainWindow : public QMainWindow
void OnUpdateInfo();
Nz::ShaderAst ToShader();
NazaraSlot(ShaderGraph, OnConditionUpdate, m_onConditionUpdate);
NazaraSlot(ShaderGraph, OnSelectedNodeUpdate, m_onSelectedNodeUpdate);
NodeEditor* m_nodeEditor;
ShaderGraph& m_shaderGraph;
QTextEdit* m_codeOutput;
};
#include <ShaderNode/Widgets/MainWindow.inl>