#include #include #include #include #include #include #include #include #include #include #include #include BufferField::BufferField(ShaderGraph& graph) : ShaderNode(graph) { UpdateFieldList(); m_onBufferListUpdateSlot.Connect(GetGraph().OnBufferListUpdate, [&](ShaderGraph*) { UpdateBufferIndex(); }); m_onBufferUpdateSlot.Connect(GetGraph().OnBufferUpdate, [&](ShaderGraph*, std::size_t bufferIndex) { if (m_currentBufferIndex == bufferIndex) { UpdatePreview(); Q_EMIT dataUpdated(0); } }); m_onStructListUpdateSlot.Connect(GetGraph().OnStructListUpdate, [&](ShaderGraph*) { UpdateFieldList(); UpdateFieldIndex(); UpdatePreview(); Q_EMIT dataUpdated(0); }); m_onStructUpdateSlot.Connect(GetGraph().OnStructUpdate, [&](ShaderGraph*, std::size_t) { UpdateFieldList(); UpdateFieldIndex(); UpdatePreview(); Q_EMIT dataUpdated(0); }); DisableCustomVariableName(); UpdatePreview(); } Nz::ShaderNodes::NodePtr BufferField::BuildNode(Nz::ShaderNodes::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const { assert(count == 0); assert(outputIndex == 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); std::vector memberIndices; memberIndices.reserve(currentField.nestedFields.size() + 1); 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); memberIndices.push_back(nestedIndex); } memberIndices.push_back(currentField.finalFieldIndex); 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), std::move(memberIndices), graph.ToShaderExpressionType(std::get(memberEntry.type))); } unsigned int BufferField::nPorts(QtNodes::PortType portType) const { switch (portType) { case QtNodes::PortType::In: return 0; case QtNodes::PortType::Out: return 1; } return 0; } void BufferField::BuildNodeEdition(QFormLayout* layout) { ShaderNode::BuildNodeEdition(layout); QComboBox* fieldSelection = new QComboBox; connect(fieldSelection, qOverload(&QComboBox::currentIndexChanged), [=](int index) { if (index >= 0) m_currentFieldText = m_fieldList[index]; else m_currentFieldText.clear(); UpdateFieldIndex(); 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); UpdateFieldList(); for (const std::string& field : m_fieldList) fieldSelection->addItem(QString::fromStdString(field)); } 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); } 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)); return ShaderGraph::ToNodeDataType(std::get(member.type)); } QString BufferField::portCaption(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const { assert(portType == QtNodes::PortType::Out); assert(portIndex == 0); if (!m_currentBufferIndex || !m_currentFieldIndex) return ""; std::stringstream ss; const ShaderGraph& graph = GetGraph(); const auto& bufferEntry = graph.GetBuffer(*m_currentBufferIndex); const auto& structEntry = graph.GetStruct(bufferEntry.structIndex); ss << bufferEntry.name << "."; const CurrentField& currentField = *m_currentFieldIndex; 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); ss << memberEntry.name << "."; } assert(currentField.finalFieldIndex < sourceStruct->members.size()); const auto& memberEntry = sourceStruct->members[currentField.finalFieldIndex]; assert(std::holds_alternative(memberEntry.type)); ss << memberEntry.name; return QString::fromStdString(ss.str()); } bool BufferField::portCaptionVisible(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const { assert(portType == QtNodes::PortType::Out); assert(portIndex == 0); return true; } 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); case PrimitiveType::Mat4x4: return std::make_shared(); } assert(false); throw std::runtime_error("Unhandled primitive type"); } void BufferField::restore(const QJsonObject& data) { m_currentBufferText = data["buffer"].toString().toStdString(); m_currentFieldText = data["field"].toString().toStdString(); UpdateBufferIndex(); UpdateFieldIndex(); 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; } 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(); } 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::PopulateFieldList(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) m_fieldList.push_back(prefix + member.name); else if constexpr (std::is_same_v) PopulateFieldList(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::UpdateBufferIndex() { 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::UpdateBufferText() { if (m_currentBufferIndex) { auto& buffer = GetGraph().GetBuffer(*m_currentBufferIndex); m_currentBufferText = buffer.name; } else m_currentBufferText.clear(); } void BufferField::UpdateFieldIndex() { 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::UpdateFieldList() { m_fieldList.clear(); if (!m_currentBufferIndex) return; const ShaderGraph& graph = GetGraph(); const auto& buffer = graph.GetBuffer(*m_currentBufferIndex); PopulateFieldList(buffer.structIndex); }