// Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "shaderdata_p.h" #include "qshaderdata.h" #include "qshaderdata_p.h" #include #include #include #include #include #include QT_BEGIN_NAMESPACE namespace Qt3DRender { namespace Render { using namespace Qt3DCore; namespace { const int qNodeIdTypeId = qMetaTypeId(); } ShaderData::ShaderData() : m_managers(nullptr) { } ShaderData::~ShaderData() { } void ShaderData::setManagers(NodeManagers *managers) { m_managers = managers; } void ShaderData::syncFromFrontEnd(const QNode *frontEnd, bool firstTime) { const QShaderData *node = qobject_cast(frontEnd); if (!node) return; BackendNode::syncFromFrontEnd(frontEnd, firstTime); if (firstTime) { m_propertyReader = node->propertyReader(); m_blockNameToPropertyValues.clear(); const QMetaObject *metaObj = node->metaObject(); const int propertyOffset = QShaderData::staticMetaObject.propertyOffset(); const int propertyCount = metaObj->propertyCount(); // Dynamic properties names const auto dynamicPropertyNames = node->dynamicPropertyNames(); QList propertyNames; propertyNames.reserve(propertyCount - propertyOffset + dynamicPropertyNames.size()); // Statiically defined properties for (int i = propertyOffset; i < propertyCount; ++i) { const QMetaProperty pro = metaObj->property(i); if (pro.isWritable()) propertyNames.push_back(QString::fromLatin1(pro.name())); } // Dynamic properties for (const QByteArray &propertyName : dynamicPropertyNames) propertyNames.push_back(QString::fromLatin1(propertyName)); for (const QString &propertyName : propertyNames) { if (propertyName == QStringLiteral("data") || propertyName == QStringLiteral("objectName") || propertyName == QStringLiteral("childNodes")) // We don't handle default Node properties continue; const QVariant &propertyValue = m_propertyReader->readProperty(node->property(propertyName.toLatin1())); bool isNode = false; bool isTransformed = false; bool isArray = false; // We check if the property is a QNodeId isNode = (propertyValue.userType() == qNodeIdTypeId); // We check if QList if (propertyValue.userType() == QMetaType::QVariantList) { isArray = true; QVariantList list = propertyValue.value(); if (list.size() > 0 && list.at(0).userType() == qNodeIdTypeId) isNode = true; } // We check if property is a Transformed property QString transformedPropertyName; if (propertyValue.userType() == QMetaType::QVector3D) { // if there is a matching QShaderData::TransformType propertyTransformed transformedPropertyName = propertyName + QLatin1String("Transformed"); isTransformed = propertyNames.contains(transformedPropertyName); if (!isTransformed) transformedPropertyName.clear(); } m_originalProperties.insert(propertyName, { propertyValue, isNode, isArray, isTransformed, transformedPropertyName }); } BackendNode::markDirty(AbstractRenderer::ParameterDirty); } else { // Updates if (!m_propertyReader.isNull()) { auto it = m_originalProperties.begin(); const auto end = m_originalProperties.end(); while (it != end) { const QVariant newValue = m_propertyReader->readProperty(node->property(it.key().toLatin1())); PropertyValue &propValue = it.value(); if (propValue.value != newValue) { // Note we aren't notified about nested QShaderData in this call // only scalar / vec properties propValue.value = newValue; BackendNode::markDirty(AbstractRenderer::ParameterDirty); } ++it; } } } } const ShaderData::PropertyValuesForBlock &ShaderData::propertyValuesForBlock(int blockName) const { std::shared_lock readLocker(m_lock); return m_blockNameToPropertyValues.at(blockName); } void ShaderData::generatePropertyValuesForBlockIfNeeded(const QString &fullBlockName) { const int fullBlockNameId = StringToInt::lookupId(fullBlockName); std::unique_lock readWriteLocker(m_lock); const bool hasPropertyValuesForBlock = m_blockNameToPropertyValues.find(fullBlockNameId) != m_blockNameToPropertyValues.cend(); if (hasPropertyValuesForBlock) { return; } const QHash &props = properties(); ShaderData::PropertyValuesForBlock valueBlock; valueBlock.reserve(props.size()); auto it = props.cbegin(); const auto end = props.cend(); while (it != end) { QString propertyName = it.key(); // If we are dealing with a nested value, check if it is an an array if (it->isArray && !it->isNode) propertyName += QLatin1String("[0]"); QString fullPropertyName; fullPropertyName.reserve(fullBlockName.size() + 1 + it.key().size()); fullPropertyName.append(fullBlockName); fullPropertyName.append(QLatin1String(".")); fullPropertyName.append(propertyName); // We only do this for properties on root level valueBlock.push_back({ StringToInt::lookupId(fullPropertyName), StringToInt::lookupId(propertyName), it.operator ->() }); ++it; } m_blockNameToPropertyValues[StringToInt::lookupId(fullBlockName)] = std::move(valueBlock); } ShaderData *ShaderData::lookupResource(NodeManagers *managers, QNodeId id) { return managers->shaderDataManager()->lookupResource(id); } ShaderData *ShaderData::lookupResource(QNodeId id) { return ShaderData::lookupResource(m_managers, id); } // RenderCommand updater jobs QVariant ShaderData::getTransformedProperty(const PropertyValue *v, const Matrix4x4 &viewMatrix) const noexcept { // Note protecting m_worldMatrix at this point as we assume all world updates // have been performed when reaching this point if (v->isTransformed) { const auto transformedIt = m_originalProperties.constFind(v->transformedPropertyName); if (transformedIt != m_originalProperties.constEnd()) { const PropertyValue &transformedValue = transformedIt.value(); const TransformType transformType = static_cast(transformedValue.value.toInt()); switch (transformType) { case ModelToEye: return QVariant::fromValue(viewMatrix.map(m_worldMatrix.map(Vector3D(v->value.value())))); case ModelToWorld: return QVariant::fromValue(m_worldMatrix.map(Vector3D(v->value.value()))); case ModelToWorldDirection: return QVariant::fromValue(Vector3D(m_worldMatrix * Vector4D(v->value.value(), 0.0f))); case NoTransform: break; } } } return v->value; } // Unit tests only ShaderData::TransformType ShaderData::propertyTransformType(const QString &name) const { const auto it = m_originalProperties.constFind(name); if (it != m_originalProperties.constEnd()) { const PropertyValue &propertyValue = it.value(); if (propertyValue.isTransformed) { auto transformedIt = m_originalProperties.constFind(name + QLatin1String("Transformed")); if (transformedIt != m_originalProperties.end()) return static_cast(transformedIt.value().value.toInt()); } } return NoTransform; } // Called by FramePreparationJob or by RenderView when dealing with lights void ShaderData::updateWorldTransform(const Matrix4x4 &worldMatrix) { if (m_worldMatrix != worldMatrix) { m_worldMatrix = worldMatrix; } } RenderShaderDataFunctor::RenderShaderDataFunctor(AbstractRenderer *renderer, NodeManagers *managers) : m_managers(managers) , m_renderer(renderer) { } Qt3DCore::QBackendNode *RenderShaderDataFunctor::create(Qt3DCore::QNodeId id) const { ShaderData *backend = m_managers->shaderDataManager()->getOrCreateResource(id); backend->setManagers(m_managers); backend->setRenderer(m_renderer); return backend; } Qt3DCore::QBackendNode *RenderShaderDataFunctor::get(Qt3DCore::QNodeId id) const { return m_managers->shaderDataManager()->lookupResource(id); } void RenderShaderDataFunctor::destroy(Qt3DCore::QNodeId id) const { m_managers->shaderDataManager()->releaseResource(id); } } // namespace Render } // namespace Qt3DRender QT_END_NAMESPACE