diff options
author | Rémi Benoit <remi.benoit@kdab.com> | 2015-07-08 17:31:53 +0200 |
---|---|---|
committer | Paul Lemire <paul.lemire@kdab.com> | 2015-08-09 21:08:24 +0000 |
commit | 7ecc1c72a1ad9d636b4314181e418e830d37e424 (patch) | |
tree | 3450b88488c4a6c475e2584aae49508e49962e93 | |
parent | 4c5e74986b0e649e4aadbc76022ad18c17ef44dc (diff) |
QRayCastingService implementation using bounding volumes
Bounding volumes are used to compute the collisions.
Returns all hits or only first bounding volume hit.
Change-Id: Ie40ffde56d126a63b1bdd498cdb7afae6eff1a9c
Reviewed-by: Paul Lemire <paul.lemire@kdab.com>
-rw-r--r-- | src/core/core.pri | 10 | ||||
-rw-r--r-- | src/core/qboundingsphere.cpp | 53 | ||||
-rw-r--r-- | src/core/qboundingsphere.h | 61 | ||||
-rw-r--r-- | src/core/qboundingvolume.cpp | 53 | ||||
-rw-r--r-- | src/core/qboundingvolume.h | 71 | ||||
-rw-r--r-- | src/core/qboundingvolumeprovider.cpp | 52 | ||||
-rw-r--r-- | src/core/qboundingvolumeprovider.h | 59 | ||||
-rw-r--r-- | src/core/services/nullservices_p.h | 3 | ||||
-rw-r--r-- | src/core/services/services.pri | 2 | ||||
-rw-r--r-- | src/render/backend/qraycastingservice.cpp | 141 | ||||
-rw-r--r-- | src/render/backend/qraycastingservice.h | 73 | ||||
-rw-r--r-- | src/render/backend/qraycastingservice_p.h | 84 | ||||
-rw-r--r-- | src/render/backend/render-backend.pri | 9 | ||||
-rw-r--r-- | src/render/frontend/sphere.cpp | 57 | ||||
-rw-r--r-- | src/render/frontend/sphere.h | 25 | ||||
-rw-r--r-- | src/render/render.pro | 2 | ||||
-rw-r--r-- | tests/auto/render/raycasting/raycasting.pro | 9 | ||||
-rw-r--r-- | tests/auto/render/raycasting/tst_raycasting.cpp | 303 | ||||
-rw-r--r-- | tests/auto/render/render.pro | 3 |
19 files changed, 1054 insertions, 16 deletions
diff --git a/src/core/core.pri b/src/core/core.pri index dd9bb52ab..73c6035f7 100644 --- a/src/core/core.pri +++ b/src/core/core.pri @@ -40,7 +40,10 @@ HEADERS += \ $$PWD/qray3d.h \ $$PWD/qt3dcore_global_p.h \ $$PWD/qscene_p.h \ - $$PWD/qabstractfunctor.h + $$PWD/qabstractfunctor.h \ + $$PWD/qboundingvolume.h \ + $$PWD/qboundingsphere.h \ + $$PWD/qboundingvolumeprovider.h SOURCES += \ $$PWD/qtickclock.cpp \ @@ -58,4 +61,7 @@ SOURCES += \ $$PWD/qbackendscenepropertychange.cpp \ $$PWD/qbackendnodefactory.cpp \ $$PWD/qray3d.cpp \ - $$PWD/qabstractfunctor.cpp + $$PWD/qabstractfunctor.cpp \ + $$PWD/qboundingvolume.cpp \ + $$PWD/qboundingsphere.cpp \ + $$PWD/qboundingvolumeprovider.cpp diff --git a/src/core/qboundingsphere.cpp b/src/core/qboundingsphere.cpp new file mode 100644 index 000000000..80af217c2 --- /dev/null +++ b/src/core/qboundingsphere.cpp @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qboundingsphere.h" + +QT_BEGIN_NAMESPACE + +namespace Qt3D { + +QBoundingSphere::QBoundingSphere() +{ +} + +QBoundingSphere::~QBoundingSphere() +{ +} + +} // namespace Qt3D + +QT_END_NAMESPACE diff --git a/src/core/qboundingsphere.h b/src/core/qboundingsphere.h new file mode 100644 index 000000000..05f4b530c --- /dev/null +++ b/src/core/qboundingsphere.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3D_QBOUDINGSPHERE_H +#define QT3D_QBOUDINGSPHERE_H + +#include <Qt3DCore/qt3dcore_global.h> +#include <Qt3DCore/qboundingvolume.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3D { + +class QT3DCORESHARED_EXPORT QBoundingSphere : public QBoundingVolume +{ +public: + QBoundingSphere(); + ~QBoundingSphere(); + + virtual QVector3D center() const = 0; + virtual float radius() const = 0; +}; + +} // namespace Qt3D + +QT_END_NAMESPACE + +#endif // QT3D_QBOUDINGSPHERE_H diff --git a/src/core/qboundingvolume.cpp b/src/core/qboundingvolume.cpp new file mode 100644 index 000000000..caba96c79 --- /dev/null +++ b/src/core/qboundingvolume.cpp @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qboundingvolume.h" + +QT_BEGIN_NAMESPACE + +namespace Qt3D { + +QBoundingVolume::QBoundingVolume() +{ +} + +QBoundingVolume::~QBoundingVolume() +{ +} + +} // namespace Qt3D + +QT_END_NAMESPACE diff --git a/src/core/qboundingvolume.h b/src/core/qboundingvolume.h new file mode 100644 index 000000000..f983b3ac9 --- /dev/null +++ b/src/core/qboundingvolume.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3D_QBOUDINGVOLUME_P_H +#define QT3D_QBOUDINGVOLUME_P_H + +#include <Qt3DCore/qt3dcore_global.h> +#include <Qt3DCore/qnodeid.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3D { + +class QRay3D; + +class QT3DCORESHARED_EXPORT QBoundingVolume +{ +public: + QBoundingVolume(); + virtual ~QBoundingVolume(); + + enum Type { + Sphere = 0 + }; + + virtual QNodeId id() const = 0; + virtual bool intersects(const QRay3D &ray, QVector3D *q = Q_NULLPTR) const = 0; + + virtual Type type() const = 0; +}; + +} // namespace Qt3D + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(Qt3D::QBoundingVolume*) + +#endif // QT3D_QBOUDINGVOLUME_P_H diff --git a/src/core/qboundingvolumeprovider.cpp b/src/core/qboundingvolumeprovider.cpp new file mode 100644 index 000000000..69091cacc --- /dev/null +++ b/src/core/qboundingvolumeprovider.cpp @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qboundingvolumeprovider.h" + + +QT_BEGIN_NAMESPACE + +namespace Qt3D { + +QBoundingVolumeProvider::~QBoundingVolumeProvider() +{ + +} + + +} // Qt3D + +QT_END_NAMESPACE diff --git a/src/core/qboundingvolumeprovider.h b/src/core/qboundingvolumeprovider.h new file mode 100644 index 000000000..3d8496d27 --- /dev/null +++ b/src/core/qboundingvolumeprovider.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3D_QBOUNDINGVOLUMEPROVIDER_H +#define QT3D_QBOUNDINGVOLUMEPROVIDER_H + +#include <Qt3DCore/qt3dcore_global.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3D { + +class QBoundingVolume; + +class QT3DCORESHARED_EXPORT QBoundingVolumeProvider +{ +public: + virtual ~QBoundingVolumeProvider(); + virtual QVector<QBoundingVolume *> boundingVolumes() const = 0; +}; + +} // namespace Qt3D + +QT_END_NAMESPACE + +#endif // QT3D_QBOUNDINGVOLUMEPROVIDER_H diff --git a/src/core/services/nullservices_p.h b/src/core/services/nullservices_p.h index 3529791a5..f11f9e29d 100644 --- a/src/core/services/nullservices_p.h +++ b/src/core/services/nullservices_p.h @@ -39,9 +39,10 @@ #include <Qt3DCore/qt3dcore_global.h> #include <Qt3DCore/qray3d.h> +#include <Qt3DCore/qcollisionqueryresult.h> #include "qopenglinformationservice.h" #include "qsysteminformationservice.h" -#include "qabstractcollisionqueryservice.h" +#include <Qt3DCore/qabstractcollisionqueryservice.h> QT_BEGIN_NAMESPACE diff --git a/src/core/services/services.pri b/src/core/services/services.pri index 59c2ed140..6c0b252c7 100644 --- a/src/core/services/services.pri +++ b/src/core/services/services.pri @@ -21,7 +21,7 @@ HEADERS += \ $$PWD/qabstractcollisionqueryservice_p.h \ $$PWD/qcollisionqueryresult.h \ $$PWD/qcollisionqueryresult_p.h \ - $$PWD/qtickclockservice.h \ + $$PWD/qtickclockservice_p.h \ $$PWD/qabstractframeadvanceservice.h \ $$PWD/qabstractframeadvanceservice_p.h diff --git a/src/render/backend/qraycastingservice.cpp b/src/render/backend/qraycastingservice.cpp new file mode 100644 index 000000000..56566f19a --- /dev/null +++ b/src/render/backend/qraycastingservice.cpp @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qraycastingservice.h" +#include "qraycastingservice_p.h" + +#include <Qt3DCore/qray3d.h> +#include <Qt3DRenderer/sphere.h> +#include <Qt3DCore/qboundingvolumeprovider.h> + +#include <QMap> +#include <QtConcurrent> + +#include "math.h" + +QT_BEGIN_NAMESPACE + +namespace Qt3D { + +QCollisionQueryResult QRayCastingServicePrivate::collides(const QRay3D &ray, QBoundingVolumeProvider *provider, + QAbstractCollisionQueryService::QueryMode mode, const QQueryHandle &handle) +{ + Q_Q(QRayCastingService); + + const QVector<QBoundingVolume *> spheres(provider->boundingVolumes()); + QCollisionQueryResult result; + q->setResultHandle(result, handle); + + 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()); + } + } + return result; +} + +QRayCastingServicePrivate::QRayCastingServicePrivate(const QString &description, QBoundingVolumeProvider *provider) + : QAbstractCollisionQueryServicePrivate(description) + , m_handlesCount(0) + , m_boundingProvider(provider) +{ +} + +QRayCastingService::QRayCastingService(QBoundingVolumeProvider *provider) + : QAbstractCollisionQueryService(*new QRayCastingServicePrivate(QStringLiteral("Collision detection service using Ray Casting"), + provider)) +{ +} + +QQueryHandle QRayCastingService::query(const QRay3D &ray, QAbstractCollisionQueryService::QueryMode mode) +{ + Q_D(QRayCastingService); + + QQueryHandle handle = d->m_handlesCount.fetchAndStoreOrdered(1); + + // TODO: Implement run using jobs to avoid multiple threadpool + FutureQueryResult future = QtConcurrent::run(d, &QRayCastingServicePrivate::collides, + ray, d->m_boundingProvider, mode, handle); + d->m_results.insert(handle, future); + + return handle; +} + +QCollisionQueryResult QRayCastingService::fetchResult(const QQueryHandle &handle) +{ + Q_D(QRayCastingService); + + return d->m_results.value(handle).result(); +} + +QVector<QCollisionQueryResult> QRayCastingService::fetchAllResults() const +{ + Q_D(const QRayCastingService); + + const QList<FutureQueryResult> futureResults = d->m_results.values(); + QVector<QCollisionQueryResult> results; + results.reserve(futureResults.size()); + + Q_FOREACH (const FutureQueryResult &future, futureResults) + results.append(future.result()); + + return results; +} + +} // Qt3D + +QT_END_NAMESPACE diff --git a/src/render/backend/qraycastingservice.h b/src/render/backend/qraycastingservice.h new file mode 100644 index 000000000..4ae2b817e --- /dev/null +++ b/src/render/backend/qraycastingservice.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3D_RENDER_QRAYCASTINGSERVICE_H +#define QT3D_RENDER_QRAYCASTINGSERVICE_H + +#include <Qt3DRenderer/qt3drenderer_global.h> + +#include <Qt3DCore/qabstractcollisionqueryservice.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3D { + +class QBoundingVolumeProvider; +class QRayCastingServicePrivate; + +class QT3DRENDERERSHARED_EXPORT QRayCastingService : public QAbstractCollisionQueryService +{ +public: + explicit QRayCastingService(QBoundingVolumeProvider *provider); + + QQueryHandle query(const QRay3D &ray, QueryMode mode) Q_DECL_OVERRIDE; + + QCollisionQueryResult fetchResult(const QQueryHandle &handle) Q_DECL_OVERRIDE; + QVector<QCollisionQueryResult> fetchAllResults() const Q_DECL_OVERRIDE; + +protected: + QRayCastingService(QRayCastingServicePrivate &dd); + +private: + Q_DISABLE_COPY(QRayCastingService) + Q_DECLARE_PRIVATE(QRayCastingService) +}; + +} // Qt3D + +QT_END_NAMESPACE + +#endif // QT3D_RENDER_QRAYCASTINGSERVICE_H diff --git a/src/render/backend/qraycastingservice_p.h b/src/render/backend/qraycastingservice_p.h new file mode 100644 index 000000000..d9ed92323 --- /dev/null +++ b/src/render/backend/qraycastingservice_p.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3D_QRAYCASTINGSERVICE_P_H +#define QT3D_QRAYCASTINGSERVICE_P_H + +#include <Qt3DCore/qt3dcore_global.h> +#include <Qt3DCore/qabstractcollisionqueryservice.h> +#include <Qt3DCore/private/qabstractcollisionqueryservice_p.h> +#include <Qt3DCore/qray3d.h> + +#include <QHash> +#include <QtConcurrent> +#include <QAtomicInt> + +QT_BEGIN_NAMESPACE + +namespace Qt3D { + +class QBoundingVolumeProvider; +class QBoundingVolume; +typedef QFuture<QCollisionQueryResult> FutureQueryResult; + +class QRayCastingServicePrivate : public QAbstractCollisionQueryServicePrivate +{ +public: + QRayCastingServicePrivate(const QString &description, QBoundingVolumeProvider *provider); + + QCollisionQueryResult collides(const QRay3D &ray, QBoundingVolumeProvider *provider, + QAbstractCollisionQueryService::QueryMode mode, const QQueryHandle &handle); + + Q_DECLARE_PUBLIC(QRayCastingService) + + struct Query + { + QQueryHandle handle; + QRay3D ray; + QRayCastingService::QueryMode mode; + }; + + QHash<QQueryHandle, FutureQueryResult> m_results; + QAtomicInt m_handlesCount; + + QBoundingVolumeProvider *m_boundingProvider; +}; + +} // Qt3D + +QT_END_NAMESPACE + +#endif // QT3D_QRAYCASTINGSERVICE_P_H diff --git a/src/render/backend/render-backend.pri b/src/render/backend/render-backend.pri index 04b4e24c8..81aecd726 100644 --- a/src/render/backend/render-backend.pri +++ b/src/render/backend/render-backend.pri @@ -56,7 +56,9 @@ HEADERS += \ $$PWD/rendergeometry_p.h \ $$PWD/rendergeometryrenderer_p.h \ $$PWD/buffermanager_p.h \ - $$PWD/geometryrenderermanager_p.h + $$PWD/geometryrenderermanager_p.h \ + $$PWD/qraycastingservice.h \ + $$PWD/qraycastingservice_p.h SOURCES += \ $$PWD/qrenderaspect.cpp \ @@ -97,10 +99,11 @@ SOURCES += \ $$PWD/rendershaderdata.cpp \ $$PWD/renderparametermapping.cpp \ $$PWD/rendertextureimage.cpp \ - $$PWD/vsyncframeadvanceservice.cpp \ $$PWD/renderbuffer.cpp \ $$PWD/renderattribute.cpp \ $$PWD/rendergeometry.cpp \ $$PWD/rendergeometryrenderer.cpp \ $$PWD/buffermanager.cpp \ - $$PWD/geometryrenderermanager.cpp + $$PWD/geometryrenderermanager.cpp \ + $$PWD/qraycastingservice.cpp \ + $$PWD/vsyncframeadvanceservice.cpp diff --git a/src/render/frontend/sphere.cpp b/src/render/frontend/sphere.cpp index 34c4be554..12077c87a 100644 --- a/src/render/frontend/sphere.cpp +++ b/src/render/frontend/sphere.cpp @@ -36,6 +36,8 @@ #include "sphere.h" +#include <Qt3DCore/qray3d.h> + #include <QPair> #include <math.h> @@ -44,6 +46,46 @@ QT_BEGIN_NAMESPACE namespace { +// Algorithms taken from Real-time collision detection, p178-179 + +// Intersects ray r = p + td, |d| = 1, with sphere s and, if intersecting, +// returns true and intersection point q; false otherwise +bool intersectRaySphere(const Qt3D::QRay3D &ray, const Qt3D::Sphere &s, QVector3D *q = Q_NULLPTR) +{ + const QVector3D p = ray.origin(); + const QVector3D d = ray.direction(); + const QVector3D m = p - s.center(); + const float c = QVector3D::dotProduct(m, m) - s.radius() * s.radius(); + + // If there is definitely at least one real root, there must be an intersection + if (q == Q_NULLPTR && c <= 0.0f) + return true; + + const float b = QVector3D::dotProduct(m, d); + // Exit if r’s origin outside s (c > 0) and r pointing away from s (b > 0) + if (c > 0.0f && b > 0.0f) + return false; + + const float discr = b*b - c; + // A negative discriminant corresponds to ray missing sphere + if (discr < 0.0f) + return false; + + // If we don't need the intersection point, return early + if (q == Q_NULLPTR) + return true; + + // Ray now found to intersect sphere, compute smallest t value of intersection + float t = -b - sqrt(discr); + + // If t is negative, ray started inside sphere so clamp t to zero + if (t < 0.0f) + t = 0.0f; + + *q = p + t * d; + return true; +} + inline QPair<int, int> findExtremePoints(const QVector<QVector3D> &points) { // Find indices of extreme points along x, y, and z axes @@ -169,6 +211,21 @@ Sphere Sphere::transformed(const QMatrix4x4 &mat) return Sphere(c, sqrt(rSquared)); } +QNodeId Sphere::id() const +{ + return m_id; +} + +bool Sphere::intersects(const QRay3D &ray, QVector3D *q) const +{ + return intersectRaySphere(ray, *this, q); +} + +QBoundingVolume::Type Sphere::type() const +{ + return QBoundingVolume::Sphere; +} + } QT_END_NAMESPACE diff --git a/src/render/frontend/sphere.h b/src/render/frontend/sphere.h index e02c01308..2c53d8fe6 100644 --- a/src/render/frontend/sphere.h +++ b/src/render/frontend/sphere.h @@ -38,6 +38,8 @@ #define SPHERE_H #include <Qt3DRenderer/qt3drenderer_global.h> +#include <Qt3DCore/qnodeid.h> +#include <Qt3DCore/qboundingsphere.h> #include <QMatrix4x4> #include <QVector3D> @@ -46,17 +48,19 @@ QT_BEGIN_NAMESPACE namespace Qt3D { -class Sphere +class Q_AUTOTEST_EXPORT Sphere : public QBoundingSphere { public: - inline Sphere() - : m_center(), - m_radius(0.0f) + inline Sphere(const QNodeId &i = QNodeId()) + : m_center() + , m_radius(0.0f) + , m_id(i) {} - inline Sphere(const QVector3D &c, float r) - : m_center(c), - m_radius(r) + inline Sphere(const QVector3D &c, float r, const QNodeId &i = QNodeId()) + : m_center(c) + , m_radius(r) + , m_id(i) {} void setCenter(const QVector3D &c); @@ -85,11 +89,16 @@ public: return *this; } + QNodeId id() const Q_DECL_FINAL; + bool intersects(const QRay3D &ray, QVector3D *q) const Q_DECL_FINAL; + Type type() const Q_DECL_FINAL; + static Sphere fromPoints(const QVector<QVector3D> &points); private: QVector3D m_center; float m_radius; + QNodeId m_id; static const float ms_epsilon; }; @@ -136,4 +145,6 @@ inline bool intersects(const Sphere &a, const Sphere &b) QT_END_NAMESPACE +Q_DECLARE_METATYPE(Qt3D::Sphere) + #endif // SPHERE_H diff --git a/src/render/render.pro b/src/render/render.pro index 03f05e9ba..21ff1269d 100644 --- a/src/render/render.pro +++ b/src/render/render.pro @@ -1,6 +1,6 @@ TARGET = Qt3DRenderer -QT += core-private gui-private 3dcore 3dcore-private openglextensions +QT += core-private gui-private 3dcore 3dcore-private openglextensions concurrent DEFINES += QT3DRENDERER_LIBRARY diff --git a/tests/auto/render/raycasting/raycasting.pro b/tests/auto/render/raycasting/raycasting.pro new file mode 100644 index 000000000..e389ef3d7 --- /dev/null +++ b/tests/auto/render/raycasting/raycasting.pro @@ -0,0 +1,9 @@ +TEMPLATE = app + +TARGET = tst_raycasting + +QT += 3dcore 3dcore-private 3drenderer 3drenderer-private testlib + +CONFIG += testcase + +SOURCES += tst_raycasting.cpp diff --git a/tests/auto/render/raycasting/tst_raycasting.cpp b/tests/auto/render/raycasting/tst_raycasting.cpp new file mode 100644 index 000000000..6b0a9d80a --- /dev/null +++ b/tests/auto/render/raycasting/tst_raycasting.cpp @@ -0,0 +1,303 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <Qt3DRenderer/private/renderentity_p.h> +#include <Qt3DRenderer/qraycastingservice.h> +#include <Qt3DRenderer/sphere.h> +#include <Qt3DCore/qboundingvolumeprovider.h> +#include <Qt3DCore/qray3d.h> + +using namespace Qt3D; +using namespace Qt3D::Render; + +class tst_RayCasting : public QObject +{ + Q_OBJECT +public: + tst_RayCasting() {} + ~tst_RayCasting() {} + +private Q_SLOTS: + void shouldReturnValidHandle(); + void shouldReturnResultForEachHandle(); + void shouldReturnAllResults(); + void shouldReturnHits(); + void shouldReturnHits_data(); + void shouldIntersect_data(); + void shouldIntersect(); + void shouldUseProvidedBoudingVolumes(); + + void cleanupTestCase(); + +private: + Sphere *volumeAt(int index); + QVector<Sphere> boundingVolumes; +}; + +void tst_RayCasting::shouldIntersect_data() +{ + QTest::addColumn<QRay3D>("ray"); + QTest::addColumn<Sphere>("sphere"); + QTest::addColumn<int>("shouldIntersect"); + + QRay3D ray(QVector3D(1, 1, 1), QVector3D(0, 0, 1)); + + Sphere sphere1(QVector3D(1, 1, 1), 2); + Sphere sphere2(QVector3D(0, 0, 0), 3); + Sphere sphere3(QVector3D(0, 1, 3), 1); + Sphere sphere4(QVector3D(4, 4, 5), 1); + Sphere sphere5(QVector3D(2, 2, 11), 5); + Sphere sphere6(QVector3D(2, 2, 13), 1); + Sphere sphere7(QVector3D(2, 2, 15), 5); + + QTest::newRow("Ray starts inside sphere") << ray << sphere1 << 1; + QTest::newRow("Ray starts inside sphere") << ray << sphere2 << 1; + QTest::newRow("Ray intersects sphere tangentially") << ray << sphere3 << 1; + QTest::newRow("No intersection") << ray << sphere4 << 0; + QTest::newRow("Ray intersect sphere") << ray << sphere5 << 1; + QTest::newRow("No intersection") << ray << sphere6 << 0; + QTest::newRow("Ray intersect sphere") << ray << sphere7 << 1; +} + +void tst_RayCasting::shouldIntersect() +{ + QFETCH(QRay3D, ray); + QFETCH(Sphere, sphere); + QFETCH(int, shouldIntersect); + + QVector3D intersectionPoint; + + QCOMPARE(sphere.intersects(ray, &intersectionPoint), shouldIntersect); +} + +class MyBoudingVolumesProvider : public QBoundingVolumeProvider +{ +public: + MyBoudingVolumesProvider(QVector<QBoundingVolume *> volumes) + : m_volumes(volumes) + {} + + QVector<QBoundingVolume *> boundingVolumes() const + { + return m_volumes; + } + +private: + QVector<QBoundingVolume *> m_volumes; +}; + +void tst_RayCasting::shouldReturnValidHandle() +{ + // GIVEN + QRay3D ray; + Sphere v1; + MyBoudingVolumesProvider provider = QVector<QBoundingVolume *>() << &v1; + + QRayCastingService service(&provider); + + // WHEN + QQueryHandle handle = service.query(ray, QAbstractCollisionQueryService::AllHits); + + // THEN + QVERIFY(handle >= 0); + + // Wait the query to finish + service.fetchResult(handle); +} + +void tst_RayCasting::shouldReturnResultForEachHandle() +{ + // GIVEN + QRay3D ray; + QVector<QBoundingVolume *> volumes; + MyBoudingVolumesProvider provider(volumes); + + QRayCastingService service(&provider); + + QQueryHandle handle1 = service.query(ray, QAbstractCollisionQueryService::AllHits); + QQueryHandle handle2 = service.query(ray, QAbstractCollisionQueryService::FirstHit); + + // WHEN + QCollisionQueryResult result2 = service.fetchResult(handle2); + QCollisionQueryResult result1 = service.fetchResult(handle1); + + // THEN + QCOMPARE(result1.handle(), handle1); + QCOMPARE(result2.handle(), handle2); +} + +void tst_RayCasting::shouldReturnAllResults() +{ + // GIVEN + QRay3D ray; + QVector<QBoundingVolume *> volumes; + MyBoudingVolumesProvider provider(volumes); + + QRayCastingService service(&provider); + + QVector<QQueryHandle> handles; + handles.append(service.query(ray, QAbstractCollisionQueryService::AllHits)); + handles.append(service.query(ray, QAbstractCollisionQueryService::FirstHit)); + + // WHEN + QVector<QCollisionQueryResult> results = service.fetchAllResults(); + + // THEN + bool expectedHandlesFound = true; + Q_FOREACH (QQueryHandle expected, handles) { + bool found = false; + Q_FOREACH (QCollisionQueryResult result, results) { + if (result.handle() == expected) + found = true; + } + + expectedHandlesFound &= found; + } + + QVERIFY(expectedHandlesFound); +} + +void tst_RayCasting::shouldReturnHits_data() +{ + QTest::addColumn<QRay3D>("ray"); + QTest::addColumn<QVector<QBoundingVolume *> >("volumes"); + QTest::addColumn<QVector<QNodeId> >("hits"); + QTest::addColumn<QAbstractCollisionQueryService::QueryMode >("queryMode"); + + QRay3D ray(QVector3D(1, 1, 1), QVector3D(0, 0, 1)); + + this->boundingVolumes.clear(); + this->boundingVolumes.append(QVector<Sphere>() << Sphere(QVector3D(1, 1, 1), 3, QNodeId::createId()) + << Sphere(QVector3D(0, 0, 0), 3, QNodeId::createId()) + << Sphere(QVector3D(0, 1, 3), 1, QNodeId::createId()) + << Sphere(QVector3D(4, 4, 5), 1, QNodeId::createId()) + << Sphere(QVector3D(2, 2, 11), 5, QNodeId::createId()) + << Sphere(QVector3D(2, 2, 13), 1, QNodeId::createId()) + << Sphere(QVector3D(2, 2, 15), 5, QNodeId::createId())); + + QTest::newRow("All hits, One sphere intersect") << ray + << (QVector<QBoundingVolume *> () << volumeAt(0) << volumeAt(3)) + << (QVector<QNodeId>() << volumeAt(0)->id()) + << QAbstractCollisionQueryService::AllHits; + + 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()) + << QAbstractCollisionQueryService::AllHits; + + QTest::newRow("All hits, No sphere intersect") << ray + << (QVector<QBoundingVolume *> () << volumeAt(3) << volumeAt(5)) + << (QVector<QNodeId>()) + << QAbstractCollisionQueryService::AllHits; + + QTest::newRow("Sphere 1 intersect, returns First Hit") << ray + << (QVector<QBoundingVolume *> () << volumeAt(0) << volumeAt(3) << volumeAt(1)) + << (QVector<QNodeId>() << volumeAt(0)->id()) + << QAbstractCollisionQueryService::FirstHit; + + QTest::newRow("Sphere 3 and 5 intersects, returns First Hit") << ray + << (QVector<QBoundingVolume *> () << volumeAt(3) << volumeAt(6) << volumeAt(4)) + << (QVector<QNodeId>() << volumeAt(4)->id()) + << QAbstractCollisionQueryService::FirstHit; + + QTest::newRow("Sphere 5 and 3 intersects, unordered list, returns First Hit") << ray + << (QVector<QBoundingVolume *> () << volumeAt(4) << volumeAt(3) << volumeAt(6)) + << (QVector<QNodeId>() << volumeAt(4)->id()) + << QAbstractCollisionQueryService::FirstHit; + + QTest::newRow("No sphere intersect, returns First Hit") << ray + << (QVector<QBoundingVolume *> () << volumeAt(3) << volumeAt(5)) + << (QVector<QNodeId>()) + << QAbstractCollisionQueryService::FirstHit; +} + +void tst_RayCasting::shouldReturnHits() +{ + // GIVEN + QFETCH(QRay3D, ray); + QFETCH(QVector<QBoundingVolume *>, volumes); + QFETCH(QVector<QNodeId>, hits); + QFETCH(QAbstractCollisionQueryService::QueryMode, queryMode); + + MyBoudingVolumesProvider provider(volumes); + QRayCastingService service(&provider); + + // WHEN + QQueryHandle handle = service.query(ray, queryMode); + QCollisionQueryResult result = service.fetchResult(handle); + + // THEN + QCOMPARE(result.entitiesHit().size(), hits.size()); + QCOMPARE(result.entitiesHit(), hits); +} + +void tst_RayCasting::shouldUseProvidedBoudingVolumes() +{ + // GIVEN + QRay3D ray(QVector3D(1, 1, 1), QVector3D(0, 0, 1)); + + Sphere sphere1(QVector3D(1, 1, 1), 3); + Sphere sphere3(QVector3D(0, 1, 3), 1); + Sphere sphere4(QVector3D(4, 4, 5), 1); + + MyBoudingVolumesProvider provider(QVector<QBoundingVolume *>() << &sphere1 << &sphere4 << &sphere3); + QVector<QNodeId> hits(QVector<QNodeId>() << sphere1.id() << sphere3.id()); + + QRayCastingService service(&provider); + + // WHEN + QQueryHandle handle = service.query(ray, QAbstractCollisionQueryService::AllHits); + QCollisionQueryResult result = service.fetchResult(handle); + + // THEN + QCOMPARE(result.entitiesHit().size(), hits.size()); + QCOMPARE(result.entitiesHit(), hits); +} + +void tst_RayCasting::cleanupTestCase() +{ + this->boundingVolumes.clear(); +} + +Sphere *tst_RayCasting::volumeAt(int index) +{ + return &*(boundingVolumes.begin() + index); +} + +QTEST_APPLESS_MAIN(tst_RayCasting) + +#include "tst_raycasting.moc" diff --git a/tests/auto/render/render.pro b/tests/auto/render/render.pro index f008bee8a..f9bf2b7d4 100644 --- a/tests/auto/render/render.pro +++ b/tests/auto/render/render.pro @@ -20,5 +20,6 @@ contains(QT_CONFIG, private_tests) { renderbuffer \ renderattribute \ rendergeometry \ - rendergeometryrenderer + rendergeometryrenderer \ + raycasting } |