diff --git a/src/ShaderNode/DataModels/InputValue.cpp b/src/ShaderNode/DataModels/InputValue.cpp index e0b117e83..a5a3a269b 100644 --- a/src/ShaderNode/DataModels/InputValue.cpp +++ b/src/ShaderNode/DataModels/InputValue.cpp @@ -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(); } diff --git a/src/ShaderNode/DataModels/OutputValue.cpp b/src/ShaderNode/DataModels/OutputValue.cpp index a6b644be3..1f65c5953 100644 --- a/src/ShaderNode/DataModels/OutputValue.cpp +++ b/src/ShaderNode/DataModels/OutputValue.cpp @@ -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(); } diff --git a/src/ShaderNode/Enums.cpp b/src/ShaderNode/Enums.cpp index 1f756b8de..610078d65 100644 --- a/src/ShaderNode/Enums.cpp +++ b/src/ShaderNode/Enums.cpp @@ -1,21 +1,32 @@ #include #include -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 ""; +} + const char* EnumToString(InputRole role) { switch (role) @@ -30,15 +41,15 @@ const char* EnumToString(InputRole role) return ""; } -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); diff --git a/src/ShaderNode/Enums.hpp b/src/ShaderNode/Enums.hpp index 72a5443f7..41e8f4360 100644 --- a/src/ShaderNode/Enums.hpp +++ b/src/ShaderNode/Enums.hpp @@ -7,6 +7,15 @@ #include #include +enum class BufferType +{ + UniformBufferObject, + + Max = UniformBufferObject +}; + +constexpr std::size_t BufferTypeCount = static_cast(BufferType::Max) + 1; + enum class InputRole { None, @@ -19,7 +28,7 @@ enum class InputRole constexpr std::size_t InputRoleCount = static_cast(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(InOutType::Max) + 1; +constexpr std::size_t PrimitiveTypeCount = static_cast(PrimitiveType::Max) + 1; enum class TextureType { @@ -43,10 +52,11 @@ constexpr std::size_t TextureTypeCount = static_cast(TextureType::M template std::optional 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 diff --git a/src/ShaderNode/Previews/PreviewValues.inl b/src/ShaderNode/Previews/PreviewValues.inl index 06ca202ec..3efbebc39 100644 --- a/src/ShaderNode/Previews/PreviewValues.inl +++ b/src/ShaderNode/Previews/PreviewValues.inl @@ -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; } diff --git a/src/ShaderNode/ShaderGraph.cpp b/src/ShaderNode/ShaderGraph.cpp index 5788e1f8b..3648fed82 100644 --- a/src/ShaderNode/ShaderGraph.cpp +++ b/src/ShaderNode/ShaderGraph.cpp @@ -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 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(bufferDoc["bindingIndex"].toInt(0)); + buffer.name = bufferDoc["name"].toString().toStdString(); + buffer.structIndex = bufferDoc["structIndex"].toInt(); + buffer.type = DecodeEnum(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(inputDoc["role"].toString().toStdString()).value(); input.roleIndex = static_cast(inputDoc["roleIndex"].toInt(0)); - input.type = DecodeEnum(inputDoc["type"].toString().toStdString()).value(); + input.type = DecodeEnum(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(outputDoc["locationIndex"].toInt(0)); output.name = outputDoc["name"].toString().toStdString(); - output.type = DecodeEnum(outputDoc["type"].toString().toStdString()).value(); + output.type = DecodeEnum(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(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; + if constexpr (std::is_same_v) + memberDoc["type"] = QString(EnumToString(arg)); + else if constexpr (std::is_same_v) + memberDoc["type"] = int(arg); + else + static_assert(AlwaysFalse::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 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()); diff --git a/src/ShaderNode/ShaderGraph.hpp b/src/ShaderNode/ShaderGraph.hpp index b65694f65..d0ff63316 100644 --- a/src/ShaderNode/ShaderGraph.hpp +++ b/src/ShaderNode/ShaderGraph.hpp @@ -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 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& GetBuffers() const; + inline const InputEntry& GetInput(std::size_t bufferIndex) const; inline std::size_t GetInputCount() const; inline const std::vector& GetInputs() const; inline const OutputEntry& GetOutput(std::size_t outputIndex) const; inline std::size_t GetOutputCount() const; inline const std::vector& GetOutputs() const; + inline const StructEntry& GetStruct(std::size_t structIndex) const; + inline std::size_t GetStructCount() const; + inline const std::vector& 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 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 members; + }; + + struct StructMemberEntry + { + std::string name; + std::variant 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 BuildRegistry(); QtNodes::FlowScene m_flowScene; + std::vector m_buffers; std::vector m_inputs; std::vector m_outputs; + std::vector m_structs; std::vector m_textures; std::unique_ptr m_previewModel; }; diff --git a/src/ShaderNode/ShaderGraph.inl b/src/ShaderNode/ShaderGraph.inl index 3f839534c..04f86834c 100644 --- a/src/ShaderNode/ShaderGraph.inl +++ b/src/ShaderNode/ShaderGraph.inl @@ -1,5 +1,21 @@ #include +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& +{ + 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& 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& +{ + return m_structs; +} + inline const PreviewModel& ShaderGraph::GetPreviewModel() const { return *m_previewModel; diff --git a/src/ShaderNode/Widgets/BufferEditDialog.cpp b/src/ShaderNode/Widgets/BufferEditDialog.cpp new file mode 100644 index 000000000..3f08ca2fa --- /dev/null +++ b/src/ShaderNode/Widgets/BufferEditDialog.cpp @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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(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(m_bindingIndex->value()); + bufferInfo.name = m_outputName->text().toStdString(); + bufferInfo.structIndex = m_structList->currentIndex(); + bufferInfo.type = static_cast(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(); +} diff --git a/src/ShaderNode/Widgets/BufferEditDialog.hpp b/src/ShaderNode/Widgets/BufferEditDialog.hpp new file mode 100644 index 000000000..3e5c12e51 --- /dev/null +++ b/src/ShaderNode/Widgets/BufferEditDialog.hpp @@ -0,0 +1,43 @@ +#pragma once + +#ifndef NAZARA_SHADERNODES_BUFFEREDITDIALOG_HPP +#define NAZARA_SHADERNODES_BUFFEREDITDIALOG_HPP + +#include +#include + +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 + +#endif diff --git a/src/ShaderNode/Widgets/BufferEditDialog.inl b/src/ShaderNode/Widgets/BufferEditDialog.inl new file mode 100644 index 000000000..a0add384b --- /dev/null +++ b/src/ShaderNode/Widgets/BufferEditDialog.inl @@ -0,0 +1 @@ +#include diff --git a/src/ShaderNode/Widgets/BufferEditor.cpp b/src/ShaderNode/Widgets/BufferEditor.cpp new file mode 100644 index 000000000..cd795ee09 --- /dev/null +++ b/src/ShaderNode/Widgets/BufferEditor.cpp @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +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)); +} diff --git a/src/ShaderNode/Widgets/BufferEditor.hpp b/src/ShaderNode/Widgets/BufferEditor.hpp new file mode 100644 index 000000000..4d97d161b --- /dev/null +++ b/src/ShaderNode/Widgets/BufferEditor.hpp @@ -0,0 +1,36 @@ +#pragma once + +#ifndef NAZARA_SHADERNODES_BUFFEREDITOR_HPP +#define NAZARA_SHADERNODES_BUFFEREDITOR_HPP + +#include +#include + +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 + +#endif diff --git a/src/ShaderNode/Widgets/BufferEditor.inl b/src/ShaderNode/Widgets/BufferEditor.inl new file mode 100644 index 000000000..ef7b911cd --- /dev/null +++ b/src/ShaderNode/Widgets/BufferEditor.inl @@ -0,0 +1 @@ +#include diff --git a/src/ShaderNode/Widgets/InputEditDialog.cpp b/src/ShaderNode/Widgets/InputEditDialog.cpp index bd56fd598..45ceed2c6 100644 --- a/src/ShaderNode/Widgets/InputEditDialog.cpp +++ b/src/ShaderNode/Widgets/InputEditDialog.cpp @@ -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(i))); + for (std::size_t i = 0; i < PrimitiveTypeCount; ++i) + m_typeList->addItem(EnumToString(static_cast(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(m_roleList->currentIndex()); inputInfo.roleIndex = static_cast(m_roleIndex->value()); - inputInfo.type = static_cast(m_typeList->currentIndex()); + inputInfo.type = static_cast(m_typeList->currentIndex()); return inputInfo; } diff --git a/src/ShaderNode/Widgets/InputEditDialog.hpp b/src/ShaderNode/Widgets/InputEditDialog.hpp index 0f6655088..edd7577bf 100644 --- a/src/ShaderNode/Widgets/InputEditDialog.hpp +++ b/src/ShaderNode/Widgets/InputEditDialog.hpp @@ -16,7 +16,7 @@ struct InputInfo std::size_t roleIndex; std::string name; InputRole role; - InOutType type; + PrimitiveType type; }; class InputEditDialog : public QDialog diff --git a/src/ShaderNode/Widgets/MainWindow.cpp b/src/ShaderNode/Widgets/MainWindow.cpp index e76946738..213d190cb 100644 --- a/src/ShaderNode/Widgets/MainWindow.cpp +++ b/src/ShaderNode/Widgets/MainWindow.cpp @@ -3,9 +3,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -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 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; + if constexpr (std::is_same_v) + member.type = GetExpressionFromInOut(arg); + else if constexpr (std::is_same_v) + member.type = m_shaderGraph.GetStruct(arg).name; + else + static_assert(AlwaysFalse::value, "non-exhaustive visitor"); + }, sMember.type); + } + + shader.AddStruct(s.name, std::move(members)); + } shader.AddFunction("main", shaderAst); diff --git a/src/ShaderNode/Widgets/OutputEditDialog.cpp b/src/ShaderNode/Widgets/OutputEditDialog.cpp index d2c83e58e..c6233e64c 100644 --- a/src/ShaderNode/Widgets/OutputEditDialog.cpp +++ b/src/ShaderNode/Widgets/OutputEditDialog.cpp @@ -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(i))); + for (std::size_t i = 0; i < PrimitiveTypeCount; ++i) + m_typeList->addItem(EnumToString(static_cast(i))); m_locationIndex = new QSpinBox; @@ -50,7 +50,7 @@ OutputInfo OutputEditDialog::GetOutputInfo() const OutputInfo inputInfo; inputInfo.locationIndex = static_cast(m_locationIndex->value()); inputInfo.name = m_outputName->text().toStdString(); - inputInfo.type = static_cast(m_typeList->currentIndex()); + inputInfo.type = static_cast(m_typeList->currentIndex()); return inputInfo; } diff --git a/src/ShaderNode/Widgets/OutputEditDialog.hpp b/src/ShaderNode/Widgets/OutputEditDialog.hpp index 6a8cc68fb..5b045faff 100644 --- a/src/ShaderNode/Widgets/OutputEditDialog.hpp +++ b/src/ShaderNode/Widgets/OutputEditDialog.hpp @@ -14,7 +14,7 @@ struct OutputInfo { std::size_t locationIndex; std::string name; - InOutType type; + PrimitiveType type; }; class OutputEditDialog : public QDialog diff --git a/src/ShaderNode/Widgets/StructEditDialog.cpp b/src/ShaderNode/Widgets/StructEditDialog.cpp new file mode 100644 index 000000000..320ad586a --- /dev/null +++ b/src/ShaderNode/Widgets/StructEditDialog.cpp @@ -0,0 +1,224 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; + if constexpr (std::is_same_v) + name += QString(EnumToString(arg)); + else if constexpr (std::is_same_v) + name += QString::fromStdString(m_shaderGraph.GetStruct(arg).name); + else + static_assert(AlwaysFalse::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(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(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); +} diff --git a/src/ShaderNode/Widgets/StructEditDialog.hpp b/src/ShaderNode/Widgets/StructEditDialog.hpp new file mode 100644 index 000000000..992aacddb --- /dev/null +++ b/src/ShaderNode/Widgets/StructEditDialog.hpp @@ -0,0 +1,59 @@ +#pragma once + +#ifndef NAZARA_SHADERNODES_STRUCTEDITDIALOG_HPP +#define NAZARA_SHADERNODES_STRUCTEDITDIALOG_HPP + +#include +#include +#include +#include + +class QLineEdit; +class QListWidget; +class QPushButton; +class ShaderGraph; + +struct StructInfo +{ + struct Member + { + std::string name; + std::variant type; + }; + + std::string name; + std::vector 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 + +#endif diff --git a/src/ShaderNode/Widgets/StructEditDialog.inl b/src/ShaderNode/Widgets/StructEditDialog.inl new file mode 100644 index 000000000..474a6f20e --- /dev/null +++ b/src/ShaderNode/Widgets/StructEditDialog.inl @@ -0,0 +1 @@ +#include diff --git a/src/ShaderNode/Widgets/StructEditor.cpp b/src/ShaderNode/Widgets/StructEditor.cpp new file mode 100644 index 000000000..a68e6106d --- /dev/null +++ b/src/ShaderNode/Widgets/StructEditor.cpp @@ -0,0 +1,107 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +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 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 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)); +} diff --git a/src/ShaderNode/Widgets/StructEditor.hpp b/src/ShaderNode/Widgets/StructEditor.hpp new file mode 100644 index 000000000..3e00fc5bb --- /dev/null +++ b/src/ShaderNode/Widgets/StructEditor.hpp @@ -0,0 +1,37 @@ +#pragma once + +#ifndef NAZARA_SHADERNODES_STRUCTEDITOR_HPP +#define NAZARA_SHADERNODES_STRUCTEDITOR_HPP + +#include +#include +#include + +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 + +#endif diff --git a/src/ShaderNode/Widgets/StructEditor.inl b/src/ShaderNode/Widgets/StructEditor.inl new file mode 100644 index 000000000..95ebbea7e --- /dev/null +++ b/src/ShaderNode/Widgets/StructEditor.inl @@ -0,0 +1 @@ +#include diff --git a/src/ShaderNode/Widgets/StructMemberEditDialog.cpp b/src/ShaderNode/Widgets/StructMemberEditDialog.cpp new file mode 100644 index 000000000..82bc4e1a2 --- /dev/null +++ b/src/ShaderNode/Widgets/StructMemberEditDialog.cpp @@ -0,0 +1,86 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +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(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; + if constexpr (std::is_same_v) + m_typeList->setCurrentIndex(static_cast(arg)); + else if constexpr (std::is_same_v) + m_typeList->setCurrentIndex(static_cast(PrimitiveTypeCount + arg)); + else + static_assert(AlwaysFalse::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(m_typeList->currentIndex()); + else + inputInfo.type = static_cast(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(); +} diff --git a/src/ShaderNode/Widgets/StructMemberEditDialog.hpp b/src/ShaderNode/Widgets/StructMemberEditDialog.hpp new file mode 100644 index 000000000..e482766dc --- /dev/null +++ b/src/ShaderNode/Widgets/StructMemberEditDialog.hpp @@ -0,0 +1,39 @@ +#pragma once + +#ifndef NAZARA_SHADERNODES_OUTPUTEDITDIALOG_HPP +#define NAZARA_SHADERNODES_OUTPUTEDITDIALOG_HPP + +#include +#include +#include + +class QComboBox; +class QLineEdit; +class ShaderGraph; + +struct StructMemberInfo +{ + std::string name; + std::variant 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 + +#endif diff --git a/src/ShaderNode/Widgets/StructMemberEditDialog.inl b/src/ShaderNode/Widgets/StructMemberEditDialog.inl new file mode 100644 index 000000000..e663ea05a --- /dev/null +++ b/src/ShaderNode/Widgets/StructMemberEditDialog.inl @@ -0,0 +1 @@ +#include