diff --git a/src/ShaderNode/DataModels/BufferField.cpp b/src/ShaderNode/DataModels/BufferField.cpp new file mode 100644 index 000000000..c9d81bdcd --- /dev/null +++ b/src/ShaderNode/DataModels/BufferField.cpp @@ -0,0 +1,409 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +BufferField::BufferField(ShaderGraph& graph) : +ShaderNode(graph) +{ + m_onBufferListUpdateSlot.Connect(GetGraph().OnBufferListUpdate, [&](ShaderGraph*) { UpdateCurrentBufferIndex(); }); + m_onBufferUpdateSlot.Connect(GetGraph().OnBufferUpdate, [&](ShaderGraph*, std::size_t bufferIndex) + { + if (m_currentBufferIndex == bufferIndex) + { + UpdatePreview(); + Q_EMIT dataUpdated(0); + } + }); + + if (graph.GetBufferCount() > 0) + { + m_currentBufferIndex = 0; + UpdateBufferText(); + } + + DisableCustomVariableName(); + UpdatePreview(); +} + +unsigned int BufferField::nPorts(QtNodes::PortType portType) const +{ + switch (portType) + { + case QtNodes::PortType::In: return 0; + case QtNodes::PortType::Out: return 1; + } + + return 0; +} + +bool BufferField::ComputePreview(QPixmap& pixmap) +{ + return false; + + /*if (!m_currentBufferIndex) + return false; + + const ShaderGraph& graph = GetGraph(); + const auto& inputEntry = graph.GetBuffer(*m_currentBufferIndex); + const auto& preview = graph.GetPreviewModel(); + + pixmap = QPixmap::fromImage(preview.GetPreview(inputEntry.role, inputEntry.roleIndex).GenerateImage()); + return true;*/ +} + +void BufferField::UpdateCurrentBufferIndex() +{ + Nz::CallOnExit resetIfNotFound([&] + { + m_currentBufferIndex.reset(); + m_currentBufferText.clear(); + m_currentFieldIndex.reset(); + m_currentFieldText.clear(); + }); + + if (m_currentBufferText.empty()) + return; + + std::size_t bufferIndex = 0; + for (const auto& bufferEntry : GetGraph().GetBuffers()) + { + if (bufferEntry.name == m_currentBufferText) + { + m_currentBufferIndex = bufferIndex; + resetIfNotFound.Reset(); + break; + } + + bufferIndex++; + } +} + +void BufferField::UpdateCurrentFieldIndex() +{ + Nz::CallOnExit resetIfNotFound([&] + { + m_currentFieldIndex.reset(); + m_currentFieldText.clear(); + }); + + if (m_currentFieldText.empty()) + return; + + if (!m_currentFieldIndex) + m_currentFieldIndex.emplace(); + + CurrentField& currentField = *m_currentFieldIndex; + currentField.nestedFields.clear(); + + const ShaderGraph& graph = GetGraph(); + auto& buffer = graph.GetBuffer(*m_currentBufferIndex); + + std::function FetchField; + FetchField = [&](std::size_t structIndex, const std::string& prefix) -> bool + { + const auto& s = graph.GetStruct(structIndex); + for (auto it = s.members.begin(); it != s.members.end(); ++it) + { + const auto& member = *it; + + bool found = std::visit([&](auto&& arg) -> bool + { + using T = std::decay_t; + if constexpr (std::is_same_v) + { + if (prefix + member.name == m_currentFieldText) + { + currentField.finalFieldIndex = std::distance(s.members.begin(), it); + return true; + } + else + return false; + } + else if constexpr (std::is_same_v) + { + currentField.nestedFields.push_back(std::distance(s.members.begin(), it)); + bool found = FetchField(arg, prefix + member.name + "."); + if (!found) + { + currentField.nestedFields.pop_back(); + return false; + } + + return true; + } + else + static_assert(Nz::AlwaysFalse::value, "non-exhaustive visitor"); + }, + member.type); + + if (found) + return true; + } + + return false; + }; + + if (FetchField(buffer.structIndex, "")) + resetIfNotFound.Reset(); +} + +void BufferField::PopulateField(QComboBox* fieldList, std::size_t structIndex, const std::string& prefix) +{ + const auto& s = GetGraph().GetStruct(structIndex); + for (const auto& member : s.members) + { + std::visit([&](auto&& arg) + { + using T = std::decay_t; + if constexpr (std::is_same_v) + fieldList->addItem(QString::fromStdString(prefix + member.name)); + else if constexpr (std::is_same_v) + PopulateField(fieldList, arg, prefix + member.name + "."); + else + static_assert(AlwaysFalse::value, "non-exhaustive visitor"); + }, + member.type); + } +} + +const ShaderGraph::StructMemberEntry& BufferField::RetrieveNestedMember() const +{ + const ShaderGraph& graph = GetGraph(); + auto& buffer = graph.GetBuffer(*m_currentBufferIndex); + + assert(m_currentFieldIndex); + const CurrentField& currentField = *m_currentFieldIndex; + + const ShaderGraph::StructEntry* structEntry = &graph.GetStruct(buffer.structIndex); + for (std::size_t nestedIndex : currentField.nestedFields) + { + assert(nestedIndex < structEntry->members.size()); + const auto& memberEntry = structEntry->members[nestedIndex]; + assert(std::holds_alternative(memberEntry.type)); + + std::size_t nestedStructIndex = std::get(memberEntry.type); + structEntry = &graph.GetStruct(nestedStructIndex); + } + + return structEntry->members[currentField.finalFieldIndex]; +} + +void BufferField::UpdateBufferText() +{ + if (m_currentBufferIndex) + { + auto& buffer = GetGraph().GetBuffer(*m_currentBufferIndex); + m_currentBufferText = buffer.name; + } + else + m_currentBufferText.clear(); +} + +void BufferField::BuildNodeEdition(QFormLayout* layout) +{ + ShaderNode::BuildNodeEdition(layout); + + QComboBox* fieldSelection = new QComboBox; + connect(fieldSelection, qOverload(&QComboBox::currentIndexChanged), [=](int index) + { + if (index >= 0) + m_currentFieldText = fieldSelection->itemText(index).toStdString(); + else + m_currentFieldText.clear(); + + UpdateCurrentFieldIndex(); + UpdatePreview(); + + Q_EMIT dataUpdated(0); + }); + + QComboBox* bufferSelection = new QComboBox; + for (const auto& inputEntry : GetGraph().GetBuffers()) + bufferSelection->addItem(QString::fromStdString(inputEntry.name)); + + connect(bufferSelection, qOverload(&QComboBox::currentIndexChanged), [=](int index) + { + fieldSelection->clear(); + fieldSelection->setCurrentIndex(-1); + + if (index >= 0) + { + m_currentBufferIndex = static_cast(index); + + const ShaderGraph& graph = GetGraph(); + const auto& buffer = graph.GetBuffer(*m_currentBufferIndex); + PopulateField(fieldSelection, buffer.structIndex); + } + else + m_currentBufferIndex.reset(); + + UpdateBufferText(); + }); + + if (m_currentBufferIndex) + { + int index = int(*m_currentBufferIndex); + QString currentFieldText = QString::fromStdString(m_currentFieldText); + + bufferSelection->setCurrentIndex(-1); + bufferSelection->setCurrentIndex(index); + + fieldSelection->setCurrentText(currentFieldText); + } + else + { + bufferSelection->setCurrentIndex(-1); + fieldSelection->setCurrentIndex(-1); + } + + layout->addRow(tr("Buffer"), bufferSelection); + layout->addRow(tr("Field"), fieldSelection); +} + +Nz::ShaderNodes::ExpressionPtr BufferField::GetExpression(Nz::ShaderNodes::ExpressionPtr* /*expressions*/, std::size_t count) const +{ + assert(count == 0); + + if (!m_currentBufferIndex) + throw std::runtime_error("no buffer"); + + const ShaderGraph& graph = GetGraph(); + + const auto& bufferEntry = graph.GetBuffer(*m_currentBufferIndex); + const auto& structEntry = graph.GetStruct(bufferEntry.structIndex); + + Nz::ShaderNodes::VariablePtr varPtr; + switch (bufferEntry.type) + { + case BufferType::UniformBufferObject: + varPtr = Nz::ShaderBuilder::Uniform(bufferEntry.name, structEntry.name); + break; + } + + assert(varPtr); + + assert(m_currentFieldIndex); + const CurrentField& currentField = *m_currentFieldIndex; + + Nz::ShaderNodes::ExpressionPtr sourceExpr = Nz::ShaderBuilder::Identifier(varPtr); + + const ShaderGraph::StructEntry* sourceStruct = &structEntry; + for (std::size_t nestedIndex : currentField.nestedFields) + { + assert(nestedIndex < sourceStruct->members.size()); + const auto& memberEntry = sourceStruct->members[nestedIndex]; + assert(std::holds_alternative(memberEntry.type)); + + std::size_t nestedStructIndex = std::get(memberEntry.type); + sourceStruct = &graph.GetStruct(nestedStructIndex); + + sourceExpr = Nz::ShaderBuilder::AccessMember(std::move(sourceExpr), 0, graph.ToShaderExpressionType(memberEntry.type)); + } + + assert(currentField.finalFieldIndex < sourceStruct->members.size()); + const auto& memberEntry = sourceStruct->members[currentField.finalFieldIndex]; + assert(std::holds_alternative(memberEntry.type)); + + return Nz::ShaderBuilder::AccessMember(std::move(sourceExpr), 0, graph.ToShaderExpressionType(std::get(memberEntry.type))); +} + +auto BufferField::dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const -> QtNodes::NodeDataType +{ + assert(portType == QtNodes::PortType::Out); + assert(portIndex == 0); + + if (!m_currentBufferIndex || !m_currentFieldIndex) + return VecData::Type(); + + const auto& member = RetrieveNestedMember(); + assert(std::holds_alternative(member.type)); + + switch (std::get(member.type)) + { + case PrimitiveType::Bool: + return BoolData::Type(); + + case PrimitiveType::Float1: + return FloatData::Type(); + + case PrimitiveType::Float2: + case PrimitiveType::Float3: + case PrimitiveType::Float4: + return VecData::Type(); + } + + assert(false); + throw std::runtime_error("Unhandled primitive type"); +} + +std::shared_ptr BufferField::outData(QtNodes::PortIndex port) +{ + if (!m_currentBufferIndex) + return nullptr; + + assert(port == 0); + + if (!m_currentBufferIndex || !m_currentFieldIndex) + return {}; + + const auto& member = RetrieveNestedMember(); + assert(std::holds_alternative(member.type)); + + switch (std::get(member.type)) + { + case PrimitiveType::Bool: return std::make_shared(); + case PrimitiveType::Float1: return std::make_shared(); + case PrimitiveType::Float2: return std::make_shared(2); + case PrimitiveType::Float3: return std::make_shared(3); + case PrimitiveType::Float4: return std::make_shared(4); + } + + assert(false); + throw std::runtime_error("Unhandled primitive type"); +} + +QtNodes::NodeValidationState BufferField::validationState() const +{ + if (!m_currentBufferIndex) + return QtNodes::NodeValidationState::Error; + + if (!m_currentFieldIndex) + return QtNodes::NodeValidationState::Error; + + return QtNodes::NodeValidationState::Valid; +} + +QString BufferField::validationMessage() const +{ + if (!m_currentBufferIndex) + return "No input selected"; + + if (!m_currentFieldIndex) + return "No field selected"; + + return QString(); +} + +void BufferField::restore(const QJsonObject& data) +{ + m_currentBufferText = data["buffer"].toString().toStdString(); + m_currentFieldText = data["field"].toString().toStdString(); + UpdateCurrentBufferIndex(); + + ShaderNode::restore(data); +} + +QJsonObject BufferField::save() const +{ + QJsonObject data = ShaderNode::save(); + data["buffer"] = QString::fromStdString(m_currentBufferText); + data["field"] = QString::fromStdString(m_currentFieldText); + + return data; +} diff --git a/src/ShaderNode/DataModels/BufferField.hpp b/src/ShaderNode/DataModels/BufferField.hpp new file mode 100644 index 000000000..304165386 --- /dev/null +++ b/src/ShaderNode/DataModels/BufferField.hpp @@ -0,0 +1,67 @@ +#pragma once + +#ifndef NAZARA_SHADERNODES_BUFFERFIELD_HPP +#define NAZARA_SHADERNODES_BUFFERFIELD_HPP + +#include +#include +#include +#include +#include +#include +#include + +class BufferField : public ShaderNode +{ + public: + BufferField(ShaderGraph& graph); + ~BufferField() = default; + + void BuildNodeEdition(QFormLayout* layout) override; + + Nz::ShaderNodes::ExpressionPtr GetExpression(Nz::ShaderNodes::ExpressionPtr* /*expressions*/, std::size_t count) const override; + + QString caption() const override { return "BufferField"; } + QString name() const override { return "BufferField"; } + + unsigned int nPorts(QtNodes::PortType portType) const override; + + QtNodes::NodeDataType dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; + + std::shared_ptr outData(QtNodes::PortIndex port) override; + + QtNodes::NodeValidationState validationState() const override; + QString validationMessage() const override; + + private: + bool ComputePreview(QPixmap& pixmap) override; + void UpdateCurrentBufferIndex(); + void UpdateCurrentFieldIndex(); + void PopulateField(QComboBox* fieldList, std::size_t structIndex, const std::string& prefix = ""); + const ShaderGraph::StructMemberEntry& RetrieveNestedMember() const; + void UpdateBufferText(); + + void restore(const QJsonObject& data) override; + QJsonObject save() const override; + + NazaraSlot(ShaderGraph, OnBufferListUpdate, m_onBufferListUpdateSlot); + NazaraSlot(ShaderGraph, OnBufferUpdate, m_onBufferUpdateSlot); + + NazaraSlot(ShaderGraph, OnStructListUpdate, m_onStructListUpdateSlot); + NazaraSlot(ShaderGraph, OnStructUpdate, m_onStructUpdateSlot); + + struct CurrentField + { + std::vector nestedFields; + std::size_t finalFieldIndex; + }; + + std::optional m_currentBufferIndex; + std::optional m_currentFieldIndex; + std::string m_currentBufferText; + std::string m_currentFieldText; +}; + +#include + +#endif diff --git a/src/ShaderNode/DataModels/BufferField.inl b/src/ShaderNode/DataModels/BufferField.inl new file mode 100644 index 000000000..67cd1a5e7 --- /dev/null +++ b/src/ShaderNode/DataModels/BufferField.inl @@ -0,0 +1,2 @@ +#include +#include diff --git a/src/ShaderNode/DataTypes/BoolData.cpp b/src/ShaderNode/DataTypes/BoolData.cpp new file mode 100644 index 000000000..7c3123701 --- /dev/null +++ b/src/ShaderNode/DataTypes/BoolData.cpp @@ -0,0 +1 @@ +#include diff --git a/src/ShaderNode/DataTypes/BoolData.hpp b/src/ShaderNode/DataTypes/BoolData.hpp new file mode 100644 index 000000000..adb410888 --- /dev/null +++ b/src/ShaderNode/DataTypes/BoolData.hpp @@ -0,0 +1,28 @@ +#pragma once + +#ifndef NAZARA_SHADERNODES_BOOLDATA_HPP +#define NAZARA_SHADERNODES_BOOLDATA_HPP + +#include +#include + +struct BoolData : public QtNodes::NodeData +{ + inline BoolData(); + + QtNodes::NodeDataType type() const override + { + return Type(); + } + + static QtNodes::NodeDataType Type() + { + return { "bool", "Bool" }; + } + + PreviewValues preview; +}; + +#include + +#endif diff --git a/src/ShaderNode/DataTypes/BoolData.inl b/src/ShaderNode/DataTypes/BoolData.inl new file mode 100644 index 000000000..c12b8f72c --- /dev/null +++ b/src/ShaderNode/DataTypes/BoolData.inl @@ -0,0 +1,7 @@ +#include + +inline BoolData::BoolData() : +preview(1, 1) +{ + preview(0, 0) = Nz::Vector4f(1.f, 1.f, 1.f, 0.f); +}