diff options
author | Paul Lemire <paul.lemire@kdab.com> | 2015-11-13 16:31:00 +0100 |
---|---|---|
committer | Andy Nichols <andy.nichols@theqtcompany.com> | 2015-11-24 14:56:49 +0000 |
commit | aadaafa3ce3d5b58cfc51b0bcbb99374c17cc658 (patch) | |
tree | 0e65d884f7104289488e9b85342dd998ffecb47f /src/render | |
parent | dee5562b22034702e2555f08271d9fc09fd458dc (diff) |
QRayCastingService improvements
Change-Id: Id8072015f153bebf3e44b9d0a6ad9e2e9254aa83
Reviewed-by: Andy Nichols <andy.nichols@theqtcompany.com>
Diffstat (limited to 'src/render')
-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 |
5 files changed, 154 insertions, 67 deletions
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 |