1/****************************************************************************
2**
3** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB).
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt3D module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qraycastingservice_p.h"
41
42#include <Qt3DRender/private/qray3d_p.h>
43#include <Qt3DRender/private/sphere_p.h>
44#include <Qt3DRender/private/qboundingvolumeprovider_p.h>
45
46#include <QtConcurrent>
47
48#include "math.h"
49
50QT_BEGIN_NAMESPACE
51
52using namespace Qt3DCore;
53
54namespace Qt3DRender {
55namespace RayCasting {
56
57namespace {
58
59struct Hit
60{
61 Hit()
62 : intersects(false)
63 , distance(-1.0f)
64 {}
65
66 bool intersects;
67 float distance;
68 Qt3DCore::QNodeId id;
69 Vector3D intersection;
70 Vector3D uvw;
71};
72
73bool compareHitsDistance(const Hit &a, const Hit &b)
74{
75 return a.distance < b.distance;
76}
77
78Hit volumeRayIntersection(const QBoundingVolume *volume, const QRay3D &ray)
79{
80 Hit hit;
81 if ((hit.intersects = volume->intersects(ray, q: &hit.intersection, uvw: &hit.uvw))) {
82 hit.distance = ray.projectedDistance(point: hit.intersection);
83 hit.id = volume->id();
84 }
85 return hit;
86}
87
88Hit reduceToFirstHit(Hit &result, const Hit &intermediate)
89{
90 if (intermediate.intersects) {
91 if (result.distance == -1.0f ||
92 (intermediate.distance >= 0.0f &&
93 intermediate.distance < result.distance))
94 result = intermediate;
95 }
96 return result;
97}
98
99// Unordered
100QVector<Hit> reduceToAllHits(QVector<Hit> &results, const Hit &intermediate)
101{
102 if (intermediate.intersects)
103 results.push_back(t: intermediate);
104 return results;
105}
106
107struct CollisionGathererFunctor
108{
109 QRay3D ray;
110
111 typedef Hit result_type;
112
113 Hit operator ()(const QBoundingVolume *volume) const
114 {
115 return volumeRayIntersection(volume, ray);
116 }
117};
118
119} // anonymous
120
121
122QCollisionQueryResult QRayCastingServicePrivate::collides(const QRay3D &ray, QBoundingVolumeProvider *provider,
123 QAbstractCollisionQueryService::QueryMode mode, const QQueryHandle &handle)
124{
125 Q_Q(QRayCastingService);
126
127 const QVector<QBoundingVolume *> volumes(provider->boundingVolumes());
128 QCollisionQueryResult result;
129 q->setResultHandle(result, handle);
130
131 CollisionGathererFunctor gathererFunctor;
132 gathererFunctor.ray = ray;
133
134 if (mode == QAbstractCollisionQueryService::FirstHit) {
135#if QT_CONFIG(concurrent)
136 Hit firstHit = QtConcurrent::blockingMappedReduced<Hit>(sequence: volumes, map: gathererFunctor, reduce: reduceToFirstHit);
137#else
138 Hit firstHit;
139 for (const QBoundingVolume *volume : volumes)
140 firstHit = reduceToFirstHit(firstHit, gathererFunctor(volume));
141#endif
142 if (firstHit.intersects)
143 q->addEntityHit(result, entity: firstHit.id, intersection: firstHit.intersection, distance: firstHit.distance, uvw: firstHit.uvw);
144 } else {
145#if QT_CONFIG(concurrent)
146 QVector<Hit> hits = QtConcurrent::blockingMappedReduced<QVector<Hit> >(sequence: volumes, map: gathererFunctor, reduce: reduceToAllHits);
147#else
148 QVector<Hit> hits;
149 for (const QBoundingVolume *volume : volumes)
150 hits = reduceToAllHits(hits, gathererFunctor(volume));
151#endif
152 std::sort(first: hits.begin(), last: hits.end(), comp: compareHitsDistance);
153 for (const Hit &hit : qAsConst(t&: hits))
154 q->addEntityHit(result, entity: hit.id, intersection: hit.intersection, distance: hit.distance, uvw: hit.uvw);
155 }
156
157 return result;
158}
159
160QCollisionQueryResult::Hit QRayCastingServicePrivate::collides(const QRay3D &ray, const QBoundingVolume *volume)
161{
162 QCollisionQueryResult::Hit result;
163 Hit hit = volumeRayIntersection(volume, ray);
164 if (hit.intersects)
165 {
166 result.m_distance = hit.distance;
167 result.m_entityId = hit.id;
168 result.m_intersection = hit.intersection;
169 result.m_uvw = hit.uvw;
170 }
171 return result;
172}
173
174QRayCastingServicePrivate::QRayCastingServicePrivate(const QString &description)
175 : QAbstractCollisionQueryServicePrivate(description)
176 , m_handlesCount(0)
177{
178}
179
180QRayCastingService::QRayCastingService()
181 : QAbstractCollisionQueryService(*new QRayCastingServicePrivate(QStringLiteral("Collision detection service using Ray Casting")))
182{
183}
184
185QQueryHandle QRayCastingService::query(const QRay3D &ray,
186 QAbstractCollisionQueryService::QueryMode mode,
187 QBoundingVolumeProvider *provider)
188{
189 Q_D(QRayCastingService);
190
191 QQueryHandle handle = d->m_handlesCount.fetchAndStoreOrdered(newValue: 1);
192
193
194 // Blocking mapReduce
195
196#if QT_CONFIG(concurrent)
197 FutureQueryResult future = QtConcurrent::run(object: d, fn: &QRayCastingServicePrivate::collides,
198 arg1: ray, arg2: provider, arg3: mode, arg4: handle);
199 d->m_results.insert(akey: handle, avalue: future);
200#else
201 d->m_results.insert(handle, d->collides(ray, provider, mode, handle));
202#endif
203
204 return handle;
205}
206
207QCollisionQueryResult::Hit QRayCastingService::query(const QRay3D &ray, const QBoundingVolume *volume)
208{
209 Q_D(QRayCastingService);
210
211 return d->collides(ray, volume);
212}
213
214QCollisionQueryResult QRayCastingService::fetchResult(const QQueryHandle &handle)
215{
216 Q_D(QRayCastingService);
217
218#if QT_CONFIG(concurrent)
219 return d->m_results.value(akey: handle).result();
220#else
221 return d->m_results.value(handle);
222#endif
223}
224
225QVector<QCollisionQueryResult> QRayCastingService::fetchAllResults() const
226{
227 Q_D(const QRayCastingService);
228
229 QVector<QCollisionQueryResult> results;
230 results.reserve(asize: d->m_results.size());
231
232#if QT_CONFIG(concurrent)
233 for (const FutureQueryResult &future : d->m_results)
234 results.append(t: future.result());
235#else
236 for (const QCollisionQueryResult &result : d->m_results)
237 results.append(result);
238#endif
239
240 return results;
241}
242
243} // namespace RayCasting
244} // namespace Qt3DRender
245
246QT_END_NAMESPACE
247

source code of qt3d/src/render/raycasting/qraycastingservice.cpp