#include #include #include #include #include #include #include #include #include #include #include #include ConditionalExpression::ConditionalExpression(ShaderGraph& graph) : ShaderNode(graph) { m_onOptionListUpdateSlot.Connect(GetGraph().OnOptionListUpdate, [&](ShaderGraph*) { OnOptionListUpdate(); }); m_onOptionUpdateSlot.Connect(GetGraph().OnOptionUpdate, [&](ShaderGraph*, std::size_t optionIndex) { if (m_currentOptionIndex == optionIndex) { UpdatePreview(); Q_EMIT dataUpdated(0); } }); if (graph.GetOptionCount() > 0) { m_currentOptionIndex = 0; UpdateOptionText(); } EnablePreview(); SetPreviewSize({ 128, 128 }); UpdatePreview(); } nzsl::Ast::NodePtr ConditionalExpression::BuildNode(nzsl::Ast::ExpressionPtr* expressions, std::size_t count, std::size_t outputIndex) const { assert(count == 2); assert(outputIndex == 0); if (!m_currentOptionIndex) throw std::runtime_error("no option"); const ShaderGraph& graph = GetGraph(); const auto& optionEntry = graph.GetOption(*m_currentOptionIndex); return nzsl::ShaderBuilder::ConditionalExpression(nzsl::ShaderBuilder::Identifier(optionEntry.name), std::move(expressions[0]), std::move(expressions[1])); } QString ConditionalExpression::caption() const { return "ConditionalExpression (" + QString::fromStdString(m_currentOptionText) + ")"; } QString ConditionalExpression::name() const { return "ConditionalExpression"; } unsigned int ConditionalExpression::nPorts(QtNodes::PortType portType) const { switch (portType) { case QtNodes::PortType::None: break; case QtNodes::PortType::In: return 2; case QtNodes::PortType::Out: return 1; } return 0; } void ConditionalExpression::BuildNodeEdition(QFormLayout* layout) { ShaderNode::BuildNodeEdition(layout); QComboBox* optionSelection = new QComboBox; for (const auto& optionEntry : GetGraph().GetOptions()) optionSelection->addItem(QString::fromStdString(optionEntry.name)); if (m_currentOptionIndex) optionSelection->setCurrentIndex(int(*m_currentOptionIndex)); else optionSelection->setCurrentIndex(-1); connect(optionSelection, qOverload(&QComboBox::currentIndexChanged), [&](int index) { if (index >= 0) m_currentOptionIndex = static_cast(index); else m_currentOptionIndex.reset(); UpdateOptionText(); UpdatePreview(); Q_EMIT dataUpdated(0); }); layout->addRow(tr("Option"), optionSelection); } auto ConditionalExpression::dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const -> QtNodes::NodeDataType { switch (portType) { case QtNodes::PortType::In: { switch (portIndex) { case 0: { if (!m_truePath && !m_falsePath) return VecData::Type(); return (m_truePath) ? m_truePath->type() : m_falsePath->type(); } case 1: { if (!m_truePath && !m_falsePath) return VecData::Type(); return (m_falsePath) ? m_falsePath->type() : m_truePath->type(); } default: break; } } case QtNodes::PortType::Out: { assert(portIndex == 0); if (!m_truePath && !m_falsePath) return VecData::Type(); return (m_truePath) ? m_truePath->type() : m_falsePath->type(); } default: break; } return VecData::Type(); } QString ConditionalExpression::portCaption(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const { switch (portType) { case QtNodes::PortType::In: { switch (portIndex) { case 0: return "True expression"; case 1: return "False expression"; default: break; } } default: break; } return QString{}; } bool ConditionalExpression::portCaptionVisible(QtNodes::PortType portType, QtNodes::PortIndex /*portIndex*/) const { return portType == QtNodes::PortType::In; } std::shared_ptr ConditionalExpression::outData(QtNodes::PortIndex port) { if (!m_currentOptionIndex) return nullptr; assert(port == 0); return (GetGraph().IsOptionEnabled(*m_currentOptionIndex)) ? m_truePath : m_falsePath; } void ConditionalExpression::restore(const QJsonObject& data) { m_currentOptionText = data["option_name"].toString().toStdString(); OnOptionListUpdate(); ShaderNode::restore(data); } QJsonObject ConditionalExpression::save() const { QJsonObject data = ShaderNode::save(); data["option_name"] = QString::fromStdString(m_currentOptionText); return data; } void ConditionalExpression::setInData(std::shared_ptr value, int index) { assert(index == 0 || index == 1); if (index == 0) m_truePath = std::move(value); else m_falsePath = std::move(value); UpdatePreview(); } QtNodes::NodeValidationState ConditionalExpression::validationState() const { if (!m_truePath || !m_falsePath) return QtNodes::NodeValidationState::Error; return QtNodes::NodeValidationState::Valid; } QString ConditionalExpression::validationMessage() const { if (!m_currentOptionIndex) return "Invalid option"; if (!m_truePath || !m_falsePath) return "Missing input"; return QString(); } bool ConditionalExpression::ComputePreview(QPixmap& pixmap) { if (!m_currentOptionIndex) return false; auto input = outData(0); if (!input || input->type().id != VecData::Type().id) return false; assert(dynamic_cast(input.get()) != nullptr); const VecData& data = static_cast(*input); pixmap = QPixmap::fromImage(data.preview.GenerateImage()); return true; } void ConditionalExpression::OnOptionListUpdate() { m_currentOptionIndex.reset(); std::size_t optionIndex = 0; for (const auto& optionEntry : GetGraph().GetOptions()) { if (optionEntry.name == m_currentOptionText) { m_currentOptionIndex = optionIndex; break; } optionIndex++; } } void ConditionalExpression::UpdateOptionText() { if (m_currentOptionIndex) { auto& option = GetGraph().GetOption(*m_currentOptionIndex); m_currentOptionText = option.name; } else m_currentOptionText.clear(); }