summaryrefslogtreecommitdiffstats
path: root/src/render/jobs
diff options
context:
space:
mode:
Diffstat (limited to 'src/render/jobs')
-rw-r--r--src/render/jobs/calcboundingvolumejob.cpp188
-rw-r--r--src/render/jobs/computefilteredboundingvolumejob.cpp130
-rw-r--r--src/render/jobs/computefilteredboundingvolumejob_p.h91
-rw-r--r--src/render/jobs/job_common_p.h2
-rw-r--r--src/render/jobs/jobs.pri9
-rw-r--r--src/render/jobs/lightgatherer.cpp9
-rw-r--r--src/render/jobs/lightgatherer_p.h8
-rw-r--r--src/render/jobs/loadscenejob.cpp68
-rw-r--r--src/render/jobs/loadscenejob_p.h2
-rw-r--r--src/render/jobs/pickboundingvolumejob.cpp75
-rw-r--r--src/render/jobs/pickboundingvolumejob_p.h10
-rw-r--r--src/render/jobs/pickboundingvolumeutils.cpp57
-rw-r--r--src/render/jobs/pickboundingvolumeutils_p.h17
-rw-r--r--src/render/jobs/renderviewjobutils.cpp33
-rw-r--r--src/render/jobs/renderviewjobutils_p.h2
-rw-r--r--src/render/jobs/sendbuffercapturejob.cpp89
-rw-r--r--src/render/jobs/sendbuffercapturejob_p.h99
-rw-r--r--src/render/jobs/updatelevelofdetailjob.cpp8
18 files changed, 781 insertions, 116 deletions
diff --git a/src/render/jobs/calcboundingvolumejob.cpp b/src/render/jobs/calcboundingvolumejob.cpp
index 9e373c655..e81836502 100644
--- a/src/render/jobs/calcboundingvolumejob.cpp
+++ b/src/render/jobs/calcboundingvolumejob.cpp
@@ -50,6 +50,7 @@
#include <Qt3DRender/private/attribute_p.h>
#include <Qt3DRender/private/buffer_p.h>
#include <Qt3DRender/private/sphere_p.h>
+#include <Qt3DRender/private/buffervisitor_p.h>
#include <QtCore/qmath.h>
#include <QtConcurrent/QtConcurrent>
@@ -73,6 +74,114 @@ struct UpdateBoundFunctor {
}
};
+class BoundingVolumeCalculator
+{
+public:
+ BoundingVolumeCalculator(NodeManagers *manager) : m_manager(manager) { }
+
+ const Sphere& result() { return m_volume; }
+
+ bool apply(Qt3DRender::Render::Attribute *positionAttribute)
+ {
+ FindExtremePoints findExtremePoints(m_manager);
+ if (!findExtremePoints.apply(positionAttribute))
+ return false;
+
+ // Calculate squared distance for the pairs of points
+ const float xDist2 = (findExtremePoints.xMaxPt - findExtremePoints.xMinPt).lengthSquared();
+ const float yDist2 = (findExtremePoints.yMaxPt - findExtremePoints.yMinPt).lengthSquared();
+ const float zDist2 = (findExtremePoints.zMaxPt - findExtremePoints.zMinPt).lengthSquared();
+
+ // Select most distant pair
+ QVector3D p = findExtremePoints.xMinPt;
+ QVector3D q = findExtremePoints.xMaxPt;
+ if (yDist2 > xDist2 && yDist2 > zDist2) {
+ p = findExtremePoints.yMinPt;
+ q = findExtremePoints.yMaxPt;
+ }
+ if (zDist2 > xDist2 && zDist2 > yDist2) {
+ p = findExtremePoints.zMinPt;
+ q = findExtremePoints.zMaxPt;
+ }
+
+ const QVector3D c = 0.5f * (p + q);
+ m_volume.setCenter(c);
+ m_volume.setRadius((q - c).length());
+
+ ExpandSphere expandSphere(m_manager, m_volume);
+ if (!expandSphere.apply(positionAttribute))
+ return false;
+
+ return true;
+ }
+
+private:
+ Sphere m_volume;
+ NodeManagers *m_manager;
+
+ class FindExtremePoints : public Buffer3fVisitor
+ {
+ public:
+ FindExtremePoints(NodeManagers *manager)
+ : Buffer3fVisitor(manager)
+ , 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;
+ QVector3D 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 = QVector3D(x, y, z);
+ }
+ if (x > xMax) {
+ xMax = x;
+ xMaxPt = QVector3D(x, y, z);
+ }
+ if (y < yMin) {
+ yMin = y;
+ yMinPt = QVector3D(x, y, z);
+ }
+ if (y > yMax) {
+ yMax = y;
+ yMaxPt = QVector3D(x, y, z);
+ }
+ if (z < zMin) {
+ zMin = z;
+ zMinPt = QVector3D(x, y, z);
+ }
+ if (z > zMax) {
+ zMax = z;
+ zMaxPt = QVector3D(x, y, z);
+ }
+ } else {
+ xMin = xMax = x;
+ yMin = yMax = y;
+ zMin = zMax = z;
+ xMinPt = xMaxPt = yMinPt = yMaxPt = zMinPt = zMaxPt = QVector3D(x, y, z);
+ }
+ }
+ };
+
+ class ExpandSphere : public Buffer3fVisitor
+ {
+ public:
+ ExpandSphere(NodeManagers *manager, Sphere& volume)
+ : Buffer3fVisitor(manager), m_volume(volume)
+ { }
+
+ Sphere& m_volume;
+ void visit(uint ndx, float x, float y, float z) override
+ {
+ Q_UNUSED(ndx);
+ m_volume.expandToContain(QVector3D(x, y, z));
+ }
+ };
+};
+
void calculateLocalBoundingVolume(NodeManagers *manager, Entity *node)
{
// The Bounding volume will only be computed if the position Buffer
@@ -86,63 +195,48 @@ void calculateLocalBoundingVolume(NodeManagers *manager, Entity *node)
Geometry *geom = manager->lookupResource<Geometry, GeometryManager>(gRenderer->geometryId());
if (geom) {
- Qt3DRender::Render::Attribute *pickVolumeAttribute = manager->lookupResource<Attribute, AttributeManager>(geom->boundingPositionAttribute());
+ Qt3DRender::Render::Attribute *positionAttribute = manager->lookupResource<Attribute, AttributeManager>(geom->boundingPositionAttribute());
// Use the default position attribute if attribute is null
- if (!pickVolumeAttribute) {
+ if (!positionAttribute) {
const auto attrIds = geom->attributes();
for (const Qt3DCore::QNodeId attrId : attrIds) {
- pickVolumeAttribute = manager->lookupResource<Attribute, AttributeManager>(attrId);
- if (pickVolumeAttribute &&
- pickVolumeAttribute->name() == QAttribute::defaultPositionAttributeName())
+ positionAttribute = manager->lookupResource<Attribute, AttributeManager>(attrId);
+ if (positionAttribute &&
+ positionAttribute->name() == QAttribute::defaultPositionAttributeName())
break;
}
}
- if (pickVolumeAttribute) {
- if (!pickVolumeAttribute
- || pickVolumeAttribute->attributeType() != QAttribute::VertexAttribute
- || pickVolumeAttribute->vertexBaseType() != QAttribute::Float
- || pickVolumeAttribute->vertexSize() < 3) {
- qWarning() << "QGeometry::boundingVolumePositionAttribute pickVolume Attribute not suited for bounding volume computation";
- return;
- }
+ if (!positionAttribute
+ || positionAttribute->attributeType() != QAttribute::VertexAttribute
+ || positionAttribute->vertexBaseType() != QAttribute::Float
+ || positionAttribute->vertexSize() < 3) {
+ qWarning() << "QGeometry::boundingVolumePositionAttribute position Attribute not suited for bounding volume computation";
+ return;
+ }
- Buffer *buf = manager->lookupResource<Buffer, BufferManager>(pickVolumeAttribute->bufferId());
- // No point in continuing if the positionAttribute doesn't have a suitable buffer
- if (!buf) {
- qWarning() << "ObjectPicker pickVolume Attribute not referencing a valid buffer";
- return;
- }
+ Buffer *buf = manager->lookupResource<Buffer, BufferManager>(positionAttribute->bufferId());
+ // No point in continuing if the positionAttribute doesn't have a suitable buffer
+ if (!buf) {
+ qWarning() << "ObjectPicker position Attribute not referencing a valid buffer";
+ return;
+ }
+
+ // 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
+ if (buf->isDirty() ||
+ node->isBoundingVolumeDirty() ||
+ positionAttribute->isDirty() ||
+ geom->isDirty() ||
+ gRenderer->isDirty()) {
- // 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
- if (buf->isDirty() ||
- node->isBoundingVolumeDirty() ||
- pickVolumeAttribute->isDirty() ||
- geom->isDirty() ||
- gRenderer->isDirty()) {
-
- const QByteArray buffer = buf->data();
- const char *rawBuffer = buffer.constData();
- rawBuffer += pickVolumeAttribute->byteOffset();
- const int stride = pickVolumeAttribute->byteStride() ? pickVolumeAttribute->byteStride() : sizeof(float) * pickVolumeAttribute->vertexSize();
- QVector<QVector3D> vertices(pickVolumeAttribute->count());
-
- // TODO avoid copying the vertices
- for (int c = 0, vC = vertices.size(); c < vC; ++c) {
- QVector3D v;
- const float *fptr = reinterpret_cast<const float*>(rawBuffer);
- // TODO unwrap loop (switch?)
- for (uint i = 0, m = qMin(pickVolumeAttribute->vertexSize(), 3U); i < m; ++i)
- v[i] = fptr[i];
- vertices[c] = v;
- rawBuffer += stride;
- }
-
- node->localBoundingVolume()->initializeFromPoints(vertices);
+ BoundingVolumeCalculator reader(manager);
+ if (reader.apply(positionAttribute)) {
+ node->localBoundingVolume()->setCenter(reader.result().center());
+ node->localBoundingVolume()->setRadius(reader.result().radius());
node->unsetBoundingVolumeDirty();
}
}
diff --git a/src/render/jobs/computefilteredboundingvolumejob.cpp b/src/render/jobs/computefilteredboundingvolumejob.cpp
new file mode 100644
index 000000000..d8a7b5094
--- /dev/null
+++ b/src/render/jobs/computefilteredboundingvolumejob.cpp
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "computefilteredboundingvolumejob_p.h"
+
+#include <Qt3DRender/private/renderer_p.h>
+#include <Qt3DRender/private/entity_p.h>
+#include <Qt3DRender/private/renderlogging_p.h>
+#include <Qt3DRender/private/sphere_p.h>
+#include <Qt3DRender/private/job_common_p.h>
+
+#include <QThread>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+
+namespace {
+
+void expandWorldBoundingVolume(Qt3DRender::Render::Sphere *sphere,
+ Qt3DRender::Render::Entity *node,
+ Qt3DRender::Render::Entity *excludeSubTree)
+{
+ Qt3DRender::Render::Sphere childSphere(*node->worldBoundingVolume());
+ // Go to the nodes that have the most depth
+ const auto children = node->children();
+ for (Entity *c : children) {
+ if (c != excludeSubTree)
+ expandWorldBoundingVolume(&childSphere, c, excludeSubTree);
+ }
+ sphere->expandToContain(childSphere);
+}
+
+} // namespace
+
+ComputeFilteredBoundingVolumeJob::ComputeFilteredBoundingVolumeJob()
+ : m_root(nullptr)
+ , m_ignoreSubTree(nullptr)
+{
+ SET_JOB_RUN_STAT_TYPE(this, JobTypes::ExpandBoundingVolume, 0);
+}
+
+void ComputeFilteredBoundingVolumeJob::setRoot(Entity *root)
+{
+ m_root = root;
+}
+
+void ComputeFilteredBoundingVolumeJob::ignoreSubTree(Entity *node)
+{
+ m_ignoreSubTree = node;
+}
+
+void ComputeFilteredBoundingVolumeJob::run()
+{
+ qCDebug(Jobs) << "Entering" << Q_FUNC_INFO << QThread::currentThread();
+
+ if (!m_root)
+ return;
+ if (!m_ignoreSubTree) {
+ finished(*m_root->worldBoundingVolumeWithChildren());
+ return;
+ }
+
+ bool isFilterChildOfRoot = false;
+ Entity *parent = m_ignoreSubTree->parent();
+ while (parent) {
+ if (parent == m_root) {
+ isFilterChildOfRoot = true;
+ break;
+ }
+ parent = parent->parent();
+ }
+ if (!isFilterChildOfRoot) {
+ finished(*m_root->worldBoundingVolumeWithChildren());
+ return;
+ }
+
+ Qt3DRender::Render::Sphere sphere;
+ expandWorldBoundingVolume(&sphere, m_root, m_ignoreSubTree);
+ finished(sphere);
+
+ qCDebug(Jobs) << "Exiting" << Q_FUNC_INFO << QThread::currentThread();
+}
+
+void ComputeFilteredBoundingVolumeJob::finished(const Qt3DRender::Render::Sphere &sphere)
+{
+ Q_UNUSED(sphere);
+}
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/jobs/computefilteredboundingvolumejob_p.h b/src/render/jobs/computefilteredboundingvolumejob_p.h
new file mode 100644
index 000000000..d7681e604
--- /dev/null
+++ b/src/render/jobs/computefilteredboundingvolumejob_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_COMPUTEFILTEREDBOUNDINGVOLUMEJOB_H
+#define QT3DRENDER_RENDER_COMPUTEFILTEREDBOUNDINGVOLUMEJOB_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DCore/qaspectjob.h>
+#include <private/qt3drender_global_p.h>
+
+#include <QtCore/QSharedPointer>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+
+class Entity;
+class Sphere;
+
+class QT3DRENDERSHARED_PRIVATE_EXPORT ComputeFilteredBoundingVolumeJob : public Qt3DCore::QAspectJob
+{
+public:
+ ComputeFilteredBoundingVolumeJob();
+
+ void setRoot(Entity *root);
+ void ignoreSubTree(Entity *node);
+ void run() Q_DECL_OVERRIDE;
+
+protected:
+ virtual void finished(const Qt3DRender::Render::Sphere &sphere);
+
+private:
+ Entity *m_root;
+ Entity *m_ignoreSubTree;
+};
+
+typedef QSharedPointer<ComputeFilteredBoundingVolumeJob> ComputeFilteredBoundingVolumeJobPtr;
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_COMPUTEFILTEREDBOUNDINGVOLUMEJOB_H
diff --git a/src/render/jobs/job_common_p.h b/src/render/jobs/job_common_p.h
index 90e29d0dc..332b62547 100644
--- a/src/render/jobs/job_common_p.h
+++ b/src/render/jobs/job_common_p.h
@@ -86,9 +86,11 @@ namespace JobTypes {
UpdateWorldBoundingVolume,
FrameSubmissionPart2,
DirtyBufferGathering,
+ DirtyVaoGathering,
DirtyTextureGathering,
DirtyShaderGathering,
SendRenderCapture,
+ SendBufferCapture,
SyncRenderViewCommandBuilding,
SyncRenderViewInitialization,
SyncRenderViewCommandBuilder,
diff --git a/src/render/jobs/jobs.pri b/src/render/jobs/jobs.pri
index d7f05ec4c..66c98b1c0 100644
--- a/src/render/jobs/jobs.pri
+++ b/src/render/jobs/jobs.pri
@@ -11,6 +11,7 @@ HEADERS += \
$$PWD/calcboundingvolumejob_p.h \
$$PWD/pickboundingvolumejob_p.h \
$$PWD/calcgeometrytrianglevolumes_p.h \
+ $$PWD/computefilteredboundingvolumejob_p.h \
$$PWD/job_common_p.h \
$$PWD/filterlayerentityjob_p.h \
$$PWD/filterentitybycomponentjob_p.h \
@@ -28,7 +29,8 @@ HEADERS += \
$$PWD/updatemeshtrianglelistjob_p.h \
$$PWD/pickboundingvolumeutils_p.h \
$$PWD/filtercompatibletechniquejob_p.h \
- $$PWD/updatetreeenabledjob_p.h
+ $$PWD/updatetreeenabledjob_p.h \
+ $$PWD/sendbuffercapturejob_p.h
SOURCES += \
$$PWD/updateworldtransformjob.cpp \
@@ -41,6 +43,7 @@ SOURCES += \
$$PWD/calcboundingvolumejob.cpp \
$$PWD/pickboundingvolumejob.cpp \
$$PWD/calcgeometrytrianglevolumes.cpp \
+ $$PWD/computefilteredboundingvolumejob.cpp \
$$PWD/filterlayerentityjob.cpp \
$$PWD/materialparametergathererjob.cpp \
$$PWD/renderviewbuilderjob.cpp \
@@ -55,4 +58,6 @@ SOURCES += \
$$PWD/updatelevelofdetailjob.cpp \
$$PWD/pickboundingvolumeutils.cpp \
$$PWD/filtercompatibletechniquejob.cpp \
- $$PWD/updatetreeenabledjob.cpp
+ $$PWD/updatetreeenabledjob.cpp \
+ $$PWD/sendbuffercapturejob.cpp
+
diff --git a/src/render/jobs/lightgatherer.cpp b/src/render/jobs/lightgatherer.cpp
index 39023bc35..e62544f15 100644
--- a/src/render/jobs/lightgatherer.cpp
+++ b/src/render/jobs/lightgatherer.cpp
@@ -51,6 +51,7 @@ namespace Render {
LightGatherer::LightGatherer()
: Qt3DCore::QAspectJob()
, m_manager(nullptr)
+ , m_environmentLight(nullptr)
{
SET_JOB_RUN_STAT_TYPE(this, JobTypes::LightGathering, 0);
}
@@ -58,13 +59,21 @@ LightGatherer::LightGatherer()
void LightGatherer::run()
{
const QVector<HEntity> handles = m_manager->activeHandles();
+ int envLightCount = 0;
for (const HEntity handle : handles) {
Entity *node = m_manager->data(handle);
const QVector<Light *> lights = node->renderComponents<Light>();
if (!lights.isEmpty())
m_lights.push_back(LightSource(node, lights));
+ const QVector<EnvironmentLight *> envLights = node->renderComponents<EnvironmentLight>();
+ envLightCount += envLights.size();
+ if (!envLights.isEmpty() && !m_environmentLight)
+ m_environmentLight = envLights.first();
}
+
+ if (envLightCount > 1)
+ qWarning() << "More than one environment light found, extra instances are ignored";
}
} // Render
diff --git a/src/render/jobs/lightgatherer_p.h b/src/render/jobs/lightgatherer_p.h
index e2954fd07..4eaeb8f19 100644
--- a/src/render/jobs/lightgatherer_p.h
+++ b/src/render/jobs/lightgatherer_p.h
@@ -61,6 +61,7 @@ namespace Qt3DRender {
namespace Render {
class EntityManager;
+class EnvironmentLight;
class Q_AUTOTEST_EXPORT LightGatherer : public Qt3DCore::QAspectJob
{
@@ -69,12 +70,19 @@ public:
inline void setManager(EntityManager *manager) Q_DECL_NOTHROW { m_manager = manager; }
inline QVector<LightSource> &lights() { return m_lights; }
+ inline EnvironmentLight *takeEnvironmentLight()
+ {
+ auto envLight = m_environmentLight;
+ m_environmentLight = nullptr;
+ return envLight;
+ }
void run() Q_DECL_FINAL;
private:
EntityManager *m_manager;
QVector<LightSource> m_lights;
+ EnvironmentLight *m_environmentLight;
};
typedef QSharedPointer<LightGatherer> LightGathererPtr;
diff --git a/src/render/jobs/loadscenejob.cpp b/src/render/jobs/loadscenejob.cpp
index 9b3374627..3d6326e93 100644
--- a/src/render/jobs/loadscenejob.cpp
+++ b/src/render/jobs/loadscenejob.cpp
@@ -44,6 +44,7 @@
#include <Qt3DCore/qentity.h>
#include <Qt3DRender/private/job_common_p.h>
#include <Qt3DRender/private/qsceneimporter_p.h>
+#include <Qt3DRender/private/qurlhelper_p.h>
#include <Qt3DRender/qsceneloader.h>
QT_BEGIN_NAMESPACE
@@ -60,6 +61,11 @@ LoadSceneJob::LoadSceneJob(const QUrl &source, Qt3DCore::QNodeId m_sceneComponen
SET_JOB_RUN_STAT_TYPE(this, JobTypes::LoadScene, 0);
}
+void LoadSceneJob::setData(const QByteArray &data)
+{
+ m_data = data;
+}
+
NodeManagers *LoadSceneJob::nodeManagers() const
{
return m_managers;
@@ -95,20 +101,54 @@ void LoadSceneJob::run()
// Perform the loading only if the source wasn't explicitly set to empty
if (!m_source.isEmpty()) {
finalStatus = QSceneLoader::Error;
- for (QSceneImporter *sceneImporter : qAsConst(m_sceneImporters)) {
- if (!sceneImporter->isFileTypeSupported(m_source))
- continue;
-
- // If the file type is supported -> enter Loading status
- scene->setStatus(QSceneLoader::Loading);
-
- // File type is supported, try to load it
- sceneImporter->setSource(m_source);
- sceneSubTree = sceneImporter->scene();
- if (sceneSubTree != nullptr) {
- // Successfully built a subtree
- finalStatus = QSceneLoader::Ready;
- break;
+
+ if (m_data.isEmpty()) {
+ const QString path = QUrlHelper::urlToLocalFileOrQrc(m_source);
+ QFileInfo finfo(path);
+ if (finfo.exists()) {
+ QStringList extensions(finfo.suffix());
+
+ for (QSceneImporter *sceneImporter : qAsConst(m_sceneImporters)) {
+ if (!sceneImporter->areFileTypesSupported(extensions))
+ continue;
+
+ // If the file type is supported -> enter Loading status
+ scene->setStatus(QSceneLoader::Loading);
+
+ // File type is supported, try to load it
+ sceneImporter->setSource(m_source);
+ sceneSubTree = sceneImporter->scene();
+ if (sceneSubTree != nullptr) {
+ // Successfully built a subtree
+ finalStatus = QSceneLoader::Ready;
+ break;
+ }
+ }
+ }
+ } else {
+ QStringList extensions;
+ QMimeDatabase db;
+ QMimeType mtype = db.mimeTypeForData(m_data);
+ if (mtype.isValid()) {
+ extensions = mtype.suffixes();
+ }
+
+ QString basePath = m_source.adjusted(QUrl::RemoveFilename).toString();
+ for (QSceneImporter *sceneImporter : qAsConst(m_sceneImporters)) {
+ if (!sceneImporter->areFileTypesSupported(extensions))
+ continue;
+
+ // If the file type is supported -> enter Loading status
+ scene->setStatus(QSceneLoader::Loading);
+
+ // File type is supported, try to load it
+ sceneImporter->setData(m_data, basePath);
+ sceneSubTree = sceneImporter->scene();
+ if (sceneSubTree != nullptr) {
+ // Successfully built a subtree
+ finalStatus = QSceneLoader::Ready;
+ break;
+ }
}
}
}
diff --git a/src/render/jobs/loadscenejob_p.h b/src/render/jobs/loadscenejob_p.h
index 7538c6eb0..5217c6f43 100644
--- a/src/render/jobs/loadscenejob_p.h
+++ b/src/render/jobs/loadscenejob_p.h
@@ -70,6 +70,7 @@ class Q_AUTOTEST_EXPORT LoadSceneJob : public Qt3DCore::QAspectJob
{
public:
explicit LoadSceneJob(const QUrl &source, Qt3DCore::QNodeId sceneComponent);
+ void setData(const QByteArray &data);
void setNodeManagers(NodeManagers *managers) { m_managers = managers; }
void setSceneImporters(const QList<QSceneImporter *> sceneImporters) { m_sceneImporters = sceneImporters; }
@@ -82,6 +83,7 @@ public:
private:
QUrl m_source;
+ QByteArray m_data;
Qt3DCore::QNodeId m_sceneComponent;
NodeManagers *m_managers;
QList<QSceneImporter *> m_sceneImporters;
diff --git a/src/render/jobs/pickboundingvolumejob.cpp b/src/render/jobs/pickboundingvolumejob.cpp
index 6b8b93647..f9c7a390c 100644
--- a/src/render/jobs/pickboundingvolumejob.cpp
+++ b/src/render/jobs/pickboundingvolumejob.cpp
@@ -49,6 +49,7 @@
#include <Qt3DRender/private/rendersettings_p.h>
#include <Qt3DRender/qgeometryrenderer.h>
#include <Qt3DRender/private/job_common_p.h>
+#include <Qt3DRender/private/qpickevent_p.h>
QT_BEGIN_NAMESPACE
@@ -59,6 +60,8 @@ namespace Render {
namespace {
+typedef PickingUtils::AbstractCollisionGathererFunctor::result_type HitList;
+
void setEventButtonAndModifiers(const QMouseEvent &event, QPickEvent::Buttons &eventButton, int &eventButtons, int &eventModifiers)
{
switch (event.button()) {
@@ -120,6 +123,11 @@ void PickBoundingVolumeJob::setMouseEvents(const QList<QMouseEvent> &pendingEven
m_pendingMouseEvents = pendingEvents;
}
+void PickBoundingVolumeJob::setKeyEvents(const QList<QKeyEvent> &pendingEvents)
+{
+ m_pendingKeyEvents = pendingEvents;
+}
+
void PickBoundingVolumeJob::setFrameGraphRoot(FrameGraphNode *frameGraphRoot)
{
m_frameGraphRoot = frameGraphRoot;
@@ -159,7 +167,8 @@ bool PickBoundingVolumeJob::runHelper()
m_oneEnabledAtLeast = false;
m_oneHoverAtLeast = false;
- for (auto handle : m_manager->objectPickerManager()->activeHandles()) {
+ const auto activeHandles = m_manager->objectPickerManager()->activeHandles();
+ for (auto handle : activeHandles) {
auto picker = m_manager->objectPickerManager()->data(handle);
m_oneEnabledAtLeast |= picker->isEnabled();
m_oneHoverAtLeast |= picker->isHoverEnabled();
@@ -214,11 +223,6 @@ bool PickBoundingVolumeJob::runHelper()
// If we have move or hover move events that someone cares about, we try to avoid expensive computations
// by compressing them into a single one
- // Gather the entities for the frame
- // TO DO: We could skip doing that every frame and only do it when we know for sure
- // that the tree structure has changed
- PickingUtils::EntityGatherer entitiesGatherer(m_node);
-
// Store the reducer function which varies depending on the picking settings set on the renderer
using ReducerFunction = PickingUtils::CollisionVisitor::HitList (*)(PickingUtils::CollisionVisitor::HitList &results, const PickingUtils::CollisionVisitor::HitList &intermediate);
@@ -244,25 +248,30 @@ bool PickBoundingVolumeJob::runHelper()
// For each triplet of Viewport / Camera and Area
for (const PickingUtils::ViewportCameraAreaTriplet &vca : vcaTriplets) {
- typedef PickingUtils::AbstractCollisionGathererFunctor::result_type HitList;
HitList sphereHits;
QRay3D ray = rayForViewportAndCamera(vca.area, event.pos(), vca.viewport, vca.cameraId);
- if (trianglePickingRequested) {
- PickingUtils::TriangleCollisionGathererFunctor gathererFunctor;
- gathererFunctor.m_frontFaceRequested = frontFaceRequested;
- gathererFunctor.m_backFaceRequested = backFaceRequested;
- gathererFunctor.m_manager = m_manager;
- gathererFunctor.m_ray = ray;
- sphereHits = QtConcurrent::blockingMappedReduced<HitList>(entitiesGatherer.entities(), gathererFunctor, reducerOp);
- } else {
- PickingUtils::EntityCollisionGathererFunctor gathererFunctor;
- gathererFunctor.m_manager = m_manager;
- gathererFunctor.m_ray = ray;
- sphereHits = QtConcurrent::blockingMappedReduced<HitList>(entitiesGatherer.entities(), gathererFunctor, reducerOp);
+
+ PickingUtils::HierarchicalEntityPicker entityPicker(ray);
+ if (entityPicker.collectHits(m_node)) {
+ if (trianglePickingRequested) {
+ PickingUtils::TriangleCollisionGathererFunctor gathererFunctor;
+ gathererFunctor.m_frontFaceRequested = frontFaceRequested;
+ gathererFunctor.m_backFaceRequested = backFaceRequested;
+ gathererFunctor.m_manager = m_manager;
+ gathererFunctor.m_ray = ray;
+ sphereHits = QtConcurrent::blockingMappedReduced<HitList>(entityPicker.entities(),
+ gathererFunctor, reducerOp);
+ } else {
+ sphereHits = entityPicker.hits();
+ PickingUtils::AbstractCollisionGathererFunctor::sortHits(sphereHits);
+ if (!allHitsRequested)
+ sphereHits = { sphereHits.front() };
+ }
}
// Dispatch events based on hit results
- dispatchPickEvents(event, sphereHits, eventButton, eventButtons, eventModifiers, trianglePickingRequested, allHitsRequested);
+ dispatchPickEvents(event, sphereHits, eventButton, eventButtons, eventModifiers,
+ trianglePickingRequested, allHitsRequested);
}
}
@@ -331,17 +340,18 @@ void PickBoundingVolumeJob::dispatchPickEvents(const QMouseEvent &event,
// Send the corresponding event
QVector3D localIntersection = hit.m_intersection;
if (entity && entity->worldTransform())
- localIntersection = hit.m_intersection * entity->worldTransform()->inverted();
+ localIntersection = entity->worldTransform()->inverted() * hit.m_intersection;
QPickEventPtr pickEvent;
- if (trianglePickingRequested)
+ if (trianglePickingRequested) {
pickEvent.reset(new QPickTriangleEvent(event.localPos(), hit.m_intersection, localIntersection, hit.m_distance,
hit.m_triangleIndex, hit.m_vertexIndex[0], hit.m_vertexIndex[1], hit.m_vertexIndex[2],
- eventButton, eventButtons, eventModifiers));
- else
+ eventButton, eventButtons, eventModifiers, hit.m_uvw));
+ QPickEventPrivate::get(pickEvent.data())->m_entity = hit.m_entityId;
+ } else {
pickEvent.reset(new QPickEvent(event.localPos(), hit.m_intersection, localIntersection, hit.m_distance,
eventButton, eventButtons, eventModifiers));
-
+ }
switch (event.type()) {
case QEvent::MouseButtonPress: {
// Store pressed object handle
@@ -352,24 +362,23 @@ void PickBoundingVolumeJob::dispatchPickEvents(const QMouseEvent &event,
}
case QEvent::MouseButtonRelease: {
- if (lastCurrentPicker != nullptr && m_currentPicker == objectPickerHandle)
- m_currentPicker = HObjectPicker();
// Only send the release event if it was pressed
- if (objectPicker->isPressed()) {
- if (lastCurrentPicker == objectPicker)
- objectPicker->onClicked(pickEvent);
+ if (objectPicker->isPressed())
objectPicker->onReleased(pickEvent);
+ if (lastCurrentPicker != nullptr && m_currentPicker == objectPickerHandle) {
+ objectPicker->onClicked(pickEvent);
+ m_currentPicker = HObjectPicker();
}
break;
}
-
+#if QT_CONFIG(gestures)
case Qt::TapGesture: {
objectPicker->onClicked(pickEvent);
break;
}
-
+#endif
case QEvent::MouseMove: {
- if (objectPicker->isPressed() && objectPicker->isDragEnabled()) {
+ if ((objectPicker->isPressed() || objectPicker->isHoverEnabled()) && objectPicker->isDragEnabled()) {
objectPicker->onMoved(pickEvent);
}
// fallthrough
diff --git a/src/render/jobs/pickboundingvolumejob_p.h b/src/render/jobs/pickboundingvolumejob_p.h
index 9f52943d1..5239c5c6c 100644
--- a/src/render/jobs/pickboundingvolumejob_p.h
+++ b/src/render/jobs/pickboundingvolumejob_p.h
@@ -59,6 +59,7 @@
#include <Qt3DRender/private/pickboundingvolumeutils_p.h>
#include <Qt3DRender/qpickevent.h>
#include <QMouseEvent>
+#include <QKeyEvent>
#include <QSharedPointer>
QT_BEGIN_NAMESPACE
@@ -82,6 +83,7 @@ public:
void setRoot(Entity *root);
void setMouseEvents(const QList<QMouseEvent> &pendingEvents);
+ void setKeyEvents(const QList<QKeyEvent> &pendingEvents);
void setFrameGraphRoot(FrameGraphNode *frameGraphRoot);
void setRenderSettings(RenderSettings *settings);
void setManagers(NodeManagers *manager);
@@ -102,8 +104,10 @@ protected:
void run() Q_DECL_FINAL;
void dispatchPickEvents(const QMouseEvent &event, const PickingUtils::CollisionVisitor::HitList &sphereHits,
QPickEvent::Buttons eventButton,
- int eventButtons, int eventModifiers,
- bool trianglePickingRequested, bool allHitsRequested);
+ int eventButtons,
+ int eventModifiers,
+ bool trianglePickingRequested,
+ bool allHitsRequested);
private:
NodeManagers *m_manager;
@@ -115,6 +119,8 @@ private:
bool m_oneEnabledAtLeast;
bool m_oneHoverAtLeast;
+ QList<QKeyEvent> m_pendingKeyEvents;
+
void viewMatrixForCamera(Qt3DCore::QNodeId cameraId,
QMatrix4x4 &viewMatrix,
QMatrix4x4 &projectionMatrix) const;
diff --git a/src/render/jobs/pickboundingvolumeutils.cpp b/src/render/jobs/pickboundingvolumeutils.cpp
index 1573aa112..f08398a5a 100644
--- a/src/render/jobs/pickboundingvolumeutils.cpp
+++ b/src/render/jobs/pickboundingvolumeutils.cpp
@@ -175,6 +175,7 @@ bool CollisionVisitor::intersectsSegmentTriangle(uint andx, const QVector3D &a,
queryResult.m_vertexIndex[0] = andx;
queryResult.m_vertexIndex[1] = bndx;
queryResult.m_vertexIndex[2] = cndx;
+ queryResult.m_uvw = uvw;
queryResult.m_intersection = m_ray.point(t * m_ray.distance());
queryResult.m_distance = m_ray.projectedDistance(queryResult.m_intersection);
hits.push_back(queryResult);
@@ -212,6 +213,15 @@ AbstractCollisionGathererFunctor::result_type AbstractCollisionGathererFunctor::
return pick(&rayCasting, entity);
}
+void AbstractCollisionGathererFunctor::sortHits(CollisionVisitor::HitList &results)
+{
+ auto compareHitsDistance = [](const CollisionVisitor::HitList::value_type &a,
+ const CollisionVisitor::HitList::value_type &b) {
+ return a.m_distance < b.m_distance;
+ };
+ std::sort(results.begin(), results.end(), compareHitsDistance);
+}
+
AbstractCollisionGathererFunctor::result_type EntityCollisionGathererFunctor::pick(RayCasting::QAbstractCollisionQueryService *rayCasting, const Entity *entity) const
{
result_type result;
@@ -236,14 +246,7 @@ AbstractCollisionGathererFunctor::result_type TriangleCollisionGathererFunctor::
visitor.apply(gRenderer, entity->peerId());
result = visitor.hits;
- struct
- {
- bool operator()(const result_type::value_type &a, const result_type::value_type &b)
- {
- return a.m_distance < b.m_distance;
- }
- } compareHitsDistance;
- std::sort(result.begin(), result.end(), compareHitsDistance);
+ sortHits(result);
}
return result;
@@ -281,6 +284,44 @@ CollisionVisitor::HitList reduceToAllHits(CollisionVisitor::HitList &results, co
return results;
}
+HierarchicalEntityPicker::HierarchicalEntityPicker(const QRay3D &ray)
+ : m_ray(ray)
+{
+
+}
+
+bool HierarchicalEntityPicker::collectHits(Entity *root)
+{
+ m_hits.clear();
+ m_entities.clear();
+
+ QRayCastingService rayCasting;
+ QVector<Entity *> worklist;
+ worklist << root;
+
+ while (!worklist.empty()) {
+ Entity *current = worklist.takeLast();
+
+ // first pick entry sub-scene-graph
+ QCollisionQueryResult::Hit queryResult =
+ rayCasting.query(m_ray, current->worldBoundingVolumeWithChildren());
+ if (queryResult.m_distance < 0.f)
+ continue;
+
+ // if we get a hit, we check again for this specific entity
+ queryResult = rayCasting.query(m_ray, current->worldBoundingVolume());
+ if (queryResult.m_distance >= 0.f) {
+ m_entities.push_back(current);
+ m_hits.push_back(queryResult);
+ }
+
+ // and pick children
+ worklist << current->children();
+ }
+
+ return !m_hits.empty();
+}
+
} // PickingUtils
} // Render
diff --git a/src/render/jobs/pickboundingvolumeutils_p.h b/src/render/jobs/pickboundingvolumeutils_p.h
index 0a65c58cc..08615c094 100644
--- a/src/render/jobs/pickboundingvolumeutils_p.h
+++ b/src/render/jobs/pickboundingvolumeutils_p.h
@@ -136,6 +136,21 @@ private:
uint cndx, const QVector3D &c);
};
+class Q_AUTOTEST_EXPORT HierarchicalEntityPicker
+{
+public:
+ explicit HierarchicalEntityPicker(const RayCasting::QRay3D &ray);
+
+ bool collectHits(Entity *root);
+ inline CollisionVisitor::HitList hits() const { return m_hits; }
+ inline QVector<Entity *> entities() const { return m_entities; }
+
+private:
+ RayCasting::QRay3D m_ray;
+ CollisionVisitor::HitList m_hits;
+ QVector<Entity *> m_entities;
+};
+
struct Q_AUTOTEST_EXPORT AbstractCollisionGathererFunctor
{
AbstractCollisionGathererFunctor();
@@ -148,6 +163,8 @@ struct Q_AUTOTEST_EXPORT AbstractCollisionGathererFunctor
typedef CollisionVisitor::HitList result_type;
result_type operator ()(const Entity *entity) const;
virtual result_type pick(RayCasting::QAbstractCollisionQueryService *rayCasting, const Entity *entity) const = 0;
+
+ static void sortHits(CollisionVisitor::HitList &results);
};
struct Q_AUTOTEST_EXPORT EntityCollisionGathererFunctor : public AbstractCollisionGathererFunctor
diff --git a/src/render/jobs/renderviewjobutils.cpp b/src/render/jobs/renderviewjobutils.cpp
index e587dd828..9f1b51cc1 100644
--- a/src/render/jobs/renderviewjobutils.cpp
+++ b/src/render/jobs/renderviewjobutils.cpp
@@ -63,6 +63,7 @@
#include <Qt3DRender/private/dispatchcompute_p.h>
#include <Qt3DRender/private/rendersurfaceselector_p.h>
#include <Qt3DRender/private/rendercapture_p.h>
+#include <Qt3DRender/private/buffercapture_p.h>
#include <Qt3DRender/private/stringtoint_p.h>
#include <Qt3DRender/private/techniquemanager_p.h>
#include <Qt3DRender/private/memorybarrier_p.h>
@@ -164,6 +165,7 @@ void setRenderViewConfigFromFrameGraphLeafNode(RenderView *rv, const FrameGraphN
// a subregion relative to that of the parent viewport
const ViewportNode *vpNode = static_cast<const ViewportNode *>(node);
rv->setViewport(computeViewport(rv->viewport(), vpNode));
+ rv->setGamma(vpNode->gamma());
break;
}
@@ -238,7 +240,15 @@ void setRenderViewConfigFromFrameGraphLeafNode(RenderView *rv, const FrameGraphN
case FrameGraphNode::MemoryBarrier: {
const Render::MemoryBarrier *barrier = static_cast<const Render::MemoryBarrier *>(node);
- rv->setMemoryBarrier(barrier->barrierTypes()|rv->memoryBarrier());
+ rv->setMemoryBarrier(barrier->waitOperations()|rv->memoryBarrier());
+ break;
+ }
+
+ case FrameGraphNode::BufferCapture: {
+ auto *bufferCapture = const_cast<Render::BufferCapture *>(
+ static_cast<const Render::BufferCapture *>(node));
+ if (bufferCapture != nullptr)
+ rv->setIsDownloadBuffersEnable(bufferCapture->isEnabled());
break;
}
@@ -404,7 +414,9 @@ const int qNodeIdTypeId = qMetaTypeId<QNodeId>();
}
UniformBlockValueBuilder::UniformBlockValueBuilder()
- : shaderDataManager(nullptr)
+ : updatedPropertiesOnly(false)
+ , shaderDataManager(nullptr)
+ , textureManager(nullptr)
{
}
@@ -420,12 +432,16 @@ void UniformBlockValueBuilder::buildActiveUniformNameValueMapHelper(ShaderData *
QVariantList list = value.value<QVariantList>();
if (list.at(0).userType() == qNodeIdTypeId) { // Array of struct qmlPropertyName[i].structMember
for (int i = 0; i < list.size(); ++i) {
+ const QVariant variantElement = list.at(i);
if (list.at(i).userType() == qNodeIdTypeId) {
- ShaderData *subShaderData = shaderDataManager->lookupResource(list.at(i).value<QNodeId>());
- if (subShaderData)
+ const auto nodeId = variantElement.value<QNodeId>();
+ ShaderData *subShaderData = shaderDataManager->lookupResource(nodeId);
+ if (subShaderData) {
buildActiveUniformNameValueMapStructHelper(subShaderData,
blockName + QLatin1Char('.') + qmlPropertyName + blockArray.arg(i),
QLatin1String(""));
+ }
+ // Note: we only handle ShaderData as nested container nodes here
}
}
} else { // Array of scalar/vec qmlPropertyName[0]
@@ -436,11 +452,16 @@ void UniformBlockValueBuilder::buildActiveUniformNameValueMapHelper(ShaderData *
}
}
} else if (value.userType() == qNodeIdTypeId) { // Struct qmlPropertyName.structMember
- ShaderData *rSubShaderData = shaderDataManager->lookupResource(value.value<QNodeId>());
- if (rSubShaderData)
+ const auto nodeId = value.value<QNodeId>();
+ ShaderData *rSubShaderData = shaderDataManager->lookupResource(nodeId);
+ if (rSubShaderData) {
buildActiveUniformNameValueMapStructHelper(rSubShaderData,
blockName,
qmlPropertyName);
+ } else if (textureManager->contains(nodeId)) {
+ const auto varId = StringToInt::lookupId(blockName + QLatin1Char('.') + qmlPropertyName);
+ activeUniformNamesToValue.insert(varId, value);
+ }
} else { // Scalar / Vec
QString varName = blockName + QLatin1Char('.') + qmlPropertyName;
if (uniforms.contains(varName)) {
diff --git a/src/render/jobs/renderviewjobutils_p.h b/src/render/jobs/renderviewjobutils_p.h
index bc042a582..c1d37b28a 100644
--- a/src/render/jobs/renderviewjobutils_p.h
+++ b/src/render/jobs/renderviewjobutils_p.h
@@ -83,6 +83,7 @@ class NodeManagers;
class ShaderDataManager;
struct ShaderUniform;
class ShaderData;
+class TextureManager;
class RenderStateManager;
class RenderStateCollection;
@@ -169,6 +170,7 @@ struct Q_AUTOTEST_EXPORT UniformBlockValueBuilder
QHash<QString, ShaderUniform> uniforms;
UniformBlockValueBuilderHash activeUniformNamesToValue;
ShaderDataManager *shaderDataManager;
+ TextureManager *textureManager;
QMatrix4x4 viewMatrix;
};
diff --git a/src/render/jobs/sendbuffercapturejob.cpp b/src/render/jobs/sendbuffercapturejob.cpp
new file mode 100644
index 000000000..7829931f7
--- /dev/null
+++ b/src/render/jobs/sendbuffercapturejob.cpp
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Juan José Casafranca
+** 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 "sendbuffercapturejob_p.h"
+
+
+#include "Qt3DRender/private/renderer_p.h"
+#include "Qt3DRender/private/nodemanagers_p.h"
+#include <Qt3DRender/private/job_common_p.h>
+#include <Qt3DRender/private/buffer_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+SendBufferCaptureJob::SendBufferCaptureJob()
+ : Qt3DCore::QAspectJob()
+{
+ SET_JOB_RUN_STAT_TYPE(this, JobTypes::SendBufferCapture, 0);
+}
+
+SendBufferCaptureJob::~SendBufferCaptureJob()
+{
+}
+
+void SendBufferCaptureJob::setManagers(NodeManagers *managers)
+{
+ m_managers = managers;
+}
+
+void SendBufferCaptureJob::addRequest(QPair<Buffer *, QByteArray> request)
+{
+ QMutexLocker locker(&m_mutex);
+ m_pendingSendBufferCaptures.push_back(request);
+}
+
+void SendBufferCaptureJob::run()
+{
+ QMutexLocker locker(&m_mutex);
+ for (const QPair<Buffer*, QByteArray> &pendingCapture : qAsConst(m_pendingSendBufferCaptures)) {
+ pendingCapture.first->updateDataFromGPUToCPU(pendingCapture.second);
+ }
+
+ m_pendingSendBufferCaptures.clear();
+}
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/jobs/sendbuffercapturejob_p.h b/src/render/jobs/sendbuffercapturejob_p.h
new file mode 100644
index 000000000..d01ecec72
--- /dev/null
+++ b/src/render/jobs/sendbuffercapturejob_p.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Juan José Casafranca
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_SENDBUFFERCAPTUREJOB_P_H
+#define QT3DRENDER_SENDBUFFERCAPTUREJOB_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/qaspectjob.h>
+#include <Qt3DRender/qt3drender_global.h>
+#include <Qt3DRender/private/qt3drender_global_p.h>
+#include <QMutex>
+
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+class NodeManagers;
+class Entity;
+class Renderer;
+class Buffer;
+
+class QT3DRENDERSHARED_PRIVATE_EXPORT SendBufferCaptureJob : public Qt3DCore::QAspectJob
+{
+public:
+ explicit SendBufferCaptureJob();
+ ~SendBufferCaptureJob();
+
+ void setManagers(NodeManagers *managers);
+
+ void addRequest(QPair<Buffer*, QByteArray> request);
+
+ void run() Q_DECL_FINAL;
+
+private:
+ NodeManagers *m_managers;
+ QMutex m_mutex;
+
+ QVector<QPair<Buffer*, QByteArray> > m_pendingSendBufferCaptures;
+};
+
+typedef QSharedPointer<SendBufferCaptureJob> SendBufferCaptureJobPtr;
+
+} //Render
+
+} //Qt3DRender
+
+QT_END_NAMESPACE
+
+
+#endif // QT3DRENDER_SENDBUFFERCAPTUREJOB_P_H
diff --git a/src/render/jobs/updatelevelofdetailjob.cpp b/src/render/jobs/updatelevelofdetailjob.cpp
index 940d26850..24891f9b8 100644
--- a/src/render/jobs/updatelevelofdetailjob.cpp
+++ b/src/render/jobs/updatelevelofdetailjob.cpp
@@ -135,10 +135,10 @@ void UpdateLevelOfDetailJob::updateEntityLod(Entity *entity)
if (lod->isEnabled() && !lod->thresholds().isEmpty()) {
switch (lod->thresholdType()) {
- case QLevelOfDetail::DistanceToCamera:
+ case QLevelOfDetail::DistanceToCameraThreshold:
updateEntityLodByDistance(entity, lod);
break;
- case QLevelOfDetail::ProjectedScreenPixelSize:
+ case QLevelOfDetail::ProjectedScreenPixelSizeThreshold:
updateEntityLodByScreenArea(entity, lod);
break;
default:
@@ -162,7 +162,7 @@ void UpdateLevelOfDetailJob::updateEntityLodByDistance(Entity *entity, LevelOfDe
const QVector<qreal> thresholds = lod->thresholds();
QVector3D center = lod->center();
- if (lod->radius() > 0.f || entity->worldBoundingVolume() == nullptr) {
+ if (lod->hasBoundingVolumeOverride() || entity->worldBoundingVolume() == nullptr) {
center = *entity->worldTransform() * center;
} else {
center = entity->worldBoundingVolume()->center();
@@ -198,7 +198,7 @@ void UpdateLevelOfDetailJob::updateEntityLodByScreenArea(Entity *entity, LevelOf
const QVector<qreal> thresholds = lod->thresholds();
Sphere bv(lod->center(), lod->radius());
- if (lod->radius() <= 0.f && entity->worldBoundingVolume() != nullptr) {
+ if (!lod->hasBoundingVolumeOverride() && entity->worldBoundingVolume() != nullptr) {
bv = *(entity->worldBoundingVolume());
} else {
bv.transform(*entity->worldTransform());