summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Krus <mike.krus@kdab.com>2020-02-26 11:01:31 +0000
committerMike Krus <mike.krus@kdab.com>2020-04-22 08:54:47 +0100
commitee476605629a74fb824d3f014deb6e6be9e40e9f (patch)
tree036c630586925a1a0a0bc956b6f15d5752f1ccea
parent5f282d1b0d10725e367c1734541da9e99e0cbb08 (diff)
Add bounding volume calculations to core aspect
If an entity uses a QBoundingVolume and a QGeometryView, compute the bounding volume data in the core aspect. All other cases will still happen in the render aspect. Result not currently used (ie it's done again in render aspect). Change-Id: I4b5ed21bac42b79777c3112aa76d876ac5b6f52d Reviewed-by: Paul Lemire <paul.lemire@kdab.com>
-rw-r--r--src/core/aspect/qcoreaspect.cpp41
-rw-r--r--src/core/aspect/qcoreaspect.h5
-rw-r--r--src/core/aspect/qcoreaspect_p.h6
-rw-r--r--src/core/core.pro7
-rw-r--r--src/core/geometry/bufferutils_p.h113
-rw-r--r--src/core/geometry/buffervisitor_p.h285
-rw-r--r--src/core/geometry/geometry.pri4
-rw-r--r--src/core/geometry/qgeometry_p.h1
-rw-r--r--src/core/geometry/qgeometryview.cpp244
-rw-r--r--src/core/geometry/qgeometryview_p.h28
-rw-r--r--src/core/jobs/calcboundingvolumejob.cpp317
-rw-r--r--src/core/jobs/calcboundingvolumejob_p.h113
-rw-r--r--src/core/jobs/job_common_p.h73
-rw-r--r--src/core/jobs/jobs.pri7
14 files changed, 1233 insertions, 11 deletions
diff --git a/src/core/aspect/qcoreaspect.cpp b/src/core/aspect/qcoreaspect.cpp
index 48f9aaf3a..dc3566edc 100644
--- a/src/core/aspect/qcoreaspect.cpp
+++ b/src/core/aspect/qcoreaspect.cpp
@@ -39,12 +39,16 @@
#include "qcoreaspect.h"
#include "qcoreaspect_p.h"
+#include <Qt3DCore/private/qaspectmanager_p.h>
+#include <Qt3DCore/private/qscene_p.h>
+#include <Qt3DCore/private/calcboundingvolumejob_p.h>
QT_BEGIN_NAMESPACE
using namespace Qt3DCore;
QCoreAspectPrivate::QCoreAspectPrivate()
+ : Qt3DCore::QAbstractAspectPrivate()
{
}
@@ -65,7 +69,7 @@ void QCoreAspectPrivate::frameDone()
}
QCoreAspect::QCoreAspect(QObject *parent)
- : Qt3DCore::QAbstractAspect(parent)
+ : Qt3DCore::QAbstractAspect(*new QCoreAspectPrivate, parent)
{
}
@@ -75,10 +79,28 @@ QCoreAspect::~QCoreAspect()
}
+QAspectJobPtr QCoreAspect::calculateBoundingVolumeJob() const
+{
+ Q_D(const QCoreAspect);
+ return d->m_calculateBoundingVolumeJob;
+}
+
QVector<QAspectJobPtr> QCoreAspect::jobsToExecute(qint64 time)
{
+ Q_D(QCoreAspect);
Q_UNUSED(time)
- return {};
+
+ QVector<QAspectJobPtr> jobs;
+
+ auto scene = d->m_aspectManager->scene();
+ auto dirtyBits = scene->dirtyBits();
+
+ if (dirtyBits & QScene::GeometryDirty ||
+ dirtyBits & QScene::BuffersDirty) {
+ jobs.push_back(d->m_calculateBoundingVolumeJob);
+ }
+
+ return jobs;
}
QVariant QCoreAspect::executeCommand(const QStringList &args)
@@ -89,12 +111,25 @@ QVariant QCoreAspect::executeCommand(const QStringList &args)
void QCoreAspect::onRegistered()
{
+ Q_D(QCoreAspect);
+ if (d->m_calculateBoundingVolumeJob.isNull())
+ d->m_calculateBoundingVolumeJob = CalculateBoundingVolumeJobPtr::create();
}
-void QCoreAspect::onUnregistered()
+void QCoreAspect::onEngineStartup()
{
+ Q_D(QCoreAspect);
+ Q_ASSERT(d->m_calculateBoundingVolumeJob);
+ d->m_calculateBoundingVolumeJob->setRoot(d->m_root);
+}
+
+void QCoreAspect::frameDone()
+{
+ Q_D(QCoreAspect);
+ auto scene = d->m_aspectManager->scene();
+ scene->clearDirtyBits();
}
QT_END_NAMESPACE
diff --git a/src/core/aspect/qcoreaspect.h b/src/core/aspect/qcoreaspect.h
index 27c72f405..071b7387a 100644
--- a/src/core/aspect/qcoreaspect.h
+++ b/src/core/aspect/qcoreaspect.h
@@ -55,6 +55,8 @@ public:
explicit QCoreAspect(QObject *parent = nullptr);
~QCoreAspect();
+ QAspectJobPtr calculateBoundingVolumeJob() const;
+
protected:
Q_DECLARE_PRIVATE(QCoreAspect)
@@ -62,7 +64,8 @@ private:
QVector<Qt3DCore::QAspectJobPtr> jobsToExecute(qint64 time) override;
QVariant executeCommand(const QStringList &args) override;
void onRegistered() override;
- void onUnregistered() override;
+ void onEngineStartup() override;
+ void frameDone() override;
};
}
diff --git a/src/core/aspect/qcoreaspect_p.h b/src/core/aspect/qcoreaspect_p.h
index ca665faf8..cda0a140a 100644
--- a/src/core/aspect/qcoreaspect_p.h
+++ b/src/core/aspect/qcoreaspect_p.h
@@ -55,10 +55,15 @@
#include <Qt3DCore/private/qabstractaspect_p.h>
#include <Qt3DCore/private/qt3dcore_global_p.h>
+#include <QSharedPointer>
+
QT_BEGIN_NAMESPACE
namespace Qt3DCore {
+class CalculateBoundingVolumeJob;
+using CalculateBoundingVolumeJobPtr = QSharedPointer<CalculateBoundingVolumeJob>;
+
class Q_3DCORE_PRIVATE_EXPORT QCoreAspectPrivate : public Qt3DCore::QAbstractAspectPrivate
{
public:
@@ -71,6 +76,7 @@ public:
void frameDone() override;
bool m_initialized;
+ CalculateBoundingVolumeJobPtr m_calculateBoundingVolumeJob;
};
}
diff --git a/src/core/core.pro b/src/core/core.pro
index b149497fb..846e9d836 100644
--- a/src/core/core.pro
+++ b/src/core/core.pro
@@ -1,7 +1,8 @@
-TARGET = Qt3DCore
-MODULE = 3dcore
+TARGET = Qt3DCore
+MODULE = 3dcore
-QT = core-private gui-private network
+QT = core-private gui-private network
+QT_FOR_PRIVATE = concurrent
gcov {
QMAKE_CXXFLAGS += -fprofile-arcs -ftest-coverage
diff --git a/src/core/geometry/bufferutils_p.h b/src/core/geometry/bufferutils_p.h
new file mode 100644
index 000000000..1b83d9949
--- /dev/null
+++ b/src/core/geometry/bufferutils_p.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Paul Lemire paul.lemire350@gmail.com
+** 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 QT3DCORE_BUFFERUTILS_P_H
+#define QT3DCORE_BUFFERUTILS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DCore/QAttribute>
+#include <QByteArray>
+
+QT_BEGIN_NAMESPACE
+
+
+namespace Qt3DCore {
+
+class QGeometryView;
+class QBuffer;
+
+struct BufferInfo
+{
+ BufferInfo()
+ : type(Qt3DCore::QAttribute::VertexBaseType::Float)
+ , dataSize(0)
+ , count(0)
+ , byteStride(0)
+ , byteOffset(0)
+ , restartEnabled(false)
+ , restartIndexValue(-1)
+ {}
+
+ QByteArray data;
+ Qt3DCore::QAttribute::VertexBaseType type;
+ uint dataSize;
+ uint count;
+ uint byteStride;
+ uint byteOffset;
+ bool restartEnabled;
+ int restartIndexValue;
+};
+
+
+namespace BufferTypeInfo {
+
+ template <Qt3DCore::QAttribute::VertexBaseType> struct EnumToType;
+ template <> struct EnumToType<Qt3DCore::QAttribute::Byte> { typedef const char type; };
+ template <> struct EnumToType<Qt3DCore::QAttribute::UnsignedByte> { typedef const uchar type; };
+ template <> struct EnumToType<Qt3DCore::QAttribute::Short> { typedef const short type; };
+ template <> struct EnumToType<Qt3DCore::QAttribute::UnsignedShort> { typedef const ushort type; };
+ template <> struct EnumToType<Qt3DCore::QAttribute::Int> { typedef const int type; };
+ template <> struct EnumToType<Qt3DCore::QAttribute::UnsignedInt> { typedef const uint type; };
+ template <> struct EnumToType<Qt3DCore::QAttribute::Float> { typedef const float type; };
+ template <> struct EnumToType<Qt3DCore::QAttribute::Double> { typedef const double type; };
+
+ template<Qt3DCore::QAttribute::VertexBaseType v>
+ typename EnumToType<v>::type *castToType(const QByteArray &u, uint byteOffset)
+ {
+ return reinterpret_cast< typename EnumToType<v>::type *>(u.constData() + byteOffset);
+ }
+
+} // namespace BufferTypeInfo
+
+} // namespace Qt3DCore
+
+QT_END_NAMESPACE
+
+
+#endif // QT3DCORE_BUFFERUTILS_P_H
diff --git a/src/core/geometry/buffervisitor_p.h b/src/core/geometry/buffervisitor_p.h
new file mode 100644
index 000000000..69cfb9b50
--- /dev/null
+++ b/src/core/geometry/buffervisitor_p.h
@@ -0,0 +1,285 @@
+/****************************************************************************
+**
+** 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 QT3DCORE_BUFFERVISITOR_P_H
+#define QT3DCORE_BUFFERVISITOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DCore/qnodeid.h>
+#include <Qt3DCore/qattribute.h>
+#include <Qt3DCore/qbuffer.h>
+#include <Qt3DCore/private/bufferutils_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+class QEntity;
+
+template <typename ValueType, Qt3DCore::QAttribute::VertexBaseType VertexBaseType, uint dataSize>
+class Q_AUTOTEST_EXPORT BufferVisitor
+{
+public:
+ explicit BufferVisitor() = default;
+ virtual ~BufferVisitor() = default;
+
+ virtual void visit(uint ndx, ValueType x) {
+ Q_UNUSED(ndx) Q_UNUSED(x)
+ }
+ virtual void visit(uint ndx, ValueType x, ValueType y) {
+ Q_UNUSED(ndx) Q_UNUSED(x) Q_UNUSED(y)
+ }
+ virtual void visit(uint ndx, ValueType x, ValueType y, ValueType z) {
+ Q_UNUSED(ndx) Q_UNUSED(x) Q_UNUSED(y) Q_UNUSED(z)
+ }
+ virtual void visit(uint ndx, ValueType x, ValueType y, ValueType z, ValueType w) {
+ Q_UNUSED(ndx) Q_UNUSED(x) Q_UNUSED(y) Q_UNUSED(z) Q_UNUSED(w)
+ }
+
+ bool apply(QAttribute *attribute,
+ QAttribute *indexAttribute,
+ int drawVertexCount,
+ bool primitiveRestartEnabled,
+ int primitiveRestartIndex)
+ {
+ if (attribute->vertexBaseType() != VertexBaseType)
+ return false;
+ if (attribute->vertexSize() < dataSize)
+ return false;
+
+ auto data = attribute->buffer()->data();
+ auto vertexBuffer = BufferTypeInfo::castToType<VertexBaseType>(data, attribute->byteOffset());
+
+ if (indexAttribute) {
+ auto indexData = indexAttribute->buffer()->data();
+ switch (indexAttribute->vertexBaseType()) {
+ case Qt3DCore::QAttribute::UnsignedShort: {
+ auto indexBuffer = BufferTypeInfo::castToType<Qt3DCore::QAttribute::UnsignedShort>(indexData, indexAttribute->byteOffset());
+ traverseCoordinateIndexed(vertexBuffer, indexBuffer, attribute->byteStride(), drawVertexCount,
+ primitiveRestartEnabled, primitiveRestartIndex);
+ break;
+ }
+ case Qt3DCore::QAttribute::UnsignedInt: {
+ auto indexBuffer = BufferTypeInfo::castToType<Qt3DCore::QAttribute::UnsignedInt>(indexData, indexAttribute->byteOffset());
+ traverseCoordinateIndexed(vertexBuffer, indexBuffer, attribute->byteStride(), drawVertexCount,
+ primitiveRestartEnabled, primitiveRestartIndex);
+ break;
+ }
+ case Qt3DCore::QAttribute::UnsignedByte: {
+ auto indexBuffer = BufferTypeInfo::castToType<Qt3DCore::QAttribute::UnsignedByte>(indexData, indexAttribute->byteOffset());
+ traverseCoordinateIndexed(vertexBuffer, indexBuffer, attribute->byteStride(), drawVertexCount,
+ primitiveRestartEnabled, primitiveRestartIndex);
+ break;
+ }
+ default: Q_UNREACHABLE();
+ }
+ } else {
+ switch (dataSize) {
+ case 1: traverseCoordinates1(vertexBuffer, attribute->byteStride(), drawVertexCount); break;
+ case 2: traverseCoordinates2(vertexBuffer, attribute->byteStride(), drawVertexCount); break;
+ case 3: traverseCoordinates3(vertexBuffer, attribute->byteStride(), drawVertexCount); break;
+ case 4: traverseCoordinates4(vertexBuffer, attribute->byteStride(), drawVertexCount); break;
+ default: Q_UNREACHABLE();
+ }
+ }
+
+ return true;
+ }
+
+protected:
+ template<typename VertexBufferType, typename IndexBufferType>
+ void traverseCoordinateIndexed(VertexBufferType *vertexBuffer,
+ IndexBufferType *indexBuffer,
+ int vertexByteStride,
+ int drawVertexCount,
+ bool primitiveRestartEnabled,
+ int primitiveRestartIndex)
+ {
+ switch (dataSize) {
+ case 1: traverseCoordinates1Indexed(vertexBuffer, vertexByteStride, indexBuffer, drawVertexCount,
+ primitiveRestartEnabled, primitiveRestartIndex);
+ break;
+ case 2: traverseCoordinates2Indexed(vertexBuffer, vertexByteStride, indexBuffer, drawVertexCount,
+ primitiveRestartEnabled, primitiveRestartIndex);
+ break;
+ case 3: traverseCoordinates3Indexed(vertexBuffer, vertexByteStride, indexBuffer, drawVertexCount,
+ primitiveRestartEnabled, primitiveRestartIndex);
+ break;
+ case 4: traverseCoordinates4Indexed(vertexBuffer, vertexByteStride, indexBuffer, drawVertexCount,
+ primitiveRestartEnabled, primitiveRestartIndex);
+ break;
+ default: Q_UNREACHABLE();
+ }
+ }
+
+ template <typename Coordinate>
+ void traverseCoordinates1(Coordinate *coordinates,
+ const uint byteStride,
+ const uint count)
+ {
+ const uint stride = byteStride / sizeof(Coordinate);
+ for (uint ndx = 0; ndx < count; ++ndx) {
+ visit(ndx, coordinates[0]);
+ coordinates += stride;
+ }
+ }
+
+ template <typename Coordinate, typename IndexElem>
+ void traverseCoordinates1Indexed(Coordinate *coordinates,
+ const uint byteStride,
+ IndexElem *indices,
+ const uint count,
+ bool primitiveRestartEnabled,
+ int primitiveRestartIndex)
+ {
+ const uint stride = byteStride / sizeof(Coordinate);
+ for (uint i = 0; i < count; ++i) {
+ if (!primitiveRestartEnabled || static_cast<int>(indices[i]) != primitiveRestartIndex) {
+ const uint n = stride * indices[i];
+ visit(i, coordinates[n]);
+ }
+ }
+ }
+
+ template <typename Coordinate>
+ void traverseCoordinates2(Coordinate *coordinates,
+ const uint byteStride,
+ const uint count)
+ {
+ const uint stride = byteStride ? byteStride / sizeof(Coordinate) : 2;
+ for (uint ndx = 0; ndx < count; ++ndx) {
+ visit(ndx, coordinates[0], coordinates[1]);
+ coordinates += stride;
+ }
+ }
+
+
+ template <typename Coordinate, typename IndexElem>
+ void traverseCoordinates2Indexed(Coordinate *coordinates,
+ const uint byteStride,
+ IndexElem *indices,
+ const uint count,
+ bool primitiveRestartEnabled,
+ int primitiveRestartIndex)
+ {
+ const uint stride = byteStride ? byteStride / sizeof(Coordinate) : 2;
+ for (uint i = 0; i < count; ++i) {
+ if (!primitiveRestartEnabled || static_cast<int>(indices[i]) != primitiveRestartIndex) {
+ const uint n = stride * indices[i];
+ visit(i, coordinates[n], coordinates[n + 1]);
+ }
+ }
+ }
+
+ template <typename Coordinate>
+ void traverseCoordinates3(Coordinate *coordinates,
+ const uint byteStride,
+ const uint count)
+ {
+ const uint stride = byteStride ? byteStride / sizeof(Coordinate) : 3;
+ for (uint ndx = 0; ndx < count; ++ndx) {
+ visit(ndx, coordinates[0], coordinates[1], coordinates[2]);
+ coordinates += stride;
+ }
+ }
+
+ template <typename Coordinate, typename IndexElem>
+ void traverseCoordinates3Indexed(Coordinate *coordinates,
+ const uint byteStride,
+ IndexElem *indices,
+ const uint count,
+ bool primitiveRestartEnabled,
+ int primitiveRestartIndex)
+ {
+ const uint stride = byteStride ? byteStride / sizeof(Coordinate) : 3;
+ for (uint i = 0; i < count; ++i) {
+ if (!primitiveRestartEnabled || static_cast<int>(indices[i]) != primitiveRestartIndex) {
+ const uint n = stride * indices[i];
+ visit(i, coordinates[n], coordinates[n + 1], coordinates[n + 2]);
+ }
+ }
+ }
+
+ template <typename Coordinate>
+ void traverseCoordinates4(Coordinate *coordinates,
+ const uint byteStride,
+ const uint count)
+ {
+ const uint stride = byteStride ? byteStride / sizeof(Coordinate) : 4;
+ for (uint ndx = 0; ndx < count; ++ndx) {
+ visit(ndx, coordinates[0], coordinates[1], coordinates[2], coordinates[3]);
+ coordinates += stride;
+ }
+ }
+
+ template <typename Coordinate, typename IndexElem>
+ void traverseCoordinates4Indexed(Coordinate *coordinates,
+ const uint byteStride,
+ IndexElem *indices,
+ const uint count,
+ bool primitiveRestartEnabled,
+ int primitiveRestartIndex)
+ {
+ const uint stride = byteStride ? byteStride / sizeof(Coordinate) : 4;
+ for (uint i = 0; i < count; ++i) {
+ if (!primitiveRestartEnabled || static_cast<int>(indices[i]) != primitiveRestartIndex) {
+ const uint n = stride * indices[i];
+ visit(i, coordinates[n], coordinates[n + 1], coordinates[n + 2], coordinates[n + 3]);
+ }
+ }
+ }
+};
+
+typedef BufferVisitor<float, Qt3DCore::QAttribute::Float, 3> Buffer3fVisitor;
+
+} // namespace Qt3DCore
+
+QT_END_NAMESPACE
+
+
+#endif // QT3DCORE_BUFFERVISITOR_P_H
diff --git a/src/core/geometry/geometry.pri b/src/core/geometry/geometry.pri
index eadf7ca0b..70bda07ed 100644
--- a/src/core/geometry/geometry.pri
+++ b/src/core/geometry/geometry.pri
@@ -12,7 +12,9 @@ HEADERS += \
$$PWD/qgeometry.h \
$$PWD/qgeometryfactory_p.h \
$$PWD/qgeometryview_p.h \
- $$PWD/qgeometryview.h
+ $$PWD/qgeometryview.h \
+ $$PWD/bufferutils_p.h \
+ $$PWD/buffervisitor_p.h
SOURCES += \
$$PWD/qabstractfunctor.cpp \
diff --git a/src/core/geometry/qgeometry_p.h b/src/core/geometry/qgeometry_p.h
index 2a05b5b6f..ed13b3feb 100644
--- a/src/core/geometry/qgeometry_p.h
+++ b/src/core/geometry/qgeometry_p.h
@@ -72,7 +72,6 @@ public:
void setScene(QScene *scene) override;
void update() override;
void setExtent(const QVector3D &minExtent, const QVector3D &maxExtent);
-
static QGeometryPrivate *get(QGeometry *q);
QVector<QAttribute *> m_attributes;
diff --git a/src/core/geometry/qgeometryview.cpp b/src/core/geometry/qgeometryview.cpp
index 65e30d982..258448ba4 100644
--- a/src/core/geometry/qgeometryview.cpp
+++ b/src/core/geometry/qgeometryview.cpp
@@ -39,11 +39,250 @@
#include "qgeometryview.h"
#include "qgeometryview_p.h"
+#include "qgeometry_p.h"
+#include "buffervisitor_p.h"
+
+#include <Qt3DCore/QAttribute>
+#include <Qt3DCore/QBuffer>
+#include <Qt3DCore/private/vector3d_p.h>
QT_BEGIN_NAMESPACE
using namespace Qt3DCore;
+namespace {
+
+class FindExtremePoints : public Buffer3fVisitor
+{
+public:
+ FindExtremePoints()
+ : Buffer3fVisitor()
+ , xMin(0.0f), xMax(0.0f), yMin(0.0f), yMax(0.0f), zMin(0.0f), zMax(0.0f)
+ { }
+
+ float xMin, xMax, yMin, yMax, zMin, zMax;
+ Vector3D xMinPt, xMaxPt, yMinPt, yMaxPt, zMinPt, zMaxPt;
+
+ void visit(uint ndx, float x, float y, float z) override
+ {
+ if (ndx) {
+ if (x < xMin) {
+ xMin = x;
+ xMinPt = Vector3D(x, y, z);
+ }
+ if (x > xMax) {
+ xMax = x;
+ xMaxPt = Vector3D(x, y, z);
+ }
+ if (y < yMin) {
+ yMin = y;
+ yMinPt = Vector3D(x, y, z);
+ }
+ if (y > yMax) {
+ yMax = y;
+ yMaxPt = Vector3D(x, y, z);
+ }
+ if (z < zMin) {
+ zMin = z;
+ zMinPt = Vector3D(x, y, z);
+ }
+ if (z > zMax) {
+ zMax = z;
+ zMaxPt = Vector3D(x, y, z);
+ }
+ } else {
+ xMin = xMax = x;
+ yMin = yMax = y;
+ zMin = zMax = z;
+ xMinPt = xMaxPt = yMinPt = yMaxPt = zMinPt = zMaxPt = Vector3D(x, y, z);
+ }
+ }
+};
+
+class FindMaxDistantPoint : public Buffer3fVisitor
+{
+public:
+ FindMaxDistantPoint()
+ : Buffer3fVisitor()
+ { }
+
+ float maxLengthSquared = 0.0f;
+ bool setReferencePoint = false;
+ bool hasNoPoints = true;
+ Vector3D maxDistPt;
+ Vector3D referencePt;
+
+ void visit(uint ndx, float x, float y, float z) override
+ {
+ Q_UNUSED(ndx)
+ const Vector3D p = Vector3D(x, y, z);
+
+ if (hasNoPoints && setReferencePoint) {
+ maxLengthSquared = 0.0f;
+ referencePt = p;
+ }
+ const float lengthSquared = (p - referencePt).lengthSquared();
+ if ( lengthSquared >= maxLengthSquared ) {
+ maxDistPt = p;
+ maxLengthSquared = lengthSquared;
+ }
+ hasNoPoints = false;
+ }
+};
+
+std::pair<QVector3D, QVector3D> calculateLocalBoundingVolume(QGeometryView *node)
+{
+ // The Bounding volume will only be computed if the position Buffer
+ // isDirty
+
+ if (!node->isEnabled())
+ return {};
+
+ if (node->primitiveType() == QGeometryView::Patches)
+ return {};
+
+ QGeometry *geom = node->geometry();
+ QGeometryPrivate *dgeom = QGeometryPrivate::get(geom);
+
+ if (!geom)
+ return {};
+
+ int drawVertexCount = node->vertexCount(); // may be 0, gets changed below if so
+
+ QAttribute *positionAttribute = dgeom->m_boundingVolumePositionAttribute;
+ const QVector<Qt3DCore::QAttribute *> attributes = geom->attributes();
+
+ // Use the default position attribute if attribute is null
+ if (!positionAttribute) {
+ for (QAttribute *attr : attributes) {
+ if (attr->name() == QAttribute::defaultPositionAttributeName()) {
+ positionAttribute = attr;
+ break;
+ }
+ }
+ }
+
+ if (!positionAttribute
+ || positionAttribute->attributeType() != QAttribute::VertexAttribute
+ || positionAttribute->vertexBaseType() != QAttribute::Float
+ || positionAttribute->vertexSize() < 3) {
+ qWarning("calculateLocalBoundingVolume: Position attribute not suited for bounding volume computation");
+ return {};
+ }
+
+ Qt3DCore::QBuffer *buf = positionAttribute->buffer();
+ // No point in continuing if the positionAttribute doesn't have a suitable buffer
+ if (!buf) {
+ qWarning("calculateLocalBoundingVolume: Position attribute not referencing a valid buffer");
+ return {};
+ }
+
+ // Check if there is an index attribute.
+ QAttribute *indexAttribute = nullptr;
+ Qt3DCore::QBuffer *indexBuf = nullptr;
+
+ for (const auto attr : attributes) {
+ if (attr->attributeType() == QAttribute::IndexAttribute) {
+ indexBuf = attr->buffer();
+ if (indexBuf) {
+ indexAttribute = attr;
+
+ if (!drawVertexCount)
+ drawVertexCount = static_cast<int>(indexAttribute->count());
+
+ static const QAttribute::VertexBaseType validIndexTypes[] = {
+ QAttribute::UnsignedShort,
+ QAttribute::UnsignedInt,
+ QAttribute::UnsignedByte
+ };
+
+ if (std::find(std::begin(validIndexTypes),
+ std::end(validIndexTypes),
+ indexAttribute->vertexBaseType()) == std::end(validIndexTypes)) {
+ qWarning() << "calculateLocalBoundingVolume: Unsupported index attribute type" << indexAttribute->name() << indexAttribute->vertexBaseType();
+ return {};
+ }
+
+ break;
+ }
+ }
+ }
+
+ if (!indexAttribute && !drawVertexCount)
+ drawVertexCount = static_cast<int>(positionAttribute->count());
+
+ // Buf will be set to not dirty once it's loaded
+ // in a job executed after this one
+ // We need to recompute the bounding volume
+ // If anything in the GeometryRenderer has changed
+ BoundingVolumeCalculator reader;
+ if (reader.apply(positionAttribute, indexAttribute, drawVertexCount,
+ node->primitiveRestartEnabled(), node->restartIndexValue()))
+ return {reader.min(), reader.max()};
+
+ return {};
+}
+
+}
+
+
+bool BoundingVolumeCalculator::apply(QAttribute *positionAttribute,
+ QAttribute *indexAttribute,
+ int drawVertexCount,
+ bool primitiveRestartEnabled,
+ int primitiveRestartIndex)
+{
+ m_radius = -1.f;
+
+ FindExtremePoints findExtremePoints;
+ if (!findExtremePoints.apply(positionAttribute, indexAttribute, drawVertexCount,
+ primitiveRestartEnabled, primitiveRestartIndex)) {
+ return false;
+ }
+
+ m_min = QVector3D(findExtremePoints.xMin, findExtremePoints.yMin, findExtremePoints.zMin);
+ m_max = QVector3D(findExtremePoints.xMax, findExtremePoints.yMax, findExtremePoints.zMax);
+
+ FindMaxDistantPoint maxDistantPointY;
+ maxDistantPointY.setReferencePoint = true;
+ if (!maxDistantPointY.apply(positionAttribute, indexAttribute, drawVertexCount,
+ primitiveRestartEnabled, primitiveRestartIndex)) {
+ return false;
+ }
+ if (maxDistantPointY.hasNoPoints)
+ return false;
+
+ //const Vector3D x = maxDistantPointY.referencePt;
+ const Vector3D y = maxDistantPointY.maxDistPt;
+
+ FindMaxDistantPoint maxDistantPointZ;
+ maxDistantPointZ.setReferencePoint = false;
+ maxDistantPointZ.referencePt = y;
+ if (!maxDistantPointZ.apply(positionAttribute, indexAttribute, drawVertexCount,
+ primitiveRestartEnabled, primitiveRestartIndex))
+ return false;
+ const Vector3D z = maxDistantPointZ.maxDistPt;
+ const Vector3D center = (y + z) * .5f;
+
+ FindMaxDistantPoint maxDistantPointCenter;
+ maxDistantPointCenter.setReferencePoint = false;
+ maxDistantPointCenter.referencePt = center;
+ if (!maxDistantPointCenter.apply(positionAttribute, indexAttribute, drawVertexCount,
+ primitiveRestartEnabled, primitiveRestartIndex))
+ return false;
+
+ const float radius = (center - maxDistantPointCenter.maxDistPt).length();
+
+ if (center == Vector3D{} || radius < 0.f)
+ return false;
+
+ m_radius = radius;
+ m_center = QVector3D{ center.x(), center.y(), center.z() };
+
+ return true;
+}
+
+
QGeometryViewPrivate::QGeometryViewPrivate()
: QNodePrivate()
, m_instanceCount(1)
@@ -65,6 +304,11 @@ QGeometryViewPrivate::~QGeometryViewPrivate()
{
}
+QGeometryViewPrivate *QGeometryViewPrivate::get(QGeometryView *q)
+{
+ return q->d_func();
+}
+
void QGeometryViewPrivate::update()
{
if (!m_blockNotifications)
diff --git a/src/core/geometry/qgeometryview_p.h b/src/core/geometry/qgeometryview_p.h
index f806c4d69..05dea1d0e 100644
--- a/src/core/geometry/qgeometryview_p.h
+++ b/src/core/geometry/qgeometryview_p.h
@@ -55,6 +55,8 @@
#include <Qt3DCore/qgeometryview.h>
#include <Qt3DCore/private/qgeometryfactory_p.h>
#include <Qt3DCore/private/qt3dcore_global_p.h>
+
+#include <QtGui/qvector3d.h>
#include <memory>
QT_BEGIN_NAMESPACE
@@ -67,6 +69,8 @@ public:
QGeometryViewPrivate();
~QGeometryViewPrivate();
+ static QGeometryViewPrivate *get(QGeometryView *q);
+
void update() override;
Q_DECLARE_PUBLIC(QGeometryView)
@@ -85,6 +89,30 @@ public:
bool m_dirty;
};
+class BoundingVolumeCalculator
+{
+public:
+ BoundingVolumeCalculator() = default;
+
+ const QVector3D min() const { return m_min; }
+ const QVector3D max() const { return m_max; }
+ const QVector3D center() const { return m_center; }
+ float radius() const { return m_radius; }
+ bool isValid() const { return m_radius >= 0.f; }
+
+ bool apply(QAttribute *positionAttribute,
+ QAttribute *indexAttribute,
+ int drawVertexCount,
+ bool primitiveRestartEnabled,
+ int primitiveRestartIndex);
+
+private:
+ QVector3D m_min;
+ QVector3D m_max;
+ QVector3D m_center;
+ float m_radius = -1.f;
+};
+
} // namespace Qt3DCore
QT_END_NAMESPACE
diff --git a/src/core/jobs/calcboundingvolumejob.cpp b/src/core/jobs/calcboundingvolumejob.cpp
new file mode 100644
index 000000000..37ee92e84
--- /dev/null
+++ b/src/core/jobs/calcboundingvolumejob.cpp
@@ -0,0 +1,317 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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 "calcboundingvolumejob_p.h"
+
+#include <Qt3DCore/qattribute.h>
+#include <Qt3DCore/qboundingvolume.h>
+#include <Qt3DCore/qbuffer.h>
+#include <Qt3DCore/qgeometryview.h>
+#include <Qt3DCore/private/job_common_p.h>
+#include <Qt3DCore/private/qaspectjob_p.h>
+#include <Qt3DCore/private/qaspectmanager_p.h>
+#include <Qt3DCore/private/qattribute_p.h>
+#include <Qt3DCore/private/qboundingvolume_p.h>
+#include <Qt3DCore/private/qbuffer_p.h>
+#include <Qt3DCore/private/qentity_p.h>
+#include <Qt3DCore/private/qgeometry_p.h>
+#include <Qt3DCore/private/qgeometryview_p.h>
+#include <Qt3DCore/private/qnodevisitor_p.h>
+
+#include <QtCore/qmath.h>
+#if QT_CONFIG(concurrent)
+#include <QtConcurrent/QtConcurrent>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+namespace {
+
+BoundingVolumeComputeData findBoundingVolumeComputeData(QGeometryView *node)
+{
+ if (!node->isEnabled())
+ return {};
+
+ if (node->primitiveType() == QGeometryView::Patches)
+ return {};
+
+ QGeometry *geom = node->geometry();
+ QGeometryPrivate *dgeom = QGeometryPrivate::get(geom);
+ if (!geom)
+ return {};
+
+ int drawVertexCount = node->vertexCount(); // may be 0, gets changed below if so
+
+ QAttribute *positionAttribute = dgeom->m_boundingVolumePositionAttribute;
+ const QVector<Qt3DCore::QAttribute *> attributes = geom->attributes();
+
+ // Use the default position attribute if attribute is null
+ if (!positionAttribute) {
+ for (QAttribute *attr : attributes) {
+ if (attr->name() == QAttribute::defaultPositionAttributeName()) {
+ positionAttribute = attr;
+ break;
+ }
+ }
+ }
+
+ if (!positionAttribute
+ || positionAttribute->attributeType() != QAttribute::VertexAttribute
+ || positionAttribute->vertexBaseType() != QAttribute::Float
+ || positionAttribute->vertexSize() < 3) {
+ qWarning("findBoundingVolumeComputeData: Position attribute not suited for bounding volume computation");
+ return {};
+ }
+
+ Qt3DCore::QBuffer *positionBuffer = positionAttribute->buffer();
+ // No point in continuing if the positionAttribute doesn't have a suitable buffer
+ if (!positionBuffer) {
+ qWarning("findBoundingVolumeComputeData: Position attribute not referencing a valid buffer");
+ return {};
+ }
+
+ // Check if there is an index attribute.
+ QAttribute *indexAttribute = nullptr;
+ Qt3DCore::QBuffer *indexBuffer = nullptr;
+
+ for (const auto attr : attributes) {
+ if (attr->attributeType() == QAttribute::IndexAttribute) {
+ indexBuffer = attr->buffer();
+ if (indexBuffer) {
+ indexAttribute = attr;
+
+ if (!drawVertexCount)
+ drawVertexCount = static_cast<int>(indexAttribute->count());
+
+ static const QAttribute::VertexBaseType validIndexTypes[] = {
+ QAttribute::UnsignedShort,
+ QAttribute::UnsignedInt,
+ QAttribute::UnsignedByte
+ };
+
+ if (std::find(std::begin(validIndexTypes),
+ std::end(validIndexTypes),
+ indexAttribute->vertexBaseType()) == std::end(validIndexTypes)) {
+ qWarning() << "findBoundingVolumeComputeData: Unsupported index attribute type" << indexAttribute->name() << indexAttribute->vertexBaseType();
+ return {};
+ }
+
+ break;
+ }
+ }
+ }
+
+ if (!indexAttribute && !drawVertexCount)
+ drawVertexCount = static_cast<int>(positionAttribute->count());
+
+ return { nullptr, nullptr, positionAttribute, indexAttribute, drawVertexCount };
+}
+
+BoundingVolumeComputeResult calculateLocalBoundingVolume(const BoundingVolumeComputeData &data) {
+ BoundingVolumeCalculator calculator;
+ if (calculator.apply(data.positionAttribute, data.indexAttribute, data.vertexCount,
+ data.provider->view()->primitiveRestartEnabled(),
+ data.provider->view()->restartIndexValue()))
+ return {
+ data.entity, data.provider, data.positionAttribute, data.indexAttribute,
+ calculator.min(), calculator.max(),
+ calculator.center(), calculator.radius()
+ };
+ return {};
+}
+
+bool isTreeEnabled(QEntity *entity) {
+ if (!entity->isEnabled())
+ return false;
+
+ QEntity *parent = entity->parentEntity();
+ while (parent) {
+ if (!parent->isEnabled())
+ return false;
+ parent = parent->parentEntity();
+ }
+
+ return true;
+}
+
+struct UpdateBoundFunctor
+{
+ // This define is required to work with QtConcurrent
+ typedef QVector<BoundingVolumeComputeResult> result_type;
+ result_type operator ()(const BoundingVolumeComputeData &data)
+ {
+ return { calculateLocalBoundingVolume(data) };
+ }
+};
+
+struct ReduceUpdateBoundFunctor
+{
+ void operator ()(QVector<BoundingVolumeComputeResult> &result, const QVector<BoundingVolumeComputeResult> &values)
+ {
+ result += values;
+ }
+};
+
+} // anonymous
+
+//class CalculateBoundingVolumeJobPrivate : public Qt3DCore::QAspectJobPrivate
+//{
+//public:
+// CalculateBoundingVolumeJobPrivate() { }
+// ~CalculateBoundingVolumeJobPrivate() override { }
+
+// void postFrame(Qt3DCore::QAspectManager *manager) override
+// {
+// Q_UNUSED(manager)
+//// for (Geometry *backend : qAsConst(m_updatedGeometries)) {
+//// Qt3DCore::QGeometry *node = qobject_cast<Qt3DCore::QGeometry *>(manager->lookupNode(backend->peerId()));
+//// if (!node)
+//// continue;
+//// Qt3DCore::QGeometryPrivate *dNode = static_cast<Qt3DCore::QGeometryPrivate *>(Qt3DCore::QNodePrivate::get(node));
+//// dNode->setExtent(backend->min(), backend->max());
+//// }
+// }
+
+//};
+
+CalculateBoundingVolumeJob::CalculateBoundingVolumeJob()
+ : Qt3DCore::QAspectJob()
+ , m_root(nullptr)
+{
+ SET_JOB_RUN_STAT_TYPE(this, JobTypes::CalcBoundingVolume, 0)
+}
+
+void CalculateBoundingVolumeJob::run()
+{
+ m_results.clear();
+
+ QHash<QEntity *, BoundingVolumeComputeData> dirtyEntities;
+ QNodeVisitor visitor;
+ visitor.traverse(m_root, [](QNode *) {}, [&dirtyEntities](QEntity *entity) {
+ if (!isTreeEnabled(entity))
+ return;
+
+ const auto bvProviders = entity->componentsOfType<QBoundingVolume>();
+ if (bvProviders.isEmpty())
+ return;
+
+ // we go through the list until be find a dirty provider,
+ // or THE primary provider
+ bool foundBV = false;
+ for (auto bv: bvProviders) {
+ if (!bv->view())
+ continue;
+ auto dbv = QBoundingVolumePrivate::get(bv);
+ if (foundBV && !dbv->m_primaryProvider)
+ continue;
+
+ auto bvdata = findBoundingVolumeComputeData(bv->view());
+ if (!bvdata.valid())
+ continue;
+ bvdata.entity = entity;
+ bvdata.provider = bv;
+
+ bool dirty = QEntityPrivate::get(entity)->m_dirty;
+ dirty |= QGeometryViewPrivate::get(bv->view())->m_dirty;
+ dirty |= QGeometryPrivate::get(bv->view()->geometry())->m_dirty;
+ dirty |= QAttributePrivate::get(bvdata.positionAttribute)->m_dirty;
+ dirty |= QBufferPrivate::get(bvdata.positionAttribute->buffer())->m_dirty;
+ if (bvdata.indexAttribute) {
+ dirty |= QAttributePrivate::get(bvdata.indexAttribute)->m_dirty;
+ dirty |= QBufferPrivate::get(bvdata.indexAttribute->buffer())->m_dirty;
+ }
+
+ if (dbv->m_primaryProvider) {
+ if (dirty)
+ dirtyEntities[entity] = bvdata;
+ break;
+ } else if (dirty) {
+ dirtyEntities[entity] = bvdata;
+ foundBV = true;
+ }
+ }
+ });
+
+#if QT_CONFIG(concurrent)
+ if (dirtyEntities.size() > 1) {
+ UpdateBoundFunctor functor;
+ ReduceUpdateBoundFunctor reduceFunctor;
+ m_results = QtConcurrent::blockingMappedReduced<decltype(m_results)>(dirtyEntities, functor, reduceFunctor);
+ } else
+#endif
+ {
+ for (auto it = dirtyEntities.begin(); it != dirtyEntities.end(); ++it) {
+ auto res = calculateLocalBoundingVolume(it.value());
+ if (res.valid())
+ m_results.push_back(res); // How do we push it to the backends????
+ }
+ }
+
+ for (auto result: qAsConst(m_results)) {
+ // set the results
+ QBoundingVolumePrivate::get(result.provider)->setImplicitBounds(result.m_min, result.m_max, result.m_center, result.m_radius);
+ }
+}
+
+void CalculateBoundingVolumeJob::postFrame(QAspectEngine *aspectEngine)
+{
+ Q_UNUSED(aspectEngine)
+
+ for (auto result: qAsConst(m_results)) {
+ // reset dirty flags
+ QEntityPrivate::get(result.entity)->m_dirty = false;
+ QGeometryViewPrivate::get(result.provider->view())->m_dirty = false;
+ QGeometryPrivate::get(result.provider->view()->geometry())->m_dirty = false;
+ QAttributePrivate::get(result.positionAttribute)->m_dirty = false;
+ QBufferPrivate::get(result.positionAttribute->buffer())->m_dirty = false;
+ if (result.indexAttribute) {
+ QAttributePrivate::get(result.indexAttribute)->m_dirty = false;
+ QBufferPrivate::get(result.indexAttribute->buffer())->m_dirty = false;
+ }
+ }
+
+ m_results.clear();
+}
+
+} // namespace Qt3DCore
+
+QT_END_NAMESPACE
+
diff --git a/src/core/jobs/calcboundingvolumejob_p.h b/src/core/jobs/calcboundingvolumejob_p.h
new file mode 100644
index 000000000..498fe766f
--- /dev/null
+++ b/src/core/jobs/calcboundingvolumejob_p.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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 QT3DCORE_CALCBOUNDINGVOLUMEJOB_H
+#define QT3DCORE_CALCBOUNDINGVOLUMEJOB_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DCore/qaspectjob.h>
+#include <Qt3DCore/private/qt3dcore_global_p.h>
+
+#include <QtCore/QSharedPointer>
+#include <QtGui/qvector3d.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+class CalculateBoundingVolumeJobPrivate;
+class QEntity;
+class QAttribute;
+class QBoundingVolume;
+
+struct BoundingVolumeComputeData {
+ QEntity *entity = nullptr;
+ QBoundingVolume *provider = nullptr;
+ QAttribute *positionAttribute = nullptr;
+ QAttribute *indexAttribute = nullptr;
+ int vertexCount = 0;
+
+ bool valid() const { return positionAttribute != nullptr; }
+};
+
+struct BoundingVolumeComputeResult {
+ QEntity *entity = nullptr;
+ QBoundingVolume *provider = nullptr;
+ QAttribute *positionAttribute = nullptr;
+ QAttribute *indexAttribute = nullptr;
+ QVector3D m_min;
+ QVector3D m_max;
+ QVector3D m_center;
+ float m_radius = -1.f;
+
+ bool valid() const { return m_radius >= 0.f; }
+};
+
+class Q_3DCORE_PRIVATE_EXPORT CalculateBoundingVolumeJob : public Qt3DCore::QAspectJob
+{
+public:
+ explicit CalculateBoundingVolumeJob();
+
+ void setRoot(QEntity *root) { m_root = root; }
+ void run() override;
+ void postFrame(QAspectEngine *aspectEngine) override;
+
+private:
+ Q_DECLARE_PRIVATE(CalculateBoundingVolumeJob)
+ QEntity *m_root;
+ QVector<BoundingVolumeComputeResult> m_results;
+};
+
+typedef QSharedPointer<CalculateBoundingVolumeJob> CalculateBoundingVolumeJobPtr;
+
+} // namespace Qt3DCore
+
+QT_END_NAMESPACE
+
+#endif // QT3DCORE_CALCBOUNDINGVOLUMEJOB_H
diff --git a/src/core/jobs/job_common_p.h b/src/core/jobs/job_common_p.h
new file mode 100644
index 000000000..6cbf17800
--- /dev/null
+++ b/src/core/jobs/job_common_p.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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 QT3DCORE_JOB_COMMON_P_H
+#define QT3DCORE_JOB_COMMON_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DCore/private/qaspectjob_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+namespace JobTypes {
+
+ enum JobType {
+ LoadBuffer = 4096,
+ CalcBoundingVolume,
+ };
+
+} // JobTypes
+
+} // Qt3DCore
+
+QT_END_NAMESPACE
+
+#endif // QT3DCORE_JOB_COMMON_P_H
diff --git a/src/core/jobs/jobs.pri b/src/core/jobs/jobs.pri
index 5d262f5c7..a0453395c 100644
--- a/src/core/jobs/jobs.pri
+++ b/src/core/jobs/jobs.pri
@@ -4,7 +4,8 @@ SOURCES += \
$$PWD/qaspectjobmanager.cpp \
$$PWD/qabstractaspectjobmanager.cpp \
$$PWD/qthreadpooler.cpp \
- $$PWD/task.cpp
+ $$PWD/task.cpp \
+ $$PWD/calcboundingvolumejob.cpp
HEADERS += \
$$PWD/qaspectjob.h \
@@ -13,7 +14,9 @@ HEADERS += \
$$PWD/qaspectjobmanager_p.h \
$$PWD/qabstractaspectjobmanager_p.h \
$$PWD/task_p.h \
- $$PWD/qthreadpooler_p.h
+ $$PWD/qthreadpooler_p.h \
+ $$PWD/calcboundingvolumejob_p.h \
+ $$PWD/job_common_p.h
INCLUDEPATH += $$PWD