diff options
25 files changed, 759 insertions, 41 deletions
diff --git a/src/core/nodes/qentity.cpp b/src/core/nodes/qentity.cpp index 2ef78deb6..6a24b1956 100644 --- a/src/core/nodes/qentity.cpp +++ b/src/core/nodes/qentity.cpp @@ -232,6 +232,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; @@ -261,6 +265,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 dc7dc62c1..f6044ce5e 100644 --- a/src/core/nodes/qentity.h +++ b/src/core/nodes/qentity.h @@ -70,6 +70,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/render/backend/abstractrenderer_p.h b/src/render/backend/abstractrenderer_p.h index f1bdca7be..eea6cbe9f 100644 --- a/src/render/backend/abstractrenderer_p.h +++ b/src/render/backend/abstractrenderer_p.h @@ -114,6 +114,7 @@ public: JointDirty = 1 << 11, LayersDirty = 1 << 12, TechniquesDirty = 1 << 13, + EntityHierarchyDirty= 1 << 14, 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 075871d85..afa7dbf23 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/jobs/job_common_p.h b/src/render/jobs/job_common_p.h index 2ae7d81ff..8f8242ec8 100644 --- a/src/render/jobs/job_common_p.h +++ b/src/render/jobs/job_common_p.h @@ -107,6 +107,7 @@ namespace JobTypes { SyncFilterEntityByLayer, SyncMaterialGatherer, UpdateLayerEntity, + UpdateEntityHierarchy, SendTextureChangesToFrontend }; diff --git a/src/render/jobs/jobs.pri b/src/render/jobs/jobs.pri index 472531681..0c326c0b5 100644 --- a/src/render/jobs/jobs.pri +++ b/src/render/jobs/jobs.pri @@ -31,6 +31,7 @@ HEADERS += \ $$PWD/filterproximitydistancejob_p.h \ $$PWD/abstractpickingjob_p.h \ $$PWD/raycastingjob_p.h \ + $$PWD/updateentityhierarchyjob_p.h \ $$PWD/updateentitylayersjob_p.h SOURCES += \ @@ -61,5 +62,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..f6ba2d584 --- /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 QT3DRENDERSHARED_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 8c73899d9..f2e63e32d 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 4b70710f2..a023b82f9 100644 --- a/src/render/renderers/opengl/renderer/renderer.cpp +++ b/src/render/renderers/opengl/renderer/renderer.cpp @@ -198,6 +198,7 @@ Renderer::Renderer(QRenderAspect::RenderType type) , m_sendTextureChangesToFrontendJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { sendTextureChangesToFrontend(); }, JobTypes::SendTextureChangesToFrontend)) , m_introspectShaderJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { reloadDirtyShaders(); }, JobTypes::DirtyShaderGathering)) , m_syncTextureLoadingJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([] {}, JobTypes::SyncTextureLoading)) + , m_updateEntityHierarchyJob(Render::UpdateEntityHierarchyJobPtr::create()) , m_ownedContext(false) , m_offscreenHelper(nullptr) #if QT_CONFIG(qt3d_profile_jobs) @@ -210,6 +211,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); @@ -299,6 +303,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) @@ -1665,14 +1670,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); @@ -1685,6 +1693,7 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs() } if (dirtyBitsForFrame & AbstractRenderer::GeometryDirty || + dirtyBitsForFrame & AbstractRenderer::EntityHierarchyDirty || dirtyBitsForFrame & AbstractRenderer::TransformDirty) { renderBinJobs.push_back(m_expandBoundingVolumeJob); } @@ -1722,11 +1731,15 @@ 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 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); diff --git a/src/render/renderers/opengl/renderer/renderer_p.h b/src/render/renderers/opengl/renderer/renderer_p.h index b89c1e7c0..6443215a4 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> @@ -361,6 +362,7 @@ private: UpdateMeshTriangleListJobPtr m_updateMeshTriangleListJob; FilterCompatibleTechniqueJobPtr m_filterCompatibleTechniqueJob; UpdateEntityLayersJobPtr m_updateEntityLayersJob; + UpdateEntityHierarchyJobPtr m_updateEntityHierarchyJob; QVector<Qt3DCore::QNodeId> m_pendingRenderCaptureSendRequests; diff --git a/tests/auto/core/nodes/tst_nodes.cpp b/tests/auto/core/nodes/tst_nodes.cpp index 3f7fb4a75..193d88c83 100644 --- a/tests/auto/core/nodes/tst_nodes.cpp +++ b/tests/auto/core/nodes/tst_nodes.cpp @@ -81,6 +81,7 @@ private slots: void removingChildEntitiesFromNode(); void checkConstructionSetParentMix(); // QTBUG-60612 + void checkParentingQEntityToQNode(); // QTBUG-73905 void checkConstructionWithParent(); void checkConstructionWithNonRootParent(); // QTBUG-73986 void checkConstructionAsListElement(); @@ -1079,6 +1080,72 @@ void tst_Nodes::checkConstructionSetParentMix() QCOMPARE(lastEvent->addedNodeId(), subTreeRoot->id()); } +void tst_Nodes::checkParentingQEntityToQNode() +{ + // GIVEN + ObserverSpy spy; + Qt3DCore::QScene scene; + QScopedPointer<MyQNode> root(new MyQNode()); + + // WHEN + root->setArbiterAndScene(&spy, &scene); + root->setSimulateBackendCreated(true); + + // THEN + QVERIFY(Qt3DCore::QNodePrivate::get(root.data())->scene() != nullptr); + + // WHEN + auto subTreeRoot = new Qt3DCore::QEntity(root.data()); + auto childEntity = new Qt3DCore::QEntity(subTreeRoot); + auto childNode = new Qt3DCore::QNode(subTreeRoot); + + // THEN + QCoreApplication::processEvents(); + + // Ensure first event is subTreeRoot creation + const Qt3DCore::QNodeCreatedChangeBasePtr firstEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QNodeCreatedChangeBase>(); + QVERIFY(!firstEvent.isNull()); + QCOMPARE(firstEvent->subjectId(), subTreeRoot->id()); + QCOMPARE(firstEvent->parentId(), root->id()); + + // Ensure 2nd event is childEntity creation + const Qt3DCore::QNodeCreatedChangeBasePtr secondEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QNodeCreatedChangeBase>(); + QVERIFY(!secondEvent.isNull()); + QCOMPARE(secondEvent->subjectId(), childEntity->id()); + QCOMPARE(secondEvent->parentId(), subTreeRoot->id()); + + // Ensure 3rd event is childNode creation + const Qt3DCore::QNodeCreatedChangeBasePtr thirdEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QNodeCreatedChangeBase>(); + QVERIFY(!thirdEvent.isNull()); + QCOMPARE(thirdEvent->subjectId(), childNode->id()); + QCOMPARE(thirdEvent->parentId(), subTreeRoot->id()); + + + // WHEN we reparent the childEntity to the childNode (QNode) + + spy.events.clear(); + childEntity->setParent(childNode); + // THEN we should get + // - one child removed change for childEntity->subTreeRoot, + // - one child added change for childEntity->childNode, + // - and one property updated event specifying the correct QEntity parent (subTreeRoot) + QCOMPARE(spy.events.size(), 3); + + const auto removedEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QPropertyNodeRemovedChange>(); + QVERIFY(!removedEvent.isNull()); + QCOMPARE(removedEvent->subjectId(), subTreeRoot->id()); + + const auto addedEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QPropertyNodeAddedChange>(); + QVERIFY(!addedEvent.isNull()); + QCOMPARE(addedEvent->subjectId(), childNode->id()); + + const auto parentChangeEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QPropertyUpdatedChange>(); + QVERIFY(!parentChangeEvent.isNull()); + QCOMPARE(parentChangeEvent->subjectId(), childEntity->id()); + QCOMPARE(parentChangeEvent->propertyName(), "parentEntityUpdated"); + QCOMPARE(parentChangeEvent->value().value<Qt3DCore::QNodeId>(), subTreeRoot->id()); +} + void tst_Nodes::checkConstructionWithParent() { // GIVEN diff --git a/tests/auto/render/boundingsphere/tst_boundingsphere.cpp b/tests/auto/render/boundingsphere/tst_boundingsphere.cpp index 992e643d2..b35c6d31a 100644 --- a/tests/auto/render/boundingsphere/tst_boundingsphere.cpp +++ b/tests/auto/render/boundingsphere/tst_boundingsphere.cpp @@ -55,6 +55,7 @@ #include <Qt3DRender/private/calcboundingvolumejob_p.h> #include <Qt3DRender/private/calcgeometrytrianglevolumes_p.h> #include <Qt3DRender/private/loadbufferjob_p.h> +#include <Qt3DRender/private/updateentityhierarchyjob_p.h> #include <Qt3DRender/private/buffermanager_p.h> #include <Qt3DRender/private/geometryrenderermanager_p.h> #include <Qt3DRender/private/sphere_p.h> @@ -116,6 +117,10 @@ namespace { void runRequiredJobs(Qt3DRender::TestAspect *test) { + Qt3DRender::Render::UpdateEntityHierarchyJob updateEntitiesJob; + updateEntitiesJob.setManager(test->nodeManagers()); + updateEntitiesJob.run(); + Qt3DRender::Render::UpdateWorldTransformJob updateWorldTransform; updateWorldTransform.setRoot(test->sceneRoot()); updateWorldTransform.run(); diff --git a/tests/auto/render/entity/tst_entity.cpp b/tests/auto/render/entity/tst_entity.cpp index 6ad958451..123a648d6 100644 --- a/tests/auto/render/entity/tst_entity.cpp +++ b/tests/auto/render/entity/tst_entity.cpp @@ -151,7 +151,7 @@ private slots: QVERIFY(!entity.componentsUuid<EnvironmentLight>().isEmpty()); QVERIFY(!entity.componentUuid<Armature>().isNull()); QVERIFY(entity.isBoundingVolumeDirty()); - QVERIFY(!entity.childrenHandles().isEmpty()); + QVERIFY(entity.childrenHandles().isEmpty()); QVERIFY(!entity.layerIds().isEmpty()); QVERIFY(renderer.dirtyBits() != 0); bool containsAll = entity.containsComponentsOfType<Transform, @@ -162,6 +162,7 @@ private slots: entity.cleanup(); // THEN + QVERIFY(entity.parentEntityId().isNull()); QVERIFY(entity.componentUuid<Transform>().isNull()); QVERIFY(entity.componentUuid<CameraLens>().isNull()); QVERIFY(entity.componentUuid<Material>().isNull()); @@ -180,6 +181,122 @@ private slots: QVERIFY(!containsAll); } + void checkRebuildingEntityHierarchy() + { + // GIVEN + TestRenderer renderer; + NodeManagers nodeManagers; + Qt3DCore::QEntity frontendEntityA, frontendEntityB, frontendEntityC; + + auto entityCreator = [&nodeManagers, &renderer](const Qt3DCore::QEntity &frontEndEntity) { + Entity *entity = nodeManagers.renderNodesManager()->getOrCreateResource(frontEndEntity.id()); + entity->setNodeManagers(&nodeManagers); + entity->setRenderer(&renderer); + return entity; + }; + + auto backendA = entityCreator(frontendEntityA); + auto backendB = entityCreator(frontendEntityB); + auto backendC = entityCreator(frontendEntityC); + + // THEN + QVERIFY(backendA->parentEntityId().isNull()); + QVERIFY(backendB->parentEntityId().isNull()); + QVERIFY(backendC->parentEntityId().isNull()); + + QVERIFY(backendA->parent() == nullptr); + QVERIFY(backendB->parent() == nullptr); + QVERIFY(backendC->parent() == nullptr); + + QVERIFY(backendA->childrenHandles().isEmpty()); + QVERIFY(backendB->childrenHandles().isEmpty()); + QVERIFY(backendC->childrenHandles().isEmpty()); + + // WHEN + renderer.clearDirtyBits(0); + QVERIFY(renderer.dirtyBits() == 0); + + auto sendParentChange = [&nodeManagers](const Qt3DCore::QEntity &entity) { + const auto parentChange = QPropertyUpdatedChangePtr::create(entity.id()); + parentChange->setPropertyName("parentEntityUpdated"); + auto parent = entity.parentEntity(); + parentChange->setValue(QVariant::fromValue(parent ? parent->id() : Qt3DCore::QNodeId())); + + Entity *backendEntity = nodeManagers.renderNodesManager()->getOrCreateResource(entity.id()); + backendEntity->sceneChangeEvent(parentChange); + }; + + // reparent B to A and C to B. + frontendEntityB.setParent(&frontendEntityA); + sendParentChange(frontendEntityB); + frontendEntityC.setParent(&frontendEntityB); + sendParentChange(frontendEntityC); + + // THEN + QVERIFY(renderer.dirtyBits() & AbstractRenderer::EntityHierarchyDirty); + + QVERIFY(backendA->parentEntityId().isNull()); + QVERIFY(backendB->parentEntityId() == frontendEntityA.id()); + QVERIFY(backendC->parentEntityId() == frontendEntityB.id()); + + QVERIFY(backendA->parent() == nullptr); + QVERIFY(backendB->parent() == nullptr); + QVERIFY(backendC->parent() == nullptr); + + QVERIFY(backendA->childrenHandles().isEmpty()); + QVERIFY(backendB->childrenHandles().isEmpty()); + QVERIFY(backendC->childrenHandles().isEmpty()); + + // WHEN + auto rebuildHierarchy = [](Entity *backend) { + backend->clearEntityHierarchy(); + backend->rebuildEntityHierarchy(); + }; + rebuildHierarchy(backendA); + rebuildHierarchy(backendB); + rebuildHierarchy(backendC); + + // THEN + QVERIFY(backendA->parent() == nullptr); + QVERIFY(backendB->parent() == backendA); + QVERIFY(backendC->parent() == backendB); + + QVERIFY(!backendA->childrenHandles().isEmpty()); + QVERIFY(!backendB->childrenHandles().isEmpty()); + QVERIFY(backendC->childrenHandles().isEmpty()); + + // WHEN - reparent B to null. + frontendEntityB.setParent(static_cast<Qt3DCore::QNode *>(nullptr)); + sendParentChange(frontendEntityB); + rebuildHierarchy(backendA); + rebuildHierarchy(backendB); + rebuildHierarchy(backendC); + + QVERIFY(backendA->parentEntityId().isNull()); + QVERIFY(backendB->parentEntityId().isNull()); + QVERIFY(backendC->parentEntityId() == frontendEntityB.id()); + + QVERIFY(backendA->parent() == nullptr); + QVERIFY(backendB->parent() == nullptr); + QVERIFY(backendC->parent() == backendB); + + QVERIFY(backendA->childrenHandles().isEmpty()); + QVERIFY(!backendB->childrenHandles().isEmpty()); + QVERIFY(backendC->childrenHandles().isEmpty()); + + // WHEN - cleanup + backendA->cleanup(); + backendB->cleanup(); + backendC->cleanup(); + + // THEN + QVERIFY(backendA->parentEntityId().isNull()); + QVERIFY(backendB->parentEntityId().isNull()); + QVERIFY(backendC->parentEntityId().isNull()); + + QVERIFY(renderer.dirtyBits() != 0); + } + void shouldHandleSingleComponentEvents_data() { QTest::addColumn<QComponent*>("component"); diff --git a/tests/auto/render/layerfiltering/tst_layerfiltering.cpp b/tests/auto/render/layerfiltering/tst_layerfiltering.cpp index eeffc69b2..9b8636f49 100644 --- a/tests/auto/render/layerfiltering/tst_layerfiltering.cpp +++ b/tests/auto/render/layerfiltering/tst_layerfiltering.cpp @@ -33,6 +33,7 @@ #include <Qt3DRender/private/entity_p.h> #include <Qt3DRender/private/filterlayerentityjob_p.h> #include <Qt3DRender/private/updatetreeenabledjob_p.h> +#include <Qt3DRender/private/updateentityhierarchyjob_p.h> #include <Qt3DRender/qlayer.h> #include <Qt3DRender/qlayerfilter.h> #include "testaspect.h" @@ -632,6 +633,11 @@ private Q_SLOTS: // WHEN Qt3DRender::Render::Entity *backendRoot = aspect->nodeManagers()->renderNodesManager()->getOrCreateResource(entitySubtree->id()); + + Qt3DRender::Render::UpdateEntityHierarchyJob updateEntitiesJob; + updateEntitiesJob.setManager(aspect->nodeManagers()); + updateEntitiesJob.run(); + Qt3DRender::Render::UpdateTreeEnabledJob updateTreeEnabledJob; updateTreeEnabledJob.setRoot(backendRoot); updateTreeEnabledJob.run(); diff --git a/tests/auto/render/pickboundingvolumejob/tst_pickboundingvolumejob.cpp b/tests/auto/render/pickboundingvolumejob/tst_pickboundingvolumejob.cpp index 60b60eb6e..21f75b7a4 100644 --- a/tests/auto/render/pickboundingvolumejob/tst_pickboundingvolumejob.cpp +++ b/tests/auto/render/pickboundingvolumejob/tst_pickboundingvolumejob.cpp @@ -47,6 +47,7 @@ #include <Qt3DRender/private/qrenderaspect_p.h> #include <Qt3DRender/private/pickboundingvolumejob_p.h> #include <Qt3DRender/private/pickboundingvolumeutils_p.h> +#include <Qt3DRender/private/updateentityhierarchyjob_p.h> #include <Qt3DRender/private/updatemeshtrianglelistjob_p.h> #include <Qt3DRender/private/updateworldboundingvolumejob_p.h> #include <Qt3DRender/private/updateworldtransformjob_p.h> @@ -107,6 +108,10 @@ namespace { void runRequiredJobs(Qt3DRender::TestAspect *test) { + Qt3DRender::Render::UpdateEntityHierarchyJob updateEntitiesJob; + updateEntitiesJob.setManager(test->nodeManagers()); + updateEntitiesJob.run(); + Qt3DRender::Render::UpdateWorldTransformJob updateWorldTransform; updateWorldTransform.setRoot(test->sceneRoot()); updateWorldTransform.run(); @@ -252,6 +257,10 @@ private Q_SLOTS: QVERIFY(root); QScopedPointer<Qt3DRender::TestAspect> test(new Qt3DRender::TestAspect(root.data())); + Qt3DRender::Render::UpdateEntityHierarchyJob updateEntitiesJob; + updateEntitiesJob.setManager(test->nodeManagers()); + updateEntitiesJob.run(); + // THEN QList<Qt3DCore::QEntity *> frontendEntities; frontendEntities << qobject_cast<Qt3DCore::QEntity *>(root.data()) << root->findChildren<Qt3DCore::QEntity *>(); diff --git a/tests/auto/render/proximityfiltering/tst_proximityfiltering.cpp b/tests/auto/render/proximityfiltering/tst_proximityfiltering.cpp index 7a5648271..7bb3c16a7 100644 --- a/tests/auto/render/proximityfiltering/tst_proximityfiltering.cpp +++ b/tests/auto/render/proximityfiltering/tst_proximityfiltering.cpp @@ -246,6 +246,11 @@ private Q_SLOTS: // WHEN Qt3DRender::Render::Entity *backendRoot = aspect->nodeManagers()->renderNodesManager()->getOrCreateResource(entitySubtree->id()); + + Qt3DRender::Render::UpdateEntityHierarchyJob updateEntitiesJob; + updateEntitiesJob.setManager(aspect->nodeManagers()); + updateEntitiesJob.run(); + Qt3DRender::Render::UpdateTreeEnabledJob updateTreeEnabledJob; updateTreeEnabledJob.setRoot(backendRoot); updateTreeEnabledJob.run(); diff --git a/tests/auto/render/qcamera/tst_qcamera.cpp b/tests/auto/render/qcamera/tst_qcamera.cpp index b630c447a..7aef2af7d 100644 --- a/tests/auto/render/qcamera/tst_qcamera.cpp +++ b/tests/auto/render/qcamera/tst_qcamera.cpp @@ -35,6 +35,7 @@ #include <Qt3DCore/QEntity> #include <Qt3DCore/private/qnodecreatedchangegenerator_p.h> +#include <Qt3DRender/private/updateentityhierarchyjob_p.h> #include <Qt3DCore/private/qaspectjobmanager_p.h> #include <Qt3DCore/qpropertyupdatedchange.h> #include <Qt3DCore/qnodecreatedchange.h> @@ -99,6 +100,10 @@ namespace { void runRequiredJobs(Qt3DRender::TestAspect *test) { + Qt3DRender::Render::UpdateEntityHierarchyJob updateEntitiesJob; + updateEntitiesJob.setManager(test->nodeManagers()); + updateEntitiesJob.run(); + Qt3DRender::Render::UpdateWorldTransformJob updateWorldTransform; updateWorldTransform.setRoot(test->sceneRoot()); updateWorldTransform.run(); diff --git a/tests/auto/render/raycastingjob/tst_raycastingjob.cpp b/tests/auto/render/raycastingjob/tst_raycastingjob.cpp index 4980bfc30..411bb9160 100644 --- a/tests/auto/render/raycastingjob/tst_raycastingjob.cpp +++ b/tests/auto/render/raycastingjob/tst_raycastingjob.cpp @@ -51,6 +51,7 @@ #include <Qt3DRender/private/updateworldtransformjob_p.h> #include <Qt3DRender/private/expandboundingvolumejob_p.h> #include <Qt3DRender/private/calcboundingvolumejob_p.h> +#include <Qt3DRender/private/updateentityhierarchyjob_p.h> #include <Qt3DRender/private/calcgeometrytrianglevolumes_p.h> #include <Qt3DRender/private/loadbufferjob_p.h> #include <Qt3DRender/private/buffermanager_p.h> @@ -106,6 +107,10 @@ namespace { void runRequiredJobs(Qt3DRender::TestAspect *test) { + Qt3DRender::Render::UpdateEntityHierarchyJob updateEntitiesJob; + updateEntitiesJob.setManager(test->nodeManagers()); + updateEntitiesJob.run(); + Qt3DRender::Render::UpdateWorldTransformJob updateWorldTransform; updateWorldTransform.setRoot(test->sceneRoot()); updateWorldTransform.run(); diff --git a/tests/auto/render/renderer/tst_renderer.cpp b/tests/auto/render/renderer/tst_renderer.cpp index 9321f5303..e67e0ed55 100644 --- a/tests/auto/render/renderer/tst_renderer.cpp +++ b/tests/auto/render/renderer/tst_renderer.cpp @@ -252,6 +252,30 @@ private Q_SLOTS: renderQueue->reset(); // WHEN + renderer.markDirty(Qt3DRender::Render::AbstractRenderer::EntityHierarchyDirty, nullptr); + jobs = renderer.renderBinJobs(); + + // THEN + QCOMPARE(jobs.size(), + 1 + // EntityEnabledDirty + 1 + // EntityHierarchyJob + 1 + // WorldTransformJob + 1 + // UpdateWorldBoundingVolume + 1 + // UpdateShaderDataTransform + 1 + // ExpandBoundingVolumeJob + 1 + // CalculateBoundingVolumeJob + 1 + // UpdateEntityLayersJob + 1 + // updateLevelOfDetailJob + 1 + // updateSkinningPaletteJob + 1 + // cleanupJob + 1 + // sendBufferCaptureJob + singleRenderViewJobCount + + layerCacheJobCount); + + renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty); + renderQueue->reset(); + + // WHEN renderer.markDirty(Qt3DRender::Render::AbstractRenderer::AllDirty, nullptr); jobs = renderer.renderBinJobs(); @@ -259,6 +283,7 @@ private Q_SLOTS: // and ShaderGathererJob are not added here) QCOMPARE(jobs.size(), 1 + // EntityEnabledDirty + 1 + // EntityHierarchyDirty 1 + // WorldTransformJob 1 + // UpdateWorldBoundingVolume 1 + // UpdateShaderDataTransform diff --git a/tests/auto/render/updateshaderdatatransformjob/tst_updateshaderdatatransformjob.cpp b/tests/auto/render/updateshaderdatatransformjob/tst_updateshaderdatatransformjob.cpp index 67ddccd9b..4bab46423 100644 --- a/tests/auto/render/updateshaderdatatransformjob/tst_updateshaderdatatransformjob.cpp +++ b/tests/auto/render/updateshaderdatatransformjob/tst_updateshaderdatatransformjob.cpp @@ -29,6 +29,7 @@ #include <QtTest/QTest> #include <Qt3DRender/private/updateshaderdatatransformjob_p.h> #include <Qt3DRender/private/updateworldtransformjob_p.h> +#include <Qt3DRender/private/updateentityhierarchyjob_p.h> #include <Qt3DRender/private/nodemanagers_p.h> #include <Qt3DRender/private/managers_p.h> #include <Qt3DRender/qrenderaspect.h> @@ -88,6 +89,10 @@ namespace { void runRequiredJobs(Qt3DRender::TestAspect *test) { + Qt3DRender::Render::UpdateEntityHierarchyJob updateEntitiesJob; + updateEntitiesJob.setManager(test->nodeManagers()); + updateEntitiesJob.run(); + Qt3DRender::Render::UpdateWorldTransformJob updateWorldTransform; updateWorldTransform.setRoot(test->sceneRoot()); updateWorldTransform.run(); diff --git a/tests/manual/cylinder-parent-test/cylinder-parent-test.pro b/tests/manual/cylinder-parent-test/cylinder-parent-test.pro new file mode 100644 index 000000000..d3db3bc76 --- /dev/null +++ b/tests/manual/cylinder-parent-test/cylinder-parent-test.pro @@ -0,0 +1,9 @@ +!include( ../manual.pri ) { + error( "Couldn't find the manual.pri file!" ) +} + +QT += 3dcore 3drender 3dinput 3dextras + +SOURCES += main.cpp + + diff --git a/tests/manual/cylinder-parent-test/main.cpp b/tests/manual/cylinder-parent-test/main.cpp new file mode 100644 index 000000000..823461a28 --- /dev/null +++ b/tests/manual/cylinder-parent-test/main.cpp @@ -0,0 +1,243 @@ +/**************************************************************************** +** +** 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:GPL-EXCEPT$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QGuiApplication> +#include <QTimer> + +#include <Qt3DInput/QInputAspect> + +#include <Qt3DRender/qcamera.h> +#include <Qt3DRender/qcameralens.h> +#include <Qt3DExtras/qcylindermesh.h> +#include <Qt3DRender/qmesh.h> +#include <Qt3DRender/qtechnique.h> +#include <Qt3DExtras/qphongmaterial.h> +#include <Qt3DRender/qeffect.h> +#include <Qt3DRender/qtexture.h> +#include <Qt3DRender/qrenderpass.h> +#include <Qt3DRender/qrenderaspect.h> +#include <Qt3DExtras/qforwardrenderer.h> + +#include <Qt3DCore/qentity.h> +#include <Qt3DCore/qtransform.h> +#include <Qt3DCore/qaspectengine.h> + +#include <Qt3DExtras/qt3dwindow.h> +#include <Qt3DExtras/qorbitcameracontroller.h> +#include <QThread> +#include <QLoggingCategory> + +#include <type_traits> + +int main(int argc, char **argv) +{ + QGuiApplication app(argc, argv); + Qt3DExtras::Qt3DWindow view; + + QLoggingCategory::setFilterRules("Qt3D.Renderer.RenderNodes=true"); + + // Root entity + Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity(); + view.setRootEntity(rootEntity); + rootEntity->setObjectName("Root Entity"); + + // Set root object of the scene + view.show(); + + // Camera + Qt3DRender::QCamera *camera = view.camera(); + camera->lens()->setPerspectiveProjection(45.0f, 16.0f / 9.0f, 0.1f, 1000.0f); + camera->setPosition(QVector3D(0, 0, 20.0f)); + camera->setUpVector(QVector3D(0, 1, 0)); + camera->setViewCenter(QVector3D(0, 0, 0)); + + // For camera controls + Qt3DExtras::QOrbitCameraController *cameraController = new Qt3DExtras::QOrbitCameraController(rootEntity); + cameraController->setCamera(camera); + + // Cylinder shape data + Qt3DExtras::QCylinderMesh *mesh = new Qt3DExtras::QCylinderMesh(); + + qDebug() << "Setup complete. Creating cylinders\n"; + + // simple setParent from nullptr (OK for QTBUG-73905) + // green cylinder, bottom left + { + Qt3DCore::QTransform *leftTransform = new Qt3DCore::QTransform; + leftTransform->setTranslation(QVector3D(-5, -2, 0)); + leftTransform->setObjectName("Green transform"); + + Qt3DExtras::QPhongMaterial *greenMaterial = new Qt3DExtras::QPhongMaterial(rootEntity); + greenMaterial->setObjectName("Green Material"); + greenMaterial->setDiffuse(Qt::green); + + Qt3DCore::QEntity *grandParentNode = new Qt3DCore::QEntity(); + Qt3DCore::QEntity *parentNode = new Qt3DCore::QEntity(); + Qt3DCore::QEntity *leafNode = new Qt3DCore::QEntity(); + grandParentNode->setObjectName("Green Grandparent"); + parentNode->setObjectName("Green Parent"); + leafNode->setObjectName("Green Leaf"); + + leafNode->addComponent(mesh); + leafNode->addComponent(greenMaterial); + parentNode->addComponent(leftTransform); + + grandParentNode->setParent(rootEntity); + parentNode->setParent(grandParentNode); + leafNode->setParent(parentNode); + } + + // simple setParent from rootEntity (doesn't work QTBUG-73905) + // yellow cylinder, top left + { + Qt3DCore::QTransform *leftTransform = new Qt3DCore::QTransform; + leftTransform->setTranslation(QVector3D(-5, 2, 0)); + leftTransform->setObjectName("Yellow Transform"); + + Qt3DExtras::QPhongMaterial *yellowMaterial = new Qt3DExtras::QPhongMaterial(rootEntity); + yellowMaterial->setObjectName("Yellow Material"); + yellowMaterial->setDiffuse(Qt::yellow); + + Qt3DCore::QEntity *grandParentNode = new Qt3DCore::QEntity(rootEntity); + Qt3DCore::QEntity *parentNode = new Qt3DCore::QEntity(rootEntity); + Qt3DCore::QEntity *leafNode = new Qt3DCore::QEntity(rootEntity); + leafNode->setObjectName("Yellow Leaf"); + grandParentNode->setObjectName("Yellow Grandparent"); + parentNode->setObjectName("Yellow Parent"); + + leafNode->addComponent(mesh); + leafNode->addComponent(yellowMaterial); + parentNode->addComponent(leftTransform); + + // sometimes this can change things + //QCoreApplication::processEvents(); + + grandParentNode->setParent(rootEntity); + parentNode->setParent(grandParentNode); + leafNode->setParent(parentNode); + } + + // complex setParent from nullptr (OK QTBUG-73905?) + // red cylinder, Bottom-right + { + Qt3DCore::QNode *tree1node1 = new Qt3DCore::QNode(); + Qt3DCore::QEntity *tree1node2 = new Qt3DCore::QEntity(); + Qt3DCore::QNode *tree1node3 = new Qt3DCore::QNode(); + tree1node1->setObjectName("Red Tree1-Node1"); + tree1node2->setObjectName("Red Tree1-Node2"); + tree1node3->setObjectName("Red Tree1-Node3"); + + Qt3DCore::QNode *tree2node1 = new Qt3DCore::QNode(); + Qt3DCore::QEntity *tree2node2 = new Qt3DCore::QEntity(); + Qt3DCore::QNode *tree2node3 = new Qt3DCore::QNode(); + tree2node1->setObjectName("Red Tree2-Node1"); + tree2node2->setObjectName("Red Tree2-Node2"); + tree2node3->setObjectName("Red Tree2-Node3"); + + Qt3DCore::QTransform *wrongRedTransform = new Qt3DCore::QTransform; + wrongRedTransform->setTranslation(QVector3D(1, -1, 0)); + Qt3DCore::QTransform *bottomRightTransform = new Qt3DCore::QTransform; + bottomRightTransform->setTranslation(QVector3D(5, -2, 0)); + bottomRightTransform->setObjectName("Red BR Transform"); + wrongRedTransform->setObjectName("Red Wrong Transform"); + + Qt3DExtras::QPhongMaterial *redMaterial = new Qt3DExtras::QPhongMaterial(rootEntity); + redMaterial->setDiffuse(Qt::red); + redMaterial->setObjectName("Red Material"); + Qt3DCore::QEntity *leafNode = new Qt3DCore::QEntity(); + leafNode->setObjectName("Red Leaf"); + leafNode->addComponent(mesh); + leafNode->addComponent(redMaterial); + + tree1node2->addComponent(wrongRedTransform); + tree2node2->addComponent(bottomRightTransform); + + tree1node1->setParent(rootEntity); + tree1node2->setParent(tree1node1); + tree1node3->setParent(tree1node2); + + tree2node1->setParent(rootEntity); + tree2node2->setParent(tree2node1); + tree2node3->setParent(tree2node2); + + leafNode->setParent(tree1node3); + leafNode->setParent(tree2node3); + } + + // complex setParent from rootEntity (doesn't work QTBUG-73905) + // blue cylinder, top right + { + Qt3DCore::QNode *tree1node1 = new Qt3DCore::QNode(rootEntity); + Qt3DCore::QEntity *tree1node2 = new Qt3DCore::QEntity(rootEntity); + Qt3DCore::QNode *tree1node3 = new Qt3DCore::QNode(rootEntity); + tree1node1->setObjectName("Blue Tree1-Node1"); + tree1node2->setObjectName("Blue Tree1-Node2"); + tree1node3->setObjectName("Blue Tree1-Node3"); + + Qt3DCore::QNode *tree2node1 = new Qt3DCore::QNode(rootEntity); + Qt3DCore::QEntity *tree2node2 = new Qt3DCore::QEntity(rootEntity); + Qt3DCore::QNode *tree2node3 = new Qt3DCore::QNode(rootEntity); + tree2node1->setObjectName("Blue Tree2-Node1"); + tree2node2->setObjectName("Blue Tree2-Node2"); + tree2node3->setObjectName("Blue Tree2-Node3"); + + Qt3DCore::QTransform *wrongBlueTransform = new Qt3DCore::QTransform; + wrongBlueTransform->setTranslation(QVector3D(1, 1, 0)); + Qt3DCore::QTransform *topRightTransform = new Qt3DCore::QTransform; + topRightTransform->setTranslation(QVector3D(5, 2, 0)); + wrongBlueTransform->setObjectName("Blue Wrong Transform"); + topRightTransform->setObjectName("Blue TR Transform"); + + Qt3DExtras::QPhongMaterial *blueMaterial = new Qt3DExtras::QPhongMaterial(rootEntity); + blueMaterial->setObjectName("Blue Material"); + blueMaterial->setDiffuse(Qt::blue); + Qt3DCore::QEntity *leafNode = new Qt3DCore::QEntity(rootEntity); + leafNode->addComponent(mesh); + leafNode->addComponent(blueMaterial); + leafNode->setObjectName("Blue Leaf"); + + // sometimes this can change things + //QCoreApplication::processEvents(); + + tree1node2->addComponent(wrongBlueTransform); + tree2node2->addComponent(topRightTransform); + + tree1node1->setParent(rootEntity); + tree1node2->setParent(tree1node1); + tree1node3->setParent(tree1node2); + + tree2node1->setParent(rootEntity); + tree2node2->setParent(tree2node1); + tree2node3->setParent(tree2node2); + + leafNode->setParent(tree1node3); + leafNode->setParent(tree2node3); + } + + return app.exec(); +} diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro index 6666245ca..4900add69 100644 --- a/tests/manual/manual.pro +++ b/tests/manual/manual.pro @@ -13,6 +13,7 @@ SUBDIRS += \ custom-mesh-update-data-cpp \ custom-mesh-update-data-qml \ cylinder-cpp \ + cylinder-parent-test \ cylinder-qml \ deferred-renderer-cpp \ deferred-renderer-qml \ |