diff options
-rw-r--r-- | src/core/services/nullservices_p.h | 3 | ||||
-rw-r--r-- | src/core/services/qabstractcollisionqueryservice.h | 3 | ||||
-rw-r--r-- | src/render/jobs/pickboundingvolumejob.cpp | 86 | ||||
-rw-r--r-- | src/render/jobs/pickboundingvolumejob_p.h | 7 | ||||
-rw-r--r-- | src/render/services/qraycastingservice.cpp | 120 | ||||
-rw-r--r-- | src/render/services/qraycastingservice.h | 4 | ||||
-rw-r--r-- | src/render/services/qraycastingservice_p.h | 4 | ||||
-rw-r--r-- | tests/auto/render/raycasting/tst_raycasting.cpp | 40 |
8 files changed, 184 insertions, 83 deletions
diff --git a/src/core/services/nullservices_p.h b/src/core/services/nullservices_p.h index 31f8da3b5..a3e3fd672 100644 --- a/src/core/services/nullservices_p.h +++ b/src/core/services/nullservices_p.h @@ -91,10 +91,11 @@ public: {} ~NullCollisionQueryService() {} - QQueryHandle query(const QRay3D &ray, QueryMode mode) Q_DECL_OVERRIDE + QQueryHandle query(const QRay3D &ray, QueryMode mode, Qt3DCore::QBoundingVolumeProvider *provider) Q_DECL_OVERRIDE { Q_UNUSED(ray); Q_UNUSED(mode); + Q_UNUSED(provider); return 0; } diff --git a/src/core/services/qabstractcollisionqueryservice.h b/src/core/services/qabstractcollisionqueryservice.h index 04d92c687..4f9d6b02f 100644 --- a/src/core/services/qabstractcollisionqueryservice.h +++ b/src/core/services/qabstractcollisionqueryservice.h @@ -50,6 +50,7 @@ namespace Qt3DCore { class QRay3D; class QAbstractCollisionQueryServicePrivate; +class QBoundingVolumeProvider; class QT3DCORESHARED_EXPORT QAbstractCollisionQueryService : public QAbstractServiceProvider { @@ -59,7 +60,7 @@ public: AllHits }; - virtual QQueryHandle query(const QRay3D &ray, QueryMode mode) = 0; + virtual QQueryHandle query(const QRay3D &ray, QueryMode mode, QBoundingVolumeProvider *provider) = 0; virtual QCollisionQueryResult fetchResult(const QQueryHandle &handle) = 0; virtual QVector<QCollisionQueryResult> fetchAllResults() const = 0; diff --git a/src/render/jobs/pickboundingvolumejob.cpp b/src/render/jobs/pickboundingvolumejob.cpp index f6c2acfff..4f82a0f39 100644 --- a/src/render/jobs/pickboundingvolumejob.cpp +++ b/src/render/jobs/pickboundingvolumejob.cpp @@ -45,7 +45,9 @@ #include <Qt3DRender/private/objectpicker_p.h> #include <Qt3DRender/private/managers_p.h> #include <Qt3DRender/private/sphere_p.h> +#include <Qt3DRender/private/geometryrenderer_p.h> #include <Qt3DRender/qraycastingservice.h> +#include <Qt3DRender/qgeometryrenderer.h> #include <Qt3DCore/qservicelocator.h> #include <Qt3DCore/qray3d.h> #include <Qt3DCore/qabstractcollisionqueryservice.h> @@ -65,8 +67,8 @@ struct ViewportCameraPair QRectF viewport; }; -class ViewportCameraGatherer { - +class ViewportCameraGatherer +{ private: QVector<FrameGraphNode *> m_leaves; @@ -120,6 +122,44 @@ public: }; +QVector<Qt3DCore::QBoundingVolume *> gatherBoundingVolumes(Entity *entity) +{ + QVector<Qt3DCore::QBoundingVolume *> volumes; + + if (entity != Q_NULLPTR) { + volumes.push_back(entity->worldBoundingVolume()); + // Traverse children + Q_FOREACH (Entity *child, entity->children()) + volumes += gatherBoundingVolumes(child); + } + return volumes; +} + +class SphereBoundingVolumesGatherer : public Qt3DCore::QBoundingVolumeProvider +{ +public: + explicit SphereBoundingVolumesGatherer(Entity *root) + : Qt3DCore::QBoundingVolumeProvider() + , m_root(root) + , m_needsRefresh(true) + { + } + + QVector<Qt3DCore::QBoundingVolume *> boundingVolumes() const Q_DECL_FINAL + { + if (m_needsRefresh) { + m_volumes = gatherBoundingVolumes(m_root); + m_needsRefresh = false; + } + return m_volumes; + } + +private: + Entity *m_root; + mutable QVector<Qt3DCore::QBoundingVolume *> m_volumes; + mutable bool m_needsRefresh; +}; + } // anonymous PickBoundingVolumeJob::PickBoundingVolumeJob(Renderer *renderer) @@ -134,14 +174,8 @@ void PickBoundingVolumeJob::setRoot(Entity *root) m_node = root; } -QVector<Qt3DCore::QBoundingVolume *> PickBoundingVolumeJob::boundingVolumes() const -{ - return gatherBoundingVolumes(m_node); -} - Qt3DCore::QRay3D PickBoundingVolumeJob::intersectionRay(const QPoint &pos, const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix, const QRect &viewport) { - // TO DO: We may need to invert the y pos (Qt vs GL coordinates) QVector3D nearPos = QVector3D(pos.x(), pos.y(), 0.0f); nearPos = nearPos.unproject(viewMatrix, projectionMatrix, viewport); QVector3D farPos = QVector3D(pos.x(), pos.y(), 1.0f); @@ -163,7 +197,7 @@ void PickBoundingVolumeJob::run() (Qt3DCore::QServiceLocator::CollisionService); if (rayCasting == Q_NULLPTR) { - Qt3DRender::QRayCastingService *rayCastingService = new QRayCastingService(this); + Qt3DRender::QRayCastingService *rayCastingService = new QRayCastingService(); m_renderer->rendererAspect()->services()->registerServiceProvider(Qt3DCore::QServiceLocator::CollisionService, rayCastingService); rayCasting = rayCastingService; } @@ -172,6 +206,8 @@ void PickBoundingVolumeJob::run() ViewportCameraGatherer vcGatherer; const QVector<ViewportCameraPair> vcPairs = vcGatherer.gather(m_renderer->frameGraphRoot()); + SphereBoundingVolumesGatherer sphereGatherer(m_node); + if (!vcPairs.empty()) { Q_FOREACH (const QMouseEvent &event, m_mouseEvents) { m_hoveredPickersToClear = m_hoveredPickers; @@ -180,10 +216,22 @@ void PickBoundingVolumeJob::run() const QVector<Qt3DCore::QNodeId> hits = hitsForViewportAndCamera(event.pos(), vc.viewport, vc.cameraId, - rayCasting); + rayCasting, + &sphereGatherer); // If we have hits if (!hits.isEmpty()) { + // Note: how can we control that we want the first/last/all elements along the ray to be picked + + // How do we differentiate betwnee an Entity with no GeometryRenderer and one with one, both having + // an ObjectPicker component when it comes + + // We want to gather hits against triangles + // build a triangle based bounding volume + + + + Q_FOREACH (const Qt3DCore::QNodeId &entityId, hits) { Entity *entity = m_manager->renderNodesManager()->lookupResource(entityId); HObjectPicker objectPickerHandle = entity->componentHandle<ObjectPicker, 16>(); @@ -276,19 +324,6 @@ void PickBoundingVolumeJob::run() m_mouseEvents.clear(); } -QVector<Qt3DCore::QBoundingVolume *> PickBoundingVolumeJob::gatherBoundingVolumes(Entity *entity) const -{ - QVector<Qt3DCore::QBoundingVolume *> volumes; - - if (entity != Q_NULLPTR) { - volumes.push_back(entity->worldBoundingVolume()); - // Traverse children - Q_FOREACH (Entity *child, entity->children()) - volumes += gatherBoundingVolumes(child); - } - return volumes; -} - void PickBoundingVolumeJob::viewMatrixForCamera(const Qt3DCore::QNodeId &cameraId, QMatrix4x4 &viewMatrix, QMatrix4x4 &projectionMatrix) const @@ -322,7 +357,8 @@ QRect PickBoundingVolumeJob::windowViewport(const QRectF &relativeViewport) cons QVector<Qt3DCore::QNodeId> PickBoundingVolumeJob::hitsForViewportAndCamera(const QPoint &pos, const QRectF &relativeViewport, const Qt3DCore::QNodeId &cameraId, - Qt3DCore::QAbstractCollisionQueryService *rayCasting) const + Qt3DCore::QAbstractCollisionQueryService *rayCasting, + Qt3DCore::QBoundingVolumeProvider *volumeProvider) const { QMatrix4x4 viewMatrix; QMatrix4x4 projectionMatrix; @@ -334,7 +370,7 @@ QVector<Qt3DCore::QNodeId> PickBoundingVolumeJob::hitsForViewportAndCamera(const // In GL the y is inverted compared to Qt const QPoint glCorrectPos = s ? QPoint(pos.x(), s->size().height() - pos.y()) : pos; const Qt3DCore::QRay3D ray = intersectionRay(glCorrectPos, viewMatrix, projectionMatrix, viewport); - const Qt3DCore::QQueryHandle rayCastingHandle = rayCasting->query(ray, Qt3DCore::QAbstractCollisionQueryService::AllHits); + const Qt3DCore::QQueryHandle rayCastingHandle = rayCasting->query(ray, Qt3DCore::QAbstractCollisionQueryService::AllHits, volumeProvider); const Qt3DCore::QCollisionQueryResult queryResult = rayCasting->fetchResult(rayCastingHandle); return queryResult.entitiesHit(); } diff --git a/src/render/jobs/pickboundingvolumejob_p.h b/src/render/jobs/pickboundingvolumejob_p.h index 6edfa23b4..aeb2f008e 100644 --- a/src/render/jobs/pickboundingvolumejob_p.h +++ b/src/render/jobs/pickboundingvolumejob_p.h @@ -70,13 +70,12 @@ class Entity; class Renderer; class NodeManagers; -class Q_AUTOTEST_EXPORT PickBoundingVolumeJob : public Qt3DCore::QAspectJob, public Qt3DCore::QBoundingVolumeProvider +class Q_AUTOTEST_EXPORT PickBoundingVolumeJob : public Qt3DCore::QAspectJob { public: PickBoundingVolumeJob(Renderer *renderer); void setRoot(Entity *root); - QVector<Qt3DCore::QBoundingVolume *> boundingVolumes() const Q_DECL_FINAL; static Qt3DCore::QRay3D intersectionRay(const QPoint &pos, const QMatrix4x4 &viewMatrix, @@ -91,7 +90,6 @@ private: Entity *m_node; QList<QMouseEvent> m_mouseEvents; - QVector<Qt3DCore::QBoundingVolume *> gatherBoundingVolumes(Entity *entity) const; void viewMatrixForCamera(const Qt3DCore::QNodeId &cameraId, QMatrix4x4 &viewMatrix, QMatrix4x4 &projectionMatrix) const; @@ -99,7 +97,8 @@ private: QVector<Qt3DCore::QNodeId> hitsForViewportAndCamera(const QPoint &pos, const QRectF &relativeViewport, const Qt3DCore::QNodeId &cameraId, - Qt3DCore::QAbstractCollisionQueryService *rayCasting) const; + Qt3DCore::QAbstractCollisionQueryService *rayCasting, + Qt3DCore::QBoundingVolumeProvider *volumeProvider) const; void clearPreviouslyHoveredPickers(); HObjectPicker m_currentPicker; QVector<HObjectPicker> m_hoveredPickers; diff --git a/src/render/services/qraycastingservice.cpp b/src/render/services/qraycastingservice.cpp index 93dbc4e2e..af06b06d7 100644 --- a/src/render/services/qraycastingservice.cpp +++ b/src/render/services/qraycastingservice.cpp @@ -52,66 +52,120 @@ using namespace Qt3DCore; namespace Qt3DRender { +namespace { + +struct Hit +{ + Hit() + : intersects(false) + , distance(-1.0f) + {} + + bool intersects; + float distance; + Qt3DCore::QNodeId id; + QVector3D intersection; +}; + +bool compareHitsDistance(const Hit &a, const Hit &b) +{ + return a.distance < b.distance; +} + +Hit volumeRayIntersection(const QBoundingVolume *volume, const Qt3DCore::QRay3D &ray) +{ + Hit hit; + if ((hit.intersects = volume->intersects(ray, &hit.intersection))) { + hit.distance = ray.projectedDistance(hit.intersection); + hit.id = volume->id(); + } + return hit; +} + +Hit reduceToFirstHit(Hit &result, const Hit &intermediate) +{ + if (intermediate.intersects) { + if (result.distance == -1.0f || + (intermediate.distance >= 0.0f && + intermediate.distance < result.distance)) + result = intermediate; + } + return result; +} + +// Unordered +QVector<Hit> reduceToAllHits(QVector<Hit> &results, const Hit &intermediate) +{ + if (intermediate.intersects) + results.push_back(intermediate); + return results; +} + +struct CollisionGathererFunctor +{ + Qt3DCore::QRay3D ray; + + typedef Hit result_type; + + Hit operator ()(const QBoundingVolume *volume) const + { + return volumeRayIntersection(volume, ray); + } +}; + +} // anonymous + + QCollisionQueryResult QRayCastingServicePrivate::collides(const Qt3DCore::QRay3D &ray, Qt3DCore::QBoundingVolumeProvider *provider, Qt3DCore::QAbstractCollisionQueryService::QueryMode mode, const Qt3DCore::QQueryHandle &handle) { Q_Q(QRayCastingService); - const QVector<QBoundingVolume *> spheres(provider->boundingVolumes()); + const QVector<QBoundingVolume *> volumes(provider->boundingVolumes()); QCollisionQueryResult result; q->setResultHandle(result, handle); + CollisionGathererFunctor gathererFunctor; + gathererFunctor.ray = ray; + if (mode == QAbstractCollisionQueryService::FirstHit) { - QVector3D intersection; - float collidingPointDistance = -1.0f; - QNodeId collidingSphere; - - Q_FOREACH (QBoundingVolume *bs, spheres) { - if (bs->intersects(ray, &intersection)) { - const float distanceFromRay = ray.projectedDistance(intersection); - - if (collidingPointDistance == -1.0f || (distanceFromRay >= 0.0f && distanceFromRay < collidingPointDistance)) { - collidingPointDistance = distanceFromRay; - collidingSphere = bs->id(); - } - } - } - - if (!collidingSphere.isNull()) { - q->addEntityHit(result, collidingSphere); - } - - } else if (mode == QAbstractCollisionQueryService::AllHits) { - Q_FOREACH (QBoundingVolume *bs, spheres) { - if (bs->intersects(ray)) - q->addEntityHit(result, bs->id()); - } + Hit firstHit = QtConcurrent::blockingMappedReduced<Hit>(volumes, gathererFunctor, reduceToFirstHit); + if (firstHit.intersects) + q->addEntityHit(result, firstHit.id); + } else { + QVector<Hit> hits = QtConcurrent::blockingMappedReduced<QVector<Hit> >(volumes, gathererFunctor, reduceToAllHits); + std::sort(hits.begin(), hits.end(), compareHitsDistance); + Q_FOREACH (const Hit &hit, hits) + q->addEntityHit(result, hit.id); } + return result; } -QRayCastingServicePrivate::QRayCastingServicePrivate(const QString &description, QBoundingVolumeProvider *provider) +QRayCastingServicePrivate::QRayCastingServicePrivate(const QString &description) : QAbstractCollisionQueryServicePrivate(description) , m_handlesCount(0) - , m_boundingProvider(provider) { } -QRayCastingService::QRayCastingService(Qt3DCore::QBoundingVolumeProvider *provider) - : QAbstractCollisionQueryService(*new QRayCastingServicePrivate(QStringLiteral("Collision detection service using Ray Casting"), - provider)) +QRayCastingService::QRayCastingService() + : QAbstractCollisionQueryService(*new QRayCastingServicePrivate(QStringLiteral("Collision detection service using Ray Casting"))) { } -Qt3DCore::QQueryHandle QRayCastingService::query(const Qt3DCore::QRay3D &ray, QAbstractCollisionQueryService::QueryMode mode) +Qt3DCore::QQueryHandle QRayCastingService::query(const Qt3DCore::QRay3D &ray, + QAbstractCollisionQueryService::QueryMode mode, + Qt3DCore::QBoundingVolumeProvider *provider) { Q_D(QRayCastingService); QQueryHandle handle = d->m_handlesCount.fetchAndStoreOrdered(1); - // TODO: Implement run using jobs to avoid multiple threadpool + + // Blocking mapReduce + FutureQueryResult future = QtConcurrent::run(d, &QRayCastingServicePrivate::collides, - ray, d->m_boundingProvider, mode, handle); + ray, provider, mode, handle); d->m_results.insert(handle, future); return handle; diff --git a/src/render/services/qraycastingservice.h b/src/render/services/qraycastingservice.h index 680d4c092..2982c161c 100644 --- a/src/render/services/qraycastingservice.h +++ b/src/render/services/qraycastingservice.h @@ -54,9 +54,9 @@ class QRayCastingServicePrivate; class QT3DRENDERSHARED_EXPORT QRayCastingService : public Qt3DCore::QAbstractCollisionQueryService { public: - explicit QRayCastingService(Qt3DCore::QBoundingVolumeProvider *provider); + QRayCastingService(); - Qt3DCore::QQueryHandle query(const Qt3DCore::QRay3D &ray, QueryMode mode) Q_DECL_OVERRIDE; + Qt3DCore::QQueryHandle query(const Qt3DCore::QRay3D &ray, QueryMode mode, Qt3DCore::QBoundingVolumeProvider *provider) Q_DECL_OVERRIDE; Qt3DCore::QCollisionQueryResult fetchResult(const Qt3DCore::QQueryHandle &handle) Q_DECL_OVERRIDE; QVector<Qt3DCore::QCollisionQueryResult> fetchAllResults() const Q_DECL_OVERRIDE; diff --git a/src/render/services/qraycastingservice_p.h b/src/render/services/qraycastingservice_p.h index 95b455ee1..252c50bfd 100644 --- a/src/render/services/qraycastingservice_p.h +++ b/src/render/services/qraycastingservice_p.h @@ -71,7 +71,7 @@ typedef QFuture<Qt3DCore::QCollisionQueryResult> FutureQueryResult; class QRayCastingServicePrivate : public Qt3DCore::QAbstractCollisionQueryServicePrivate { public: - QRayCastingServicePrivate(const QString &description, Qt3DCore::QBoundingVolumeProvider *provider); + QRayCastingServicePrivate(const QString &description); Qt3DCore::QCollisionQueryResult collides(const Qt3DCore::QRay3D &ray, Qt3DCore::QBoundingVolumeProvider *provider, @@ -89,8 +89,6 @@ public: QHash<Qt3DCore::QQueryHandle, FutureQueryResult> m_results; QAtomicInt m_handlesCount; - - Qt3DCore::QBoundingVolumeProvider *m_boundingProvider; }; } // namespace Qt3DRender diff --git a/tests/auto/render/raycasting/tst_raycasting.cpp b/tests/auto/render/raycasting/tst_raycasting.cpp index 57a22a8ee..ced9bb136 100644 --- a/tests/auto/render/raycasting/tst_raycasting.cpp +++ b/tests/auto/render/raycasting/tst_raycasting.cpp @@ -133,10 +133,12 @@ void tst_RayCasting::shouldReturnValidHandle() Sphere v1; MyBoudingVolumesProvider provider = QVector<QBoundingVolume *>() << &v1; - QRayCastingService service(&provider); + QRayCastingService service; // WHEN - QQueryHandle handle = service.query(ray, QAbstractCollisionQueryService::AllHits); + QQueryHandle handle = service.query(ray, + QAbstractCollisionQueryService::AllHits, + &provider); // THEN QVERIFY(handle >= 0); @@ -152,10 +154,14 @@ void tst_RayCasting::shouldReturnResultForEachHandle() QVector<QBoundingVolume *> volumes; MyBoudingVolumesProvider provider(volumes); - QRayCastingService service(&provider); + QRayCastingService service; - QQueryHandle handle1 = service.query(ray, QAbstractCollisionQueryService::AllHits); - QQueryHandle handle2 = service.query(ray, QAbstractCollisionQueryService::FirstHit); + QQueryHandle handle1 = service.query(ray, + QAbstractCollisionQueryService::AllHits, + &provider); + QQueryHandle handle2 = service.query(ray, + QAbstractCollisionQueryService::FirstHit, + &provider); // WHEN QCollisionQueryResult result2 = service.fetchResult(handle2); @@ -173,11 +179,15 @@ void tst_RayCasting::shouldReturnAllResults() QVector<QBoundingVolume *> volumes; MyBoudingVolumesProvider provider(volumes); - QRayCastingService service(&provider); + QRayCastingService service; QVector<QQueryHandle> handles; - handles.append(service.query(ray, QAbstractCollisionQueryService::AllHits)); - handles.append(service.query(ray, QAbstractCollisionQueryService::FirstHit)); + handles.append(service.query(ray, + QAbstractCollisionQueryService::AllHits, + &provider)); + handles.append(service.query(ray, + QAbstractCollisionQueryService::FirstHit, + &provider)); // WHEN QVector<QCollisionQueryResult> results = service.fetchAllResults(); @@ -222,7 +232,7 @@ void tst_RayCasting::shouldReturnHits_data() QTest::newRow("All hits, Three sphere intersect") << ray << (QVector<QBoundingVolume *> () << volumeAt(0) << volumeAt(3) << volumeAt(6) << volumeAt(2)) - << (QVector<QNodeId>() << volumeAt(0)->id() << volumeAt(6)->id() << volumeAt(2)->id()) + << (QVector<QNodeId>() << volumeAt(0)->id() << volumeAt(2)->id() << volumeAt(6)->id()) << QAbstractCollisionQueryService::AllHits; QTest::newRow("All hits, No sphere intersect") << ray @@ -231,7 +241,7 @@ void tst_RayCasting::shouldReturnHits_data() << QAbstractCollisionQueryService::AllHits; QTest::newRow("Sphere 1 intersect, returns First Hit") << ray - << (QVector<QBoundingVolume *> () << volumeAt(0) << volumeAt(3) << volumeAt(1)) + << (QVector<QBoundingVolume *> () << volumeAt(0) << volumeAt(3) << volumeAt(6)) << (QVector<QNodeId>() << volumeAt(0)->id()) << QAbstractCollisionQueryService::FirstHit; @@ -260,10 +270,10 @@ void tst_RayCasting::shouldReturnHits() QFETCH(QAbstractCollisionQueryService::QueryMode, queryMode); MyBoudingVolumesProvider provider(volumes); - QRayCastingService service(&provider); + QRayCastingService service; // WHEN - QQueryHandle handle = service.query(ray, queryMode); + QQueryHandle handle = service.query(ray, queryMode, &provider); QCollisionQueryResult result = service.fetchResult(handle); // THEN @@ -283,10 +293,12 @@ void tst_RayCasting::shouldUseProvidedBoudingVolumes() MyBoudingVolumesProvider provider(QVector<QBoundingVolume *>() << &sphere1 << &sphere4 << &sphere3); QVector<QNodeId> hits(QVector<QNodeId>() << sphere1.id() << sphere3.id()); - QRayCastingService service(&provider); + QRayCastingService service; // WHEN - QQueryHandle handle = service.query(ray, QAbstractCollisionQueryService::AllHits); + QQueryHandle handle = service.query(ray, + QAbstractCollisionQueryService::AllHits, + &provider); QCollisionQueryResult result = service.fetchResult(handle); // THEN |