summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Lemire <paul.lemire@kdab.com>2019-01-03 09:14:27 +0100
committerMike Krus <mike.krus@kdab.com>2019-01-07 16:52:35 +0000
commit55b52c47030c759f8b38013eb873c0b161d0e426 (patch)
tree08d97fbd7e45a15dc1ffa7df18119094e8a62997
parent6f59eb1990d2ad700a109c6407aaef28004e135b (diff)
QGeometry: add minExtent/maxExtent properties
To allow computing a bounding box for a given QGeometry while waiting for a proper bounding volume aspect. Change-Id: If1ecf2f9236beaf569c650e5f8b05a6151ca6381 Reviewed-by: Mike Krus <mike.krus@kdab.com>
-rw-r--r--src/render/geometry/geometry.cpp39
-rw-r--r--src/render/geometry/geometry_p.h10
-rw-r--r--src/render/geometry/qgeometry.cpp56
-rw-r--r--src/render/geometry/qgeometry.h8
-rw-r--r--src/render/geometry/qgeometry_p.h3
-rw-r--r--src/render/jobs/calcboundingvolumejob.cpp58
-rw-r--r--tests/auto/render/geometry/tst_geometry.cpp28
-rw-r--r--tests/auto/render/qgeometry/tst_qgeometry.cpp55
8 files changed, 242 insertions, 15 deletions
diff --git a/src/render/geometry/geometry.cpp b/src/render/geometry/geometry.cpp
index d87b4d8eb..4ee02a74d 100644
--- a/src/render/geometry/geometry.cpp
+++ b/src/render/geometry/geometry.cpp
@@ -53,8 +53,10 @@ namespace Qt3DRender {
namespace Render {
Geometry::Geometry()
- : BackendNode(ReadOnly)
+ : BackendNode(ReadWrite)
, m_geometryDirty(false)
+ , m_shouldNotifyMinExtentChanged(false)
+ , m_shouldNotifyMaxExtentChanged(false)
{
}
@@ -68,6 +70,10 @@ void Geometry::cleanup()
m_attributes.clear();
m_geometryDirty = false;
m_boundingPositionAttribute = Qt3DCore::QNodeId();
+ m_min = QVector3D();
+ m_max = QVector3D();
+ m_shouldNotifyMinExtentChanged = false;
+ m_shouldNotifyMaxExtentChanged = false;
}
void Geometry::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change)
@@ -122,6 +128,37 @@ void Geometry::unsetDirty()
m_geometryDirty = false;
}
+// Called from calcboundingvolumejob (in a QtConcurrent thead (can't send
+// update changes from such a thread))
+void Geometry::updateExtent(const QVector3D &min, const QVector3D &max)
+{
+ // Send notification to frontend
+ if (m_min != min) {
+ m_min = min;
+ m_shouldNotifyMinExtentChanged = true;
+ }
+
+ if (m_max != max) {
+ m_max = max;
+ m_shouldNotifyMaxExtentChanged = true;
+ }
+}
+
+// Called from calcboundingvolumejob after all bounding volumes have been
+// updated (in an aspect thread)
+void Geometry::notifyExtentChanged()
+{
+ if (m_shouldNotifyMinExtentChanged || m_shouldNotifyMaxExtentChanged) {
+ auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(peerId());
+ change->setDeliveryFlags(Qt3DCore::QSceneChange::Nodes);
+ change->setPropertyName("extent");
+ change->setValue(QVariant::fromValue(QPair<QVector3D, QVector3D>(m_min, m_max)));
+ notifyObservers(change);
+ m_shouldNotifyMinExtentChanged = false;
+ m_shouldNotifyMaxExtentChanged = false;
+ }
+}
+
} // namespace Render
} // namespace Qt3DRender
diff --git a/src/render/geometry/geometry_p.h b/src/render/geometry/geometry_p.h
index b158648ad..e66524787 100644
--- a/src/render/geometry/geometry_p.h
+++ b/src/render/geometry/geometry_p.h
@@ -75,12 +75,22 @@ public:
inline Qt3DCore::QNodeId boundingPositionAttribute() const { return m_boundingPositionAttribute; }
void unsetDirty();
+ inline QVector3D min() const { return m_min; }
+ inline QVector3D max() const { return m_max; }
+
+ void updateExtent(const QVector3D &min, const QVector3D &max);
+ void notifyExtentChanged();
+
private:
void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) final;
QVector<Qt3DCore::QNodeId> m_attributes;
bool m_geometryDirty;
Qt3DCore::QNodeId m_boundingPositionAttribute;
+ QVector3D m_min;
+ QVector3D m_max;
+ bool m_shouldNotifyMinExtentChanged;
+ bool m_shouldNotifyMaxExtentChanged;
};
} // namespace Render
diff --git a/src/render/geometry/qgeometry.cpp b/src/render/geometry/qgeometry.cpp
index c49dde822..ec80e2657 100644
--- a/src/render/geometry/qgeometry.cpp
+++ b/src/render/geometry/qgeometry.cpp
@@ -153,6 +153,28 @@ QGeometry::QGeometry(QGeometryPrivate &dd, QNode *parent)
{
}
+void QGeometry::sceneChangeEvent(const QSceneChangePtr &change)
+{
+ Q_D(QGeometry);
+ QPropertyUpdatedChangePtr e = qSharedPointerCast<QPropertyUpdatedChange>(change);
+ if (e->type() == PropertyUpdated) {
+ const bool blocked = blockNotifications(true);
+ if (e->propertyName() == QByteArrayLiteral("extent")) {
+ const QPair<QVector3D, QVector3D> extent = e->value().value<QPair<QVector3D, QVector3D>>();
+
+ if (extent.first != d->m_minExtent) {
+ d->m_minExtent = extent.first;
+ emit minExtentChanged(extent.first);
+ }
+ if (extent.second != d->m_maxExtent) {
+ d->m_maxExtent = extent.second;
+ emit maxExtentChanged(d->m_maxExtent);
+ }
+ }
+ blockNotifications(blocked);
+ }
+}
+
/*!
\fn void Qt3DRender::QGeometry::addAttribute(Qt3DRender::QAttribute *attribute)
Adds an \a attribute to this geometry.
@@ -216,6 +238,40 @@ QAttribute *QGeometry::boundingVolumePositionAttribute() const
}
/*!
+ \qmlproperty vector3d Geometry::minExtent
+
+ Holds the vertex with the lowest x, y, z position values.
+ */
+
+/*!
+ \property QGeometry::minExtent
+
+ Holds the vertex with the lowest x, y, z position values.
+ */
+QVector3D QGeometry::minExtent() const
+{
+ Q_D(const QGeometry);
+ return d->m_minExtent;
+}
+
+/*!
+ \qmlproperty vector3d Geometry::maxExtent
+
+ Holds the vertex with the highest x, y, z position values.
+ */
+
+/*!
+ \property QGeometry::maxExtent
+
+ Holds the vertex with the highest x, y, z position values.
+ */
+QVector3D QGeometry::maxExtent() const
+{
+ Q_D(const QGeometry);
+ return d->m_maxExtent;
+}
+
+/*!
Returns the list of attributes in this geometry.
*/
QVector<QAttribute *> QGeometry::attributes() const
diff --git a/src/render/geometry/qgeometry.h b/src/render/geometry/qgeometry.h
index 0e6f7d68e..61508c7d2 100644
--- a/src/render/geometry/qgeometry.h
+++ b/src/render/geometry/qgeometry.h
@@ -54,6 +54,8 @@ class QT3DRENDERSHARED_EXPORT QGeometry : public Qt3DCore::QNode
{
Q_OBJECT
Q_PROPERTY(Qt3DRender::QAttribute *boundingVolumePositionAttribute READ boundingVolumePositionAttribute WRITE setBoundingVolumePositionAttribute NOTIFY boundingVolumePositionAttributeChanged)
+ Q_PROPERTY(QVector3D minExtent READ minExtent NOTIFY minExtentChanged REVISION 13)
+ Q_PROPERTY(QVector3D maxExtent READ maxExtent NOTIFY maxExtentChanged REVISION 13)
public:
explicit QGeometry(Qt3DCore::QNode *parent = nullptr);
~QGeometry();
@@ -63,15 +65,19 @@ public:
Q_INVOKABLE void removeAttribute(Qt3DRender::QAttribute *attribute);
QAttribute *boundingVolumePositionAttribute() const;
+ QVector3D minExtent() const;
+ QVector3D maxExtent() const;
public Q_SLOTS:
void setBoundingVolumePositionAttribute(QAttribute *boundingVolumePositionAttribute);
Q_SIGNALS:
void boundingVolumePositionAttributeChanged(QAttribute *boundingVolumePositionAttribute);
-
+ void minExtentChanged(const QVector3D &minExtent);
+ void maxExtentChanged(const QVector3D &maxExtent);
protected:
explicit QGeometry(QGeometryPrivate &dd, Qt3DCore::QNode *parent = nullptr);
+ void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) override;
private:
Q_DECLARE_PRIVATE(QGeometry)
diff --git a/src/render/geometry/qgeometry_p.h b/src/render/geometry/qgeometry_p.h
index e07b9ff0d..f53548e43 100644
--- a/src/render/geometry/qgeometry_p.h
+++ b/src/render/geometry/qgeometry_p.h
@@ -53,6 +53,7 @@
#include <Qt3DRender/private/qt3drender_global_p.h>
#include <Qt3DCore/private/qnode_p.h>
+#include <QVector3D>
QT_BEGIN_NAMESPACE
@@ -68,6 +69,8 @@ public:
QVector<QAttribute *> m_attributes;
QAttribute *m_boundingVolumePositionAttribute;
+ QVector3D m_minExtent;
+ QVector3D m_maxExtent;
};
struct QGeometryData
diff --git a/src/render/jobs/calcboundingvolumejob.cpp b/src/render/jobs/calcboundingvolumejob.cpp
index 66d59f812..68eb308c5 100644
--- a/src/render/jobs/calcboundingvolumejob.cpp
+++ b/src/render/jobs/calcboundingvolumejob.cpp
@@ -65,14 +65,25 @@ namespace Render {
namespace {
-void calculateLocalBoundingVolume(NodeManagers *manager, Entity *node);
+QVector<Geometry*> calculateLocalBoundingVolume(NodeManagers *manager, Entity *node);
-struct UpdateBoundFunctor {
+struct UpdateBoundFunctor
+{
NodeManagers *manager;
- void operator ()(Qt3DRender::Render::Entity *node)
+ // This define is required to work with QtConcurrent
+ typedef QVector<Geometry *> result_type;
+ QVector<Geometry *> operator ()(Qt3DRender::Render::Entity *node)
+ {
+ return calculateLocalBoundingVolume(manager, node);
+ }
+};
+
+struct ReduceUpdateBoundFunctor
+{
+ void operator ()(QVector<Geometry *> &result, const QVector<Geometry *> &values)
{
- calculateLocalBoundingVolume(manager, node);
+ result += values;
}
};
@@ -82,6 +93,8 @@ public:
BoundingVolumeCalculator(NodeManagers *manager) : m_manager(manager) { }
const Sphere& result() { return m_volume; }
+ const QVector3D min() const { return m_min; }
+ const QVector3D max() const { return m_max; }
bool apply(Qt3DRender::Render::Attribute *positionAttribute,
Qt3DRender::Render::Attribute *indexAttribute,
@@ -95,6 +108,9 @@ public:
return false;
}
+ m_min = QVector3D(findExtremePoints.xMin, findExtremePoints.yMin, findExtremePoints.zMin);
+ m_max = QVector3D(findExtremePoints.xMax, findExtremePoints.yMax, findExtremePoints.zMax);
+
// Calculate squared distance for the pairs of points
const float xDist2 = (findExtremePoints.xMaxPt - findExtremePoints.xMinPt).lengthSquared();
const float yDist2 = (findExtremePoints.yMaxPt - findExtremePoints.yMinPt).lengthSquared();
@@ -127,6 +143,8 @@ public:
private:
Sphere m_volume;
NodeManagers *m_manager;
+ QVector3D m_min;
+ QVector3D m_max;
class FindExtremePoints : public Buffer3fVisitor
{
@@ -191,17 +209,20 @@ private:
};
};
-void calculateLocalBoundingVolume(NodeManagers *manager, Entity *node)
+QVector<Geometry *> calculateLocalBoundingVolume(NodeManagers *manager, Entity *node)
{
// The Bounding volume will only be computed if the position Buffer
// isDirty
+ QVector<Geometry *> updatedGeometries;
+
if (!node->isTreeEnabled())
- return;
+ return updatedGeometries;
GeometryRenderer *gRenderer = node->renderComponent<GeometryRenderer>();
+ GeometryManager *geometryManager = manager->geometryManager();
if (gRenderer && gRenderer->primitiveType() != QGeometryRenderer::Patches) {
- Geometry *geom = manager->lookupResource<Geometry, GeometryManager>(gRenderer->geometryId());
+ Geometry *geom = geometryManager->lookupResource(gRenderer->geometryId());
if (geom) {
int drawVertexCount = gRenderer->vertexCount(); // may be 0, gets changed below if so
@@ -224,14 +245,14 @@ void calculateLocalBoundingVolume(NodeManagers *manager, Entity *node)
|| positionAttribute->vertexBaseType() != QAttribute::Float
|| positionAttribute->vertexSize() < 3) {
qWarning("calculateLocalBoundingVolume: Position attribute not suited for bounding volume computation");
- return;
+ return updatedGeometries;
}
Buffer *buf = manager->lookupResource<Buffer, BufferManager>(positionAttribute->bufferId());
// 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;
+ return updatedGeometries;
}
// Check if there is an index attribute.
@@ -259,7 +280,7 @@ void calculateLocalBoundingVolume(NodeManagers *manager, Entity *node)
std::end(validIndexTypes),
indexAttribute->vertexBaseType()) == std::end(validIndexTypes)) {
qWarning() << "calculateLocalBoundingVolume: Unsupported index attribute type" << indexAttribute->name() << indexAttribute->vertexBaseType();
- return;
+ return updatedGeometries;
}
break;
@@ -287,6 +308,11 @@ void calculateLocalBoundingVolume(NodeManagers *manager, Entity *node)
node->localBoundingVolume()->setCenter(reader.result().center());
node->localBoundingVolume()->setRadius(reader.result().radius());
node->unsetBoundingVolumeDirty();
+
+ // Record min/max vertex in Geometry
+ geom->updateExtent(reader.min(), reader.max());
+ // Mark geometry as requiring a call to update its frontend
+ updatedGeometries.push_back(geom);
}
}
}
@@ -297,14 +323,16 @@ void calculateLocalBoundingVolume(NodeManagers *manager, Entity *node)
if (children.size() > 1) {
UpdateBoundFunctor functor;
functor.manager = manager;
- QtConcurrent::blockingMap(children, functor);
+ ReduceUpdateBoundFunctor reduceFunctor;
+ updatedGeometries += QtConcurrent::blockingMappedReduced<decltype(updatedGeometries)>(children, functor, reduceFunctor);
} else
#endif
{
const auto children = node->children();
for (Entity *child : children)
- calculateLocalBoundingVolume(manager, child);
+ updatedGeometries += calculateLocalBoundingVolume(manager, child);
}
+ return updatedGeometries;
}
} // anonymous
@@ -318,7 +346,11 @@ CalculateBoundingVolumeJob::CalculateBoundingVolumeJob()
void CalculateBoundingVolumeJob::run()
{
- calculateLocalBoundingVolume(m_manager, m_node);
+ const QVector<Geometry *> updatedGeometries = calculateLocalBoundingVolume(m_manager, m_node);
+
+ // Send extent updates to frontend
+ for (Geometry *geometry : updatedGeometries)
+ geometry->notifyExtentChanged();
}
void CalculateBoundingVolumeJob::setRoot(Entity *node)
diff --git a/tests/auto/render/geometry/tst_geometry.cpp b/tests/auto/render/geometry/tst_geometry.cpp
index 958edfd09..7e65d27aa 100644
--- a/tests/auto/render/geometry/tst_geometry.cpp
+++ b/tests/auto/render/geometry/tst_geometry.cpp
@@ -34,7 +34,9 @@
#include <Qt3DCore/qpropertyupdatedchange.h>
#include <Qt3DCore/qpropertynodeaddedchange.h>
#include <Qt3DCore/qpropertynoderemovedchange.h>
+#include <Qt3DCore/private/qbackendnode_p.h>
#include "testrenderer.h"
+#include "testpostmanarbiter.h"
class DummyAttribute : public Qt3DRender::QAttribute
{
@@ -187,6 +189,32 @@ private Q_SLOTS:
QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::GeometryDirty);
renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
}
+
+ void checkExtentTransmission()
+ {
+ // GIVEN
+ TestRenderer renderer;
+ TestArbiter arbiter;
+ Qt3DRender::Render::Geometry renderGeometry;
+
+ Qt3DCore::QBackendNodePrivate::get(&renderGeometry)->setArbiter(&arbiter);
+ renderGeometry.setRenderer(&renderer);
+
+ // WHEN
+ renderGeometry.updateExtent(QVector3D(-1.0f, -1.0f, -1.0f), QVector3D(1.0f, 1.0f, 1.0f));
+ renderGeometry.notifyExtentChanged();
+
+ // THEN
+ QCOMPARE(arbiter.events.count(), 1);
+
+ Qt3DCore::QPropertyUpdatedChangePtr change = arbiter.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "extent");
+ const QPair<QVector3D, QVector3D> v = change->value().value<QPair<QVector3D, QVector3D>>();
+ QCOMPARE(v.first, QVector3D(-1.0f, -1.0f, -1.0f));
+ QCOMPARE(v.second, QVector3D(1.0f, 1.0f, 1.0f));
+
+ arbiter.events.clear();
+ }
};
diff --git a/tests/auto/render/qgeometry/tst_qgeometry.cpp b/tests/auto/render/qgeometry/tst_qgeometry.cpp
index b9271a8c0..55b7e752c 100644
--- a/tests/auto/render/qgeometry/tst_qgeometry.cpp
+++ b/tests/auto/render/qgeometry/tst_qgeometry.cpp
@@ -39,9 +39,21 @@
#include <Qt3DCore/QPropertyUpdatedChange>
#include <Qt3DCore/QPropertyNodeAddedChange>
#include <Qt3DCore/QPropertyNodeRemovedChange>
+#include <QSignalSpy>
#include "testpostmanarbiter.h"
+class FakeGeometry : public Qt3DRender::QGeometry
+{
+ Q_OBJECT
+
+public:
+ void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) override
+ {
+ Qt3DRender::QGeometry::sceneChangeEvent(change);
+ }
+};
+
class tst_QGeometry: public QObject
{
Q_OBJECT
@@ -180,6 +192,49 @@ private Q_SLOTS:
// THEN Should not crash when the attribute is destroyed (tests for failed removal of destruction helper)
}
}
+
+ void checkExtentUpdates()
+ {
+ // GIVEN
+ TestArbiter arbiter;
+ QScopedPointer<FakeGeometry> geometry(new FakeGeometry());
+ arbiter.setArbiterOnNode(geometry.data());
+ QSignalSpy spyMinExtent(geometry.data(), SIGNAL(minExtentChanged(QVector3D)));
+ QSignalSpy spyMaxExtent(geometry.data(), SIGNAL(maxExtentChanged(QVector3D)));
+
+ // THEN
+ QVERIFY(spyMinExtent.isValid());
+ QVERIFY(spyMaxExtent.isValid());
+ QCOMPARE(geometry->minExtent(), QVector3D());
+ QCOMPARE(geometry->maxExtent(), QVector3D());
+
+ // WHEN
+ Qt3DCore::QPropertyUpdatedChangePtr valueChange(new Qt3DCore::QPropertyUpdatedChange(Qt3DCore::QNodeId()));
+ valueChange->setPropertyName("extent");
+ valueChange->setValue(QVariant::fromValue(QPair<QVector3D, QVector3D>(QVector3D(10.0f, 10.f, 10.0f),
+ QVector3D())));
+ geometry->sceneChangeEvent(valueChange);
+
+ // THEN
+ QCOMPARE(spyMinExtent.count(), 1);
+ QCOMPARE(spyMaxExtent.count(), 0);
+ QCOMPARE(geometry->minExtent(), QVector3D(10.0f, 10.0f, 10.0f));
+
+ spyMinExtent.clear();
+
+ // WHEN
+ valueChange->setPropertyName("extent");
+ valueChange->setValue(QVariant::fromValue(QPair<QVector3D, QVector3D>(QVector3D(10.0f, 10.f, 10.0f),
+ QVector3D(11.0f, 11.f, 11.0f))));
+ geometry->sceneChangeEvent(valueChange);
+
+ // THEN
+ QCOMPARE(spyMinExtent.count(), 0);
+ QCOMPARE(spyMaxExtent.count(), 1);
+ QCOMPARE(geometry->maxExtent(), QVector3D(11.0f, 11.0f, 11.0f));
+
+ spyMaxExtent.clear();
+ }
};
QTEST_MAIN(tst_QGeometry)