ShaderNode: Add buffers and structs

This commit is contained in:
Jérôme Leclercq 2020-07-14 22:00:17 +02:00
parent fbba281d14
commit c7a8091e68
28 changed files with 1169 additions and 65 deletions

View File

@ -120,11 +120,11 @@ Nz::ShaderNodes::ExpressionPtr InputValue::GetExpression(Nz::ShaderNodes::Expres
{
switch (inputEntry.type)
{
case InOutType::Bool: return Nz::ShaderNodes::ExpressionType::Boolean;
case InOutType::Float1: return Nz::ShaderNodes::ExpressionType::Float1;
case InOutType::Float2: return Nz::ShaderNodes::ExpressionType::Float2;
case InOutType::Float3: return Nz::ShaderNodes::ExpressionType::Float3;
case InOutType::Float4: return Nz::ShaderNodes::ExpressionType::Float4;
case PrimitiveType::Bool: return Nz::ShaderNodes::ExpressionType::Boolean;
case PrimitiveType::Float1: return Nz::ShaderNodes::ExpressionType::Float1;
case PrimitiveType::Float2: return Nz::ShaderNodes::ExpressionType::Float2;
case PrimitiveType::Float3: return Nz::ShaderNodes::ExpressionType::Float3;
case PrimitiveType::Float4: return Nz::ShaderNodes::ExpressionType::Float4;
}
assert(false);
@ -146,12 +146,12 @@ auto InputValue::dataType(QtNodes::PortType portType, QtNodes::PortIndex portInd
switch (inputEntry.type)
{
//case InputType::Bool: return Nz::ShaderNodes::ExpressionType::Boolean;
case InOutType::Float1:
case PrimitiveType::Float1:
return FloatData::Type();
case InOutType::Float2:
case InOutType::Float3:
case InOutType::Float4:
case PrimitiveType::Float2:
case PrimitiveType::Float3:
case PrimitiveType::Float4:
return VecData::Type();
}

View File

@ -67,11 +67,11 @@ Nz::ShaderNodes::ExpressionPtr OutputValue::GetExpression(Nz::ShaderNodes::Expre
{
switch (outputEntry.type)
{
case InOutType::Bool: return Nz::ShaderNodes::ExpressionType::Boolean;
case InOutType::Float1: return Nz::ShaderNodes::ExpressionType::Float1;
case InOutType::Float2: return Nz::ShaderNodes::ExpressionType::Float2;
case InOutType::Float3: return Nz::ShaderNodes::ExpressionType::Float3;
case InOutType::Float4: return Nz::ShaderNodes::ExpressionType::Float4;
case PrimitiveType::Bool: return Nz::ShaderNodes::ExpressionType::Boolean;
case PrimitiveType::Float1: return Nz::ShaderNodes::ExpressionType::Float1;
case PrimitiveType::Float2: return Nz::ShaderNodes::ExpressionType::Float2;
case PrimitiveType::Float3: return Nz::ShaderNodes::ExpressionType::Float3;
case PrimitiveType::Float4: return Nz::ShaderNodes::ExpressionType::Float4;
}
assert(false);
@ -96,9 +96,9 @@ QtNodes::NodeDataType OutputValue::dataType(QtNodes::PortType portType, QtNodes:
{
//case InOutType::Bool: return Nz::ShaderNodes::ExpressionType::Boolean;
//case InOutType::Float1: return Nz::ShaderNodes::ExpressionType::Float1;
case InOutType::Float2:
case InOutType::Float3:
case InOutType::Float4:
case PrimitiveType::Float2:
case PrimitiveType::Float3:
case PrimitiveType::Float4:
return VecData::Type();
}

View File

@ -1,21 +1,32 @@
#include <ShaderNode/Enums.hpp>
#include <cassert>
std::size_t GetComponentCount(InOutType type)
std::size_t GetComponentCount(PrimitiveType type)
{
switch (type)
{
case InOutType::Bool: return 1;
case InOutType::Float1: return 1;
case InOutType::Float2: return 2;
case InOutType::Float3: return 3;
case InOutType::Float4: return 4;
case PrimitiveType::Bool: return 1;
case PrimitiveType::Float1: return 1;
case PrimitiveType::Float2: return 2;
case PrimitiveType::Float3: return 3;
case PrimitiveType::Float4: return 4;
}
assert(false);
return 0;
}
const char* EnumToString(BufferType bufferType)
{
switch (bufferType)
{
case BufferType::UniformBufferObject: return "UniformBufferObject";
}
assert(false);
return "<Unhandled>";
}
const char* EnumToString(InputRole role)
{
switch (role)
@ -30,15 +41,15 @@ const char* EnumToString(InputRole role)
return "<Unhandled>";
}
const char* EnumToString(InOutType input)
const char* EnumToString(PrimitiveType input)
{
switch (input)
{
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";
case PrimitiveType::Bool: return "Bool";
case PrimitiveType::Float1: return "Float";
case PrimitiveType::Float2: return "Float2";
case PrimitiveType::Float3: return "Float3";
case PrimitiveType::Float4: return "Float4";
}
assert(false);

View File

@ -7,6 +7,15 @@
#include <optional>
#include <string>
enum class BufferType
{
UniformBufferObject,
Max = UniformBufferObject
};
constexpr std::size_t BufferTypeCount = static_cast<std::size_t>(BufferType::Max) + 1;
enum class InputRole
{
None,
@ -19,7 +28,7 @@ enum class InputRole
constexpr std::size_t InputRoleCount = static_cast<std::size_t>(InputRole::Max) + 1;
enum class InOutType
enum class PrimitiveType
{
Bool,
Float1,
@ -30,7 +39,7 @@ enum class InOutType
Max = Float4
};
constexpr std::size_t InOutTypeCount = static_cast<std::size_t>(InOutType::Max) + 1;
constexpr std::size_t PrimitiveTypeCount = static_cast<std::size_t>(PrimitiveType::Max) + 1;
enum class TextureType
{
@ -43,10 +52,11 @@ constexpr std::size_t TextureTypeCount = static_cast<std::size_t>(TextureType::M
template<typename T> std::optional<T> DecodeEnum(const std::string_view& str);
const char* EnumToString(BufferType bufferType);
const char* EnumToString(InputRole role);
const char* EnumToString(InOutType input);
const char* EnumToString(PrimitiveType input);
const char* EnumToString(TextureType textureType);
std::size_t GetComponentCount(InOutType type);
std::size_t GetComponentCount(PrimitiveType type);
#include <ShaderNode/Enums.inl>

View File

@ -12,10 +12,10 @@ inline const Nz::Vector4f* PreviewValues::GetData() const
inline std::size_t PreviewValues::GetHeight() const
{
return m_width;
return m_height;
}
inline std::size_t PreviewValues::GetWidth() const
{
return m_height;
return m_width;
}

View File

@ -46,9 +46,23 @@ m_flowScene(BuildRegistry())
});
// Test
AddInput("UV", InOutType::Float2, InputRole::TexCoord, 0, 0);
AddOutput("RenderTarget0", InOutType::Float4, 0);
AddInput("UV", PrimitiveType::Float2, InputRole::TexCoord, 0, 0);
AddOutput("RenderTarget0", PrimitiveType::Float4, 0);
AddTexture("Potato", TextureType::Sampler2D, 1);
AddStruct("TestStruct", {
{
{ "position", PrimitiveType::Float3 },
{ "normal", PrimitiveType::Float3 },
{ "uv", PrimitiveType::Float2 },
}
});
AddStruct("TestStruct2", {
{
{ "position", PrimitiveType::Float3 },
{ "normal", PrimitiveType::Float3 },
{ "uv", PrimitiveType::Float2 },
}
});
UpdateTexturePreview(0, QImage(R"(C:\Users\Lynix\Pictures\potatavril.png)"));
@ -79,7 +93,21 @@ ShaderGraph::~ShaderGraph()
m_flowScene.clearScene();
}
std::size_t ShaderGraph::AddInput(std::string name, InOutType type, InputRole role, std::size_t roleIndex, std::size_t locationIndex)
std::size_t ShaderGraph::AddBuffer(std::string name, BufferType bufferType, std::size_t structIndex, std::size_t bindingIndex)
{
std::size_t index = m_buffers.size();
auto& bufferEntry = m_buffers.emplace_back();
bufferEntry.bindingIndex = bindingIndex;
bufferEntry.name = std::move(name);
bufferEntry.structIndex = structIndex;
bufferEntry.type = bufferType;
OnBufferListUpdate(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();
auto& inputEntry = m_inputs.emplace_back();
@ -94,7 +122,7 @@ std::size_t ShaderGraph::AddInput(std::string name, InOutType type, InputRole ro
return index;
}
std::size_t ShaderGraph::AddOutput(std::string name, InOutType type, std::size_t locationIndex)
std::size_t ShaderGraph::AddOutput(std::string name, PrimitiveType type, std::size_t locationIndex)
{
std::size_t index = m_outputs.size();
auto& outputEntry = m_outputs.emplace_back();
@ -107,6 +135,18 @@ std::size_t ShaderGraph::AddOutput(std::string name, InOutType type, std::size_t
return index;
}
std::size_t ShaderGraph::AddStruct(std::string name, std::vector<StructMemberEntry> members)
{
std::size_t index = m_structs.size();
auto& structEntry = m_structs.emplace_back();
structEntry.name = std::move(name);
structEntry.members = std::move(members);
OnStructListUpdate(this);
return index;
}
std::size_t ShaderGraph::AddTexture(std::string name, TextureType type, std::size_t bindingIndex)
{
std::size_t index = m_textures.size();
@ -125,11 +165,15 @@ void ShaderGraph::Clear()
m_flowScene.clearScene();
m_flowScene.clear();
m_buffers.clear();
m_inputs.clear();
m_structs.clear();
m_outputs.clear();
m_textures.clear();
OnBufferListUpdate(this);
OnInputListUpdate(this);
OnStructListUpdate(this);
OnOutputListUpdate(this);
OnTextureListUpdate(this);
}
@ -138,6 +182,20 @@ void ShaderGraph::Load(const QJsonObject& data)
{
Clear();
QJsonArray bufferArray = data["buffers"].toArray();
for (const auto& bufferDocRef : bufferArray)
{
QJsonObject bufferDoc = bufferDocRef.toObject();
BufferEntry& buffer = m_buffers.emplace_back();
buffer.bindingIndex = static_cast<std::size_t>(bufferDoc["bindingIndex"].toInt(0));
buffer.name = bufferDoc["name"].toString().toStdString();
buffer.structIndex = bufferDoc["structIndex"].toInt();
buffer.type = DecodeEnum<BufferType>(bufferDoc["type"].toString().toStdString()).value();
}
OnBufferListUpdate(this);
QJsonArray inputArray = data["inputs"].toArray();
for (const auto& inputDocRef : inputArray)
{
@ -148,7 +206,7 @@ void ShaderGraph::Load(const QJsonObject& data)
input.name = inputDoc["name"].toString().toStdString();
input.role = DecodeEnum<InputRole>(inputDoc["role"].toString().toStdString()).value();
input.roleIndex = static_cast<std::size_t>(inputDoc["roleIndex"].toInt(0));
input.type = DecodeEnum<InOutType>(inputDoc["type"].toString().toStdString()).value();
input.type = DecodeEnum<PrimitiveType>(inputDoc["type"].toString().toStdString()).value();
}
OnInputListUpdate(this);
@ -161,11 +219,37 @@ void ShaderGraph::Load(const QJsonObject& data)
OutputEntry& output = m_outputs.emplace_back();
output.locationIndex = static_cast<std::size_t>(outputDoc["locationIndex"].toInt(0));
output.name = outputDoc["name"].toString().toStdString();
output.type = DecodeEnum<InOutType>(outputDoc["type"].toString().toStdString()).value();
output.type = DecodeEnum<PrimitiveType>(outputDoc["type"].toString().toStdString()).value();
}
OnOutputListUpdate(this);
QJsonArray structArray = data["structs"].toArray();
for (const auto& structDocRef : structArray)
{
QJsonObject structDoc = structDocRef.toObject();
StructEntry& structInfo = m_structs.emplace_back();
structInfo.name = structDoc["name"].toString().toStdString();
QJsonArray memberArray = structDoc["members"].toArray();
for (const auto& memberDocRef : memberArray)
{
QJsonObject memberDoc = memberDocRef.toObject();
auto& memberInfo = structInfo.members.emplace_back();
memberInfo.name = memberDoc["name"].toString().toStdString();
const auto& typeDocRef = memberDoc["type"];
if (typeDocRef.isString())
memberInfo.type = DecodeEnum<PrimitiveType>(typeDocRef.toString().toStdString()).value();
else
memberInfo.type = typeDocRef.toInt();
}
}
OnStructListUpdate(this);
QJsonArray textureArray = data["textures"].toArray();
for (const auto& textureDocRef : textureArray)
{
@ -190,6 +274,21 @@ QJsonObject ShaderGraph::Save()
{
QJsonObject sceneJson;
QJsonArray bufferArray;
{
for (const auto& buffer : m_buffers)
{
QJsonObject bufferDoc;
bufferDoc["bindingIndex"] = int(buffer.bindingIndex);
bufferDoc["name"] = QString::fromStdString(buffer.name);
bufferDoc["structIndex"] = int(buffer.structIndex);
bufferDoc["type"] = QString(EnumToString(buffer.type));
bufferArray.append(bufferDoc);
}
}
sceneJson["buffers"] = bufferArray;
QJsonArray inputArray;
{
for (const auto& input : m_inputs)
@ -220,6 +319,39 @@ QJsonObject ShaderGraph::Save()
}
sceneJson["outputs"] = outputArray;
QJsonArray structArray;
{
for (const auto& s : m_structs)
{
QJsonObject structDoc;
structDoc["name"] = QString::fromStdString(s.name);
QJsonArray memberArray;
for (const auto& member : s.members)
{
QJsonObject memberDoc;
memberDoc["name"] = QString::fromStdString(member.name);
std::visit([&](auto&& arg)
{
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, PrimitiveType>)
memberDoc["type"] = QString(EnumToString(arg));
else if constexpr (std::is_same_v<T, std::size_t>)
memberDoc["type"] = int(arg);
else
static_assert(AlwaysFalse<T>::value, "non-exhaustive visitor");
}, member.type);
memberDoc["type"] = QString::fromStdString(member.name);
}
structDoc["members"] = memberArray;
structArray.append(structDoc);
}
}
sceneJson["structs"] = structArray;
QJsonArray textureArray;
{
for (const auto& texture : m_textures)
@ -366,7 +498,19 @@ Nz::ShaderNodes::StatementPtr ShaderGraph::ToAst()
return Nz::ShaderNodes::StatementBlock::Build(std::move(statements));
}
void ShaderGraph::UpdateInput(std::size_t inputIndex, std::string name, InOutType type, InputRole role, std::size_t roleIndex, std::size_t locationIndex)
void ShaderGraph::UpdateBuffer(std::size_t bufferIndex, std::string name, BufferType bufferType, std::size_t structIndex, std::size_t bindingIndex)
{
assert(bufferIndex < m_buffers.size());
auto& bufferEntry = m_buffers[bufferIndex];
bufferEntry.bindingIndex = bindingIndex;
bufferEntry.name = std::move(name);
bufferEntry.structIndex = structIndex;
bufferEntry.type = bufferType;
OnBufferUpdate(this, bufferIndex);
}
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());
auto& inputEntry = m_inputs[inputIndex];
@ -379,7 +523,7 @@ void ShaderGraph::UpdateInput(std::size_t inputIndex, std::string name, InOutTyp
OnInputUpdate(this, inputIndex);
}
void ShaderGraph::UpdateOutput(std::size_t outputIndex, std::string name, InOutType type, std::size_t locationIndex)
void ShaderGraph::UpdateOutput(std::size_t outputIndex, std::string name, PrimitiveType type, std::size_t locationIndex)
{
assert(outputIndex < m_outputs.size());
auto& outputEntry = m_outputs[outputIndex];
@ -390,6 +534,16 @@ void ShaderGraph::UpdateOutput(std::size_t outputIndex, std::string name, InOutT
OnOutputUpdate(this, outputIndex);
}
void ShaderGraph::UpdateStruct(std::size_t structIndex, std::string name, std::vector<StructMemberEntry> members)
{
assert(structIndex < m_structs.size());
auto& structEntry = m_structs[structIndex];
structEntry.name = std::move(name);
structEntry.members = std::move(members);
OnStructUpdate(this, structIndex);
}
void ShaderGraph::UpdateTexture(std::size_t textureIndex, std::string name, TextureType type, std::size_t bindingIndex)
{
assert(textureIndex < m_textures.size());

View File

@ -16,25 +16,36 @@ class ShaderNode;
class ShaderGraph
{
public:
struct BufferEntry;
struct InputEntry;
struct OutputEntry;
struct StructEntry;
struct StructMemberEntry;
struct TextureEntry;
ShaderGraph();
~ShaderGraph();
std::size_t AddInput(std::string name, InOutType type, InputRole role, std::size_t roleIndex, std::size_t locationIndex);
std::size_t AddOutput(std::string name, InOutType type, std::size_t locationIndex);
std::size_t AddBuffer(std::string name, BufferType bufferType, std::size_t structIndex, std::size_t bindingIndex);
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);
std::size_t AddTexture(std::string name, TextureType type, std::size_t bindingIndex);
void Clear();
inline const InputEntry& GetInput(std::size_t inputIndex) const;
inline const BufferEntry& GetBuffer(std::size_t bufferIndex) const;
inline std::size_t GetBufferCount() const;
inline const std::vector<BufferEntry>& GetBuffers() const;
inline const InputEntry& GetInput(std::size_t bufferIndex) 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 StructEntry& GetStruct(std::size_t structIndex) const;
inline std::size_t GetStructCount() const;
inline const std::vector<StructEntry>& GetStructs() const;
inline const PreviewModel& GetPreviewModel() const;
inline QtNodes::FlowScene& GetScene();
inline const TextureEntry& GetTexture(std::size_t textureIndex) const;
@ -46,25 +57,47 @@ class ShaderGraph
Nz::ShaderNodes::StatementPtr ToAst();
void UpdateInput(std::size_t inputIndex, std::string name, InOutType type, InputRole role, std::size_t roleIndex, std::size_t locationIndex);
void UpdateOutput(std::size_t outputIndex, std::string name, InOutType type, std::size_t locationIndex);
void UpdateBuffer(std::size_t bufferIndex, std::string name, BufferType bufferType, std::size_t structIndex, std::size_t bindingIndex);
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);
void UpdateTexture(std::size_t textureIndex, std::string name, TextureType type, std::size_t bindingIndex);
void UpdateTexturePreview(std::size_t texture, QImage preview);
struct BufferEntry
{
std::size_t bindingIndex;
std::size_t structIndex;
std::string name;
BufferType type;
};
struct InputEntry
{
std::size_t locationIndex;
std::size_t roleIndex;
std::string name;
InputRole role;
InOutType type;
PrimitiveType type;
};
struct OutputEntry
{
std::size_t locationIndex;
std::string name;
InOutType type;
PrimitiveType type;
};
struct StructEntry
{
std::string name;
std::vector<StructMemberEntry> members;
};
struct StructMemberEntry
{
std::string name;
std::variant<PrimitiveType, std::size_t /*structIndex*/> type;
};
struct TextureEntry
@ -75,11 +108,15 @@ class ShaderGraph
QImage preview;
};
NazaraSignal(OnBufferListUpdate, ShaderGraph*);
NazaraSignal(OnBufferUpdate, ShaderGraph*, std::size_t /*outputIndex*/);
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(OnStructListUpdate, ShaderGraph*);
NazaraSignal(OnStructUpdate, ShaderGraph*, std::size_t /*structIndex*/);
NazaraSignal(OnTextureListUpdate, ShaderGraph*);
NazaraSignal(OnTexturePreviewUpdate, ShaderGraph*, std::size_t /*textureIndex*/);
NazaraSignal(OnTextureUpdate, ShaderGraph*, std::size_t /*textureIndex*/);
@ -88,8 +125,10 @@ class ShaderGraph
std::shared_ptr<QtNodes::DataModelRegistry> BuildRegistry();
QtNodes::FlowScene m_flowScene;
std::vector<BufferEntry> m_buffers;
std::vector<InputEntry> m_inputs;
std::vector<OutputEntry> m_outputs;
std::vector<StructEntry> m_structs;
std::vector<TextureEntry> m_textures;
std::unique_ptr<PreviewModel> m_previewModel;
};

View File

@ -1,5 +1,21 @@
#include <ShaderNode/ShaderGraph.hpp>
inline auto ShaderGraph::GetBuffer(std::size_t bufferIndex) const -> const BufferEntry&
{
assert(bufferIndex < m_buffers.size());
return m_buffers[bufferIndex];
}
inline std::size_t ShaderGraph::GetBufferCount() const
{
return m_buffers.size();
}
inline auto ShaderGraph::GetBuffers() const -> const std::vector<BufferEntry>&
{
return m_buffers;
}
inline auto ShaderGraph::GetInput(std::size_t inputIndex) const -> const InputEntry&
{
assert(inputIndex < m_inputs.size());
@ -32,6 +48,22 @@ inline auto ShaderGraph::GetOutputs() const -> const std::vector<OutputEntry>&
return m_outputs;
}
inline auto ShaderGraph::GetStruct(std::size_t structIndex) const -> const StructEntry&
{
assert(structIndex < m_structs.size());
return m_structs[structIndex];
}
inline std::size_t ShaderGraph::GetStructCount() const
{
return m_structs.size();
}
inline auto ShaderGraph::GetStructs() const -> const std::vector<StructEntry>&
{
return m_structs;
}
inline const PreviewModel& ShaderGraph::GetPreviewModel() const
{
return *m_previewModel;

View File

@ -0,0 +1,88 @@
#include <ShaderNode/Widgets/BufferEditDialog.hpp>
#include <ShaderNode/ShaderGraph.hpp>
#include <QtWidgets/QComboBox>
#include <QtWidgets/QDialogButtonBox>
#include <QtWidgets/QFormLayout>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QSpinBox>
#include <QtWidgets/QVBoxLayout>
BufferEditDialog::BufferEditDialog(const ShaderGraph& shaderGraph, QWidget* parent) :
QDialog(parent),
m_shaderGraph(shaderGraph)
{
setWindowTitle(tr("Buffer edit dialog"));
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
m_outputName = new QLineEdit;
m_typeList = new QComboBox;
for (std::size_t i = 0; i < BufferTypeCount; ++i)
m_typeList->addItem(EnumToString(static_cast<BufferType>(i)));
m_structList = new QComboBox;
for (const auto& structEntry : m_shaderGraph.GetStructs())
m_structList->addItem(QString::fromStdString(structEntry.name));
m_bindingIndex = new QSpinBox;
QFormLayout* formLayout = new QFormLayout;
formLayout->addRow(tr("Name"), m_outputName);
formLayout->addRow(tr("Type"), m_typeList);
formLayout->addRow(tr("Struct"), m_structList);
formLayout->addRow(tr("Binding index"), m_bindingIndex);
QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttonBox, &QDialogButtonBox::accepted, this, &BufferEditDialog::OnAccept);
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
QVBoxLayout* verticalLayout = new QVBoxLayout;
verticalLayout->addLayout(formLayout);
verticalLayout->addWidget(buttonBox);
setLayout(verticalLayout);
}
BufferEditDialog::BufferEditDialog(const ShaderGraph& shaderGraph, const BufferInfo& buffer, QWidget* parent) :
BufferEditDialog(shaderGraph, parent)
{
m_bindingIndex->setValue(int(buffer.bindingIndex));
m_outputName->setText(QString::fromStdString(buffer.name));
m_structList->setCurrentIndex(buffer.structIndex);
m_typeList->setCurrentIndex(int(buffer.type));
}
BufferInfo BufferEditDialog::GetBufferInfo() const
{
BufferInfo bufferInfo;
bufferInfo.bindingIndex = static_cast<std::size_t>(m_bindingIndex->value());
bufferInfo.name = m_outputName->text().toStdString();
bufferInfo.structIndex = m_structList->currentIndex();
bufferInfo.type = static_cast<BufferType>(m_typeList->currentIndex());
return bufferInfo;
}
void BufferEditDialog::OnAccept()
{
if (m_outputName->text().isEmpty())
{
QMessageBox::critical(this, tr("Empty name"), tr("Buffer name must be set"), QMessageBox::Ok);
return;
}
if (m_structList->currentIndex() < 0)
{
QMessageBox::critical(this, tr("Invalid struct"), tr("You must select a struct"), 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,43 @@
#pragma once
#ifndef NAZARA_SHADERNODES_BUFFEREDITDIALOG_HPP
#define NAZARA_SHADERNODES_BUFFEREDITDIALOG_HPP
#include <ShaderNode/Enums.hpp>
#include <QtWidgets/QDialog>
class QComboBox;
class QLineEdit;
class QSpinBox;
class ShaderGraph;
struct BufferInfo
{
std::size_t bindingIndex;
std::size_t structIndex;
std::string name;
BufferType type;
};
class BufferEditDialog : public QDialog
{
public:
BufferEditDialog(const ShaderGraph& shaderGraph, QWidget* parent = nullptr);
BufferEditDialog(const ShaderGraph& shaderGraph, const BufferInfo& output, QWidget* parent = nullptr);
~BufferEditDialog() = default;
BufferInfo GetBufferInfo() const;
private:
void OnAccept();
const ShaderGraph& m_shaderGraph;
QComboBox* m_typeList;
QComboBox* m_structList;
QLineEdit* m_outputName;
QSpinBox* m_bindingIndex;
};
#include <ShaderNode/Widgets/BufferEditDialog.inl>
#endif

View File

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

View File

@ -0,0 +1,85 @@
#include <ShaderNode/Widgets/BufferEditor.hpp>
#include <ShaderNode/Widgets/BufferEditDialog.hpp>
#include <ShaderNode/ShaderGraph.hpp>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QListWidget>
#include <QtWidgets/QVBoxLayout>
BufferEditor::BufferEditor(ShaderGraph& graph) :
m_shaderGraph(graph)
{
m_bufferList = new QListWidget(this);
connect(m_bufferList, &QListWidget::itemDoubleClicked, [this](QListWidgetItem* item)
{
OnEditBuffer(m_bufferList->row(item));
});
QPushButton* addBufferButton = new QPushButton(tr("Add buffer..."));
connect(addBufferButton, &QPushButton::released, this, &BufferEditor::OnAddBuffer);
m_layout = new QVBoxLayout;
m_layout->addWidget(m_bufferList);
m_layout->addWidget(addBufferButton);
setLayout(m_layout);
m_onBufferListUpdateSlot.Connect(m_shaderGraph.OnBufferListUpdate, this, &BufferEditor::OnBufferListUpdate);
m_onBufferUpdateSlot.Connect(m_shaderGraph.OnBufferUpdate, this, &BufferEditor::OnBufferUpdate);
RefreshBuffers();
}
void BufferEditor::OnAddBuffer()
{
BufferEditDialog* dialog = new BufferEditDialog(m_shaderGraph, this);
dialog->setAttribute(Qt::WA_DeleteOnClose, true);
connect(dialog, &QDialog::accepted, [this, dialog]
{
BufferInfo bufferInfo = dialog->GetBufferInfo();
m_shaderGraph.AddBuffer(std::move(bufferInfo.name), bufferInfo.type, bufferInfo.structIndex, bufferInfo.bindingIndex);
});
dialog->open();
}
void BufferEditor::OnEditBuffer(int inputIndex)
{
const auto& buffer = m_shaderGraph.GetBuffer(inputIndex);
BufferInfo info;
info.name = buffer.name;
info.structIndex = buffer.structIndex;
info.type = buffer.type;
BufferEditDialog* dialog = new BufferEditDialog(m_shaderGraph, std::move(info), this);
dialog->setAttribute(Qt::WA_DeleteOnClose, true);
connect(dialog, &QDialog::accepted, [this, dialog, inputIndex]
{
BufferInfo bufferInfo = dialog->GetBufferInfo();
m_shaderGraph.UpdateBuffer(inputIndex, std::move(bufferInfo.name), bufferInfo.type, bufferInfo.structIndex, bufferInfo.bindingIndex);
});
dialog->open();
}
void BufferEditor::OnBufferListUpdate(ShaderGraph* /*graph*/)
{
RefreshBuffers();
}
void BufferEditor::OnBufferUpdate(ShaderGraph* /*graph*/, std::size_t bufferIndex)
{
const auto& bufferEntry = m_shaderGraph.GetBuffer(bufferIndex);
m_bufferList->item(int(bufferIndex))->setText(QString::fromStdString(bufferEntry.name));
}
void BufferEditor::RefreshBuffers()
{
m_bufferList->clear();
m_bufferList->setCurrentRow(-1);
for (const auto& bufferEntry : m_shaderGraph.GetBuffers())
m_bufferList->addItem(QString::fromStdString(bufferEntry.name));
}

View File

@ -0,0 +1,36 @@
#pragma once
#ifndef NAZARA_SHADERNODES_BUFFEREDITOR_HPP
#define NAZARA_SHADERNODES_BUFFEREDITOR_HPP
#include <ShaderNode/ShaderGraph.hpp>
#include <QtWidgets/QWidget>
class QLabel;
class QListWidget;
class QVBoxLayout;
class BufferEditor : public QWidget
{
public:
BufferEditor(ShaderGraph& graph);
~BufferEditor() = default;
private:
void OnAddBuffer();
void OnEditBuffer(int inputIndex);
void OnBufferListUpdate(ShaderGraph* graph);
void OnBufferUpdate(ShaderGraph* graph, std::size_t inputIndex);
void RefreshBuffers();
NazaraSlot(ShaderGraph, OnBufferListUpdate, m_onBufferListUpdateSlot);
NazaraSlot(ShaderGraph, OnBufferUpdate, m_onBufferUpdateSlot);
ShaderGraph& m_shaderGraph;
QListWidget* m_bufferList;
QVBoxLayout* m_layout;
};
#include <ShaderNode/Widgets/BufferEditor.inl>
#endif

View File

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

View File

@ -16,8 +16,8 @@ QDialog(parent)
m_inputName = new QLineEdit;
m_typeList = new QComboBox;
for (std::size_t i = 0; i < InOutTypeCount; ++i)
m_typeList->addItem(EnumToString(static_cast<InOutType>(i)));
for (std::size_t i = 0; i < PrimitiveTypeCount; ++i)
m_typeList->addItem(EnumToString(static_cast<PrimitiveType>(i)));
m_roleList = new QComboBox;
for (std::size_t i = 0; i < InputRoleCount; ++i)
@ -62,7 +62,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<InOutType>(m_typeList->currentIndex());
inputInfo.type = static_cast<PrimitiveType>(m_typeList->currentIndex());
return inputInfo;
}

View File

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

View File

@ -3,9 +3,11 @@
#include <Nazara/Renderer/GlslWriter.hpp>
#include <Nazara/Renderer/ShaderSerializer.hpp>
#include <ShaderNode/ShaderGraph.hpp>
#include <ShaderNode/Widgets/BufferEditor.hpp>
#include <ShaderNode/Widgets/InputEditor.hpp>
#include <ShaderNode/Widgets/OutputEditor.hpp>
#include <ShaderNode/Widgets/NodeEditor.hpp>
#include <ShaderNode/Widgets/StructEditor.hpp>
#include <ShaderNode/Widgets/TextureEditor.hpp>
#include <nodes/FlowView>
#include <QtCore/QFile>
@ -63,6 +65,24 @@ m_shaderGraph(graph)
addDockWidget(Qt::RightDockWidgetArea, nodeEditorDock);
// Buffer editor
BufferEditor* bufferEditor = new BufferEditor(m_shaderGraph);
QDockWidget* bufferDock = new QDockWidget(tr("Buffers"));
bufferDock->setFeatures(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable);
bufferDock->setWidget(bufferEditor);
addDockWidget(Qt::RightDockWidgetArea, bufferDock);
// Struct editor
StructEditor* structEditor = new StructEditor(m_shaderGraph);
QDockWidget* structDock = new QDockWidget(tr("Structs"));
structDock->setFeatures(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable);
structDock->setWidget(structEditor);
addDockWidget(Qt::RightDockWidgetArea, structDock);
m_onSelectedNodeUpdate.Connect(m_shaderGraph.OnSelectedNodeUpdate, [&](ShaderGraph*, ShaderNode* node)
{
if (node)
@ -193,15 +213,15 @@ Nz::ShaderAst MainWindow::ToShader()
Nz::ShaderNodes::StatementPtr shaderAst = m_shaderGraph.ToAst();
//TODO: Put in another function
auto GetExpressionFromInOut = [&](InOutType type)
auto GetExpressionFromInOut = [&](PrimitiveType type)
{
switch (type)
{
case InOutType::Bool: return Nz::ShaderNodes::ExpressionType::Boolean;
case InOutType::Float1: return Nz::ShaderNodes::ExpressionType::Float1;
case InOutType::Float2: return Nz::ShaderNodes::ExpressionType::Float2;
case InOutType::Float3: return Nz::ShaderNodes::ExpressionType::Float3;
case InOutType::Float4: return Nz::ShaderNodes::ExpressionType::Float4;
case PrimitiveType::Bool: return Nz::ShaderNodes::ExpressionType::Boolean;
case PrimitiveType::Float1: return Nz::ShaderNodes::ExpressionType::Float1;
case PrimitiveType::Float2: return Nz::ShaderNodes::ExpressionType::Float2;
case PrimitiveType::Float3: return Nz::ShaderNodes::ExpressionType::Float3;
case PrimitiveType::Float4: return Nz::ShaderNodes::ExpressionType::Float4;
}
assert(false);
@ -226,8 +246,37 @@ Nz::ShaderAst MainWindow::ToShader()
for (const auto& output : m_shaderGraph.GetOutputs())
shader.AddOutput(output.name, GetExpressionFromInOut(output.type), output.locationIndex);
for (const auto& buffer : m_shaderGraph.GetBuffers())
{
const auto& structInfo = m_shaderGraph.GetStruct(buffer.structIndex);
shader.AddUniform(buffer.name, structInfo.name, buffer.bindingIndex, Nz::ShaderNodes::MemoryLayout::Std140);
}
for (const auto& uniform : m_shaderGraph.GetTextures())
shader.AddUniform(uniform.name, GetExpressionFromTexture(uniform.type), uniform.bindingIndex);
shader.AddUniform(uniform.name, GetExpressionFromTexture(uniform.type), uniform.bindingIndex, {});
for (const auto& s : m_shaderGraph.GetStructs())
{
std::vector<Nz::ShaderAst::StructMember> members;
for (const auto& sMember : s.members)
{
auto& member = members.emplace_back();
member.name = sMember.name;
std::visit([&](auto&& arg)
{
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, PrimitiveType>)
member.type = GetExpressionFromInOut(arg);
else if constexpr (std::is_same_v<T, std::size_t>)
member.type = m_shaderGraph.GetStruct(arg).name;
else
static_assert(AlwaysFalse<T>::value, "non-exhaustive visitor");
}, sMember.type);
}
shader.AddStruct(s.name, std::move(members));
}
shader.AddFunction("main", shaderAst);

View File

@ -16,8 +16,8 @@ QDialog(parent)
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)));
for (std::size_t i = 0; i < PrimitiveTypeCount; ++i)
m_typeList->addItem(EnumToString(static_cast<PrimitiveType>(i)));
m_locationIndex = new QSpinBox;
@ -50,7 +50,7 @@ OutputInfo OutputEditDialog::GetOutputInfo() const
OutputInfo inputInfo;
inputInfo.locationIndex = static_cast<std::size_t>(m_locationIndex->value());
inputInfo.name = m_outputName->text().toStdString();
inputInfo.type = static_cast<InOutType>(m_typeList->currentIndex());
inputInfo.type = static_cast<PrimitiveType>(m_typeList->currentIndex());
return inputInfo;
}

View File

@ -14,7 +14,7 @@ struct OutputInfo
{
std::size_t locationIndex;
std::string name;
InOutType type;
PrimitiveType type;
};
class OutputEditDialog : public QDialog

View File

@ -0,0 +1,224 @@
#include <ShaderNode/Widgets/StructEditDialog.hpp>
#include <ShaderNode/Widgets/StructMemberEditDialog.hpp>
#include <Nazara/Core/Algorithm.hpp>
#include <ShaderNode/ShaderGraph.hpp>
#include <QtWidgets/QComboBox>
#include <QtWidgets/QDialogButtonBox>
#include <QtWidgets/QFormLayout>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QListWidget>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QVBoxLayout>
StructEditDialog::StructEditDialog(ShaderGraph& graph, QWidget* parent) :
QDialog(parent),
m_shaderGraph(graph)
{
setWindowTitle(tr("Struct edit dialog"));
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
m_structName = new QLineEdit;
connect(m_structName, &QLineEdit::textEdited, [this](QString newText)
{
m_info.name = newText.toStdString();
});
m_memberList = new QListWidget;
connect(m_memberList, &QListWidget::currentRowChanged, this, &StructEditDialog::OnMemberSelected);
connect(m_memberList, &QListWidget::itemDoubleClicked, [this](QListWidgetItem* item)
{
OnEditMember(m_memberList->row(item));
});
m_memberMoveUpButton = new QPushButton(tr("Move up"));
m_memberMoveUpButton->setEnabled(false);
connect(m_memberMoveUpButton, &QPushButton::released, this, &StructEditDialog::OnMemberMoveUp);
m_memberMoveDownButton = new QPushButton(tr("Move down"));
m_memberMoveDownButton->setEnabled(false);
connect(m_memberMoveDownButton, &QPushButton::released, this, &StructEditDialog::OnMemberMoveDown);
m_deleteMemberButton = new QPushButton(tr("Delete member"));
m_deleteMemberButton->setEnabled(false);
connect(m_deleteMemberButton, &QPushButton::released, this, &StructEditDialog::OnDeleteMember);
QPushButton* addMemberButton = new QPushButton(tr("Add member..."));
connect(addMemberButton, &QPushButton::released, this, &StructEditDialog::OnAddMember);
QVBoxLayout* arrowLayout = new QVBoxLayout;
arrowLayout->addWidget(m_memberMoveUpButton);
arrowLayout->addWidget(m_memberMoveDownButton);
arrowLayout->addWidget(m_deleteMemberButton);
arrowLayout->addWidget(addMemberButton);
QHBoxLayout* entityListLayout = new QHBoxLayout;
entityListLayout->addWidget(m_memberList);
entityListLayout->addLayout(arrowLayout);
QFormLayout* formLayout = new QFormLayout;
formLayout->addRow(tr("Name"), m_structName);
formLayout->addRow(tr("Members"), entityListLayout);
QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttonBox, &QDialogButtonBox::accepted, this, &StructEditDialog::OnAccept);
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
QVBoxLayout* verticalLayout = new QVBoxLayout;
verticalLayout->addLayout(formLayout);
verticalLayout->addWidget(buttonBox);
setLayout(verticalLayout);
}
StructEditDialog::StructEditDialog(ShaderGraph& graph, const StructInfo& structInfo, QWidget* parent) :
StructEditDialog(graph, parent)
{
m_info = structInfo;
m_structName->setText(QString::fromStdString(m_info.name));
UpdateMemberList();
}
const StructInfo& StructEditDialog::GetStructInfo() const
{
return m_info;
}
QString StructEditDialog::GetMemberName(const StructInfo::Member& member)
{
QString name = QString::fromStdString(member.name) + " (";
std::visit([&](auto&& arg)
{
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, PrimitiveType>)
name += QString(EnumToString(arg));
else if constexpr (std::is_same_v<T, std::size_t>)
name += QString::fromStdString(m_shaderGraph.GetStruct(arg).name);
else
static_assert(AlwaysFalse<T>::value, "non-exhaustive visitor");
},
member.type);
name += ")";
return name;
}
void StructEditDialog::OnAccept()
{
if (m_info.name.empty())
{
QMessageBox::critical(this, tr("Empty name"), tr("Struct name must be set"), QMessageBox::Ok);
return;
}
accept();
}
void StructEditDialog::OnAddMember()
{
StructMemberEditDialog* dialog = new StructMemberEditDialog(m_shaderGraph, this);
dialog->setAttribute(Qt::WA_DeleteOnClose, true);
connect(dialog, &QDialog::accepted, [this, dialog]
{
const StructMemberInfo& structInfo = dialog->GetMemberInfo();
auto& memberInfo = m_info.members.emplace_back();
memberInfo.name = structInfo.name;
memberInfo.type = structInfo.type;
UpdateMemberList(true);
});
dialog->open();
}
void StructEditDialog::OnDeleteMember()
{
int memberIndex = m_memberList->currentRow();
if (memberIndex < 0)
return;
m_info.members.erase(m_info.members.begin() + memberIndex);
UpdateMemberList();
}
void StructEditDialog::OnEditMember(int memberIndex)
{
assert(memberIndex >= 0);
auto& memberInfo = m_info.members[memberIndex];
StructMemberInfo info;
info.name = memberInfo.name;
info.type = memberInfo.type;
StructMemberEditDialog* dialog = new StructMemberEditDialog(m_shaderGraph, std::move(info), this);
dialog->setAttribute(Qt::WA_DeleteOnClose, true);
connect(dialog, &QDialog::accepted, [this, dialog, &memberInfo]
{
const StructMemberInfo& structInfo = dialog->GetMemberInfo();
memberInfo.name = structInfo.name;
memberInfo.type = structInfo.type;
UpdateMemberList(true);
});
dialog->open();
}
void StructEditDialog::OnMemberMoveUp()
{
int memberIndex = m_memberList->currentRow();
if (memberIndex <= 0)
return;
std::size_t newMemberIndex = static_cast<std::size_t>(memberIndex - 1);
std::swap(m_info.members[memberIndex], m_info.members[newMemberIndex]);
UpdateMemberList();
m_memberList->setCurrentRow(int(newMemberIndex));
}
void StructEditDialog::OnMemberMoveDown()
{
int memberIndex = m_memberList->currentRow();
if (memberIndex < 0 || memberIndex + 1 >= m_memberList->count())
return;
std::size_t newMemberIndex = static_cast<std::size_t>(memberIndex + 1);
std::swap(m_info.members[memberIndex], m_info.members[newMemberIndex]);
UpdateMemberList();
m_memberList->setCurrentRow(int(newMemberIndex));
}
void StructEditDialog::OnMemberSelected(int memberIndex)
{
if (memberIndex >= 0)
{
m_deleteMemberButton->setEnabled(true);
m_memberMoveDownButton->setEnabled(memberIndex + 1 < m_memberList->count());
m_memberMoveUpButton->setEnabled(memberIndex != 0);
}
else
{
m_deleteMemberButton->setEnabled(false);
m_memberMoveDownButton->setEnabled(false);
m_memberMoveUpButton->setEnabled(false);
}
}
void StructEditDialog::UpdateMemberList(bool keepSelection)
{
int selectionIndex = m_memberList->currentRow();
m_memberList->clear();
for (const auto& memberInfo : m_info.members)
m_memberList->addItem(GetMemberName(memberInfo));
if (keepSelection && selectionIndex >= 0 && selectionIndex < m_memberList->count())
m_memberList->setCurrentRow(selectionIndex);
}

View File

@ -0,0 +1,59 @@
#pragma once
#ifndef NAZARA_SHADERNODES_STRUCTEDITDIALOG_HPP
#define NAZARA_SHADERNODES_STRUCTEDITDIALOG_HPP
#include <ShaderNode/Enums.hpp>
#include <QtWidgets/QDialog>
#include <variant>
#include <vector>
class QLineEdit;
class QListWidget;
class QPushButton;
class ShaderGraph;
struct StructInfo
{
struct Member
{
std::string name;
std::variant<PrimitiveType, std::size_t> type;
};
std::string name;
std::vector<Member> members;
};
class StructEditDialog : public QDialog
{
public:
StructEditDialog(ShaderGraph& graph, QWidget* parent = nullptr);
StructEditDialog(ShaderGraph& graph, const StructInfo& output, QWidget* parent = nullptr);
~StructEditDialog() = default;
const StructInfo& GetStructInfo() const;
private:
QString GetMemberName(const StructInfo::Member& member);
void OnAccept();
void OnAddMember();
void OnDeleteMember();
void OnEditMember(int memberIndex);
void OnMemberMoveUp();
void OnMemberMoveDown();
void OnMemberSelected(int memberIndex);
void UpdateMemberList(bool keepSelection = false);
QLineEdit* m_structName;
QListWidget* m_memberList;
QPushButton* m_deleteMemberButton;
QPushButton* m_memberMoveUpButton;
QPushButton* m_memberMoveDownButton;
ShaderGraph& m_shaderGraph;
StructInfo m_info;
};
#include <ShaderNode/Widgets/StructEditDialog.inl>
#endif

View File

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

View File

@ -0,0 +1,107 @@
#include <ShaderNode/Widgets/StructEditor.hpp>
#include <ShaderNode/Widgets/StructEditDialog.hpp>
#include <ShaderNode/ShaderGraph.hpp>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QListWidget>
#include <QtWidgets/QVBoxLayout>
StructEditor::StructEditor(ShaderGraph& graph) :
m_shaderGraph(graph)
{
m_structList = new QListWidget(this);
connect(m_structList, &QListWidget::itemDoubleClicked, [this](QListWidgetItem* item)
{
OnEditStruct(m_structList->row(item));
});
QPushButton* addStructButton = new QPushButton(tr("Add struct..."));
connect(addStructButton, &QPushButton::released, this, &StructEditor::OnAddStruct);
m_layout = new QVBoxLayout;
m_layout->addWidget(m_structList);
m_layout->addWidget(addStructButton);
setLayout(m_layout);
m_onStructListUpdateSlot.Connect(m_shaderGraph.OnStructListUpdate, this, &StructEditor::OnStructListUpdate);
m_onStructUpdateSlot.Connect(m_shaderGraph.OnStructUpdate, this, &StructEditor::OnStructUpdate);
RefreshStructs();
}
void StructEditor::OnAddStruct()
{
StructEditDialog* dialog = new StructEditDialog(m_shaderGraph, this);
dialog->setAttribute(Qt::WA_DeleteOnClose, true);
connect(dialog, &QDialog::accepted, [this, dialog]
{
const StructInfo& structInfo = dialog->GetStructInfo();
std::vector<ShaderGraph::StructMemberEntry> members;
for (const auto& memberInfo : structInfo.members)
{
auto& member = members.emplace_back();
member.name = memberInfo.name;
member.type = memberInfo.type;
}
m_shaderGraph.AddStruct(std::move(structInfo.name), std::move(members));
});
dialog->open();
}
void StructEditor::OnEditStruct(int inputIndex)
{
const auto& structInfo = m_shaderGraph.GetStruct(inputIndex);
StructInfo info;
info.name = structInfo.name;
for (const auto& memberInfo : structInfo.members)
{
auto& member = info.members.emplace_back();
member.name = memberInfo.name;
member.type = memberInfo.type;
}
StructEditDialog* dialog = new StructEditDialog(m_shaderGraph, std::move(info), this);
dialog->setAttribute(Qt::WA_DeleteOnClose, true);
connect(dialog, &QDialog::accepted, [this, dialog, inputIndex]
{
const StructInfo& structInfo = dialog->GetStructInfo();
std::vector<ShaderGraph::StructMemberEntry> members;
for (const auto& memberInfo : structInfo.members)
{
auto& member = members.emplace_back();
member.name = memberInfo.name;
member.type = memberInfo.type;
}
m_shaderGraph.UpdateStruct(inputIndex, std::move(structInfo.name), std::move(members));
});
dialog->open();
}
void StructEditor::OnStructListUpdate(ShaderGraph* /*graph*/)
{
RefreshStructs();
}
void StructEditor::OnStructUpdate(ShaderGraph* /*graph*/, std::size_t structIndex)
{
const auto& structEntry = m_shaderGraph.GetStruct(structIndex);
m_structList->item(int(structIndex))->setText(QString::fromStdString(structEntry.name));
}
void StructEditor::RefreshStructs()
{
m_structList->clear();
m_structList->setCurrentRow(-1);
for (const auto& structEntry : m_shaderGraph.GetStructs())
m_structList->addItem(QString::fromStdString(structEntry.name));
}

View File

@ -0,0 +1,37 @@
#pragma once
#ifndef NAZARA_SHADERNODES_STRUCTEDITOR_HPP
#define NAZARA_SHADERNODES_STRUCTEDITOR_HPP
#include <ShaderNode/ShaderGraph.hpp>
#include <QtWidgets/QWidget>
#include <optional>
class QLabel;
class QListWidget;
class QVBoxLayout;
class StructEditor : public QWidget
{
public:
StructEditor(ShaderGraph& graph);
~StructEditor() = default;
private:
void OnAddStruct();
void OnEditStruct(int inputIndex);
void OnStructListUpdate(ShaderGraph* graph);
void OnStructUpdate(ShaderGraph* graph, std::size_t inputIndex);
void RefreshStructs();
NazaraSlot(ShaderGraph, OnStructListUpdate, m_onStructListUpdateSlot);
NazaraSlot(ShaderGraph, OnStructUpdate, m_onStructUpdateSlot);
ShaderGraph& m_shaderGraph;
QListWidget* m_structList;
QVBoxLayout* m_layout;
};
#include <ShaderNode/Widgets/StructEditor.inl>
#endif

View File

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

View File

@ -0,0 +1,86 @@
#include <ShaderNode/Widgets/StructMemberEditDialog.hpp>
#include <ShaderNode/ShaderGraph.hpp>
#include <QtWidgets/QComboBox>
#include <QtWidgets/QDialogButtonBox>
#include <QtWidgets/QFormLayout>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QVBoxLayout>
StructMemberEditDialog::StructMemberEditDialog(const ShaderGraph& shaderGraph, QWidget* parent) :
QDialog(parent),
m_shaderGraph(shaderGraph)
{
setWindowTitle(tr("Struct member edit dialog"));
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
m_memberName = new QLineEdit;
m_typeList = new QComboBox;
for (std::size_t i = 0; i < PrimitiveTypeCount; ++i)
m_typeList->addItem(EnumToString(static_cast<PrimitiveType>(i)));
for (const auto& structInfo : m_shaderGraph.GetStructs())
m_typeList->addItem(QString::fromStdString(structInfo.name));
QFormLayout* formLayout = new QFormLayout;
formLayout->addRow(tr("Name"), m_memberName);
formLayout->addRow(tr("Type"), m_typeList);
QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttonBox, &QDialogButtonBox::accepted, this, &StructMemberEditDialog::OnAccept);
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
QVBoxLayout* verticalLayout = new QVBoxLayout;
verticalLayout->addLayout(formLayout);
verticalLayout->addWidget(buttonBox);
setLayout(verticalLayout);
}
StructMemberEditDialog::StructMemberEditDialog(const ShaderGraph& shaderGraph, const StructMemberInfo& member, QWidget* parent) :
StructMemberEditDialog(shaderGraph, parent)
{
m_memberName->setText(QString::fromStdString(member.name));
std::visit([&](auto&& arg)
{
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, PrimitiveType>)
m_typeList->setCurrentIndex(static_cast<int>(arg));
else if constexpr (std::is_same_v<T, std::size_t>)
m_typeList->setCurrentIndex(static_cast<int>(PrimitiveTypeCount + arg));
else
static_assert(AlwaysFalse<T>::value, "non-exhaustive visitor");
},
member.type);
}
StructMemberInfo StructMemberEditDialog::GetMemberInfo() const
{
StructMemberInfo inputInfo;
inputInfo.name = m_memberName->text().toStdString();
if (m_typeList->currentIndex() < PrimitiveTypeCount)
inputInfo.type = static_cast<PrimitiveType>(m_typeList->currentIndex());
else
inputInfo.type = static_cast<std::size_t>(m_typeList->currentIndex() - PrimitiveTypeCount);
return inputInfo;
}
void StructMemberEditDialog::OnAccept()
{
if (m_memberName->text().isEmpty())
{
QMessageBox::critical(this, tr("Empty name"), tr("Struct member 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,39 @@
#pragma once
#ifndef NAZARA_SHADERNODES_OUTPUTEDITDIALOG_HPP
#define NAZARA_SHADERNODES_OUTPUTEDITDIALOG_HPP
#include <ShaderNode/Enums.hpp>
#include <QtWidgets/QDialog>
#include <variant>
class QComboBox;
class QLineEdit;
class ShaderGraph;
struct StructMemberInfo
{
std::string name;
std::variant<PrimitiveType, std::size_t> type;
};
class StructMemberEditDialog : public QDialog
{
public:
StructMemberEditDialog(const ShaderGraph& shaderGraph, QWidget* parent = nullptr);
StructMemberEditDialog(const ShaderGraph& shaderGraph, const StructMemberInfo& output, QWidget* parent = nullptr);
~StructMemberEditDialog() = default;
StructMemberInfo GetMemberInfo() const;
private:
void OnAccept();
const ShaderGraph& m_shaderGraph;
QComboBox* m_typeList;
QLineEdit* m_memberName;
};
#include <ShaderNode/Widgets/StructMemberEditDialog.inl>
#endif

View File

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