summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Krus <mike.krus@kdab.com>2017-01-25 08:07:42 +0000
committerMike Krus <mike.krus@kdab.com>2017-05-23 17:31:00 +0000
commit939b9b4b7591e8a421cf048a0a84ed3e75d81d21 (patch)
tree828ee7a6862ec5c0bd24f97cb540625a2c647376
parente27ce2bf3b76ab4ed09508f780a8601875ea2fe8 (diff)
Add support to move the camera so that the entire model is visible
Adds QCamera::viewAll(), QCamera::viewEntity() and QCamera::viewSphere() to move and rotate the camera so that the view center is the center of the sphere and the sphere fits inside the viewport Only works in perspective projection. Introduces a job to compute the bounding sphere of the scene excluding the camera. Change-Id: Id9d67787ea91c354009d5358d5db63a1c9480c70 Reviewed-by: Sean Harmer <sean.harmer@kdab.com>
-rw-r--r--examples/qt3d/controls/Logo.qml14
-rw-r--r--examples/qt3d/controls/main.qml9
-rw-r--r--src/render/backend/abstractrenderer_p.h1
-rw-r--r--src/render/backend/cameralens.cpp88
-rw-r--r--src/render/backend/cameralens_p.h7
-rw-r--r--src/render/backend/renderer.cpp5
-rw-r--r--src/render/backend/renderer_p.h2
-rw-r--r--src/render/frontend/qcamera.cpp82
-rw-r--r--src/render/frontend/qcamera.h4
-rw-r--r--src/render/frontend/qcameralens.cpp53
-rw-r--r--src/render/frontend/qcameralens.h5
-rw-r--r--src/render/frontend/qcameralens_p.h6
-rw-r--r--src/render/jobs/computefilteredboundingvolumejob.cpp130
-rw-r--r--src/render/jobs/computefilteredboundingvolumejob_p.h91
-rw-r--r--src/render/jobs/jobs.pri2
-rw-r--r--tests/auto/render/commons/testrenderer.h1
16 files changed, 493 insertions, 7 deletions
diff --git a/examples/qt3d/controls/Logo.qml b/examples/qt3d/controls/Logo.qml
index a686e29bd..2eb9abd1a 100644
--- a/examples/qt3d/controls/Logo.qml
+++ b/examples/qt3d/controls/Logo.qml
@@ -56,6 +56,18 @@ import Qt3D.Extras 2.0
Entity {
id: sceneRoot
+ readonly property double cameraZ: camera.position.z
+
+ function viewAll() {
+ camera.viewAll()
+ }
+ function viewLogo() {
+ camera.viewEntity(logoEntity)
+ }
+ function setPositionZ(z) {
+ camera.position = Qt.vector3d( 0.0, 0.0, z )
+ }
+
Camera {
id: camera
projectionType: CameraLens.PerspectiveProjection
@@ -63,7 +75,7 @@ Entity {
aspectRatio: 4/3
nearPlane : 0.1
farPlane : 1000.0
- position: Qt.vector3d( 0.0, 0.0, viewCenter_z.value )
+ position: Qt.vector3d( 0.0, 0.0, 7.5 )
upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
}
diff --git a/examples/qt3d/controls/main.qml b/examples/qt3d/controls/main.qml
index 2b438ac20..5d1f9c02c 100644
--- a/examples/qt3d/controls/main.qml
+++ b/examples/qt3d/controls/main.qml
@@ -178,13 +178,20 @@ Item {
spacing: 5
Text { text: "Camera"; font.bold: true }
- Text { text: "View Center Z" }
+ Text { text: "View Ctr Z: " + watch.cameraZ.toFixed(2) }
Slider {
id: viewCenter_z
Layout.fillWidth: true
minimumValue: 4
maximumValue: 12
value: 7.5
+ onValueChanged: watch.setPositionZ(value)
+ }
+ Button {
+ id: viewAll
+ Layout.fillWidth: true
+ text: "View All"
+ onClicked: watch.viewLogo()
}
}
diff --git a/src/render/backend/abstractrenderer_p.h b/src/render/backend/abstractrenderer_p.h
index f8d9850e7..2f7545454 100644
--- a/src/render/backend/abstractrenderer_p.h
+++ b/src/render/backend/abstractrenderer_p.h
@@ -139,6 +139,7 @@ public:
virtual QVector<Qt3DCore::QAspectJobPtr> renderBinJobs() = 0;
virtual Qt3DCore::QAspectJobPtr pickBoundingVolumeJob() = 0;
virtual Qt3DCore::QAspectJobPtr syncTextureLoadingJob() = 0;
+ virtual Qt3DCore::QAspectJobPtr expandBoundingVolumeJob() = 0;
virtual void setSceneRoot(Qt3DCore::QBackendNodeFactory *factory, Entity *root) = 0;
diff --git a/src/render/backend/cameralens.cpp b/src/render/backend/cameralens.cpp
index 5bb3ca863..ef71507eb 100644
--- a/src/render/backend/cameralens.cpp
+++ b/src/render/backend/cameralens.cpp
@@ -39,10 +39,14 @@
#include "cameralens_p.h"
#include <Qt3DRender/qcameralens.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/managers_p.h>
#include <Qt3DRender/private/qcameralens_p.h>
#include <Qt3DRender/private/renderlogging_p.h>
-#include <Qt3DRender/private/managers_p.h>
-#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/renderer_p.h>
+#include <Qt3DRender/private/entity_p.h>
+#include <Qt3DRender/private/sphere_p.h>
+#include <Qt3DRender/private/computefilteredboundingvolumejob_p.h>
#include <Qt3DCore/qentity.h>
#include <Qt3DCore/qpropertyupdatedchange.h>
#include <Qt3DCore/qtransform.h>
@@ -54,8 +58,33 @@ using namespace Qt3DCore;
namespace Qt3DRender {
namespace Render {
+
+namespace {
+
+class GetBoundingVolumeWithoutCameraJob : public ComputeFilteredBoundingVolumeJob
+{
+public:
+ GetBoundingVolumeWithoutCameraJob(CameraLens *lens,
+ QNodeCommand::CommandId commandId)
+ : m_lens(lens), m_commandId(commandId)
+ {
+ }
+
+protected:
+ void finished(const Sphere &sphere) override
+ {
+ m_lens->notifySceneBoundingVolume(sphere, m_commandId);
+ }
+
+private:
+ CameraLens *m_lens;
+ QNodeCommand::CommandId m_commandId;
+};
+
+} // namespace
+
CameraLens::CameraLens()
- : BackendNode()
+ : BackendNode(QBackendNode::ReadWrite)
, m_renderAspect(nullptr)
, m_exposure(0.0f)
{
@@ -84,6 +113,41 @@ void CameraLens::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &c
m_exposure = data.exposure;
}
+void CameraLens::computeSceneBoundingVolume(QNodeId entityId,
+ QNodeId cameraId,
+ QNodeCommand::CommandId commandId)
+{
+ if (!m_renderer || !m_renderAspect)
+ return;
+ NodeManagers *nodeManagers = m_renderer->nodeManagers();
+
+ Entity *root = m_renderer->sceneRoot();
+ if (!entityId.isNull())
+ root = nodeManagers->renderNodesManager()->lookupResource(entityId);
+ if (!root)
+ return;
+
+ Entity *camNode = nodeManagers->renderNodesManager()->lookupResource(cameraId);
+ ComputeFilteredBoundingVolumeJobPtr job(new GetBoundingVolumeWithoutCameraJob(this, commandId));
+ job->addDependency(m_renderer->expandBoundingVolumeJob());
+ job->setRoot(root);
+ job->ignoreSubTree(camNode);
+ m_renderAspect->scheduleSingleShotJob(job);
+}
+
+void CameraLens::notifySceneBoundingVolume(const Sphere &sphere, QNodeCommand::CommandId commandId)
+{
+ if (m_pendingViewAllCommand != commandId)
+ return;
+ if (sphere.radius() > 0.f) {
+ QVector<float> data = { sphere.center().x(), sphere.center().y(), sphere.center().z(),
+ sphere.radius() };
+ QVariant v;
+ v.setValue(data);
+ sendCommand(QLatin1Literal("ViewAll"), v, m_pendingViewAllCommand);
+ }
+}
+
void CameraLens::setProjection(const QMatrix4x4 &projection)
{
m_projection = projection;
@@ -111,6 +175,24 @@ void CameraLens::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
}
break;
+ case CommandRequested: {
+ QNodeCommandPtr command = qSharedPointerCast<QNodeCommand>(e);
+
+ if (command->name() == QLatin1Literal("QueryRootBoundingVolume")) {
+ m_pendingViewAllCommand = command->commandId();
+ QVariant v = command->data();
+ QNodeId id = v.value<QNodeId>();
+ computeSceneBoundingVolume({}, id, command->commandId());
+ } else if (command->name() == QLatin1Literal("QueryEntityBoundingVolume")) {
+ m_pendingViewAllCommand = command->commandId();
+ QVariant v = command->data();
+ QVector<QNodeId> ids = v.value<QVector<QNodeId>>();
+ if (ids.size() == 2)
+ computeSceneBoundingVolume(ids[0], ids[1], command->commandId());
+ }
+ }
+ break;
+
default:
break;
}
diff --git a/src/render/backend/cameralens_p.h b/src/render/backend/cameralens_p.h
index 4d9df84e1..4bf147f48 100644
--- a/src/render/backend/cameralens_p.h
+++ b/src/render/backend/cameralens_p.h
@@ -52,6 +52,7 @@
//
#include <Qt3DRender/private/backendnode_p.h>
+#include <Qt3DCore/private/qnodecommand_p.h>
#include <QMatrix4x4>
#include <QRectF>
@@ -62,6 +63,7 @@ namespace Qt3DRender {
namespace Render {
class CameraManager;
+class Sphere;
class CameraLensFunctor : public Qt3DCore::QBackendNodeMapper
{
@@ -93,12 +95,17 @@ public:
inline float exposure() const { return m_exposure; }
void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) Q_DECL_OVERRIDE;
+ void notifySceneBoundingVolume(const Sphere &sphere, Qt3DCore::QNodeCommand::CommandId commandId);
private:
void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) Q_DECL_FINAL;
+ void computeSceneBoundingVolume(Qt3DCore::QNodeId entityId,
+ Qt3DCore::QNodeId cameraId,
+ Qt3DCore::QNodeCommand::CommandId commandId);
QRenderAspect *m_renderAspect;
QMatrix4x4 m_projection;
+ Qt3DCore::QNodeCommand::CommandId m_pendingViewAllCommand;
float m_exposure;
};
diff --git a/src/render/backend/renderer.cpp b/src/render/backend/renderer.cpp
index 6cc0dba7b..b89f21522 100644
--- a/src/render/backend/renderer.cpp
+++ b/src/render/backend/renderer.cpp
@@ -1467,6 +1467,11 @@ QAspectJobPtr Renderer::syncTextureLoadingJob()
return m_syncTextureLoadingJob;
}
+QAspectJobPtr Renderer::expandBoundingVolumeJob()
+{
+ return m_expandBoundingVolumeJob;
+}
+
QAbstractFrameAdvanceService *Renderer::frameAdvanceService() const
{
return static_cast<Qt3DCore::QAbstractFrameAdvanceService *>(m_vsyncFrameAdvanceService.data());
diff --git a/src/render/backend/renderer_p.h b/src/render/backend/renderer_p.h
index de0b2a59b..e311261e0 100644
--- a/src/render/backend/renderer_p.h
+++ b/src/render/backend/renderer_p.h
@@ -189,11 +189,11 @@ public:
QVector<Qt3DCore::QAspectJobPtr> renderBinJobs() Q_DECL_OVERRIDE;
Qt3DCore::QAspectJobPtr pickBoundingVolumeJob() Q_DECL_OVERRIDE;
Qt3DCore::QAspectJobPtr syncTextureLoadingJob() Q_DECL_OVERRIDE;
+ Qt3DCore::QAspectJobPtr expandBoundingVolumeJob() Q_DECL_OVERRIDE;
QVector<Qt3DCore::QAspectJobPtr> createRenderBufferJobs() const;
inline FrameCleanupJobPtr frameCleanupJob() const { return m_cleanupJob; }
- inline ExpandBoundingVolumeJobPtr expandBoundingVolumeJob() const { return m_expandBoundingVolumeJob; }
inline UpdateShaderDataTransformJobPtr updateShaderDataTransformJob() const { return m_updateShaderDataTransformJob; }
inline CalculateBoundingVolumeJobPtr calculateBoundingVolumeJob() const { return m_calculateBoundingVolumeJob; }
inline UpdateTreeEnabledJobPtr updateTreeEnabledJob() const { return m_updateTreeEnabledJob; }
diff --git a/src/render/frontend/qcamera.cpp b/src/render/frontend/qcamera.cpp
index 38fda277f..13d689e0e 100644
--- a/src/render/frontend/qcamera.cpp
+++ b/src/render/frontend/qcamera.cpp
@@ -40,6 +40,8 @@
#include "qcamera.h"
#include "qcamera_p.h"
+#include <QtMath>
+
QT_BEGIN_NAMESPACE
namespace Qt3DRender {
@@ -199,6 +201,36 @@ QCameraPrivate::QCameraPrivate()
*/
/*!
+ * \qmlmethod void Qt3D.Render::Camera::viewAll()
+ *
+ * Rotates and moves the camera so that it's viewCenter is the center of the scene's bounding volume
+ * and the entire scene fits in the view port.
+ *
+ * \note Only works if the lens is in perspective projection mode.
+ * \sa Qt3D.Render::Camera::projectionType
+ */
+
+/*!
+ * \qmlmethod void Qt3D.Render::Camera::viewEntity(Entity entity)
+ *
+ * Rotates and moves the camera so that it's viewCenter is the center of the entity's bounding volume
+ * and the entire entity fits in the view port.
+ *
+ * \note Only works if the lens is in perspective projection mode.
+ * \sa Qt3D.Render::Camera::projectionType
+ */
+
+/*!
+ * \qmlmethod void Qt3D.Render::Camera::viewSphere(vector3d center, real radius)
+ *
+ * Rotates and moves the camera so that it's viewCenter is \a center
+ * and a sphere of \a radius fits in the view port.
+ *
+ * \note Only works if the lens is in perspective projection mode.
+ * \sa Qt3D.Render::Camera::projectionType
+ */
+
+/*!
* \qmlproperty enumeration Qt3D.Render::Camera::projectionType
*
* Holds the type of the camera projection.
@@ -393,6 +425,7 @@ QCamera::QCamera(Qt3DCore::QNode *parent)
QObject::connect(d_func()->m_lens, SIGNAL(topChanged(float)), this, SIGNAL(topChanged(float)));
QObject::connect(d_func()->m_lens, SIGNAL(projectionMatrixChanged(const QMatrix4x4 &)), this, SIGNAL(projectionMatrixChanged(const QMatrix4x4 &)));
QObject::connect(d_func()->m_lens, SIGNAL(exposureChanged(float)), this, SIGNAL(exposureChanged(float)));
+ QObject::connect(d_func()->m_lens, &QCameraLens::viewSphere, this, &QCamera::viewSphere);
QObject::connect(d_func()->m_transform, SIGNAL(matrixChanged()), this, SIGNAL(viewMatrixChanged()));
addComponent(d_func()->m_lens);
addComponent(d_func()->m_transform);
@@ -421,6 +454,7 @@ QCamera::QCamera(QCameraPrivate &dd, Qt3DCore::QNode *parent)
QObject::connect(d_func()->m_lens, SIGNAL(bottomChanged(float)), this, SIGNAL(bottomChanged(float)));
QObject::connect(d_func()->m_lens, SIGNAL(topChanged(float)), this, SIGNAL(topChanged(float)));
QObject::connect(d_func()->m_lens, SIGNAL(projectionMatrixChanged(const QMatrix4x4 &)), this, SIGNAL(projectionMatrixChanged(const QMatrix4x4 &)));
+ QObject::connect(d_func()->m_lens, &QCameraLens::viewSphere, this, &QCamera::viewSphere);
QObject::connect(d_func()->m_transform, SIGNAL(matrixChanged()), this, SIGNAL(viewMatrixChanged()));
addComponent(d_func()->m_lens);
addComponent(d_func()->m_transform);
@@ -636,6 +670,54 @@ void QCamera::rotateAboutViewCenter(const QQuaternion& q)
}
/*!
+ * Rotates and moves the camera so that it's viewCenter is the center of the scene's bounding volume
+ * and the entire scene fits in the view port.
+ *
+ * \note Only works if the lens is in perspective projection mode.
+ * \sa Qt3D.Render::Camera::projectionType
+ */
+void QCamera::viewAll()
+{
+ Q_D(QCamera);
+ d->m_lens->viewAll(id());
+}
+
+/*!
+ * Rotates and moves the camera so that it's viewCenter is \a center
+ * and a sphere of \a radius fits in the view port.
+ *
+ * \note Only works if the lens is in perspective projection mode.
+ * \sa Qt3D.Render::Camera::projectionType
+ */
+void QCamera::viewSphere(const QVector3D &center, float radius)
+{
+ Q_D(QCamera);
+ if (d->m_lens->projectionType() != QCameraLens::PerspectiveProjection || radius <= 0.f)
+ return;
+ double dist = radius / std::tan(qDegreesToRadians(d->m_lens->fieldOfView()) / 2.0f);
+ QVector3D dir = (d->m_viewCenter - d->m_position).normalized();
+ QVector3D newPos = center - (dir * dist);
+ setViewCenter(center);
+ setPosition(newPos);
+}
+
+/*!
+ * Rotates and moves the camera so that it's viewCenter is the center of the entity's bounding volume
+ * and the entire entity fits in the view port.
+ *
+ * \note Only works if the lens is in perspective projection mode.
+ * \sa Qt3D.Render::Camera::projectionType
+ */
+void QCamera::viewEntity(Qt3DCore::QEntity *entity)
+{
+ if (!entity)
+ return;
+
+ Q_D(QCamera);
+ d->m_lens->viewEntity(entity->id(), id());
+}
+
+/*!
* Sets the camera's projection type to \a type.
*/
void QCamera::setProjectionType(QCameraLens::ProjectionType type)
diff --git a/src/render/frontend/qcamera.h b/src/render/frontend/qcamera.h
index dd7c63778..5c86ea122 100644
--- a/src/render/frontend/qcamera.h
+++ b/src/render/frontend/qcamera.h
@@ -150,6 +150,10 @@ public Q_SLOTS:
void setUpVector(const QVector3D &upVector);
void setViewCenter(const QVector3D &viewCenter);
+ void viewAll();
+ void viewSphere(const QVector3D &center, float radius);
+ void viewEntity(Qt3DCore::QEntity *entity);
+
Q_SIGNALS:
void projectionTypeChanged(QCameraLens::ProjectionType projectionType);
void nearPlaneChanged(float nearPlane);
diff --git a/src/render/frontend/qcameralens.cpp b/src/render/frontend/qcameralens.cpp
index 3c6b8db68..c9be49484 100644
--- a/src/render/frontend/qcameralens.cpp
+++ b/src/render/frontend/qcameralens.cpp
@@ -224,6 +224,43 @@ QCameraLensPrivate::QCameraLensPrivate()
{
}
+void QCameraLens::viewAll(Qt3DCore::QNodeId cameraId)
+{
+ Q_D(QCameraLens);
+ if (d->m_projectionType == PerspectiveProjection) {
+ QVariant v;
+ v.setValue(cameraId);
+ d->m_pendingViewAllCommand = sendCommand(QLatin1Literal("QueryRootBoundingVolume"), v);
+ }
+}
+
+void QCameraLens::viewEntity(Qt3DCore::QNodeId entityId, Qt3DCore::QNodeId cameraId)
+{
+ Q_D(QCameraLens);
+ if (d->m_projectionType == PerspectiveProjection) {
+ QVector<Qt3DCore::QNodeId> ids = {entityId, cameraId};
+ QVariant v;
+ v.setValue(ids);
+ d->m_pendingViewAllCommand = sendCommand(QLatin1Literal("QueryEntityBoundingVolume"), v);
+ }
+}
+
+void QCameraLensPrivate::processViewAllCommand(Qt3DCore::QNodeCommand::CommandId commandId,
+ const QVariant &data)
+{
+ Q_Q(QCameraLens);
+ if (m_pendingViewAllCommand != commandId)
+ return;
+
+ QVector<float> boundingVolumeData = data.value< QVector<float> >();
+ if (boundingVolumeData.size() != 4)
+ return;
+ QVector3D center(boundingVolumeData[0], boundingVolumeData[1], boundingVolumeData[2]);
+ float radius = boundingVolumeData[3];
+ Q_EMIT q->viewSphere(center, radius);
+ m_pendingViewAllCommand = Qt3DCore::QNodeCommand::CommandId();
+}
+
/*!
* Constructs a QCameraLens with given \a parent
*/
@@ -593,6 +630,22 @@ Qt3DCore::QNodeCreatedChangeBasePtr QCameraLens::createNodeCreationChange() cons
return creationChange;
}
+void QCameraLens::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change)
+{
+ Q_D(QCameraLens);
+ switch (change->type()) {
+ case Qt3DCore::CommandRequested: {
+ Qt3DCore::QNodeCommandPtr command = qSharedPointerCast<Qt3DCore::QNodeCommand>(change);
+
+ if (command->name() == QLatin1Literal("ViewAll"))
+ d->processViewAllCommand(command->inReplyTo(), command->data());
+ }
+ break;
+ default:
+ break;
+ }
+}
+
} // Qt3DRender
QT_END_NAMESPACE
diff --git a/src/render/frontend/qcameralens.h b/src/render/frontend/qcameralens.h
index fdb0d5868..0cd22e348 100644
--- a/src/render/frontend/qcameralens.h
+++ b/src/render/frontend/qcameralens.h
@@ -105,6 +105,9 @@ public:
float exposure() const;
+ void viewAll(Qt3DCore::QNodeId cameraId);
+ void viewEntity(Qt3DCore::QNodeId entityId, Qt3DCore::QNodeId cameraId);
+
public Q_SLOTS:
void setProjectionType(ProjectionType projectionType);
void setNearPlane(float nearPlane);
@@ -130,6 +133,7 @@ Q_SIGNALS:
void topChanged(float top);
void projectionMatrixChanged(const QMatrix4x4 &projectionMatrix);
void exposureChanged(float exposure);
+ void viewSphere(const QVector3D &center, float radius);
protected:
explicit QCameraLens(QCameraLensPrivate &dd, QNode *parent = nullptr);
@@ -137,6 +141,7 @@ protected:
private:
Q_DECLARE_PRIVATE(QCameraLens)
Qt3DCore::QNodeCreatedChangeBasePtr createNodeCreationChange() const Q_DECL_OVERRIDE;
+ void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) Q_DECL_OVERRIDE;
};
} // Qt3DRender
diff --git a/src/render/frontend/qcameralens_p.h b/src/render/frontend/qcameralens_p.h
index 71759d5f8..d78579b70 100644
--- a/src/render/frontend/qcameralens_p.h
+++ b/src/render/frontend/qcameralens_p.h
@@ -51,7 +51,8 @@
// We mean it.
//
-#include <Qt3DCore/private/qcomponent_p.h>
+#include <private/qcomponent_p.h>
+#include <private/qnodecommand_p.h>
#include "qcameralens.h"
#include <Qt3DCore/qpropertyupdatedchange.h>
@@ -103,6 +104,9 @@ public:
float m_exposure;
+ Qt3DCore::QNodeCommand::CommandId m_pendingViewAllCommand;
+ void processViewAllCommand(Qt3DCore::QNodeCommand::CommandId commandId, const QVariant &data);
+
private:
inline void updatePerpectiveProjection()
{
diff --git a/src/render/jobs/computefilteredboundingvolumejob.cpp b/src/render/jobs/computefilteredboundingvolumejob.cpp
new file mode 100644
index 000000000..d8a7b5094
--- /dev/null
+++ b/src/render/jobs/computefilteredboundingvolumejob.cpp
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 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 "computefilteredboundingvolumejob_p.h"
+
+#include <Qt3DRender/private/renderer_p.h>
+#include <Qt3DRender/private/entity_p.h>
+#include <Qt3DRender/private/renderlogging_p.h>
+#include <Qt3DRender/private/sphere_p.h>
+#include <Qt3DRender/private/job_common_p.h>
+
+#include <QThread>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+
+namespace {
+
+void expandWorldBoundingVolume(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);
+ }
+ sphere->expandToContain(childSphere);
+}
+
+} // namespace
+
+ComputeFilteredBoundingVolumeJob::ComputeFilteredBoundingVolumeJob()
+ : m_root(nullptr)
+ , m_ignoreSubTree(nullptr)
+{
+ SET_JOB_RUN_STAT_TYPE(this, JobTypes::ExpandBoundingVolume, 0);
+}
+
+void ComputeFilteredBoundingVolumeJob::setRoot(Entity *root)
+{
+ m_root = root;
+}
+
+void ComputeFilteredBoundingVolumeJob::ignoreSubTree(Entity *node)
+{
+ m_ignoreSubTree = node;
+}
+
+void ComputeFilteredBoundingVolumeJob::run()
+{
+ qCDebug(Jobs) << "Entering" << Q_FUNC_INFO << QThread::currentThread();
+
+ if (!m_root)
+ return;
+ if (!m_ignoreSubTree) {
+ finished(*m_root->worldBoundingVolumeWithChildren());
+ return;
+ }
+
+ bool isFilterChildOfRoot = false;
+ Entity *parent = m_ignoreSubTree->parent();
+ while (parent) {
+ if (parent == m_root) {
+ isFilterChildOfRoot = true;
+ break;
+ }
+ parent = parent->parent();
+ }
+ if (!isFilterChildOfRoot) {
+ finished(*m_root->worldBoundingVolumeWithChildren());
+ return;
+ }
+
+ Qt3DRender::Render::Sphere sphere;
+ expandWorldBoundingVolume(&sphere, m_root, m_ignoreSubTree);
+ finished(sphere);
+
+ qCDebug(Jobs) << "Exiting" << Q_FUNC_INFO << QThread::currentThread();
+}
+
+void ComputeFilteredBoundingVolumeJob::finished(const Qt3DRender::Render::Sphere &sphere)
+{
+ Q_UNUSED(sphere);
+}
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/jobs/computefilteredboundingvolumejob_p.h b/src/render/jobs/computefilteredboundingvolumejob_p.h
new file mode 100644
index 000000000..d7681e604
--- /dev/null
+++ b/src/render/jobs/computefilteredboundingvolumejob_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 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_COMPUTEFILTEREDBOUNDINGVOLUMEJOB_H
+#define QT3DRENDER_RENDER_COMPUTEFILTEREDBOUNDINGVOLUMEJOB_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 <Qt3DCore/qaspectjob.h>
+#include <private/qt3drender_global_p.h>
+
+#include <QtCore/QSharedPointer>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+
+class Entity;
+class Sphere;
+
+class QT3DRENDERSHARED_PRIVATE_EXPORT ComputeFilteredBoundingVolumeJob : public Qt3DCore::QAspectJob
+{
+public:
+ ComputeFilteredBoundingVolumeJob();
+
+ void setRoot(Entity *root);
+ void ignoreSubTree(Entity *node);
+ void run() Q_DECL_OVERRIDE;
+
+protected:
+ virtual void finished(const Qt3DRender::Render::Sphere &sphere);
+
+private:
+ Entity *m_root;
+ Entity *m_ignoreSubTree;
+};
+
+typedef QSharedPointer<ComputeFilteredBoundingVolumeJob> ComputeFilteredBoundingVolumeJobPtr;
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_COMPUTEFILTEREDBOUNDINGVOLUMEJOB_H
diff --git a/src/render/jobs/jobs.pri b/src/render/jobs/jobs.pri
index 010914f88..66c98b1c0 100644
--- a/src/render/jobs/jobs.pri
+++ b/src/render/jobs/jobs.pri
@@ -11,6 +11,7 @@ HEADERS += \
$$PWD/calcboundingvolumejob_p.h \
$$PWD/pickboundingvolumejob_p.h \
$$PWD/calcgeometrytrianglevolumes_p.h \
+ $$PWD/computefilteredboundingvolumejob_p.h \
$$PWD/job_common_p.h \
$$PWD/filterlayerentityjob_p.h \
$$PWD/filterentitybycomponentjob_p.h \
@@ -42,6 +43,7 @@ SOURCES += \
$$PWD/calcboundingvolumejob.cpp \
$$PWD/pickboundingvolumejob.cpp \
$$PWD/calcgeometrytrianglevolumes.cpp \
+ $$PWD/computefilteredboundingvolumejob.cpp \
$$PWD/filterlayerentityjob.cpp \
$$PWD/materialparametergathererjob.cpp \
$$PWD/renderviewbuilderjob.cpp \
diff --git a/tests/auto/render/commons/testrenderer.h b/tests/auto/render/commons/testrenderer.h
index 74f529fa0..031ca214b 100644
--- a/tests/auto/render/commons/testrenderer.h
+++ b/tests/auto/render/commons/testrenderer.h
@@ -60,6 +60,7 @@ public:
QVector<Qt3DCore::QAspectJobPtr> renderBinJobs() Q_DECL_OVERRIDE { return QVector<Qt3DCore::QAspectJobPtr>(); }
Qt3DCore::QAspectJobPtr pickBoundingVolumeJob() Q_DECL_OVERRIDE { return Qt3DCore::QAspectJobPtr(); }
Qt3DCore::QAspectJobPtr syncTextureLoadingJob() Q_DECL_OVERRIDE { return Qt3DCore::QAspectJobPtr(); }
+ Qt3DCore::QAspectJobPtr expandBoundingVolumeJob() Q_DECL_OVERRIDE { return Qt3DCore::QAspectJobPtr(); }
void setSceneRoot(Qt3DCore::QBackendNodeFactory *factory, Qt3DRender::Render::Entity *root) Q_DECL_OVERRIDE { Q_UNUSED(factory); Q_UNUSED(root); }
Qt3DRender::Render::Entity *sceneRoot() const Q_DECL_OVERRIDE { return nullptr; }
Qt3DRender::Render::FrameGraphNode *frameGraphRoot() const Q_DECL_OVERRIDE { return nullptr; }