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 | |
50 | QT_BEGIN_NAMESPACE |
51 | |
52 | using namespace Qt3DCore; |
53 | |
54 | namespace Qt3DRender { |
55 | namespace RayCasting { |
56 | |
57 | namespace { |
58 | |
59 | struct 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 | |
73 | bool compareHitsDistance(const Hit &a, const Hit &b) |
74 | { |
75 | return a.distance < b.distance; |
76 | } |
77 | |
78 | Hit 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 | |
88 | Hit 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 |
100 | QVector<Hit> reduceToAllHits(QVector<Hit> &results, const Hit &intermediate) |
101 | { |
102 | if (intermediate.intersects) |
103 | results.push_back(t: intermediate); |
104 | return results; |
105 | } |
106 | |
107 | struct 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 | |
122 | QCollisionQueryResult 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 | |
160 | QCollisionQueryResult::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 | |
174 | QRayCastingServicePrivate::QRayCastingServicePrivate(const QString &description) |
175 | : QAbstractCollisionQueryServicePrivate(description) |
176 | , m_handlesCount(0) |
177 | { |
178 | } |
179 | |
180 | QRayCastingService::QRayCastingService() |
181 | : QAbstractCollisionQueryService(*new QRayCastingServicePrivate(QStringLiteral("Collision detection service using Ray Casting" ))) |
182 | { |
183 | } |
184 | |
185 | QQueryHandle 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 | |
207 | QCollisionQueryResult::Hit QRayCastingService::query(const QRay3D &ray, const QBoundingVolume *volume) |
208 | { |
209 | Q_D(QRayCastingService); |
210 | |
211 | return d->collides(ray, volume); |
212 | } |
213 | |
214 | QCollisionQueryResult 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 | |
225 | QVector<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 | |
246 | QT_END_NAMESPACE |
247 | |