diff options
Diffstat (limited to 'src')
33 files changed, 592 insertions, 169 deletions
diff --git a/src/animation/backend/animationutils.cpp b/src/animation/backend/animationutils.cpp index c876fcb87..17826e946 100644 --- a/src/animation/backend/animationutils.cpp +++ b/src/animation/backend/animationutils.cpp @@ -58,36 +58,6 @@ QT_BEGIN_NAMESPACE namespace Qt3DAnimation { namespace Animation { -int componentsForType(int type) -{ - int componentCount = 1; - switch (type) { - case QMetaType::Float: - case QVariant::Double: - componentCount = 1; - break; - - case QVariant::Vector2D: - componentCount = 2; - break; - - case QVariant::Vector3D: - case QVariant::Color: - componentCount = 3; - break; - - case QVariant::Vector4D: - case QVariant::Quaternion: - componentCount = 4; - break; - - default: - qWarning() << "Unhandled animation type"; - } - - return componentCount; -} - inline QVector<float> valueToVector(const QVector3D &value) { return { value.x(), value.y(), value.z() }; @@ -178,7 +148,10 @@ double phaseFromElapsedTime(double t_current_local, later be used as part of the format vector in the formatClipResults() function to remap the channels into the standard W, X, Y, Z order required by QQuaternion. */ -ComponentIndices channelComponentsToIndices(const Channel &channel, int dataType, int offset) +ComponentIndices channelComponentsToIndices(const Channel &channel, + int dataType, + int expectedComponentCount, + int offset) { #if defined Q_COMPILER_UNIFORM_INIT static const QVector<char> standardSuffixes = { 'X', 'Y', 'Z', 'W' }; @@ -192,20 +165,22 @@ ComponentIndices channelComponentsToIndices(const Channel &channel, int dataType switch (dataType) { case QVariant::Quaternion: - return channelComponentsToIndicesHelper(channel, dataType, offset, quaternionSuffixes); + return channelComponentsToIndicesHelper(channel, expectedComponentCount, + offset, quaternionSuffixes); case QVariant::Color: - return channelComponentsToIndicesHelper(channel, dataType, offset, colorSuffixes); + return channelComponentsToIndicesHelper(channel, expectedComponentCount, + offset, colorSuffixes); default: - return channelComponentsToIndicesHelper(channel, dataType, offset, standardSuffixes); + return channelComponentsToIndicesHelper(channel, expectedComponentCount, + offset, standardSuffixes); } } ComponentIndices channelComponentsToIndicesHelper(const Channel &channel, - int dataType, + int expectedComponentCount, int offset, const QVector<char> &suffixes) { - const int expectedComponentCount = componentsForType(dataType); const int actualComponentCount = channel.channelComponents.size(); if (actualComponentCount != expectedComponentCount) { qWarning() << "Data type expects" << expectedComponentCount @@ -299,10 +274,10 @@ ClipResults evaluateClipAtLocalTime(AnimationClip *clip, float localTime) if (qFuzzyIsNull(omega)) { // If the two keyframe quaternions are equal, just return the first one as the interpolated value. - channelResults[0] = lowerQuat.scalar(); - channelResults[1] = lowerQuat.x(); - channelResults[2] = lowerQuat.y(); - channelResults[3] = lowerQuat.z(); + channelResults[i++] = lowerQuat.scalar(); + channelResults[i++] = lowerQuat.x(); + channelResults[i++] = lowerQuat.y(); + channelResults[i++] = lowerQuat.z(); } else { for (const auto &channelComponent : qAsConst(channel.channelComponents)) channelResults[i++] = channelComponent.fcurve.evaluateAtTimeAsSlerp(localTime, lowerKeyframeBound, omega); @@ -329,30 +304,44 @@ ClipResults evaluateClipAtPhase(AnimationClip *clip, float phase) return evaluateClipAtLocalTime(clip, localTime); } +template<typename Container> +Container mapChannelResultsToContainer(const MappingData &mappingData, + const QVector<float> &channelResults) +{ + Container r; + r.reserve(channelResults.size()); + + const ComponentIndices channelIndices = mappingData.channelIndices; + for (const int channelIndex : channelIndices) + r.push_back(channelResults.at(channelIndex)); + + return r; +} + QVariant buildPropertyValue(const MappingData &mappingData, const QVector<float> &channelResults) { - QVariant v; + const int vectorOfFloatType = qMetaTypeId<QVector<float>>(); + + if (mappingData.type == vectorOfFloatType) + return QVariant::fromValue(channelResults); switch (mappingData.type) { case QMetaType::Float: case QVariant::Double: { - v = QVariant::fromValue(channelResults[mappingData.channelIndices[0]]); - break; + return QVariant::fromValue(channelResults[mappingData.channelIndices[0]]); } case QVariant::Vector2D: { const QVector2D vector(channelResults[mappingData.channelIndices[0]], channelResults[mappingData.channelIndices[1]]); - v = QVariant::fromValue(vector); - break; + return QVariant::fromValue(vector); } case QVariant::Vector3D: { const QVector3D vector(channelResults[mappingData.channelIndices[0]], channelResults[mappingData.channelIndices[1]], channelResults[mappingData.channelIndices[2]]); - v = QVariant::fromValue(vector); - break; + return QVariant::fromValue(vector); } case QVariant::Vector4D: { @@ -360,8 +349,7 @@ QVariant buildPropertyValue(const MappingData &mappingData, const QVector<float> channelResults[mappingData.channelIndices[1]], channelResults[mappingData.channelIndices[2]], channelResults[mappingData.channelIndices[3]]); - v = QVariant::fromValue(vector); - break; + return QVariant::fromValue(vector); } case QVariant::Quaternion: { @@ -370,24 +358,28 @@ QVariant buildPropertyValue(const MappingData &mappingData, const QVector<float> channelResults[mappingData.channelIndices[2]], channelResults[mappingData.channelIndices[3]]); q.normalize(); - v = QVariant::fromValue(q); - break; + return QVariant::fromValue(q); } case QVariant::Color: { - const QColor color = QColor::fromRgbF(channelResults[mappingData.channelIndices[0]], + const QColor color = + QColor::fromRgbF(channelResults[mappingData.channelIndices[0]], channelResults[mappingData.channelIndices[1]], channelResults[mappingData.channelIndices[2]]); - v = QVariant::fromValue(color); - break; + return QVariant::fromValue(color); } + case QVariant::List: { + const QVariantList results = mapChannelResultsToContainer<QVariantList>( + mappingData, channelResults); + return QVariant::fromValue(results); + } default: qWarning() << "Unhandled animation type" << mappingData.type; break; } - return v; + return QVariant(); } QVector<Qt3DCore::QSceneChangePtr> preparePropertyChanges(Qt3DCore::QNodeId animatorId, @@ -540,7 +532,11 @@ QVector<MappingData> buildPropertyMappings(const QVector<ChannelMapping*> &chann } // Try to find matching channel name and type - const ChannelNameAndType nameAndType = { mapping->channelName(), mapping->type(), mapping->peerId() }; + const ChannelNameAndType nameAndType = { mapping->channelName(), + mapping->type(), + mapping->componentCount(), + mapping->peerId() + }; const int index = channelNamesAndTypes.indexOf(nameAndType); if (index != -1) { // Do we have any animation data for this channel? If not, don't bother @@ -645,7 +641,10 @@ QVector<ChannelNameAndType> buildRequiredChannelsAndTypes(Handler *handler, case ChannelMapping::ChannelMappingType: case ChannelMapping::CallbackMappingType: { // Get the name and type - const ChannelNameAndType nameAndType{ mapping->channelName(), mapping->type(), mappingId }; + const ChannelNameAndType nameAndType{ mapping->channelName(), + mapping->type(), + mapping->componentCount(), + mappingId }; // Add if not already contained if (!namesAndTypes.contains(nameAndType)) @@ -694,7 +693,7 @@ QVector<ComponentIndices> assignChannelComponentIndices(const QVector<ChannelNam int baseIndex = 0; for (const auto &entry : namesAndTypes) { // Populate indices in order - const int componentCount = componentsForType(entry.type); + const int componentCount = entry.componentCount; ComponentIndices indices(componentCount); std::iota(indices.begin(), indices.end(), baseIndex); @@ -780,6 +779,7 @@ ClipFormat generateClipFormatIndices(const QVector<ChannelNameAndType> &targetCh const int baseIndex = clip->channelComponentBaseIndex(clipChannelIndex); const auto channelIndices = channelComponentsToIndices(clip->channels()[clipChannelIndex], targetChannel.type, + targetChannel.componentCount, baseIndex); std::copy(channelIndices.begin(), channelIndices.end(), formatIt); @@ -909,7 +909,7 @@ QVector<float> defaultValueForChannel(Handler *handler, } // Everything else gets all zeros - const int componentCount = componentsForType(channelDescription.type); + const int componentCount = mapping->componentCount(); result = QVector<float>(componentCount, 0.0f); break; } diff --git a/src/animation/backend/animationutils_p.h b/src/animation/backend/animationutils_p.h index 42402c5ec..bded12bd2 100644 --- a/src/animation/backend/animationutils_p.h +++ b/src/animation/backend/animationutils_p.h @@ -134,7 +134,7 @@ struct ChannelNameAndType int jointIndex; Qt3DCore::QNodeId mappingId; JointTransformComponent jointTransformComponent; - float pad; // Unused + int componentCount; static const int invalidIndex = -1; @@ -145,11 +145,12 @@ struct ChannelNameAndType , jointIndex(-1) , mappingId() , jointTransformComponent(NoTransformComponent) - , pad(0) + , componentCount(-1) {} ChannelNameAndType(const QString &_name, int _type, + int componentCount, Qt3DCore::QNodeId _mappingId = Qt3DCore::QNodeId(), int _jointIndex = invalidIndex) : jointName() @@ -158,7 +159,7 @@ struct ChannelNameAndType , jointIndex(_jointIndex) , mappingId(_mappingId) , jointTransformComponent(NoTransformComponent) - , pad(0) + , componentCount(componentCount) {} ChannelNameAndType(const QString &_name, @@ -170,8 +171,20 @@ struct ChannelNameAndType , jointIndex(invalidIndex) , mappingId() , jointTransformComponent(_jointTransformComponent) - , pad(0) - {} + , componentCount(-1) + { + switch (_jointTransformComponent) { + case NoTransformComponent: + break; + case Scale: + case Translation: + componentCount = 3; + break; + case Rotation: + componentCount = 4; + break; + }; + } bool operator==(const ChannelNameAndType &rhs) const { @@ -179,7 +192,8 @@ struct ChannelNameAndType && type == rhs.type && jointIndex == rhs.jointIndex && mappingId == rhs.mappingId - && jointTransformComponent == rhs.jointTransformComponent; + && jointTransformComponent == rhs.jointTransformComponent + && componentCount == rhs.componentCount; } }; @@ -192,7 +206,8 @@ inline QDebug operator<<(QDebug dbg, const ChannelNameAndType &nameAndType) << "mappingId =" << nameAndType.mappingId << "jointIndex =" << nameAndType.jointIndex << "jointName =" << nameAndType.jointName - << "jointTransformComponent =" << nameAndType.jointTransformComponent; + << "jointTransformComponent =" << nameAndType.jointTransformComponent + << "componentCount =" << nameAndType.componentCount; return dbg; } #endif @@ -289,20 +304,18 @@ inline bool isValidNormalizedTime(float t) } Q_AUTOTEST_EXPORT -int componentsForType(int type); - -Q_AUTOTEST_EXPORT ClipEvaluationData evaluationDataForClip(AnimationClip *clip, const AnimatorEvaluationData &animatorData); Q_AUTOTEST_EXPORT ComponentIndices channelComponentsToIndices(const Channel &channel, int dataType, - int offset = 0); + int expectedComponentCount, + int offset); Q_AUTOTEST_EXPORT ComponentIndices channelComponentsToIndicesHelper(const Channel &channelGroup, - int dataType, + int expectedComponentCount, int offset, const QVector<char> &suffixes); diff --git a/src/animation/backend/channelmapping.cpp b/src/animation/backend/channelmapping.cpp index ecae8bbae..d8572a074 100644 --- a/src/animation/backend/channelmapping.cpp +++ b/src/animation/backend/channelmapping.cpp @@ -55,6 +55,7 @@ ChannelMapping::ChannelMapping() , m_targetId() , m_property() , m_type(static_cast<int>(QVariant::Invalid)) + , m_componentCount(0) , m_propertyName(nullptr) , m_callback(nullptr) , m_callbackFlags(0) @@ -74,6 +75,7 @@ void ChannelMapping::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePt m_targetId = data.targetId; m_property = data.property; m_type = data.type; + m_componentCount = data.componentCount; m_propertyName = data.propertyName; m_mappingType = ChannelMappingType; break; @@ -108,6 +110,7 @@ void ChannelMapping::cleanup() m_property.clear(); m_type = static_cast<int>(QVariant::Invalid); m_propertyName = nullptr; + m_componentCount = 0; m_callback = nullptr; m_callbackFlags = 0; m_skeletonId = Qt3DCore::QNodeId(); @@ -128,6 +131,8 @@ void ChannelMapping::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) m_type = change->value().toInt(); else if (change->propertyName() == QByteArrayLiteral("propertyName")) m_propertyName = static_cast<const char *>(const_cast<const void *>(change->value().value<void *>())); + else if (change->propertyName() == QByteArrayLiteral("componentCount")) + m_componentCount = change->value().toInt(); else if (change->propertyName() == QByteArrayLiteral("callback")) m_callback = static_cast<QAnimationCallback *>(change->value().value<void *>()); else if (change->propertyName() == QByteArrayLiteral("callbackFlags")) diff --git a/src/animation/backend/channelmapping_p.h b/src/animation/backend/channelmapping_p.h index 82de5c7b4..5159adae2 100644 --- a/src/animation/backend/channelmapping_p.h +++ b/src/animation/backend/channelmapping_p.h @@ -93,6 +93,9 @@ public: void setPropertyName(const char *propertyName) { m_propertyName = propertyName; } const char *propertyName() const { return m_propertyName; } + void setComponentCount(int componentCount) { m_componentCount = componentCount; } + int componentCount() const { return m_componentCount; } + void setCallback(QAnimationCallback *callback) { m_callback = callback; } QAnimationCallback *callback() const { return m_callback; } @@ -114,6 +117,7 @@ private: Qt3DCore::QNodeId m_targetId; QString m_property; int m_type; + int m_componentCount; const char *m_propertyName; // TODO: Properties from QCallbackMapping diff --git a/src/animation/frontend/qabstractchannelmapping_p.h b/src/animation/frontend/qabstractchannelmapping_p.h index 1a1de4ba9..2d77ee265 100644 --- a/src/animation/frontend/qabstractchannelmapping_p.h +++ b/src/animation/frontend/qabstractchannelmapping_p.h @@ -60,7 +60,7 @@ namespace Qt3DAnimation { class QAbstractChannelMapping; -class QAbstractChannelMappingPrivate : public Qt3DCore::QNodePrivate +class Q_AUTOTEST_EXPORT QAbstractChannelMappingPrivate : public Qt3DCore::QNodePrivate { public: QAbstractChannelMappingPrivate(); diff --git a/src/animation/frontend/qchannelmapping.cpp b/src/animation/frontend/qchannelmapping.cpp index 0d9150c50..0dbe68c8c 100644 --- a/src/animation/frontend/qchannelmapping.cpp +++ b/src/animation/frontend/qchannelmapping.cpp @@ -47,6 +47,61 @@ QT_BEGIN_NAMESPACE namespace Qt3DAnimation { +namespace { + +template<typename T> +int componentCountForValue(const T &) +{ + return 0; +} + +template<> +int componentCountForValue<QVector<float>>(const QVector<float> &v) +{ + return v.size(); +} + +template<> +int componentCountForValue<QVariantList>(const QVariantList &v) +{ + return v.size(); +} + + +int componentCountForType(int type, const QVariant &value) +{ + const int vectorOfFloatTypeId = qMetaTypeId<QVector<float>>(); + + if (type == vectorOfFloatTypeId) + return componentCountForValue<QVector<float>>(value.value<QVector<float>>()); + + switch (type) { + case QMetaType::Float: + case QVariant::Double: + return 1; + + case QVariant::Vector2D: + return 2; + + case QVariant::Vector3D: + case QVariant::Color: + return 3; + + case QVariant::Vector4D: + case QVariant::Quaternion: + return 4; + + case QVariant::List: + return componentCountForValue<QVariantList>(value.toList()); + + default: + qWarning() << "Unhandled animation type"; + return 0; + } +} + +} // anonymous + QChannelMappingPrivate::QChannelMappingPrivate() : QAbstractChannelMappingPrivate() , m_channelName() @@ -54,6 +109,7 @@ QChannelMappingPrivate::QChannelMappingPrivate() , m_property() , m_propertyName(nullptr) , m_type(static_cast<int>(QVariant::Invalid)) + , m_componentCount(0) { m_mappingType = QChannelMappingCreatedChangeBase::ChannelMapping; } @@ -63,9 +119,10 @@ QChannelMappingPrivate::QChannelMappingPrivate() Find the type of the property specified on the target node */ -void QChannelMappingPrivate::updatePropertyNameAndType() +void QChannelMappingPrivate::updatePropertyNameTypeAndComponentCount() { int type; + int componentCount = 0; const char *propertyName = nullptr; if (!m_target || m_property.isNull()) { @@ -76,8 +133,8 @@ void QChannelMappingPrivate::updatePropertyNameAndType() QMetaProperty mp = mo->property(propertyIndex); propertyName = mp.name(); type = mp.userType(); + const QVariant currentValue = m_target->property(mp.name()); if (type == QMetaType::QVariant) { - QVariant currentValue = m_target->property(mp.name()); if (currentValue.isValid()) { type = currentValue.userType(); } else { @@ -85,6 +142,7 @@ void QChannelMappingPrivate::updatePropertyNameAndType() "Set a value first in order to be able to determine the type."); } } + componentCount = componentCountForType(type, currentValue); } if (m_type != type) { @@ -98,6 +156,17 @@ void QChannelMappingPrivate::updatePropertyNameAndType() notifyObservers(e); } + if (m_componentCount != componentCount) { + m_componentCount = componentCount; + + // Send update to the backend + Q_Q(QChannelMapping); + auto e = Qt3DCore::QPropertyUpdatedChangePtr::create(q->id()); + e->setPropertyName("componentCount"); + e->setValue(QVariant(m_componentCount)); + notifyObservers(e); + } + if (qstrcmp(m_propertyName, propertyName) != 0) { m_propertyName = propertyName; @@ -178,7 +247,7 @@ void QChannelMapping::setTarget(Qt3DCore::QNode *target) d->registerDestructionHelper(d->m_target, &QChannelMapping::setTarget, d->m_target); emit targetChanged(target); - d->updatePropertyNameAndType(); + d->updatePropertyNameTypeAndComponentCount(); } void QChannelMapping::setProperty(const QString &property) @@ -189,7 +258,7 @@ void QChannelMapping::setProperty(const QString &property) d->m_property = property; emit propertyChanged(property); - d->updatePropertyNameAndType(); + d->updatePropertyNameTypeAndComponentCount(); } Qt3DCore::QNodeCreatedChangeBasePtr QChannelMapping::createNodeCreationChange() const @@ -201,6 +270,7 @@ Qt3DCore::QNodeCreatedChangeBasePtr QChannelMapping::createNodeCreationChange() data.targetId = Qt3DCore::qIdForNode(d->m_target); data.property = d->m_property; data.type = d->m_type; + data.componentCount = d->m_componentCount; data.propertyName = d->m_propertyName; return creationChange; } diff --git a/src/animation/frontend/qchannelmapping_p.h b/src/animation/frontend/qchannelmapping_p.h index b7de3b821..6a3f1afc5 100644 --- a/src/animation/frontend/qchannelmapping_p.h +++ b/src/animation/frontend/qchannelmapping_p.h @@ -50,25 +50,27 @@ #include <Qt3DAnimation/private/qabstractchannelmapping_p.h> #include <Qt3DAnimation/qanimationcallback.h> +#include <QVector> QT_BEGIN_NAMESPACE namespace Qt3DAnimation { -class QChannelMappingPrivate : public QAbstractChannelMappingPrivate +class Q_AUTOTEST_EXPORT QChannelMappingPrivate : public QAbstractChannelMappingPrivate { public: QChannelMappingPrivate(); Q_DECLARE_PUBLIC(QChannelMapping) - void updatePropertyNameAndType(); + void updatePropertyNameTypeAndComponentCount(); QString m_channelName; Qt3DCore::QNode *m_target; QString m_property; const char *m_propertyName; int m_type; + int m_componentCount; }; struct QChannelMappingData @@ -77,6 +79,7 @@ struct QChannelMappingData Qt3DCore::QNodeId targetId; QString property; int type; + int componentCount; const char *propertyName; }; @@ -85,4 +88,7 @@ struct QChannelMappingData QT_END_NAMESPACE +// Used to define the meta type id +Q_DECLARE_METATYPE(QVector<float>) // LCOV_EXCL_LINE + #endif // QT3DANIMATION_QCHANNELMAPPING_P_H diff --git a/src/core/changes/qpropertynodeaddedchange.cpp b/src/core/changes/qpropertynodeaddedchange.cpp index 865de2731..347d7f188 100644 --- a/src/core/changes/qpropertynodeaddedchange.cpp +++ b/src/core/changes/qpropertynodeaddedchange.cpp @@ -85,7 +85,7 @@ QPropertyNodeAddedChange::QPropertyNodeAddedChange(QNodeId subjectId, QNode *nod // loop will still be blocked. So force it here and we catch this // eventuality in the _q_postConstructorInit() function so that we // do not repeat the creation and new child scene change events. - QNodePrivate::get(node)->_q_postConstructorInit(); + QNodePrivate::get(node)->_q_ensureBackendNodeCreated(); } /*! \internal */ diff --git a/src/core/configure.json b/src/core/configure.json index e72c5ed2b..2ba205530 100644 --- a/src/core/configure.json +++ b/src/core/configure.json @@ -1,5 +1,9 @@ { "module": "3dcore", + "depends": [ + "gui" + ], + "condition": "module.gui && features.opengl && !config.wasm", "testDir": "../../config.tests", "commandline": { diff --git a/src/core/nodes/qentity.cpp b/src/core/nodes/qentity.cpp index 69d774dd1..164e0feff 100644 --- a/src/core/nodes/qentity.cpp +++ b/src/core/nodes/qentity.cpp @@ -239,6 +239,10 @@ QNodeId QEntityPrivate::parentEntityId() const QNodeCreatedChangeBasePtr QEntity::createNodeCreationChange() const { + // connect to the parentChanged signal here rather than constructor because + // until now there's no backend node to notify when parent changes + connect(this, &QNode::parentChanged, this, &QEntity::onParentChanged); + auto creationChange = QNodeCreatedChangePtr<QEntityData>::create(this); auto &data = creationChange->data; @@ -268,6 +272,17 @@ QNodeCreatedChangeBasePtr QEntity::createNodeCreationChange() const return creationChange; } +void QEntity::onParentChanged(QObject *) +{ + const auto parentID = parentEntity() ? parentEntity()->id() : Qt3DCore::QNodeId(); + auto parentChange = Qt3DCore::QPropertyUpdatedChangePtr::create(id()); + parentChange->setPropertyName("parentEntityUpdated"); + parentChange->setValue(QVariant::fromValue(parentID)); + const bool blocked = blockNotifications(false); + notifyObservers(parentChange); + blockNotifications(blocked); +} + } // namespace Qt3DCore QT_END_NAMESPACE diff --git a/src/core/nodes/qentity.h b/src/core/nodes/qentity.h index bbf38efb4..f1a369c48 100644 --- a/src/core/nodes/qentity.h +++ b/src/core/nodes/qentity.h @@ -83,6 +83,9 @@ public: protected: explicit QEntity(QEntityPrivate &dd, QNode *parent = nullptr); +private Q_SLOTS: + void onParentChanged(QObject *); + private: Q_DECLARE_PRIVATE(QEntity) diff --git a/src/core/nodes/qnode.cpp b/src/core/nodes/qnode.cpp index c2373b805..900c3f8ce 100644 --- a/src/core/nodes/qnode.cpp +++ b/src/core/nodes/qnode.cpp @@ -101,7 +101,7 @@ void QNodePrivate::init(QNode *parent) Q_Q(QNode); if (m_scene) { // schedule the backend notification and scene registering -> set observers through scene - QMetaObject::invokeMethod(q, "_q_postConstructorInit", Qt::QueuedConnection); + m_scene->postConstructorInit()->addNode(q); } } @@ -165,7 +165,7 @@ void QNodePrivate::notifyDestructionChangesAndRemoveFromScene() * * Sends a QNodeCreatedChange event to the aspects and then also notifies the * parent backend node of its new child. This is called in a deferred manner - * by the QNodePrivate::init() method to notify the backend of newly created + * by NodePostConstructorInit::processNodes to notify the backend of newly created * nodes with a parent that is already part of the scene. * * Also notify the scene of this node, so it may set it's change arbiter. @@ -873,6 +873,12 @@ void QNode::setParent(QNode *parent) if (parentNode() == parent && ((parent != nullptr && d->m_parentId == parentNode()->id()) || parent == nullptr)) return; + + // remove ourself from postConstructorInit queue. The call to _q_setParentHelper + // will take care of creating the backend node if necessary depending on new parent. + if (d->m_scene) + d->m_scene->postConstructorInit()->removeNode(this); + d->_q_setParentHelper(parent); // Block notifications as we want to let the _q_setParentHelper @@ -1132,6 +1138,75 @@ const QMetaObject *QNodePrivate::findStaticMetaObject(const QMetaObject *metaObj return lastStaticMetaobject; } +/*! + * \internal + * + * NodePostConstructorInit handles calling QNode::_q_postConstructorInit for + * all nodes. By keeping track of nodes that need initialization we can + * create them all together ensuring they get sent to the backend in a single + * batch. + */ +NodePostConstructorInit::NodePostConstructorInit(QObject *parent) + : QObject(parent) + , m_requestedProcessing(false) +{ +} + +NodePostConstructorInit::~NodePostConstructorInit() {} + +/*! + * \internal + * + * Add a node to the list of nodes needing a call to _q_postConstructorInit + * We only add the node if it does not have an ancestor already in the queue + * because initializing the ancestor will initialize all it's children. + * This ensures that all backend nodes are created from the top-down, with + * all parents created before their children + * + */ +void NodePostConstructorInit::addNode(QNode *node) +{ + Q_ASSERT(node); + QNode *nextNode = node; + while (nextNode != nullptr && !m_nodesToConstruct.contains(QNodePrivate::get(nextNode))) + nextNode = nextNode->parentNode(); + + if (!nextNode) { + m_nodesToConstruct.append(QNodePrivate::get(node)); + if (!m_requestedProcessing){ + QMetaObject::invokeMethod(this, "processNodes", Qt::QueuedConnection); + m_requestedProcessing = true; + } + } +} + +/*! + * \internal + * + * Remove a node from the queue. This will ensure none of its + * children get initialized + */ +void NodePostConstructorInit::removeNode(QNode *node) +{ + Q_ASSERT(node); + m_nodesToConstruct.removeAll(QNodePrivate::get(node)); +} + +/*! + * \internal + * + * call _q_postConstructorInit for all nodes in the queue + * and clear the queue + */ +void NodePostConstructorInit::processNodes() +{ + m_requestedProcessing = false; + while (!m_nodesToConstruct.empty()) { + auto node = m_nodesToConstruct.takeFirst(); + node->_q_postConstructorInit(); + } +} + } // namespace Qt3DCore QT_END_NAMESPACE diff --git a/src/core/nodes/qnode_p.h b/src/core/nodes/qnode_p.h index fd3265870..506708762 100644 --- a/src/core/nodes/qnode_p.h +++ b/src/core/nodes/qnode_p.h @@ -60,6 +60,7 @@ #include <Qt3DCore/private/qobservableinterface_p.h> #include <Qt3DCore/private/qt3dcore_global_p.h> #include <QtCore/private/qobject_p.h> +#include <QQueue> QT_BEGIN_NAMESPACE @@ -174,6 +175,23 @@ private: QHash<QNode *, QMetaObject::Connection> m_destructionConnections; }; +class NodePostConstructorInit : public QObject +{ + Q_OBJECT +public: + NodePostConstructorInit(QObject *parent = nullptr); + virtual ~NodePostConstructorInit(); + void removeNode(QNode *node); + void addNode(QNode *node); + +private Q_SLOTS: + void processNodes(); + +private: + QQueue<QNodePrivate *> m_nodesToConstruct; + bool m_requestedProcessing; +}; + } // namespace Qt3DCore QT_END_NAMESPACE diff --git a/src/core/qscene.cpp b/src/core/qscene.cpp index b5895c7aa..c94272e90 100644 --- a/src/core/qscene.cpp +++ b/src/core/qscene.cpp @@ -57,6 +57,7 @@ public: QScenePrivate(QAspectEngine *engine) : m_engine(engine) , m_arbiter(nullptr) + , m_postConstructorInit(new NodePostConstructorInit) , m_rootNode(nullptr) { } @@ -68,6 +69,7 @@ public: QHash<QObservableInterface *, QNodeId> m_observableToUuid; QHash<QNodeId, QScene::NodePropertyTrackData> m_nodePropertyTrackModeLookupTable; QLockableObserverInterface *m_arbiter; + QScopedPointer<NodePostConstructorInit> m_postConstructorInit; mutable QReadWriteLock m_lock; mutable QReadWriteLock m_nodePropertyTrackModeLock; QNode *m_rootNode; @@ -247,6 +249,12 @@ void QScene::removePropertyTrackDataForNode(QNodeId nodeId) d->m_nodePropertyTrackModeLookupTable.remove(nodeId); } +NodePostConstructorInit *QScene::postConstructorInit() const +{ + Q_D(const QScene); + return d->m_postConstructorInit.get(); +} + void QScene::setRootNode(QNode *root) { Q_D(QScene); diff --git a/src/core/qscene_p.h b/src/core/qscene_p.h index 379316247..afcfb9b40 100644 --- a/src/core/qscene_p.h +++ b/src/core/qscene_p.h @@ -64,6 +64,7 @@ namespace Qt3DCore { class QScenePrivate; class QAspectEngine; +class NodePostConstructorInit; typedef QList<QObservableInterface *> QObservableList; @@ -106,6 +107,8 @@ public: void setPropertyTrackDataForNode(QNodeId id, const NodePropertyTrackData &data); void removePropertyTrackDataForNode(QNodeId id); + NodePostConstructorInit* postConstructorInit() const; + private: Q_DECLARE_PRIVATE(QScene) QScopedPointer<QScenePrivate> d_ptr; diff --git a/src/quick3d/quick3d/items/quick3dentityloader.cpp b/src/quick3d/quick3d/items/quick3dentityloader.cpp index bdf310d02..d17492b68 100644 --- a/src/quick3d/quick3d/items/quick3dentityloader.cpp +++ b/src/quick3d/quick3d/items/quick3dentityloader.cpp @@ -309,9 +309,6 @@ void Quick3DEntityLoaderPrivate::_q_componentStatusChanged(QQmlComponent::Status Q_ASSERT(m_context == nullptr); Q_ASSERT(m_incubator == nullptr); - - qDebug() << Q_FUNC_INFO << status; - if (!m_component) { clear(); emit q->entityChanged(); diff --git a/src/render/backend/abstractrenderer_p.h b/src/render/backend/abstractrenderer_p.h index f9c8e29eb..8aa994eb2 100644 --- a/src/render/backend/abstractrenderer_p.h +++ b/src/render/backend/abstractrenderer_p.h @@ -114,7 +114,8 @@ public: JointDirty = 1 << 11, LayersDirty = 1 << 12, TechniquesDirty = 1 << 13, - LightsDirty = 1 << 14, + EntityHierarchyDirty= 1 << 14, + LightsDirty = 1 << 15, AllDirty = 0xffffff }; Q_DECLARE_FLAGS(BackendNodeDirtySet, BackendNodeDirtyFlag) diff --git a/src/render/backend/entity.cpp b/src/render/backend/entity.cpp index 8d03cd75f..d8d04aef1 100644 --- a/src/render/backend/entity.cpp +++ b/src/render/backend/entity.cpp @@ -65,8 +65,6 @@ #include <Qt3DCore/qpropertyupdatedchange.h> #include <Qt3DCore/qtransform.h> #include <Qt3DCore/private/qentity_p.h> -#include <Qt3DCore/qpropertynoderemovedchange.h> -#include <Qt3DCore/qpropertynodeaddedchange.h> #include <Qt3DCore/qnodecreatedchange.h> #include <QMatrix4x4> @@ -95,14 +93,13 @@ Entity::~Entity() void Entity::cleanup() { if (m_nodeManagers != nullptr) { - Entity *parentEntity = parent(); - if (parentEntity != nullptr) - parentEntity->removeChildHandle(m_handle); m_nodeManagers->worldMatrixManager()->releaseResource(peerId()); - qCDebug(Render::RenderNodes) << Q_FUNC_INFO; - } + if (!m_parentEntityId.isNull()) + markDirty(AbstractRenderer::EntityHierarchyDirty); + + m_parentEntityId = Qt3DCore::QNodeId(); m_worldTransform = HMatrix(); // Release all component will have to perform their own release when they receive the // NodeDeleted notification @@ -132,12 +129,8 @@ void Entity::cleanup() void Entity::setParentHandle(HEntity parentHandle) { Q_ASSERT(m_nodeManagers); - // Remove ourselves from previous parent children list - Entity *parent = m_nodeManagers->renderNodesManager()->data(parentHandle); - if (parent != nullptr && parent->m_childrenHandles.contains(m_handle)) - parent->m_childrenHandles.removeAll(m_handle); m_parentHandle = parentHandle; - parent = m_nodeManagers->renderNodesManager()->data(parentHandle); + auto parent = m_nodeManagers->renderNodesManager()->data(parentHandle); if (parent != nullptr && !parent->m_childrenHandles.contains(m_handle)) parent->m_childrenHandles.append(m_handle); } @@ -159,8 +152,8 @@ void Entity::initializeFromPeer(const QNodeCreatedChangeBasePtr &change) // Note this is *not* the parentId as that is the ID of the parent QNode, which is not // necessarily the same as the parent QEntity (which may be further up the tree). - const QNodeId parentEntityId = data.parentEntityId; - qCDebug(Render::RenderNodes) << "Creating Entity id =" << peerId() << "parentId =" << parentEntityId; + m_parentEntityId = data.parentEntityId; + qCDebug(Render::RenderNodes) << "Creating Entity id =" << peerId() << "parentId =" << m_parentEntityId; // TODO: Store string id instead and only in debug mode //m_objectName = peer->objectName(); @@ -187,10 +180,7 @@ void Entity::initializeFromPeer(const QNodeCreatedChangeBasePtr &change) for (const auto &idAndType : qAsConst(data.componentIdsAndTypes)) addComponent(idAndType); - if (!parentEntityId.isNull()) - setParentHandle(m_nodeManagers->renderNodesManager()->lookupHandle(parentEntityId)); - else - qCDebug(Render::RenderNodes) << Q_FUNC_INFO << "No parent entity found for Entity" << peerId(); + markDirty(AbstractRenderer::EntityHierarchyDirty); } void Entity::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) @@ -214,30 +204,21 @@ void Entity::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) break; } - case PropertyValueAdded: { - QPropertyNodeAddedChangePtr change = qSharedPointerCast<QPropertyNodeAddedChange>(e); - if (change->metaObject()->inherits(&QEntity::staticMetaObject)) { - appendChildHandle(m_nodeManagers->renderNodesManager()->lookupHandle(change->addedNodeId())); - markDirty(AbstractRenderer::AllDirty); - } - break; - } - - case PropertyValueRemoved: { - QPropertyNodeRemovedChangePtr change = qSharedPointerCast<QPropertyNodeRemovedChange>(e); - if (change->metaObject()->inherits(&QEntity::staticMetaObject)) { - removeChildHandle(m_nodeManagers->renderNodesManager()->lookupHandle(change->removedNodeId())); - markDirty(AbstractRenderer::AllDirty); - } - break; - } - case PropertyUpdated: { QPropertyUpdatedChangePtr change = qSharedPointerCast<QPropertyUpdatedChange>(e); if (change->propertyName() == QByteArrayLiteral("enabled")) { // We only mark as dirty the renderer markDirty(AbstractRenderer::EntityEnabledDirty); // We let QBackendNode::sceneChangeEvent change the enabled property + } else if (change->propertyName() == QByteArrayLiteral("parentEntityUpdated")) { + auto newParent = change->value().value<Qt3DCore::QNodeId>(); + qCDebug(Render::RenderNodes) << "Setting parent for " << peerId() << ", new parentId =" << newParent; + if (m_parentEntityId != newParent) { + m_parentEntityId = newParent; + // TODO: change to EventHierarchyDirty and update renderer to + // ensure all jobs are run that depend on Entity hierarchy. + markDirty(AbstractRenderer::AllDirty); + } } break; @@ -265,6 +246,27 @@ Entity *Entity::parent() const return m_nodeManagers->renderNodesManager()->data(m_parentHandle); } + +// clearEntityHierarchy and rebuildEntityHierarchy should only be called +// from UpdateEntityHierarchyJob to update the entity hierarchy for the +// entire scene at once +void Entity::clearEntityHierarchy() +{ + m_childrenHandles.clear(); + m_parentHandle = HEntity(); +} + +// clearEntityHierarchy and rebuildEntityHierarchy should only be called +// from UpdateEntityHierarchyJob to update the entity hierarchy for the +// entire scene at once +void Entity::rebuildEntityHierarchy() +{ + if (!m_parentEntityId.isNull()) + setParentHandle(m_nodeManagers->renderNodesManager()->lookupHandle(m_parentEntityId)); + else + qCDebug(Render::RenderNodes) << Q_FUNC_INFO << "No parent entity found for Entity" << peerId(); +} + void Entity::appendChildHandle(HEntity childHandle) { if (!m_childrenHandles.contains(childHandle)) { diff --git a/src/render/backend/entity_p.h b/src/render/backend/entity_p.h index 149ac1143..c170fd42d 100644 --- a/src/render/backend/entity_p.h +++ b/src/render/backend/entity_p.h @@ -97,6 +97,10 @@ public: HEntity handle() const { return m_handle; } Entity *parent() const; HEntity parentHandle() const { return m_parentHandle; } + Qt3DCore::QNodeId parentEntityId() const { return m_parentEntityId; } + + void clearEntityHierarchy(); + void rebuildEntityHierarchy(); void appendChildHandle(HEntity childHandle); void removeChildHandle(HEntity childHandle) { m_childrenHandles.removeOne(childHandle); } @@ -181,6 +185,8 @@ private: HEntity m_parentHandle; QVector<HEntity > m_childrenHandles; + Qt3DCore::QNodeId m_parentEntityId; + HMatrix m_worldTransform; QSharedPointer<Sphere> m_localBoundingVolume; QSharedPointer<Sphere> m_worldBoundingVolume; diff --git a/src/render/backend/managers.cpp b/src/render/backend/managers.cpp index 3b1f8e910..26fd68600 100644 --- a/src/render/backend/managers.cpp +++ b/src/render/backend/managers.cpp @@ -72,7 +72,11 @@ FrameGraphNode* FrameGraphManager::lookupNode(Qt3DCore::QNodeId id) const void FrameGraphManager::releaseNode(Qt3DCore::QNodeId id) { - delete m_nodes.take(id); + auto node = m_nodes.take(id); + if (node) { + node->cleanup(); + delete node; + } } void SkeletonManager::addDirtySkeleton(DirtyFlag dirtyFlag, HSkeleton skeletonHandle) diff --git a/src/render/configure.json b/src/render/configure.json index ee5002707..02a6e3747 100644 --- a/src/render/configure.json +++ b/src/render/configure.json @@ -1,5 +1,9 @@ { "module": "3drender", + "depends": [ + "3dcore" + ], + "condition": "module.3dcore", "testDir": "./config.tests", "commandline": { diff --git a/src/render/framegraph/framegraphnode.cpp b/src/render/framegraph/framegraphnode.cpp index 562548e46..41a94f0e3 100644 --- a/src/render/framegraph/framegraphnode.cpp +++ b/src/render/framegraph/framegraphnode.cpp @@ -40,9 +40,8 @@ #include "framegraphnode_p.h" #include <Qt3DRender/private/renderer_p.h> #include <Qt3DRender/private/nodemanagers_p.h> -#include <Qt3DCore/qpropertynoderemovedchange.h> -#include <Qt3DCore/qpropertynodeaddedchange.h> #include <Qt3DRender/qframegraphnodecreatedchange.h> +#include <Qt3DCore/qpropertyupdatedchange.h> QT_BEGIN_NAMESPACE @@ -101,28 +100,6 @@ void FrameGraphNode::setParentId(Qt3DCore::QNodeId parentId) } } -void FrameGraphNode::appendChildId(Qt3DCore::QNodeId childId) -{ - if (!m_childrenIds.contains(childId)) { - FrameGraphNode *child = m_manager->lookupNode(childId); - if (child != nullptr) { - m_childrenIds.append(childId); - child->m_parentId = peerId(); - } - } -} - -void FrameGraphNode::removeChildId(Qt3DCore::QNodeId childId) -{ - if (m_childrenIds.contains(childId)) { - FrameGraphNode *child = m_manager->lookupNode(childId); - if (child != nullptr) { - child->m_parentId = Qt3DCore::QNodeId(); - } - m_childrenIds.removeAll(childId); - } -} - Qt3DCore::QNodeId FrameGraphNode::parentId() const { return m_parentId; @@ -155,28 +132,15 @@ void FrameGraphNode::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) { switch (e->type()) { - case Qt3DCore::PropertyValueAdded: { - Qt3DCore::QPropertyNodeAddedChangePtr change = qSharedPointerCast<Qt3DCore::QPropertyNodeAddedChange>(e); - if (change->metaObject()->inherits(&QFrameGraphNode::staticMetaObject)) { - appendChildId(change->addedNodeId()); - markDirty(AbstractRenderer::FrameGraphDirty); - } - break; - } - - case Qt3DCore::PropertyValueRemoved: { - Qt3DCore::QPropertyNodeRemovedChangePtr change = qSharedPointerCast<Qt3DCore::QPropertyNodeRemovedChange>(e); - if (change->metaObject()->inherits(&QFrameGraphNode::staticMetaObject)) { - removeChildId(change->removedNodeId()); - markDirty(AbstractRenderer::FrameGraphDirty); - } - break; - } case Qt3DCore::PropertyUpdated: { Qt3DCore::QPropertyUpdatedChangePtr propertyChange = qSharedPointerCast<Qt3DCore::QPropertyUpdatedChange>(e); if (propertyChange->propertyName() == QByteArrayLiteral("enabled")) { d_func()->m_enabled = propertyChange->value().toBool(); markDirty(AbstractRenderer::FrameGraphDirty); + } else if (propertyChange->propertyName() == QByteArrayLiteral("parentFrameGraphUpdated")) { + auto newParent = propertyChange->value().value<Qt3DCore::QNodeId>(); + setParentId(newParent); + markDirty(AbstractRenderer::AllDirty); } break; } @@ -185,6 +149,12 @@ void FrameGraphNode::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) } } +void FrameGraphNode::cleanup() +{ + setParentId({}); +} + + } // namespace Render } // namespace Qt3DRender diff --git a/src/render/framegraph/framegraphnode_p.h b/src/render/framegraph/framegraphnode_p.h index 5bd9b1b12..420abce56 100644 --- a/src/render/framegraph/framegraphnode_p.h +++ b/src/render/framegraph/framegraphnode_p.h @@ -112,8 +112,6 @@ public: FrameGraphManager *manager() const; void setParentId(Qt3DCore::QNodeId parentId); - void appendChildId(Qt3DCore::QNodeId childHandle); - void removeChildId(Qt3DCore::QNodeId childHandle); Qt3DCore::QNodeId parentId() const; QVector<Qt3DCore::QNodeId> childrenIds() const; @@ -121,6 +119,8 @@ public: FrameGraphNode *parent() const; QVector<FrameGraphNode *> children() const; + void cleanup(); + void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) override; protected: diff --git a/src/render/framegraph/qframegraphnode.cpp b/src/render/framegraph/qframegraphnode.cpp index 9acfd1209..d52b728a8 100644 --- a/src/render/framegraph/qframegraphnode.cpp +++ b/src/render/framegraph/qframegraphnode.cpp @@ -40,6 +40,7 @@ #include "qframegraphnode.h" #include "qframegraphnode_p.h" #include <Qt3DRender/qframegraphnodecreatedchange.h> +#include <Qt3DCore/qpropertyupdatedchange.h> #include <Qt3DCore/QNode> @@ -248,9 +249,24 @@ QFrameGraphNode::QFrameGraphNode(QFrameGraphNodePrivate &dd, QNode *parent) Qt3DCore::QNodeCreatedChangeBasePtr QFrameGraphNode::createNodeCreationChange() const { + // connect to the parentChanged signal here rather than constructor because + // until now there's no backend node to notify when parent changes + connect(this, &QNode::parentChanged, this, &QFrameGraphNode::onParentChanged); + return QFrameGraphNodeCreatedChangeBasePtr::create(this); } +void QFrameGraphNode::onParentChanged(QObject *) +{ + const auto parentID = parentFrameGraphNode() ? parentFrameGraphNode()->id() : Qt3DCore::QNodeId(); + auto parentChange = Qt3DCore::QPropertyUpdatedChangePtr::create(id()); + parentChange->setPropertyName("parentFrameGraphUpdated"); + parentChange->setValue(QVariant::fromValue(parentID)); + const bool blocked = blockNotifications(false); + notifyObservers(parentChange); + blockNotifications(blocked); +} + } // namespace Qt3DRender QT_END_NAMESPACE diff --git a/src/render/framegraph/qframegraphnode.h b/src/render/framegraph/qframegraphnode.h index 9b08c98d3..826f01a1e 100644 --- a/src/render/framegraph/qframegraphnode.h +++ b/src/render/framegraph/qframegraphnode.h @@ -63,6 +63,9 @@ protected: explicit QFrameGraphNode(QFrameGraphNodePrivate &dd, Qt3DCore::QNode *parent = nullptr); Qt3DCore::QNodeCreatedChangeBasePtr createNodeCreationChange() const override; +private Q_SLOTS: + void onParentChanged(QObject *); + private: Q_DECLARE_PRIVATE(QFrameGraphNode) }; diff --git a/src/render/jobs/job_common_p.h b/src/render/jobs/job_common_p.h index 9c83624b8..5fe16f933 100644 --- a/src/render/jobs/job_common_p.h +++ b/src/render/jobs/job_common_p.h @@ -109,6 +109,7 @@ namespace JobTypes { UpdateLayerEntity, SendTextureChangesToFrontend, SendSetFenceHandlesToFrontend, + UpdateEntityHierarchy, }; } // JobTypes diff --git a/src/render/jobs/jobs.pri b/src/render/jobs/jobs.pri index 2181e4a95..6cdf891fc 100644 --- a/src/render/jobs/jobs.pri +++ b/src/render/jobs/jobs.pri @@ -30,6 +30,7 @@ HEADERS += \ $$PWD/filterproximitydistancejob_p.h \ $$PWD/abstractpickingjob_p.h \ $$PWD/raycastingjob_p.h \ + $$PWD/updateentityhierarchyjob_p.h \ $$PWD/updateentitylayersjob_p.h SOURCES += \ @@ -59,5 +60,6 @@ SOURCES += \ $$PWD/filterproximitydistancejob.cpp \ $$PWD/abstractpickingjob.cpp \ $$PWD/raycastingjob.cpp \ + $$PWD/updateentityhierarchyjob.cpp \ $$PWD/updateentitylayersjob.cpp diff --git a/src/render/jobs/updateentityhierarchyjob.cpp b/src/render/jobs/updateentityhierarchyjob.cpp new file mode 100644 index 000000000..7c18514bb --- /dev/null +++ b/src/render/jobs/updateentityhierarchyjob.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "updateentityhierarchyjob_p.h" +#include <Qt3DRender/private/managers_p.h> +#include <Qt3DRender/private/nodemanagers_p.h> +#include <Qt3DRender/private/entity_p.h> +#include <Qt3DRender/private/job_common_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +UpdateEntityHierarchyJob::UpdateEntityHierarchyJob() + : m_manager(nullptr) +{ + SET_JOB_RUN_STAT_TYPE(this, JobTypes::UpdateEntityHierarchy, 0); +} + +void UpdateEntityHierarchyJob::run() +{ + Q_ASSERT(m_manager); + EntityManager *entityManager = m_manager->renderNodesManager(); + + const QVector<HEntity> handles = entityManager->activeHandles(); + + // Clear the parents and children + for (const HEntity &handle : handles) { + Entity *entity = entityManager->data(handle); + entity->clearEntityHierarchy(); + } + for (const HEntity &handle : handles) { + Entity *entity = entityManager->data(handle); + entity->rebuildEntityHierarchy(); + } +} + +} // Render + +} // Qt3DRender + +QT_END_NAMESPACE diff --git a/src/render/jobs/updateentityhierarchyjob_p.h b/src/render/jobs/updateentityhierarchyjob_p.h new file mode 100644 index 000000000..fd2b13631 --- /dev/null +++ b/src/render/jobs/updateentityhierarchyjob_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QT3DRENDER_RENDER_UPDATEENTITYHIERARCHYJOB_P_H +#define QT3DRENDER_RENDER_UPDATEENTITYHIERARCHYJOB_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DRender/private/qt3drender_global_p.h> +#include <Qt3DCore/qaspectjob.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +class Entity; +class NodeManagers; + +class Q_3DRENDERSHARED_PRIVATE_EXPORT UpdateEntityHierarchyJob: public Qt3DCore::QAspectJob +{ +public: + UpdateEntityHierarchyJob(); + + inline void setManager(NodeManagers *manager) { m_manager = manager; } + inline NodeManagers *manager() const { return m_manager; } + + // QAspectJob interface + void run() final; + +private: + NodeManagers *m_manager; +}; + + +using UpdateEntityHierarchyJobPtr = QSharedPointer<UpdateEntityHierarchyJob>; + +} // Render + +} // Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_UPDATEENTITYHIERARCHYJOB_P_H diff --git a/src/render/jobs/updateentitylayersjob_p.h b/src/render/jobs/updateentitylayersjob_p.h index 13cc2fc4c..5d36042c4 100644 --- a/src/render/jobs/updateentitylayersjob_p.h +++ b/src/render/jobs/updateentitylayersjob_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2018 Klaralvdalens Datakonsult AB (KDAB). +** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB). ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt3D module of the Qt Toolkit. diff --git a/src/render/renderers/opengl/renderer/renderer.cpp b/src/render/renderers/opengl/renderer/renderer.cpp index 012ec7056..4e40906a6 100644 --- a/src/render/renderers/opengl/renderer/renderer.cpp +++ b/src/render/renderers/opengl/renderer/renderer.cpp @@ -200,6 +200,7 @@ Renderer::Renderer(QRenderAspect::RenderType type) , m_sendSetFenceHandlesToFrontendJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { sendSetFenceHandlesToFrontend(); }, JobTypes::SendSetFenceHandlesToFrontend)) , m_introspectShaderJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { reloadDirtyShaders(); }, JobTypes::DirtyShaderGathering)) , m_syncLoadingJobs(Render::GenericLambdaJobPtr<std::function<void ()>>::create([] {}, JobTypes::SyncLoadingJobs)) + , m_updateEntityHierarchyJob(Render::UpdateEntityHierarchyJobPtr::create()) , m_ownedContext(false) , m_offscreenHelper(nullptr) #if QT_CONFIG(qt3d_profile_jobs) @@ -212,6 +213,9 @@ Renderer::Renderer(QRenderAspect::RenderType type) if (m_renderThread) m_renderThread->waitForStart(); + m_worldTransformJob->addDependency(m_updateEntityHierarchyJob); + m_updateEntityLayersJob->addDependency(m_updateEntityHierarchyJob); + // Create jobs to update transforms and bounding volumes // We can only update bounding volumes once all world transforms are known m_updateWorldBoundingVolumeJob->addDependency(m_worldTransformJob); @@ -297,6 +301,7 @@ void Renderer::setNodeManagers(NodeManagers *managers) m_updateMeshTriangleListJob->setManagers(m_nodesManager); m_filterCompatibleTechniqueJob->setManager(m_nodesManager->techniqueManager()); m_updateEntityLayersJob->setManager(m_nodesManager); + m_updateEntityHierarchyJob->setManager(m_nodesManager); } void Renderer::setServices(QServiceLocator *services) @@ -1752,14 +1757,17 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs() // Add jobs const bool entitiesEnabledDirty = dirtyBitsForFrame & AbstractRenderer::EntityEnabledDirty; - if (entitiesEnabledDirty) { + const bool entityHierarchyNeedsToBeRebuilt = dirtyBitsForFrame & AbstractRenderer::EntityHierarchyDirty; + if (entitiesEnabledDirty || entityHierarchyNeedsToBeRebuilt) { renderBinJobs.push_back(m_updateTreeEnabledJob); // This dependency is added here because we clear all dependencies // at the start of this function. m_calculateBoundingVolumeJob->addDependency(m_updateTreeEnabledJob); + m_calculateBoundingVolumeJob->addDependency(m_updateEntityHierarchyJob); } - if (dirtyBitsForFrame & AbstractRenderer::TransformDirty) { + if (dirtyBitsForFrame & AbstractRenderer::TransformDirty || + dirtyBitsForFrame & AbstractRenderer::EntityHierarchyDirty) { renderBinJobs.push_back(m_worldTransformJob); renderBinJobs.push_back(m_updateWorldBoundingVolumeJob); renderBinJobs.push_back(m_updateShaderDataTransformJob); @@ -1772,6 +1780,7 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs() } if (dirtyBitsForFrame & AbstractRenderer::GeometryDirty || + dirtyBitsForFrame & AbstractRenderer::EntityHierarchyDirty || dirtyBitsForFrame & AbstractRenderer::TransformDirty) { renderBinJobs.push_back(m_expandBoundingVolumeJob); } @@ -1798,7 +1807,7 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs() // Layer cache is dependent on layers, layer filters (hence FG structure // changes) and the enabled flag on entities const bool frameGraphDirty = dirtyBitsForFrame & AbstractRenderer::FrameGraphDirty; - const bool layersDirty = dirtyBitsForFrame & AbstractRenderer::LayersDirty; + const bool layersDirty = dirtyBitsForFrame & AbstractRenderer::LayersDirty || entityHierarchyNeedsToBeRebuilt; const bool layersCacheNeedsToBeRebuilt = layersDirty || entitiesEnabledDirty || frameGraphDirty; const bool materialDirty = dirtyBitsForFrame & AbstractRenderer::MaterialDirty; const bool lightsDirty = dirtyBitsForFrame & AbstractRenderer::LightsDirty; @@ -1806,6 +1815,10 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs() const bool renderableDirty = dirtyBitsForFrame & AbstractRenderer::GeometryDirty; const bool materialCacheNeedsToBeRebuilt = materialDirty || frameGraphDirty; + // Rebuild Entity Hierarchy if dirty + if (entityHierarchyNeedsToBeRebuilt) + renderBinJobs.push_back(m_updateEntityHierarchyJob); + // Rebuild Entity Layers list if layers are dirty if (layersDirty) renderBinJobs.push_back(m_updateEntityLayersJob); @@ -1835,6 +1848,10 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs() builder.setComputableCacheNeedsToBeRebuilt(computeableDirty); builder.setLightGathererCacheNeedsToBeRebuilt(lightsDirty); builder.setMaterialGathererCacheNeedsToBeRebuilt(materialCacheNeedsToBeRebuilt); + builder.setRenderableCacheNeedsToBeRebuilt(renderableDirty); + builder.setComputableCacheNeedsToBeRebuilt(computeableDirty); + builder.setLightGathererCacheNeedsToBeRebuilt(lightsDirty); + builder.prepareJobs(); renderBinJobs.append(builder.buildJobHierachy()); } diff --git a/src/render/renderers/opengl/renderer/renderer_p.h b/src/render/renderers/opengl/renderer/renderer_p.h index 9a2c99c30..e82d18f77 100644 --- a/src/render/renderers/opengl/renderer/renderer_p.h +++ b/src/render/renderers/opengl/renderer/renderer_p.h @@ -78,6 +78,7 @@ #include <Qt3DRender/private/filtercompatibletechniquejob_p.h> #include <Qt3DRender/private/updateskinningpalettejob_p.h> #include <Qt3DRender/private/updateentitylayersjob_p.h> +#include <Qt3DRender/private/updateentityhierarchyjob_p.h> #include <Qt3DRender/private/renderercache_p.h> #include <Qt3DRender/private/texture_p.h> #include <Qt3DRender/private/glfence_p.h> @@ -367,6 +368,7 @@ private: UpdateMeshTriangleListJobPtr m_updateMeshTriangleListJob; FilterCompatibleTechniqueJobPtr m_filterCompatibleTechniqueJob; UpdateEntityLayersJobPtr m_updateEntityLayersJob; + UpdateEntityHierarchyJobPtr m_updateEntityHierarchyJob; QVector<Qt3DCore::QNodeId> m_pendingRenderCaptureSendRequests; diff --git a/src/src.pro b/src/src.pro index 853ae8fb5..0a3ed8cb7 100644 --- a/src/src.pro +++ b/src/src.pro @@ -1,5 +1,8 @@ TEMPLATE = subdirs +!qtHaveModule(gui): \ + return() + src_core.subdir = $$PWD/core src_core.target = sub-core |