summaryrefslogtreecommitdiffstats
path: root/src/render
diff options
context:
space:
mode:
authorPaul Lemire <paul.lemire@kdab.com>2019-06-03 12:29:29 +0200
committerLiang Qi <liang.qi@qt.io>2019-06-03 12:29:52 +0200
commit4eee1ab5a51ac482e2252e2e85c0fe624d5d4bad (patch)
tree71fb24fc69b2f77e08a57c56011ee5b72f23a52a /src/render
parent79aa74b5d9df52605a8833e3853b422ca8e7a651 (diff)
parent666a645d1edc98dfea92fdaf7eaeed10c86dbc56 (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')
-rw-r--r--src/render/backend/cameralens.cpp1
-rw-r--r--src/render/backend/entity.cpp20
-rw-r--r--src/render/backend/entity_p.h2
-rw-r--r--src/render/backend/entityaccumulator.cpp99
-rw-r--r--src/render/backend/entityaccumulator_p.h83
-rw-r--r--src/render/backend/entityvisitor.cpp113
-rw-r--r--src/render/backend/entityvisitor_p.h95
-rw-r--r--src/render/backend/render-backend.pri4
-rw-r--r--src/render/framegraph/qclearbuffers.cpp2
-rw-r--r--src/render/jobs/calcboundingvolumejob.cpp78
-rw-r--r--src/render/jobs/computefilteredboundingvolumejob.cpp22
-rw-r--r--src/render/jobs/computefilteredboundingvolumejob_p.h3
-rw-r--r--src/render/jobs/expandboundingvolumejob.cpp30
-rw-r--r--src/render/jobs/expandboundingvolumejob_p.h3
-rw-r--r--src/render/jobs/framecleanupjob.cpp27
-rw-r--r--src/render/jobs/frustumcullingjob.cpp43
-rw-r--r--src/render/jobs/frustumcullingjob_p.h3
-rw-r--r--src/render/jobs/pickboundingvolumeutils.cpp15
-rw-r--r--src/render/jobs/raycastingjob.cpp44
-rw-r--r--src/render/jobs/updateentitylayersjob.cpp17
-rw-r--r--src/render/jobs/updatelevelofdetailjob.cpp255
-rw-r--r--src/render/jobs/updatelevelofdetailjob_p.h6
-rw-r--r--src/render/jobs/updateskinningpalettejob.cpp22
-rw-r--r--src/render/jobs/updateskinningpalettejob_p.h1
-rw-r--r--src/render/jobs/updatetreeenabledjob.cpp22
-rw-r--r--src/render/jobs/updatetreeenabledjob_p.h3
-rw-r--r--src/render/jobs/updateworldtransformjob.cpp21
-rw-r--r--src/render/jobs/updateworldtransformjob_p.h4
-rw-r--r--src/render/renderers/opengl/renderer/renderer.cpp5
-rw-r--r--src/render/texture/qtexture.cpp16
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