summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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