diff options
author | Paul Lemire <paul.lemire@kdab.com> | 2019-06-03 12:29:29 +0200 |
---|---|---|
committer | Liang Qi <liang.qi@qt.io> | 2019-06-03 12:29:52 +0200 |
commit | 4eee1ab5a51ac482e2252e2e85c0fe624d5d4bad (patch) | |
tree | 71fb24fc69b2f77e08a57c56011ee5b72f23a52a /src/render | |
parent | 79aa74b5d9df52605a8833e3853b422ca8e7a651 (diff) | |
parent | 666a645d1edc98dfea92fdaf7eaeed10c86dbc56 (diff) |
Merge remote-tracking branch 'origin/5.13' into dev
Conflicts:
src/render/renderers/opengl/renderer/renderer.cpp
tests/auto/render/textures/tst_textures.cpp
Change-Id: I4da0eafe7ddd4dd822c3dcb5f5fa826653a335b4
Diffstat (limited to 'src/render')
30 files changed, 771 insertions, 288 deletions
diff --git a/src/render/backend/cameralens.cpp b/src/render/backend/cameralens.cpp index b540b24c8..42635bad7 100644 --- a/src/render/backend/cameralens.cpp +++ b/src/render/backend/cameralens.cpp @@ -145,6 +145,7 @@ void CameraLens::computeSceneBoundingVolume(QNodeId entityId, ComputeFilteredBoundingVolumeJobPtr job(new GetBoundingVolumeWithoutCameraJob(this, commandId)); job->addDependency(m_renderer->expandBoundingVolumeJob()); job->setRoot(root); + job->setManagers(nodeManagers); job->ignoreSubTree(camNode); m_renderAspect->scheduleSingleShotJob(job); } diff --git a/src/render/backend/entity.cpp b/src/render/backend/entity.cpp index d8d04aef1..bf128b508 100644 --- a/src/render/backend/entity.cpp +++ b/src/render/backend/entity.cpp @@ -289,6 +289,26 @@ QVector<Entity *> Entity::children() const return childrenVector; } +void Entity::traverse(const std::function<void(Entity *)> &operation) +{ + operation(this); + for (const HEntity &handle : qAsConst(m_childrenHandles)) { + Entity *child = m_nodeManagers->renderNodesManager()->data(handle); + if (child != nullptr) + child->traverse(operation); + } +} + +void Entity::traverse(const std::function<void(const Entity *)> &operation) const +{ + operation(this); + for (const HEntity &handle : m_childrenHandles) { + const Entity *child = m_nodeManagers->renderNodesManager()->data(handle); + if (child != nullptr) + child->traverse(operation); + } +} + Matrix4x4 *Entity::worldTransform() { return m_nodeManagers->worldMatrixManager()->data(m_worldTransform); diff --git a/src/render/backend/entity_p.h b/src/render/backend/entity_p.h index c170fd42d..b4c9541f2 100644 --- a/src/render/backend/entity_p.h +++ b/src/render/backend/entity_p.h @@ -107,6 +107,8 @@ public: QVector<HEntity> childrenHandles() const { return m_childrenHandles; } QVector<Entity *> children() const; bool hasChildren() const { return !m_childrenHandles.empty(); } + void traverse(const std::function<void(Entity *)> &operation); + void traverse(const std::function<void(const Entity *)> &operation) const; Matrix4x4 *worldTransform(); const Matrix4x4 *worldTransform() const; diff --git a/src/render/backend/entityaccumulator.cpp b/src/render/backend/entityaccumulator.cpp new file mode 100644 index 000000000..f003420dd --- /dev/null +++ b/src/render/backend/entityaccumulator.cpp @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** 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 "entityaccumulator_p.h" +#include "entityvisitor_p.h" + +QT_USE_NAMESPACE +using namespace Qt3DRender::Render; + +namespace { + +class Accumulator : public EntityVisitor +{ +public: + Accumulator(std::function<bool(Entity *)> predicate, NodeManagers *manager) + : EntityVisitor(manager) + , m_predicate(predicate) + { + } + + EntityVisitor::Operation visit(Entity *entity) override { + if (m_predicate(entity)) + m_entities << entity; + return Continue; + } + + QVector<Entity *> m_entities; + +private: + std::function<bool(Entity *)> m_predicate; +}; + +} + +EntityAccumulator::EntityAccumulator(NodeManagers *manager) + : m_manager(manager) + , m_predicate([](Entity*) { return true; }) +{ + +} + +EntityAccumulator::EntityAccumulator(std::function<bool (Entity *)> predicate, NodeManagers *manager) + : m_manager(manager) + , m_predicate(predicate) +{ + +} + +/*! + * \internal + * + * Call this to traverse the scene graph and return all entities for + * which the predicate returns true. + * + * Can be useful to get all the entities that contain a specific type + * of component. + */ +QVector<Entity *> EntityAccumulator::apply(Entity *root) const +{ + Accumulator a(m_predicate, m_manager); + a.apply(root); + return a.m_entities; +} diff --git a/src/render/backend/entityaccumulator_p.h b/src/render/backend/entityaccumulator_p.h new file mode 100644 index 000000000..69656815f --- /dev/null +++ b/src/render/backend/entityaccumulator_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** 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_ENTITYACCUMULATOR_H +#define QT3DRENDER_RENDER_ENTITYACCUMULATOR_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/entity_p.h> +#include <Qt3DRender/private/qt3drender_global_p.h> +#include <QVector> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +class NodeManagers; + +class Q_3DRENDERSHARED_PRIVATE_EXPORT EntityAccumulator +{ +public: + EntityAccumulator(NodeManagers *manager); + EntityAccumulator(std::function<bool(Entity*)> predicate, NodeManagers *manager); + + QVector<Entity *> apply(Entity *root) const; + +private: + NodeManagers *m_manager; + std::function<bool(Entity *)> m_predicate; +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_ENTITYACCUMULATOR_H diff --git a/src/render/backend/entityvisitor.cpp b/src/render/backend/entityvisitor.cpp new file mode 100644 index 000000000..87dd353bb --- /dev/null +++ b/src/render/backend/entityvisitor.cpp @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** 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 "entityvisitor_p.h" +#include <Qt3DRender/private/managers_p.h> +#include <Qt3DRender/private/nodemanagers_p.h> + +QT_USE_NAMESPACE +using namespace Qt3DRender::Render; + +EntityVisitor::EntityVisitor(NodeManagers *manager) + : m_manager(manager) + , m_pruneDisabled(false) +{ + +} + +EntityVisitor::~EntityVisitor() = default; + +/*! + * \internal + * + * Override in derived class to do work on the current entity + * + * Return value (Continue, Prune, Stop) will affect traversal + */ +EntityVisitor::Operation EntityVisitor::visit(Entity *entity) { + // return false to stop traversal + if (!entity) + return Stop; + return Continue; +} + +/*! + * \internal + * + * If true, disabled entities and all their children will be ignored + * during traversal + * + */ +bool EntityVisitor::pruneDisabled() const +{ + return m_pruneDisabled; +} + +void EntityVisitor::setPruneDisabled(bool pruneDisabled) +{ + m_pruneDisabled = pruneDisabled; +} + +/*! + * \internal + * + * Call on the root of the tree that should be traversed. + * Returns false if any visit resulted in Stop + */ +bool EntityVisitor::apply(Entity *root) { + if (!root) + return false; + if (m_pruneDisabled && !root->isEnabled()) + return true; + + const auto op = visit(root); + if (op == Stop) + return false; + if (op == Prune) + return true; + + const auto childrenHandles = root->childrenHandles(); + for (const HEntity &handle : childrenHandles) { + Entity *child = m_manager->renderNodesManager()->data(handle); + if (child != nullptr && !apply(child)) + return false; + } + + return true; +} diff --git a/src/render/backend/entityvisitor_p.h b/src/render/backend/entityvisitor_p.h new file mode 100644 index 000000000..2f4f1c813 --- /dev/null +++ b/src/render/backend/entityvisitor_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** 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_ENTITYVISITOR_H +#define QT3DRENDER_RENDER_ENTITYVISITOR_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 <qglobal.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +class Entity; +class NodeManagers; + +class Q_AUTOTEST_EXPORT EntityVisitor +{ +public: + enum Operation { + Continue, //! continue traversal + Prune, //! don't traverse children + Stop //! abort traversal + }; + + EntityVisitor(NodeManagers *manager); + virtual ~EntityVisitor(); + + virtual Operation visit(Entity *entity = nullptr); + + bool pruneDisabled() const; + void setPruneDisabled(bool pruneDisabled); + + bool apply(Entity *root); + +protected: + NodeManagers *m_manager; + +private: + bool m_pruneDisabled; +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_ENTITYVISITOR_H diff --git a/src/render/backend/render-backend.pri b/src/render/backend/render-backend.pri index 352de3be5..6b60dfcda 100644 --- a/src/render/backend/render-backend.pri +++ b/src/render/backend/render-backend.pri @@ -11,6 +11,8 @@ HEADERS += \ $$PWD/platformsurfacefilter_p.h \ $$PWD/cameralens_p.h \ $$PWD/entity_p.h \ + $$PWD/entityvisitor_p.h \ + $$PWD/entityaccumulator_p.h \ $$PWD/layer_p.h \ $$PWD/levelofdetail_p.h \ $$PWD/nodefunctor_p.h \ @@ -44,6 +46,8 @@ SOURCES += \ $$PWD/platformsurfacefilter.cpp \ $$PWD/cameralens.cpp \ $$PWD/entity.cpp \ + $$PWD/entityvisitor.cpp \ + $$PWD/entityaccumulator.cpp \ $$PWD/layer.cpp \ $$PWD/levelofdetail.cpp \ $$PWD/transform.cpp \ diff --git a/src/render/framegraph/qclearbuffers.cpp b/src/render/framegraph/qclearbuffers.cpp index 597a30c19..67773a6b7 100644 --- a/src/render/framegraph/qclearbuffers.cpp +++ b/src/render/framegraph/qclearbuffers.cpp @@ -164,7 +164,7 @@ void QClearBuffers::setBuffers(QClearBuffers::BufferType buffers) Specifies the clear color to be used. */ /*! - \qmlproperty color Qt3D.Render::ClearBuffers::color + \qmlproperty color Qt3D.Render::ClearBuffers::clearColor Specifies the clear color to be used. */ void QClearBuffers::setClearColor(const QColor &color) diff --git a/src/render/jobs/calcboundingvolumejob.cpp b/src/render/jobs/calcboundingvolumejob.cpp index 68eb308c5..113dc34ce 100644 --- a/src/render/jobs/calcboundingvolumejob.cpp +++ b/src/render/jobs/calcboundingvolumejob.cpp @@ -51,6 +51,7 @@ #include <Qt3DRender/private/buffer_p.h> #include <Qt3DRender/private/sphere_p.h> #include <Qt3DRender/private/buffervisitor_p.h> +#include <Qt3DRender/private/entityaccumulator_p.h> #include <QtCore/qmath.h> #if QT_CONFIG(concurrent) @@ -65,28 +66,6 @@ namespace Render { namespace { -QVector<Geometry*> calculateLocalBoundingVolume(NodeManagers *manager, Entity *node); - -struct UpdateBoundFunctor -{ - NodeManagers *manager; - - // This define is required to work with QtConcurrent - typedef QVector<Geometry *> result_type; - QVector<Geometry *> operator ()(Qt3DRender::Render::Entity *node) - { - return calculateLocalBoundingVolume(manager, node); - } -}; - -struct ReduceUpdateBoundFunctor -{ - void operator ()(QVector<Geometry *> &result, const QVector<Geometry *> &values) - { - result += values; - } -}; - class BoundingVolumeCalculator { public: @@ -318,23 +297,29 @@ QVector<Geometry *> calculateLocalBoundingVolume(NodeManagers *manager, Entity * } } -#if QT_CONFIG(concurrent) - const QVector<Qt3DRender::Render::Entity *> children = node->children(); - if (children.size() > 1) { - UpdateBoundFunctor functor; - functor.manager = manager; - ReduceUpdateBoundFunctor reduceFunctor; - updatedGeometries += QtConcurrent::blockingMappedReduced<decltype(updatedGeometries)>(children, functor, reduceFunctor); - } else -#endif - { - const auto children = node->children(); - for (Entity *child : children) - updatedGeometries += calculateLocalBoundingVolume(manager, child); - } return updatedGeometries; } +struct UpdateBoundFunctor +{ + NodeManagers *manager; + + // This define is required to work with QtConcurrent + typedef QVector<Geometry *> result_type; + QVector<Geometry *> operator ()(Qt3DRender::Render::Entity *node) + { + return calculateLocalBoundingVolume(manager, node); + } +}; + +struct ReduceUpdateBoundFunctor +{ + void operator ()(QVector<Geometry *> &result, const QVector<Geometry *> &values) + { + result += values; + } +}; + } // anonymous CalculateBoundingVolumeJob::CalculateBoundingVolumeJob() @@ -346,7 +331,26 @@ CalculateBoundingVolumeJob::CalculateBoundingVolumeJob() void CalculateBoundingVolumeJob::run() { - const QVector<Geometry *> updatedGeometries = calculateLocalBoundingVolume(m_manager, m_node); + EntityAccumulator accumulator([](Entity *entity) { + return !entity->componentUuid<GeometryRenderer>().isNull(); + }, m_manager); + auto entities = accumulator.apply(m_node); + + QVector<Geometry *> updatedGeometries; + updatedGeometries.reserve(entities.size()); + +#if QT_CONFIG(concurrent) + if (entities.size() > 1) { + UpdateBoundFunctor functor; + functor.manager = m_manager; + ReduceUpdateBoundFunctor reduceFunctor; + updatedGeometries += QtConcurrent::blockingMappedReduced<decltype(updatedGeometries)>(entities, functor, reduceFunctor); + } else +#endif + { + for (Entity *child : entities) + updatedGeometries += calculateLocalBoundingVolume(m_manager, child); + } // Send extent updates to frontend for (Geometry *geometry : updatedGeometries) diff --git a/src/render/jobs/computefilteredboundingvolumejob.cpp b/src/render/jobs/computefilteredboundingvolumejob.cpp index d8a7b5094..02852685c 100644 --- a/src/render/jobs/computefilteredboundingvolumejob.cpp +++ b/src/render/jobs/computefilteredboundingvolumejob.cpp @@ -44,6 +44,8 @@ #include <Qt3DRender/private/renderlogging_p.h> #include <Qt3DRender/private/sphere_p.h> #include <Qt3DRender/private/job_common_p.h> +#include <Qt3DRender/private/managers_p.h> +#include <Qt3DRender/private/nodemanagers_p.h> #include <QThread> @@ -54,16 +56,18 @@ namespace Render { namespace { -void expandWorldBoundingVolume(Qt3DRender::Render::Sphere *sphere, +void expandWorldBoundingVolume(NodeManagers *manager, + Qt3DRender::Render::Sphere *sphere, Qt3DRender::Render::Entity *node, Qt3DRender::Render::Entity *excludeSubTree) { Qt3DRender::Render::Sphere childSphere(*node->worldBoundingVolume()); // Go to the nodes that have the most depth - const auto children = node->children(); - for (Entity *c : children) { - if (c != excludeSubTree) - expandWorldBoundingVolume(&childSphere, c, excludeSubTree); + const auto childrenHandles = node->childrenHandles(); + for (const HEntity &handle : childrenHandles) { + Entity *c = manager->renderNodesManager()->data(handle); + if (c && c != excludeSubTree) + expandWorldBoundingVolume(manager, &childSphere, c, excludeSubTree); } sphere->expandToContain(childSphere); } @@ -73,6 +77,7 @@ void expandWorldBoundingVolume(Qt3DRender::Render::Sphere *sphere, ComputeFilteredBoundingVolumeJob::ComputeFilteredBoundingVolumeJob() : m_root(nullptr) , m_ignoreSubTree(nullptr) + , m_manager(nullptr) { SET_JOB_RUN_STAT_TYPE(this, JobTypes::ExpandBoundingVolume, 0); } @@ -82,6 +87,11 @@ void ComputeFilteredBoundingVolumeJob::setRoot(Entity *root) m_root = root; } +void ComputeFilteredBoundingVolumeJob::setManagers(NodeManagers *manager) +{ + m_manager = manager; +} + void ComputeFilteredBoundingVolumeJob::ignoreSubTree(Entity *node) { m_ignoreSubTree = node; @@ -113,7 +123,7 @@ void ComputeFilteredBoundingVolumeJob::run() } Qt3DRender::Render::Sphere sphere; - expandWorldBoundingVolume(&sphere, m_root, m_ignoreSubTree); + expandWorldBoundingVolume(m_manager, &sphere, m_root, m_ignoreSubTree); finished(sphere); qCDebug(Jobs) << "Exiting" << Q_FUNC_INFO << QThread::currentThread(); diff --git a/src/render/jobs/computefilteredboundingvolumejob_p.h b/src/render/jobs/computefilteredboundingvolumejob_p.h index 797a685ee..d2aca575c 100644 --- a/src/render/jobs/computefilteredboundingvolumejob_p.h +++ b/src/render/jobs/computefilteredboundingvolumejob_p.h @@ -62,6 +62,7 @@ namespace Qt3DRender { namespace Render { class Entity; +class NodeManagers; class Sphere; class Q_3DRENDERSHARED_PRIVATE_EXPORT ComputeFilteredBoundingVolumeJob : public Qt3DCore::QAspectJob @@ -70,6 +71,7 @@ public: ComputeFilteredBoundingVolumeJob(); void setRoot(Entity *root); + void setManagers(NodeManagers *manager); void ignoreSubTree(Entity *node); void run() override; @@ -79,6 +81,7 @@ protected: private: Entity *m_root; Entity *m_ignoreSubTree; + NodeManagers *m_manager; }; typedef QSharedPointer<ComputeFilteredBoundingVolumeJob> ComputeFilteredBoundingVolumeJobPtr; diff --git a/src/render/jobs/expandboundingvolumejob.cpp b/src/render/jobs/expandboundingvolumejob.cpp index ec175f312..d63934b54 100644 --- a/src/render/jobs/expandboundingvolumejob.cpp +++ b/src/render/jobs/expandboundingvolumejob.cpp @@ -44,6 +44,8 @@ #include <Qt3DRender/private/renderlogging_p.h> #include <Qt3DRender/private/sphere_p.h> #include <Qt3DRender/private/job_common_p.h> +#include <Qt3DRender/private/managers_p.h> +#include <Qt3DRender/private/nodemanagers_p.h> #include <QThread> @@ -54,19 +56,25 @@ namespace Render { namespace { -void expandWorldBoundingVolume(Qt3DRender::Render::Entity *node) +void expandWorldBoundingVolume(NodeManagers *manager, Entity *node) { // Go to the nodes that have the most depth - const auto children = node->children(); - for (Entity *c : children) - expandWorldBoundingVolume(c); + const auto childrenHandles = node->childrenHandles(); + for (const HEntity &handle : childrenHandles) { + Entity *c = manager->renderNodesManager()->data(handle); + if (c) + expandWorldBoundingVolume(manager, c); + } // Then traverse back from leaf to root // Initialize parent bounding volume to be equal to that of the first child - if (!children.empty()) { + if (!childrenHandles.empty()) { Qt3DRender::Render::Sphere *parentBoundingVolume = node->worldBoundingVolumeWithChildren(); - for (Entity *c : children) - parentBoundingVolume->expandToContain(*c->worldBoundingVolumeWithChildren()); + for (const HEntity &handle : childrenHandles) { + Entity *c = manager->renderNodesManager()->data(handle); + if (c) + parentBoundingVolume->expandToContain(*c->worldBoundingVolumeWithChildren()); + } } } @@ -74,6 +82,7 @@ void expandWorldBoundingVolume(Qt3DRender::Render::Entity *node) ExpandBoundingVolumeJob::ExpandBoundingVolumeJob() : m_node(nullptr) + , m_manager(nullptr) { SET_JOB_RUN_STAT_TYPE(this, JobTypes::ExpandBoundingVolume, 0); } @@ -83,6 +92,11 @@ void ExpandBoundingVolumeJob::setRoot(Entity *root) m_node = root; } +void ExpandBoundingVolumeJob::setManagers(NodeManagers *manager) +{ + m_manager = manager; +} + void ExpandBoundingVolumeJob::run() { // Expand worldBoundingVolumeWithChildren of each node that has children by the @@ -90,7 +104,7 @@ void ExpandBoundingVolumeJob::run() // TODO: Implement this using a parallel_for qCDebug(Jobs) << "Entering" << Q_FUNC_INFO << QThread::currentThread(); - expandWorldBoundingVolume(m_node); + expandWorldBoundingVolume(m_manager, m_node); qCDebug(Jobs) << "Exiting" << Q_FUNC_INFO << QThread::currentThread(); } diff --git a/src/render/jobs/expandboundingvolumejob_p.h b/src/render/jobs/expandboundingvolumejob_p.h index c577c90e2..a2745b456 100644 --- a/src/render/jobs/expandboundingvolumejob_p.h +++ b/src/render/jobs/expandboundingvolumejob_p.h @@ -62,6 +62,7 @@ namespace Qt3DRender { namespace Render { class Entity; +class NodeManagers; class Q_3DRENDERSHARED_PRIVATE_EXPORT ExpandBoundingVolumeJob : public Qt3DCore::QAspectJob { @@ -69,10 +70,12 @@ public: ExpandBoundingVolumeJob(); void setRoot(Entity *root); + void setManagers(NodeManagers *manager); void run() override; private: Entity *m_node; + NodeManagers *m_manager; }; typedef QSharedPointer<ExpandBoundingVolumeJob> ExpandBoundingVolumeJobPtr; diff --git a/src/render/jobs/framecleanupjob.cpp b/src/render/jobs/framecleanupjob.cpp index 7ebcead37..17ca60bff 100644 --- a/src/render/jobs/framecleanupjob.cpp +++ b/src/render/jobs/framecleanupjob.cpp @@ -78,23 +78,22 @@ void FrameCleanupJob::run() void FrameCleanupJob::updateBoundingVolumesDebug(Entity *node) { + Q_UNUSED(node); #if 0 - BoundingVolumeDebug *debugBV = node->renderComponent<BoundingVolumeDebug>(); - if (debugBV) { - Qt3DRender::Render::Sphere s; - if (!debugBV->isRecursive()) { - s = *node->worldBoundingVolume(); - } else { - s = *node->worldBoundingVolumeWithChildren(); + node->traverse([](Entity *node) { + BoundingVolumeDebug *debugBV = node->renderComponent<BoundingVolumeDebug>(); + if (debugBV) { + Qt3DRender::Render::Sphere s; + if (!debugBV->isRecursive()) { + s = *node->worldBoundingVolume(); + } else { + s = *node->worldBoundingVolumeWithChildren(); + } + debugBV->setRadius(s.radius()); + debugBV->setCenter(s.center()); } - debugBV->setRadius(s.radius()); - debugBV->setCenter(s.center()); - } + }); - - const auto children = node->children(); - for (Entity *c : children) - updateBoundingVolumesDebug(c); #endif } diff --git a/src/render/jobs/frustumcullingjob.cpp b/src/render/jobs/frustumcullingjob.cpp index 1a03b691d..0922fb0cb 100644 --- a/src/render/jobs/frustumcullingjob.cpp +++ b/src/render/jobs/frustumcullingjob.cpp @@ -43,6 +43,8 @@ #include <Qt3DRender/private/entity_p.h> #include <Qt3DRender/private/renderview_p.h> #include <Qt3DRender/private/sphere_p.h> +#include <Qt3DRender/private/managers_p.h> +#include <Qt3DRender/private/nodemanagers_p.h> QT_BEGIN_NAMESPACE @@ -53,6 +55,7 @@ namespace Render { FrustumCullingJob::FrustumCullingJob() : Qt3DCore::QAspectJob() , m_root(nullptr) + , m_manager(nullptr) , m_active(false) { SET_JOB_RUN_STAT_TYPE(this, JobTypes::FrustumCulling, 0); @@ -83,27 +86,25 @@ void FrustumCullingJob::run() void FrustumCullingJob::cullScene(Entity *e, const Plane *planes) { - const Sphere *s = e->worldBoundingVolumeWithChildren(); - - // Unrolled loop - if (Vector3D::dotProduct(s->center(), planes[0].normal) + planes[0].d < -s->radius()) - return; - if (Vector3D::dotProduct(s->center(), planes[1].normal) + planes[1].d < -s->radius()) - return; - if (Vector3D::dotProduct(s->center(), planes[2].normal) + planes[2].d < -s->radius()) - return; - if (Vector3D::dotProduct(s->center(), planes[3].normal) + planes[3].d < -s->radius()) - return; - if (Vector3D::dotProduct(s->center(), planes[4].normal) + planes[4].d < -s->radius()) - return; - if (Vector3D::dotProduct(s->center(), planes[5].normal) + planes[5].d < -s->radius()) - return; - - m_visibleEntities.push_back(e); - - const QVector<Entity *> children = e->children(); - for (Entity *c : children) - cullScene(c, planes); + e->traverse([planes, this](Entity *e) { + const Sphere *s = e->worldBoundingVolumeWithChildren(); + + // Unrolled loop + if (Vector3D::dotProduct(s->center(), planes[0].normal) + planes[0].d < -s->radius()) + return; + if (Vector3D::dotProduct(s->center(), planes[1].normal) + planes[1].d < -s->radius()) + return; + if (Vector3D::dotProduct(s->center(), planes[2].normal) + planes[2].d < -s->radius()) + return; + if (Vector3D::dotProduct(s->center(), planes[3].normal) + planes[3].d < -s->radius()) + return; + if (Vector3D::dotProduct(s->center(), planes[4].normal) + planes[4].d < -s->radius()) + return; + if (Vector3D::dotProduct(s->center(), planes[5].normal) + planes[5].d < -s->radius()) + return; + + m_visibleEntities.push_back(e); + }); } } // Render diff --git a/src/render/jobs/frustumcullingjob_p.h b/src/render/jobs/frustumcullingjob_p.h index f81e4c5b9..cddbbd409 100644 --- a/src/render/jobs/frustumcullingjob_p.h +++ b/src/render/jobs/frustumcullingjob_p.h @@ -63,6 +63,7 @@ namespace Render { class Entity; class EntityManager; +class NodeManagers; struct Plane; class FrustumCullingJob : public Qt3DCore::QAspectJob @@ -73,6 +74,7 @@ public: QT3D_ALIGNED_MALLOC_AND_FREE() inline void setRoot(Entity *root) Q_DECL_NOTHROW { m_root = root; } + inline void setManagers(NodeManagers *manager) Q_DECL_NOTHROW { m_manager = manager; } inline void setActive(bool active) Q_DECL_NOTHROW { m_active = active; } inline bool isActive() const Q_DECL_NOTHROW { return m_active; } inline void setViewProjection(const Matrix4x4 &viewProjection) Q_DECL_NOTHROW { m_viewProjection = viewProjection; } @@ -86,6 +88,7 @@ private: void cullScene(Entity *e, const Plane *planes); Matrix4x4 m_viewProjection; Entity *m_root; + NodeManagers *m_manager; QVector<Entity *> m_visibleEntities; bool m_active; }; diff --git a/src/render/jobs/pickboundingvolumeutils.cpp b/src/render/jobs/pickboundingvolumeutils.cpp index 39069e374..1d22e8645 100644 --- a/src/render/jobs/pickboundingvolumeutils.cpp +++ b/src/render/jobs/pickboundingvolumeutils.cpp @@ -800,12 +800,15 @@ bool HierarchicalEntityPicker::collectHits(NodeManagers *manager, Entity *root) } // and pick children - const auto children = current.entity->children(); - for (Entity *child: children) { - ObjectPicker *childPicker = child->renderComponent<ObjectPicker>(); - worklist.push_back({child, current.hasObjectPicker || childPicker, - current.recursiveLayers + recursiveLayers, - (childPicker ? childPicker->priority() : current.priority)}); + const auto childrenHandles = current.entity->childrenHandles(); + for (const HEntity &handle : childrenHandles) { + Entity *child = manager->renderNodesManager()->data(handle); + if (child) { + ObjectPicker *childPicker = child->renderComponent<ObjectPicker>(); + worklist.push_back({child, current.hasObjectPicker || childPicker, + current.recursiveLayers + recursiveLayers, + (childPicker ? childPicker->priority() : current.priority)}); + } } } diff --git a/src/render/jobs/raycastingjob.cpp b/src/render/jobs/raycastingjob.cpp index 887f203bf..f3571c210 100644 --- a/src/render/jobs/raycastingjob.cpp +++ b/src/render/jobs/raycastingjob.cpp @@ -50,6 +50,7 @@ #include <Qt3DRender/private/renderer_p.h> #include <Qt3DRender/private/rendersettings_p.h> #include <Qt3DRender/private/trianglesvisitor_p.h> +#include <Qt3DRender/private/entityvisitor_p.h> QT_BEGIN_NAMESPACE @@ -59,43 +60,23 @@ using namespace Render; namespace { -class EntityCasterGatherer +class EntityCasterGatherer : public EntityVisitor { public: using EntityCasterList = QVector<QPair<Entity *, RayCaster*>>; + EntityCasterList m_result; - explicit EntityCasterGatherer(Entity *root) : m_root(root), m_needsRefresh(true) { } + explicit EntityCasterGatherer(NodeManagers *manager) : EntityVisitor(manager) { setPruneDisabled(true); } - EntityCasterList result() const { - if (m_needsRefresh) { - m_result.clear(); - m_result = gatherEntities(m_root, std::move(m_result)); - m_needsRefresh = false; + Operation visit(Entity *entity) override { + QVector<RayCaster *> components = entity->renderComponents<RayCaster>(); + for (const auto c: qAsConst(components)) { + if (c->isEnabled()) + m_result.push_back(qMakePair(entity, c)); } - return m_result; - } - -private: - EntityCasterList gatherEntities(Entity *entity, EntityCasterList entities) const - { - if (entity != nullptr && entity->isEnabled()) { - QVector<RayCaster *> components = entity->renderComponents<RayCaster>(); - for (const auto c: qAsConst(components)) { - if (c->isEnabled()) - entities.push_back(qMakePair(entity, c)); - } - // Traverse children - const auto children = entity->children(); - for (Entity *child : children) - entities = gatherEntities(child, std::move(entities)); - } - return entities; + return Continue; } - - Entity *m_root; - mutable EntityCasterList m_result; - mutable bool m_needsRefresh; }; } // anonymous @@ -145,8 +126,9 @@ bool RayCastingJob::runHelper() m_renderSettings->faceOrientationPickingMode() != QPickingSettings::FrontFace; const float pickWorldSpaceTolerance = m_renderSettings->pickWorldSpaceTolerance(); - EntityCasterGatherer gatherer(m_node); - const EntityCasterGatherer::EntityCasterList &entities = gatherer.result(); + EntityCasterGatherer gatherer(m_manager); + gatherer.apply(m_node); + const EntityCasterGatherer::EntityCasterList &entities = gatherer.m_result; PickingUtils::ViewportCameraAreaGatherer vcaGatherer; const QVector<PickingUtils::ViewportCameraAreaDetails> vcaDetails = vcaGatherer.gather(m_frameGraphRoot); diff --git a/src/render/jobs/updateentitylayersjob.cpp b/src/render/jobs/updateentitylayersjob.cpp index 1fa34684f..2c5e38364 100644 --- a/src/render/jobs/updateentitylayersjob.cpp +++ b/src/render/jobs/updateentitylayersjob.cpp @@ -49,19 +49,6 @@ namespace Qt3DRender { namespace Render { -namespace { - -void addLayerIdToEntityChildren(const QVector<Entity *> &children, - const Qt3DCore::QNodeId layerId) -{ - for (Entity *child : children) { - child->addRecursiveLayerId(layerId); - addLayerIdToEntityChildren(child->children(), layerId); - } -} - -} // anonymous - UpdateEntityLayersJob::UpdateEntityLayersJob() : m_manager(nullptr) { @@ -93,7 +80,9 @@ void UpdateEntityLayersJob::run() Layer *layer = layerManager->lookupResource(layerId); if (layer->recursive()) { // Find all children of the entity and add the layers to them - addLayerIdToEntityChildren(entity->children(), layerId); + entity->traverse([layerId](Entity *e) { + e->addRecursiveLayerId(layerId); + }); } } } diff --git a/src/render/jobs/updatelevelofdetailjob.cpp b/src/render/jobs/updatelevelofdetailjob.cpp index e4b651949..b5349a2c1 100644 --- a/src/render/jobs/updatelevelofdetailjob.cpp +++ b/src/render/jobs/updatelevelofdetailjob.cpp @@ -39,6 +39,7 @@ #include "updatelevelofdetailjob_p.h" #include <Qt3DRender/QLevelOfDetail> +#include <Qt3DRender/private/entityvisitor_p.h> #include <Qt3DRender/private/job_common_p.h> #include <Qt3DRender/private/nodemanagers_p.h> #include <Qt3DRender/private/managers_p.h> @@ -57,9 +58,145 @@ double approxRollingAverage(double avg, double input) { return avg; } +class LODUpdateVisitor : public Qt3DRender::Render::EntityVisitor +{ +public: + LODUpdateVisitor(double filterValue, Qt3DRender::Render::FrameGraphNode *frameGraphRoot, Qt3DRender::Render::NodeManagers *manager) + : Qt3DRender::Render::EntityVisitor(manager) + , m_filterValue(filterValue) + , m_frameGraphRoot(frameGraphRoot) + { + } + + double filterValue() const { return m_filterValue; } + + Operation visit(Qt3DRender::Render::Entity *entity = nullptr) override { + using namespace Qt3DRender; + using namespace Qt3DRender::Render; + + if (!entity->isEnabled()) + return Prune; // skip disabled sub-trees, since their bounding box is probably not valid anyway + + QVector<LevelOfDetail *> lods = entity->renderComponents<LevelOfDetail>(); + if (!lods.empty()) { + LevelOfDetail* lod = lods.front(); // other lods are ignored + + if (lod->isEnabled() && !lod->thresholds().isEmpty()) { + switch (lod->thresholdType()) { + case QLevelOfDetail::DistanceToCameraThreshold: + updateEntityLodByDistance(entity, lod); + break; + case QLevelOfDetail::ProjectedScreenPixelSizeThreshold: + updateEntityLodByScreenArea(entity, lod); + break; + default: + Q_ASSERT(false); + break; + } + } + } + + return Continue; + } + +private: + double m_filterValue = 0.; + Qt3DRender::Render::FrameGraphNode *m_frameGraphRoot; + + void updateEntityLodByDistance(Qt3DRender::Render::Entity *entity, Qt3DRender::Render::LevelOfDetail *lod) + { + using namespace Qt3DRender; + using namespace Qt3DRender::Render; + + Matrix4x4 viewMatrix; + Matrix4x4 projectionMatrix; + if (!Render::CameraLens::viewMatrixForCamera(m_manager->renderNodesManager(), lod->camera(), viewMatrix, projectionMatrix)) + return; + + const QVector<qreal> thresholds = lod->thresholds(); + Vector3D center(lod->center()); + if (lod->hasBoundingVolumeOverride() || entity->worldBoundingVolume() == nullptr) { + center = *entity->worldTransform() * center; + } else { + center = entity->worldBoundingVolume()->center(); + } + + const Vector3D tcenter = viewMatrix * center; + const float dist = tcenter.length(); + const int n = thresholds.size(); + for (int i=0; i<n; ++i) { + if (dist <= thresholds[i] || i == n -1) { + m_filterValue = approxRollingAverage<30>(m_filterValue, i); + i = qBound(0, static_cast<int>(qRound(m_filterValue)), n - 1); + if (lod->currentIndex() != i) + lod->setCurrentIndex(i); + break; + } + } + } + + void updateEntityLodByScreenArea(Qt3DRender::Render::Entity *entity, Qt3DRender::Render::LevelOfDetail *lod) + { + using namespace Qt3DRender; + using namespace Qt3DRender::Render; + + Matrix4x4 viewMatrix; + Matrix4x4 projectionMatrix; + if (!Render::CameraLens::viewMatrixForCamera(m_manager->renderNodesManager(), lod->camera(), viewMatrix, projectionMatrix)) + return; + + PickingUtils::ViewportCameraAreaGatherer vcaGatherer(lod->camera()); + const QVector<PickingUtils::ViewportCameraAreaDetails> vcaTriplets = vcaGatherer.gather(m_frameGraphRoot); + if (vcaTriplets.isEmpty()) + return; + + const PickingUtils::ViewportCameraAreaDetails &vca = vcaTriplets.front(); + + const QVector<qreal> thresholds = lod->thresholds(); + Sphere bv(Vector3D(lod->center()), lod->radius()); + if (!lod->hasBoundingVolumeOverride() && entity->worldBoundingVolume() != nullptr) { + bv = *(entity->worldBoundingVolume()); + } else { + bv.transform(*entity->worldTransform()); + } + + bv.transform(projectionMatrix * viewMatrix); + const float sideLength = bv.radius() * 2.f; + float area = vca.viewport.width() * sideLength * vca.viewport.height() * sideLength; + + const QRect r = windowViewport(vca.area, vca.viewport); + area = std::sqrt(area * r.width() * r.height()); + + const int n = thresholds.size(); + for (int i = 0; i < n; ++i) { + if (thresholds[i] < area || i == n -1) { + m_filterValue = approxRollingAverage<30>(m_filterValue, i); + i = qBound(0, static_cast<int>(qRound(m_filterValue)), n - 1); + if (lod->currentIndex() != i) + lod->setCurrentIndex(i); + break; + } + } + } + + QRect windowViewport(const QSize &area, const QRectF &relativeViewport) const + { + if (area.isValid()) { + const int areaWidth = area.width(); + const int areaHeight = area.height(); + return QRect(relativeViewport.x() * areaWidth, + (1.0 - relativeViewport.y() - relativeViewport.height()) * areaHeight, + relativeViewport.width() * areaWidth, + relativeViewport.height() * areaHeight); + } + return relativeViewport.toRect(); + } +}; + } -namespace Qt3DRender { + +namespace Qt3DRender { namespace Render { UpdateLevelOfDetailJob::UpdateLevelOfDetailJob() @@ -98,119 +235,13 @@ void UpdateLevelOfDetailJob::run() if (m_manager->levelOfDetailManager()->count() == 0) return; - updateEntityLod(m_root); -} - -QRect UpdateLevelOfDetailJob::windowViewport(const QSize &area, const QRectF &relativeViewport) const -{ - if (area.isValid()) { - const int areaWidth = area.width(); - const int areaHeight = area.height(); - return QRect(relativeViewport.x() * areaWidth, - (1.0 - relativeViewport.y() - relativeViewport.height()) * areaHeight, - relativeViewport.width() * areaWidth, - relativeViewport.height() * areaHeight); - } - return relativeViewport.toRect(); -} - -void UpdateLevelOfDetailJob::updateEntityLod(Entity *entity) -{ - if (!entity->isEnabled()) - return; // skip disabled sub-trees, since their bounding box is probably not valid anyway - - QVector<LevelOfDetail *> lods = entity->renderComponents<LevelOfDetail>(); - if (!lods.empty()) { - LevelOfDetail* lod = lods.front(); // other lods are ignored - - if (lod->isEnabled() && !lod->thresholds().isEmpty()) { - switch (lod->thresholdType()) { - case QLevelOfDetail::DistanceToCameraThreshold: - updateEntityLodByDistance(entity, lod); - break; - case QLevelOfDetail::ProjectedScreenPixelSizeThreshold: - updateEntityLodByScreenArea(entity, lod); - break; - default: - Q_ASSERT(false); - break; - } - } - } - - const auto children = entity->children(); - for (Qt3DRender::Render::Entity *child : children) - updateEntityLod(child); -} - -void UpdateLevelOfDetailJob::updateEntityLodByDistance(Entity *entity, LevelOfDetail *lod) -{ - Matrix4x4 viewMatrix; - Matrix4x4 projectionMatrix; - if (!Render::CameraLens::viewMatrixForCamera(m_manager->renderNodesManager(), lod->camera(), viewMatrix, projectionMatrix)) - return; - - const QVector<qreal> thresholds = lod->thresholds(); - Vector3D center(lod->center()); - if (lod->hasBoundingVolumeOverride() || entity->worldBoundingVolume() == nullptr) { - center = *entity->worldTransform() * center; - } else { - center = entity->worldBoundingVolume()->center(); - } - - const Vector3D tcenter = viewMatrix * center; - const float dist = tcenter.length(); - const int n = thresholds.size(); - for (int i=0; i<n; ++i) { - if (dist <= thresholds[i] || i == n -1) { - m_filterValue = approxRollingAverage<30>(m_filterValue, i); - i = qBound(0, static_cast<int>(qRound(m_filterValue)), n - 1); - if (lod->currentIndex() != i) - lod->setCurrentIndex(i); - break; - } - } -} - -void UpdateLevelOfDetailJob::updateEntityLodByScreenArea(Entity *entity, LevelOfDetail *lod) -{ - Matrix4x4 viewMatrix; - Matrix4x4 projectionMatrix; - if (!Render::CameraLens::viewMatrixForCamera(m_manager->renderNodesManager(), lod->camera(), viewMatrix, projectionMatrix)) - return; - - PickingUtils::ViewportCameraAreaGatherer vcaGatherer(lod->camera()); - const QVector<PickingUtils::ViewportCameraAreaDetails> vcaTriplets = vcaGatherer.gather(m_frameGraphRoot); - if (vcaTriplets.isEmpty()) - return; - - const PickingUtils::ViewportCameraAreaDetails &vca = vcaTriplets.front(); - const QVector<qreal> thresholds = lod->thresholds(); - Sphere bv(Vector3D(lod->center()), lod->radius()); - if (!lod->hasBoundingVolumeOverride() && entity->worldBoundingVolume() != nullptr) { - bv = *(entity->worldBoundingVolume()); - } else { - bv.transform(*entity->worldTransform()); - } + if (m_manager->levelOfDetailManager()->count() == 0) + return; // no LODs, lets bail out early - bv.transform(projectionMatrix * viewMatrix); - const float sideLength = bv.radius() * 2.f; - float area = vca.viewport.width() * sideLength * vca.viewport.height() * sideLength; - - const QRect r = windowViewport(vca.area, vca.viewport); - area = std::sqrt(area * r.width() * r.height()); - - const int n = thresholds.size(); - for (int i = 0; i < n; ++i) { - if (thresholds[i] < area || i == n -1) { - m_filterValue = approxRollingAverage<30>(m_filterValue, i); - i = qBound(0, static_cast<int>(qRound(m_filterValue)), n - 1); - if (lod->currentIndex() != i) - lod->setCurrentIndex(i); - break; - } - } + LODUpdateVisitor visitor(m_filterValue, m_frameGraphRoot, m_manager); + visitor.apply(m_root); + m_filterValue = visitor.filterValue(); } } // Render diff --git a/src/render/jobs/updatelevelofdetailjob_p.h b/src/render/jobs/updatelevelofdetailjob_p.h index c7045ee7f..3c7d00d2c 100644 --- a/src/render/jobs/updatelevelofdetailjob_p.h +++ b/src/render/jobs/updatelevelofdetailjob_p.h @@ -81,12 +81,6 @@ public: Entity *root() const { return m_root; } private: - void updateEntityLod(Entity *entity); - void updateEntityLodByDistance(Entity *entity, LevelOfDetail *lod); - void updateEntityLodByScreenArea(Entity *entity, LevelOfDetail *lod); - - QRect windowViewport(const QSize &area, const QRectF &relativeViewport) const; - NodeManagers *m_manager; FrameGraphNode *m_frameGraphRoot; Entity *m_root; diff --git a/src/render/jobs/updateskinningpalettejob.cpp b/src/render/jobs/updateskinningpalettejob.cpp index 1ee9101f9..0f5d3d6d6 100644 --- a/src/render/jobs/updateskinningpalettejob.cpp +++ b/src/render/jobs/updateskinningpalettejob.cpp @@ -79,7 +79,11 @@ void UpdateSkinningPaletteJob::run() // Find all the armature components and update their skinning palettes QVector<HArmature> dirtyArmatures; - findDirtyArmatures(m_root, dirtyArmatures); + m_root->traverse([&dirtyArmatures](Entity *entity) { + const auto armatureHandle = entity->componentHandle<Armature>(); + if (!armatureHandle.isNull() && !dirtyArmatures.contains(armatureHandle)) + dirtyArmatures.push_back(armatureHandle); + }); // Update the skeleton for each dirty armature auto skeletonManager = m_nodeManagers->skeletonManager(); @@ -96,22 +100,6 @@ void UpdateSkinningPaletteJob::run() } } -void UpdateSkinningPaletteJob::findDirtyArmatures(Entity *entity, - QVector<HArmature> &armatures) const -{ - // Just return all enabled armatures found on entities for now - // TODO: Be smarter about limiting which armatures we update. For e.g. only - // those with skeletons that have changed and only those that are within view - // of one or more renderviews. - const auto armatureHandle = entity->componentHandle<Armature>(); - if (!armatureHandle.isNull() && !armatures.contains(armatureHandle)) - armatures.push_back(armatureHandle); - - const auto children = entity->children(); - for (const auto child : children) - findDirtyArmatures(child, armatures); -} - } // namespace Render } // namespace Qt3DRender diff --git a/src/render/jobs/updateskinningpalettejob_p.h b/src/render/jobs/updateskinningpalettejob_p.h index 9e230f143..c52e0841c 100644 --- a/src/render/jobs/updateskinningpalettejob_p.h +++ b/src/render/jobs/updateskinningpalettejob_p.h @@ -75,7 +75,6 @@ public: protected: void run() override; - void findDirtyArmatures(Entity *entity, QVector<HArmature> &armatures) const; NodeManagers *m_nodeManagers; Entity *m_root; QVector<HJoint> m_dirtyJoints; diff --git a/src/render/jobs/updatetreeenabledjob.cpp b/src/render/jobs/updatetreeenabledjob.cpp index 6475ea78c..e97fc6414 100644 --- a/src/render/jobs/updatetreeenabledjob.cpp +++ b/src/render/jobs/updatetreeenabledjob.cpp @@ -41,6 +41,8 @@ #include <Qt3DRender/private/entity_p.h> #include <Qt3DRender/private/job_common_p.h> +#include <Qt3DRender/private/managers_p.h> +#include <Qt3DRender/private/nodemanagers_p.h> #include <QThread> @@ -51,14 +53,17 @@ namespace Render { namespace { -void updateTreeEnabled(Entity *node, bool parentEnabled) +void updateTreeEnabled(NodeManagers *manager, Entity *node, bool parentEnabled) { const bool treeEnabled = node->isEnabled() && parentEnabled; node->setTreeEnabled(treeEnabled); - const QVector<Entity*> children = node->children(); - for (Entity *child : children) - updateTreeEnabled(child, treeEnabled); + const auto childrenHandles = node->childrenHandles(); + for (const HEntity &handle : childrenHandles) { + Entity *child = manager->renderNodesManager()->data(handle); + if (child) + updateTreeEnabled(manager, child, treeEnabled); + } } } @@ -75,10 +80,15 @@ void UpdateTreeEnabledJob::setRoot(Entity *root) m_node = root; } +void UpdateTreeEnabledJob::setManagers(NodeManagers *manager) +{ + m_manager = manager; +} + void UpdateTreeEnabledJob::run() { - if (m_node) - updateTreeEnabled(m_node, true); + if (m_node && m_manager) + updateTreeEnabled(m_manager, m_node, true); } } // namespace Render diff --git a/src/render/jobs/updatetreeenabledjob_p.h b/src/render/jobs/updatetreeenabledjob_p.h index 6eac75ea6..ba28998bc 100644 --- a/src/render/jobs/updatetreeenabledjob_p.h +++ b/src/render/jobs/updatetreeenabledjob_p.h @@ -62,6 +62,7 @@ namespace Qt3DRender { namespace Render { class Entity; +class NodeManagers; class Q_3DRENDERSHARED_PRIVATE_EXPORT UpdateTreeEnabledJob : public Qt3DCore::QAspectJob { @@ -69,10 +70,12 @@ public: UpdateTreeEnabledJob(); void setRoot(Entity *root); + void setManagers(NodeManagers *manager); void run() override; private: Entity *m_node; + NodeManagers *m_manager; }; typedef QSharedPointer<UpdateTreeEnabledJob> UpdateTreeEnabledJobPtr; diff --git a/src/render/jobs/updateworldtransformjob.cpp b/src/render/jobs/updateworldtransformjob.cpp index c56ed8507..1a9697843 100644 --- a/src/render/jobs/updateworldtransformjob.cpp +++ b/src/render/jobs/updateworldtransformjob.cpp @@ -44,6 +44,8 @@ #include <Qt3DRender/private/transform_p.h> #include <Qt3DRender/private/renderlogging_p.h> #include <Qt3DRender/private/job_common_p.h> +#include <Qt3DRender/private/managers_p.h> +#include <Qt3DRender/private/nodemanagers_p.h> #include <QThread> @@ -54,7 +56,7 @@ namespace Render { namespace { -void updateWorldTransformAndBounds(Qt3DRender::Render::Entity *node, const Matrix4x4 &parentTransform) +void updateWorldTransformAndBounds(NodeManagers *manager, Entity *node, const Matrix4x4 &parentTransform) { Matrix4x4 worldTransform(parentTransform); Transform *nodeTransform = node->renderComponent<Transform>(); @@ -64,9 +66,12 @@ void updateWorldTransformAndBounds(Qt3DRender::Render::Entity *node, const Matri *(node->worldTransform()) = worldTransform; - const auto children = node->children(); - for (Qt3DRender::Render::Entity *child : children) - updateWorldTransformAndBounds(child, worldTransform); + const auto childrenHandles = node->childrenHandles(); + for (const HEntity &handle : childrenHandles) { + Entity *child = manager->renderNodesManager()->data(handle); + if (child) + updateWorldTransformAndBounds(manager, child, worldTransform); + } } } @@ -74,6 +79,7 @@ void updateWorldTransformAndBounds(Qt3DRender::Render::Entity *node, const Matri UpdateWorldTransformJob::UpdateWorldTransformJob() : Qt3DCore::QAspectJob() , m_node(nullptr) + , m_manager(nullptr) { SET_JOB_RUN_STAT_TYPE(this, JobTypes::UpdateTransform, 0); } @@ -83,6 +89,11 @@ void UpdateWorldTransformJob::setRoot(Entity *root) m_node = root; } +void UpdateWorldTransformJob::setManagers(NodeManagers *manager) +{ + m_manager = manager; +} + void UpdateWorldTransformJob::run() { // Iterate over each level of hierarchy in our scene @@ -98,7 +109,7 @@ void UpdateWorldTransformJob::run() Entity *parent = m_node->parent(); if (parent != nullptr) parentTransform = *(parent->worldTransform()); - updateWorldTransformAndBounds(m_node, parentTransform); + updateWorldTransformAndBounds(m_manager, m_node, parentTransform); qCDebug(Jobs) << "Exiting" << Q_FUNC_INFO << QThread::currentThread(); } diff --git a/src/render/jobs/updateworldtransformjob_p.h b/src/render/jobs/updateworldtransformjob_p.h index a0ef25679..2689fe45a 100644 --- a/src/render/jobs/updateworldtransformjob_p.h +++ b/src/render/jobs/updateworldtransformjob_p.h @@ -62,6 +62,7 @@ namespace Qt3DRender { namespace Render { class Entity; +class NodeManagers; class Q_3DRENDERSHARED_PRIVATE_EXPORT UpdateWorldTransformJob : public Qt3DCore::QAspectJob { @@ -69,10 +70,13 @@ public: UpdateWorldTransformJob(); void setRoot(Entity *root); + void setManagers(NodeManagers *manager); + void run() override; private: Entity *m_node; + NodeManagers *m_manager; }; typedef QSharedPointer<UpdateWorldTransformJob> UpdateWorldTransformJobPtr; diff --git a/src/render/renderers/opengl/renderer/renderer.cpp b/src/render/renderers/opengl/renderer/renderer.cpp index 614439f0b..68d31be71 100644 --- a/src/render/renderers/opengl/renderer/renderer.cpp +++ b/src/render/renderers/opengl/renderer/renderer.cpp @@ -193,6 +193,7 @@ Renderer::Renderer(QRenderAspect::RenderType type) , m_updateMeshTriangleListJob(Render::UpdateMeshTriangleListJobPtr::create()) , m_filterCompatibleTechniqueJob(Render::FilterCompatibleTechniqueJobPtr::create()) , m_updateEntityLayersJob(Render::UpdateEntityLayersJobPtr::create()) + , m_updateEntityHierarchyJob(Render::UpdateEntityHierarchyJobPtr::create()) , m_bufferGathererJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { lookForDirtyBuffers(); }, JobTypes::DirtyBufferGathering)) , m_vaoGathererJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { lookForAbandonedVaos(); }, JobTypes::DirtyVaoGathering)) , m_textureGathererJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { lookForDirtyTextures(); }, JobTypes::DirtyTextureGathering)) @@ -200,7 +201,6 @@ 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) @@ -292,6 +292,8 @@ void Renderer::setNodeManagers(NodeManagers *managers) m_updateShaderDataTransformJob->setManagers(m_nodesManager); m_cleanupJob->setManagers(m_nodesManager); m_calculateBoundingVolumeJob->setManagers(m_nodesManager); + m_expandBoundingVolumeJob->setManagers(m_nodesManager); + m_worldTransformJob->setManagers(m_nodesManager); m_pickBoundingVolumeJob->setManagers(m_nodesManager); m_rayCastingJob->setManagers(m_nodesManager); m_updateWorldBoundingVolumeJob->setManager(m_nodesManager->renderNodesManager()); @@ -301,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_updateTreeEnabledJob->setManagers(m_nodesManager); m_updateEntityHierarchyJob->setManager(m_nodesManager); } diff --git a/src/render/texture/qtexture.cpp b/src/render/texture/qtexture.cpp index fc62b76ff..5fd2f4169 100644 --- a/src/render/texture/qtexture.cpp +++ b/src/render/texture/qtexture.cpp @@ -1258,6 +1258,13 @@ void QTextureLoaderPrivate::updateGenerator() \brief Handles the texture loading and setting the texture's properties. */ /*! + \qmltype TextureLoader + \instantiates Qt3DRender::QTextureLoader + \inqmlmodule Qt3D.Render + + \brief Handles the texture loading and setting the texture's properties. +*/ +/*! * Constructs a new Qt3DRender::QTextureLoader instance with \a parent as parent. * * Note that by default, if not contradicted by the file metadata, the loaded texture @@ -1295,7 +1302,12 @@ QTextureLoader::~QTextureLoader() /*! \property QTextureLoader::source - Returns the current texture source. + \brief The current texture source. +*/ +/*! + \qmlproperty url Qt3D.Render::TextureLoader::source + + This property holds the current texture source. */ QUrl QTextureLoader::source() const { @@ -1347,7 +1359,7 @@ void QTextureLoader::setSource(const QUrl& source) */ /*! - \qmlproperty bool Qt3DRender::QTextureLoader::mirrored + \qmlproperty bool Qt3D.Render::TextureLoader::mirrored This property specifies whether the texture should be mirrored when loaded. This is a convenience to avoid having to manipulate images to match the origin of |