1 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
2 | /* |
---|
3 | * OPCODE - Optimized Collision Detection |
---|
4 | * Copyright (C) 2001 Pierre Terdiman |
---|
5 | * Homepage: http://www.codercorner.com/Opcode.htm |
---|
6 | */ |
---|
7 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
8 | |
---|
9 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
10 | /** |
---|
11 | * Contains code for a ray collider. |
---|
12 | * \file OPC_RayCollider.cpp |
---|
13 | * \author Pierre Terdiman |
---|
14 | * \date June, 2, 2001 |
---|
15 | */ |
---|
16 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
17 | |
---|
18 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
19 | /** |
---|
20 | * Contains a ray-vs-tree collider. |
---|
21 | * This class performs a stabbing query on an AABB tree, i.e. does a ray-mesh collision. |
---|
22 | * |
---|
23 | * HIGHER DISTANCE BOUND: |
---|
24 | * |
---|
25 | * If P0 and P1 are two 3D points, let's define: |
---|
26 | * - d = distance between P0 and P1 |
---|
27 | * - Origin = P0 |
---|
28 | * - Direction = (P1 - P0) / d = normalized direction vector |
---|
29 | * - A parameter t such as a point P on the line (P0,P1) is P = Origin + t * Direction |
---|
30 | * - t = 0 --> P = P0 |
---|
31 | * - t = d --> P = P1 |
---|
32 | * |
---|
33 | * Then we can define a general "ray" as: |
---|
34 | * |
---|
35 | * struct Ray |
---|
36 | * { |
---|
37 | * Point Origin; |
---|
38 | * Point Direction; |
---|
39 | * }; |
---|
40 | * |
---|
41 | * But it actually maps three different things: |
---|
42 | * - a segment, when 0 <= t <= d |
---|
43 | * - a half-line, when 0 <= t < +infinity, or -infinity < t <= d |
---|
44 | * - a line, when -infinity < t < +infinity |
---|
45 | * |
---|
46 | * In Opcode, we support segment queries, which yield half-line queries by setting d = +infinity. |
---|
47 | * We don't support line-queries. If you need them, shift the origin along the ray by an appropriate margin. |
---|
48 | * |
---|
49 | * In short, the lower bound is always 0, and you can setup the higher bound "d" with RayCollider::SetMaxDist(). |
---|
50 | * |
---|
51 | * Query |segment |half-line |line |
---|
52 | * --------|-------------------|---------------|---------------- |
---|
53 | * Usages |-shadow feelers |-raytracing |- |
---|
54 | * |-sweep tests |-in/out tests | |
---|
55 | * |
---|
56 | * FIRST CONTACT: |
---|
57 | * |
---|
58 | * - You can setup "first contact" mode or "all contacts" mode with RayCollider::SetFirstContact(). |
---|
59 | * - In "first contact" mode we return as soon as the ray hits one face. If can be useful e.g. for shadow feelers, where |
---|
60 | * you want to know whether the path to the light is free or not (a boolean answer is enough). |
---|
61 | * - In "all contacts" mode we return all faces hit by the ray. |
---|
62 | * |
---|
63 | * TEMPORAL COHERENCE: |
---|
64 | * |
---|
65 | * - You can enable or disable temporal coherence with RayCollider::SetTemporalCoherence(). |
---|
66 | * - It currently only works in "first contact" mode. |
---|
67 | * - If temporal coherence is enabled, the previously hit triangle is cached during the first query. Then, next queries |
---|
68 | * start by colliding the ray against the cached triangle. If they still collide, we return immediately. |
---|
69 | * |
---|
70 | * CLOSEST HIT: |
---|
71 | * |
---|
72 | * - You can enable or disable "closest hit" with RayCollider::SetClosestHit(). |
---|
73 | * - It currently only works in "all contacts" mode. |
---|
74 | * - If closest hit is enabled, faces are sorted by distance on-the-fly and the closest one only is reported. |
---|
75 | * |
---|
76 | * BACKFACE CULLING: |
---|
77 | * |
---|
78 | * - You can enable or disable backface culling with RayCollider::SetCulling(). |
---|
79 | * - If culling is enabled, ray will not hit back faces (only front faces). |
---|
80 | * |
---|
81 | * |
---|
82 | * |
---|
83 | * \class RayCollider |
---|
84 | * \author Pierre Terdiman |
---|
85 | * \version 1.3 |
---|
86 | * \date June, 2, 2001 |
---|
87 | */ |
---|
88 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
89 | |
---|
90 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
91 | /** |
---|
92 | * This class describes a face hit by a ray or segment. |
---|
93 | * This is a particular class dedicated to stabbing queries. |
---|
94 | * |
---|
95 | * \class CollisionFace |
---|
96 | * \author Pierre Terdiman |
---|
97 | * \version 1.3 |
---|
98 | * \date March, 20, 2001 |
---|
99 | */ |
---|
100 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
101 | |
---|
102 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
103 | /** |
---|
104 | * This class is a dedicated collection of CollisionFace. |
---|
105 | * |
---|
106 | * \class CollisionFaces |
---|
107 | * \author Pierre Terdiman |
---|
108 | * \version 1.3 |
---|
109 | * \date March, 20, 2001 |
---|
110 | */ |
---|
111 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
112 | |
---|
113 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
114 | // Precompiled Header |
---|
115 | #include "Stdafx.h" |
---|
116 | |
---|
117 | using namespace Opcode; |
---|
118 | |
---|
119 | #include "OPC_RayAABBOverlap.h" |
---|
120 | #include "OPC_RayTriOverlap.h" |
---|
121 | |
---|
122 | #define SET_CONTACT(prim_index, flag) \ |
---|
123 | mNbIntersections++; \ |
---|
124 | /* Set contact status */ \ |
---|
125 | mFlags |= flag; \ |
---|
126 | /* In any case the contact has been found and recorded in mStabbedFace */ \ |
---|
127 | mStabbedFace.mFaceID = prim_index; |
---|
128 | |
---|
129 | #ifdef OPC_RAYHIT_CALLBACK |
---|
130 | |
---|
131 | #define HANDLE_CONTACT(prim_index, flag) \ |
---|
132 | SET_CONTACT(prim_index, flag) \ |
---|
133 | \ |
---|
134 | if(mHitCallback) (mHitCallback)(mStabbedFace, mUserData); |
---|
135 | |
---|
136 | #define UPDATE_CACHE \ |
---|
137 | if(cache && GetContactStatus()) \ |
---|
138 | { \ |
---|
139 | *cache = mStabbedFace.mFaceID; \ |
---|
140 | } |
---|
141 | #else |
---|
142 | |
---|
143 | #define HANDLE_CONTACT(prim_index, flag) \ |
---|
144 | SET_CONTACT(prim_index, flag) \ |
---|
145 | \ |
---|
146 | /* Now we can also record it in mStabbedFaces if available */ \ |
---|
147 | if(mStabbedFaces) \ |
---|
148 | { \ |
---|
149 | /* If we want all faces or if that's the first one we hit */ \ |
---|
150 | if(!mClosestHit || !mStabbedFaces->GetNbFaces()) \ |
---|
151 | { \ |
---|
152 | mStabbedFaces->AddFace(mStabbedFace); \ |
---|
153 | } \ |
---|
154 | else \ |
---|
155 | { \ |
---|
156 | /* We only keep closest hit */ \ |
---|
157 | CollisionFace* Current = const_cast<CollisionFace*>(mStabbedFaces->GetFaces()); \ |
---|
158 | if(Current && mStabbedFace.mDistance<Current->mDistance) \ |
---|
159 | { \ |
---|
160 | *Current = mStabbedFace; \ |
---|
161 | } \ |
---|
162 | } \ |
---|
163 | } |
---|
164 | |
---|
165 | #define UPDATE_CACHE \ |
---|
166 | if(cache && GetContactStatus() && mStabbedFaces) \ |
---|
167 | { \ |
---|
168 | const CollisionFace* Current = mStabbedFaces->GetFaces(); \ |
---|
169 | if(Current) *cache = Current->mFaceID; \ |
---|
170 | else *cache = INVALID_ID; \ |
---|
171 | } |
---|
172 | #endif |
---|
173 | |
---|
174 | #define SEGMENT_PRIM(prim_index, flag) \ |
---|
175 | /* Request vertices from the app */ \ |
---|
176 | VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \ |
---|
177 | \ |
---|
178 | /* Perform ray-tri overlap test and return */ \ |
---|
179 | if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \ |
---|
180 | { \ |
---|
181 | /* Intersection point is valid if dist < segment's length */ \ |
---|
182 | /* We know dist>0 so we can use integers */ \ |
---|
183 | if(IR(mStabbedFace.mDistance)<IR(mMaxDist)) \ |
---|
184 | { \ |
---|
185 | HANDLE_CONTACT(prim_index, flag) \ |
---|
186 | } \ |
---|
187 | } |
---|
188 | |
---|
189 | #define RAY_PRIM(prim_index, flag) \ |
---|
190 | /* Request vertices from the app */ \ |
---|
191 | VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \ |
---|
192 | \ |
---|
193 | /* Perform ray-tri overlap test and return */ \ |
---|
194 | if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \ |
---|
195 | { \ |
---|
196 | HANDLE_CONTACT(prim_index, flag) \ |
---|
197 | } |
---|
198 | |
---|
199 | |
---|
200 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
201 | /** |
---|
202 | * Constructor. |
---|
203 | */ |
---|
204 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
205 | RayCollider::RayCollider() : |
---|
206 | mNbRayBVTests (0), |
---|
207 | mNbRayPrimTests (0), |
---|
208 | mNbIntersections (0), |
---|
209 | mCulling (true), |
---|
210 | #ifdef OPC_RAYHIT_CALLBACK |
---|
211 | mHitCallback (null), |
---|
212 | mUserData (0), |
---|
213 | #else |
---|
214 | mClosestHit (false), |
---|
215 | mStabbedFaces (null), |
---|
216 | #endif |
---|
217 | mMaxDist (MAX_FLOAT) |
---|
218 | { |
---|
219 | } |
---|
220 | |
---|
221 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
222 | /** |
---|
223 | * Destructor. |
---|
224 | */ |
---|
225 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
226 | RayCollider::~RayCollider() |
---|
227 | { |
---|
228 | } |
---|
229 | |
---|
230 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
231 | /** |
---|
232 | * Validates current settings. You should call this method after all the settings and callbacks have been defined. |
---|
233 | * \return null if everything is ok, else a string describing the problem |
---|
234 | */ |
---|
235 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
236 | const char* RayCollider::ValidateSettings() |
---|
237 | { |
---|
238 | if(mMaxDist<0.0f) return "Higher distance bound must be positive!"; |
---|
239 | if(TemporalCoherenceEnabled() && !FirstContactEnabled()) return "Temporal coherence only works with ""First contact"" mode!"; |
---|
240 | #ifndef OPC_RAYHIT_CALLBACK |
---|
241 | if(mClosestHit && FirstContactEnabled()) return "Closest hit doesn't work with ""First contact"" mode!"; |
---|
242 | if(TemporalCoherenceEnabled() && mClosestHit) return "Temporal coherence can't guarantee to report closest hit!"; |
---|
243 | #endif |
---|
244 | if(SkipPrimitiveTests()) return "SkipPrimitiveTests not possible for RayCollider ! (not implemented)"; |
---|
245 | return null; |
---|
246 | } |
---|
247 | |
---|
248 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
249 | /** |
---|
250 | * Generic stabbing query for generic OPCODE models. After the call, access the results: |
---|
251 | * - with GetContactStatus() |
---|
252 | * - in the user-provided destination array |
---|
253 | * |
---|
254 | * \param world_ray [in] stabbing ray in world space |
---|
255 | * \param model [in] Opcode model to collide with |
---|
256 | * \param world [in] model's world matrix, or null |
---|
257 | * \param cache [in] a possibly cached face index, or null |
---|
258 | * \return true if success |
---|
259 | * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. |
---|
260 | */ |
---|
261 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
262 | bool RayCollider::Collide(const Ray& world_ray, const Model& model, const Matrix4x4* world, udword* cache) |
---|
263 | { |
---|
264 | // Checkings |
---|
265 | if(!Setup(&model)) return false; |
---|
266 | |
---|
267 | // Init collision query |
---|
268 | if(InitQuery(world_ray, world, cache)) return true; |
---|
269 | |
---|
270 | if(!model.HasLeafNodes()) |
---|
271 | { |
---|
272 | if(model.IsQuantized()) |
---|
273 | { |
---|
274 | const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree(); |
---|
275 | |
---|
276 | // Setup dequantization coeffs |
---|
277 | mCenterCoeff = Tree->mCenterCoeff; |
---|
278 | mExtentsCoeff = Tree->mExtentsCoeff; |
---|
279 | |
---|
280 | // Perform stabbing query |
---|
281 | if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes()); |
---|
282 | else _RayStab(Tree->GetNodes()); |
---|
283 | } |
---|
284 | else |
---|
285 | { |
---|
286 | const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree(); |
---|
287 | |
---|
288 | // Perform stabbing query |
---|
289 | if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes()); |
---|
290 | else _RayStab(Tree->GetNodes()); |
---|
291 | } |
---|
292 | } |
---|
293 | else |
---|
294 | { |
---|
295 | if(model.IsQuantized()) |
---|
296 | { |
---|
297 | const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree(); |
---|
298 | |
---|
299 | // Setup dequantization coeffs |
---|
300 | mCenterCoeff = Tree->mCenterCoeff; |
---|
301 | mExtentsCoeff = Tree->mExtentsCoeff; |
---|
302 | |
---|
303 | // Perform stabbing query |
---|
304 | if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes()); |
---|
305 | else _RayStab(Tree->GetNodes()); |
---|
306 | } |
---|
307 | else |
---|
308 | { |
---|
309 | const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree(); |
---|
310 | |
---|
311 | // Perform stabbing query |
---|
312 | if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes()); |
---|
313 | else _RayStab(Tree->GetNodes()); |
---|
314 | } |
---|
315 | } |
---|
316 | |
---|
317 | // Update cache if needed |
---|
318 | UPDATE_CACHE |
---|
319 | return true; |
---|
320 | } |
---|
321 | |
---|
322 | |
---|
323 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
324 | /** |
---|
325 | * Initializes a stabbing query : |
---|
326 | * - reset stats & contact status |
---|
327 | * - compute ray in local space |
---|
328 | * - check temporal coherence |
---|
329 | * |
---|
330 | * \param world_ray [in] stabbing ray in world space |
---|
331 | * \param world [in] object's world matrix, or null |
---|
332 | * \param face_id [in] index of previously stabbed triangle |
---|
333 | * \return TRUE if we can return immediately |
---|
334 | * \warning SCALE NOT SUPPORTED. The matrix must contain rotation & translation parts only. |
---|
335 | */ |
---|
336 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
337 | BOOL RayCollider::InitQuery(const Ray& world_ray, const Matrix4x4* world, udword* face_id) |
---|
338 | { |
---|
339 | // Reset stats & contact status |
---|
340 | Collider::InitQuery(); |
---|
341 | mNbRayBVTests = 0; |
---|
342 | mNbRayPrimTests = 0; |
---|
343 | mNbIntersections = 0; |
---|
344 | #ifndef OPC_RAYHIT_CALLBACK |
---|
345 | if(mStabbedFaces) mStabbedFaces->Reset(); |
---|
346 | #endif |
---|
347 | |
---|
348 | // Compute ray in local space |
---|
349 | // The (Origin/Dir) form is needed for the ray-triangle test anyway (even for segment tests) |
---|
350 | if(world) |
---|
351 | { |
---|
352 | Matrix3x3 InvWorld = *world; |
---|
353 | mDir = InvWorld * world_ray.mDir; |
---|
354 | |
---|
355 | Matrix4x4 World; |
---|
356 | InvertPRMatrix(World, *world); |
---|
357 | mOrigin = world_ray.mOrig * World; |
---|
358 | } |
---|
359 | else |
---|
360 | { |
---|
361 | mDir = world_ray.mDir; |
---|
362 | mOrigin = world_ray.mOrig; |
---|
363 | } |
---|
364 | |
---|
365 | // 4) Special case: 1-triangle meshes [Opcode 1.3] |
---|
366 | if(mCurrentModel && mCurrentModel->HasSingleNode()) |
---|
367 | { |
---|
368 | // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0. |
---|
369 | if(!SkipPrimitiveTests()) |
---|
370 | { |
---|
371 | // Perform overlap test between the unique triangle and the ray (and set contact status if needed) |
---|
372 | SEGMENT_PRIM(udword(0), OPC_CONTACT) |
---|
373 | |
---|
374 | // Return immediately regardless of status |
---|
375 | return TRUE; |
---|
376 | } |
---|
377 | } |
---|
378 | |
---|
379 | // Check temporal coherence : |
---|
380 | |
---|
381 | // Test previously colliding primitives first |
---|
382 | if(TemporalCoherenceEnabled() && FirstContactEnabled() && face_id && *face_id!=INVALID_ID) |
---|
383 | { |
---|
384 | #ifdef OLD_CODE |
---|
385 | #ifndef OPC_RAYHIT_CALLBACK |
---|
386 | if(!mClosestHit) |
---|
387 | #endif |
---|
388 | { |
---|
389 | // Request vertices from the app |
---|
390 | VertexPointers VP; |
---|
391 | mIMesh->GetTriangle(VP, *face_id); |
---|
392 | // Perform ray-cached tri overlap test |
---|
393 | if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) |
---|
394 | { |
---|
395 | // Intersection point is valid if: |
---|
396 | // - distance is positive (else it can just be a face behind the orig point) |
---|
397 | // - distance is smaller than a given max distance (useful for shadow feelers) |
---|
398 | // if(mStabbedFace.mDistance>0.0f && mStabbedFace.mDistance<mMaxDist) |
---|
399 | if(IR(mStabbedFace.mDistance)<IR(mMaxDist)) // The other test is already performed in RayTriOverlap |
---|
400 | { |
---|
401 | // Set contact status |
---|
402 | mFlags |= OPC_TEMPORAL_CONTACT; |
---|
403 | |
---|
404 | mStabbedFace.mFaceID = *face_id; |
---|
405 | |
---|
406 | #ifndef OPC_RAYHIT_CALLBACK |
---|
407 | if(mStabbedFaces) mStabbedFaces->AddFace(mStabbedFace); |
---|
408 | #endif |
---|
409 | return TRUE; |
---|
410 | } |
---|
411 | } |
---|
412 | } |
---|
413 | #else |
---|
414 | // New code |
---|
415 | // We handle both Segment/ray queries with the same segment code, and a possible infinite limit |
---|
416 | SEGMENT_PRIM(*face_id, OPC_TEMPORAL_CONTACT) |
---|
417 | |
---|
418 | // Return immediately if possible |
---|
419 | if(GetContactStatus()) return TRUE; |
---|
420 | #endif |
---|
421 | } |
---|
422 | |
---|
423 | // Precompute data (moved after temporal coherence since only needed for ray-AABB) |
---|
424 | if(IR(mMaxDist)!=IEEE_MAX_FLOAT) |
---|
425 | { |
---|
426 | // For Segment-AABB overlap |
---|
427 | mData = 0.5f * mDir * mMaxDist; |
---|
428 | mData2 = mOrigin + mData; |
---|
429 | |
---|
430 | // Precompute mFDir; |
---|
431 | mFDir.x = fabsf(mData.x); |
---|
432 | mFDir.y = fabsf(mData.y); |
---|
433 | mFDir.z = fabsf(mData.z); |
---|
434 | } |
---|
435 | else |
---|
436 | { |
---|
437 | // For Ray-AABB overlap |
---|
438 | // udword x = SIR(mDir.x)-1; |
---|
439 | // udword y = SIR(mDir.y)-1; |
---|
440 | // udword z = SIR(mDir.z)-1; |
---|
441 | // mData.x = FR(x); |
---|
442 | // mData.y = FR(y); |
---|
443 | // mData.z = FR(z); |
---|
444 | |
---|
445 | // Precompute mFDir; |
---|
446 | mFDir.x = fabsf(mDir.x); |
---|
447 | mFDir.y = fabsf(mDir.y); |
---|
448 | mFDir.z = fabsf(mDir.z); |
---|
449 | } |
---|
450 | |
---|
451 | return FALSE; |
---|
452 | } |
---|
453 | |
---|
454 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
455 | /** |
---|
456 | * Stabbing query for vanilla AABB trees. |
---|
457 | * \param world_ray [in] stabbing ray in world space |
---|
458 | * \param tree [in] AABB tree |
---|
459 | * \param box_indices [out] indices of stabbed boxes |
---|
460 | * \return true if success |
---|
461 | */ |
---|
462 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
463 | bool RayCollider::Collide(const Ray& world_ray, const AABBTree* tree, Container& box_indices) |
---|
464 | { |
---|
465 | // ### bad design here |
---|
466 | |
---|
467 | // This is typically called for a scene tree, full of -AABBs-, not full of triangles. |
---|
468 | // So we don't really have "primitives" to deal with. Hence it doesn't work with |
---|
469 | // "FirstContact" + "TemporalCoherence". |
---|
470 | ASSERT( !(FirstContactEnabled() && TemporalCoherenceEnabled()) ); |
---|
471 | |
---|
472 | // Checkings |
---|
473 | if(!tree) return false; |
---|
474 | |
---|
475 | // Init collision query |
---|
476 | // Basically this is only called to initialize precomputed data |
---|
477 | if(InitQuery(world_ray)) return true; |
---|
478 | |
---|
479 | // Perform stabbing query |
---|
480 | if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(tree, box_indices); |
---|
481 | else _RayStab(tree, box_indices); |
---|
482 | |
---|
483 | return true; |
---|
484 | } |
---|
485 | |
---|
486 | |
---|
487 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
488 | /** |
---|
489 | * Recursive stabbing query for normal AABB trees. |
---|
490 | * \param node [in] current collision node |
---|
491 | */ |
---|
492 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
493 | void RayCollider::_SegmentStab(const AABBCollisionNode* node) |
---|
494 | { |
---|
495 | // Perform Segment-AABB overlap test |
---|
496 | if(!SegmentAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; |
---|
497 | |
---|
498 | if(node->IsLeaf()) |
---|
499 | { |
---|
500 | SEGMENT_PRIM(node->GetPrimitive(), OPC_CONTACT) |
---|
501 | } |
---|
502 | else |
---|
503 | { |
---|
504 | _SegmentStab(node->GetPos()); |
---|
505 | |
---|
506 | if(ContactFound()) return; |
---|
507 | |
---|
508 | _SegmentStab(node->GetNeg()); |
---|
509 | } |
---|
510 | } |
---|
511 | |
---|
512 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
513 | /** |
---|
514 | * Recursive stabbing query for quantized AABB trees. |
---|
515 | * \param node [in] current collision node |
---|
516 | */ |
---|
517 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
518 | void RayCollider::_SegmentStab(const AABBQuantizedNode* node) |
---|
519 | { |
---|
520 | // Dequantize box |
---|
521 | const QuantizedAABB& Box = node->mAABB; |
---|
522 | const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); |
---|
523 | const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); |
---|
524 | |
---|
525 | // Perform Segment-AABB overlap test |
---|
526 | if(!SegmentAABBOverlap(Center, Extents)) return; |
---|
527 | |
---|
528 | if(node->IsLeaf()) |
---|
529 | { |
---|
530 | SEGMENT_PRIM(node->GetPrimitive(), OPC_CONTACT) |
---|
531 | } |
---|
532 | else |
---|
533 | { |
---|
534 | _SegmentStab(node->GetPos()); |
---|
535 | |
---|
536 | if(ContactFound()) return; |
---|
537 | |
---|
538 | _SegmentStab(node->GetNeg()); |
---|
539 | } |
---|
540 | } |
---|
541 | |
---|
542 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
543 | /** |
---|
544 | * Recursive stabbing query for no-leaf AABB trees. |
---|
545 | * \param node [in] current collision node |
---|
546 | */ |
---|
547 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
548 | void RayCollider::_SegmentStab(const AABBNoLeafNode* node) |
---|
549 | { |
---|
550 | // Perform Segment-AABB overlap test |
---|
551 | if(!SegmentAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; |
---|
552 | |
---|
553 | if(node->HasPosLeaf()) |
---|
554 | { |
---|
555 | SEGMENT_PRIM(node->GetPosPrimitive(), OPC_CONTACT) |
---|
556 | } |
---|
557 | else _SegmentStab(node->GetPos()); |
---|
558 | |
---|
559 | if(ContactFound()) return; |
---|
560 | |
---|
561 | if(node->HasNegLeaf()) |
---|
562 | { |
---|
563 | SEGMENT_PRIM(node->GetNegPrimitive(), OPC_CONTACT) |
---|
564 | } |
---|
565 | else _SegmentStab(node->GetNeg()); |
---|
566 | } |
---|
567 | |
---|
568 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
569 | /** |
---|
570 | * Recursive stabbing query for quantized no-leaf AABB trees. |
---|
571 | * \param node [in] current collision node |
---|
572 | */ |
---|
573 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
574 | void RayCollider::_SegmentStab(const AABBQuantizedNoLeafNode* node) |
---|
575 | { |
---|
576 | // Dequantize box |
---|
577 | const QuantizedAABB& Box = node->mAABB; |
---|
578 | const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); |
---|
579 | const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); |
---|
580 | |
---|
581 | // Perform Segment-AABB overlap test |
---|
582 | if(!SegmentAABBOverlap(Center, Extents)) return; |
---|
583 | |
---|
584 | if(node->HasPosLeaf()) |
---|
585 | { |
---|
586 | SEGMENT_PRIM(node->GetPosPrimitive(), OPC_CONTACT) |
---|
587 | } |
---|
588 | else _SegmentStab(node->GetPos()); |
---|
589 | |
---|
590 | if(ContactFound()) return; |
---|
591 | |
---|
592 | if(node->HasNegLeaf()) |
---|
593 | { |
---|
594 | SEGMENT_PRIM(node->GetNegPrimitive(), OPC_CONTACT) |
---|
595 | } |
---|
596 | else _SegmentStab(node->GetNeg()); |
---|
597 | } |
---|
598 | |
---|
599 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
600 | /** |
---|
601 | * Recursive stabbing query for vanilla AABB trees. |
---|
602 | * \param node [in] current collision node |
---|
603 | * \param box_indices [out] indices of stabbed boxes |
---|
604 | */ |
---|
605 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
606 | void RayCollider::_SegmentStab(const AABBTreeNode* node, Container& box_indices) |
---|
607 | { |
---|
608 | // Test the box against the segment |
---|
609 | Point Center, Extents; |
---|
610 | node->GetAABB()->GetCenter(Center); |
---|
611 | node->GetAABB()->GetExtents(Extents); |
---|
612 | if(!SegmentAABBOverlap(Center, Extents)) return; |
---|
613 | |
---|
614 | if(node->IsLeaf()) |
---|
615 | { |
---|
616 | box_indices.Add(node->GetPrimitives(), node->GetNbPrimitives()); |
---|
617 | } |
---|
618 | else |
---|
619 | { |
---|
620 | _SegmentStab(node->GetPos(), box_indices); |
---|
621 | _SegmentStab(node->GetNeg(), box_indices); |
---|
622 | } |
---|
623 | } |
---|
624 | |
---|
625 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
626 | /** |
---|
627 | * Recursive stabbing query for normal AABB trees. |
---|
628 | * \param node [in] current collision node |
---|
629 | */ |
---|
630 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
631 | void RayCollider::_RayStab(const AABBCollisionNode* node) |
---|
632 | { |
---|
633 | // Perform Ray-AABB overlap test |
---|
634 | if(!RayAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; |
---|
635 | |
---|
636 | if(node->IsLeaf()) |
---|
637 | { |
---|
638 | RAY_PRIM(node->GetPrimitive(), OPC_CONTACT) |
---|
639 | } |
---|
640 | else |
---|
641 | { |
---|
642 | _RayStab(node->GetPos()); |
---|
643 | |
---|
644 | if(ContactFound()) return; |
---|
645 | |
---|
646 | _RayStab(node->GetNeg()); |
---|
647 | } |
---|
648 | } |
---|
649 | |
---|
650 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
651 | /** |
---|
652 | * Recursive stabbing query for quantized AABB trees. |
---|
653 | * \param node [in] current collision node |
---|
654 | */ |
---|
655 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
656 | void RayCollider::_RayStab(const AABBQuantizedNode* node) |
---|
657 | { |
---|
658 | // Dequantize box |
---|
659 | const QuantizedAABB& Box = node->mAABB; |
---|
660 | const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); |
---|
661 | const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); |
---|
662 | |
---|
663 | // Perform Ray-AABB overlap test |
---|
664 | if(!RayAABBOverlap(Center, Extents)) return; |
---|
665 | |
---|
666 | if(node->IsLeaf()) |
---|
667 | { |
---|
668 | RAY_PRIM(node->GetPrimitive(), OPC_CONTACT) |
---|
669 | } |
---|
670 | else |
---|
671 | { |
---|
672 | _RayStab(node->GetPos()); |
---|
673 | |
---|
674 | if(ContactFound()) return; |
---|
675 | |
---|
676 | _RayStab(node->GetNeg()); |
---|
677 | } |
---|
678 | } |
---|
679 | |
---|
680 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
681 | /** |
---|
682 | * Recursive stabbing query for no-leaf AABB trees. |
---|
683 | * \param node [in] current collision node |
---|
684 | */ |
---|
685 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
686 | void RayCollider::_RayStab(const AABBNoLeafNode* node) |
---|
687 | { |
---|
688 | // Perform Ray-AABB overlap test |
---|
689 | if(!RayAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; |
---|
690 | |
---|
691 | if(node->HasPosLeaf()) |
---|
692 | { |
---|
693 | RAY_PRIM(node->GetPosPrimitive(), OPC_CONTACT) |
---|
694 | } |
---|
695 | else _RayStab(node->GetPos()); |
---|
696 | |
---|
697 | if(ContactFound()) return; |
---|
698 | |
---|
699 | if(node->HasNegLeaf()) |
---|
700 | { |
---|
701 | RAY_PRIM(node->GetNegPrimitive(), OPC_CONTACT) |
---|
702 | } |
---|
703 | else _RayStab(node->GetNeg()); |
---|
704 | } |
---|
705 | |
---|
706 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
707 | /** |
---|
708 | * Recursive stabbing query for quantized no-leaf AABB trees. |
---|
709 | * \param node [in] current collision node |
---|
710 | */ |
---|
711 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
712 | void RayCollider::_RayStab(const AABBQuantizedNoLeafNode* node) |
---|
713 | { |
---|
714 | // Dequantize box |
---|
715 | const QuantizedAABB& Box = node->mAABB; |
---|
716 | const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); |
---|
717 | const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); |
---|
718 | |
---|
719 | // Perform Ray-AABB overlap test |
---|
720 | if(!RayAABBOverlap(Center, Extents)) return; |
---|
721 | |
---|
722 | if(node->HasPosLeaf()) |
---|
723 | { |
---|
724 | RAY_PRIM(node->GetPosPrimitive(), OPC_CONTACT) |
---|
725 | } |
---|
726 | else _RayStab(node->GetPos()); |
---|
727 | |
---|
728 | if(ContactFound()) return; |
---|
729 | |
---|
730 | if(node->HasNegLeaf()) |
---|
731 | { |
---|
732 | RAY_PRIM(node->GetNegPrimitive(), OPC_CONTACT) |
---|
733 | } |
---|
734 | else _RayStab(node->GetNeg()); |
---|
735 | } |
---|
736 | |
---|
737 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
738 | /** |
---|
739 | * Recursive stabbing query for vanilla AABB trees. |
---|
740 | * \param node [in] current collision node |
---|
741 | * \param box_indices [out] indices of stabbed boxes |
---|
742 | */ |
---|
743 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
744 | void RayCollider::_RayStab(const AABBTreeNode* node, Container& box_indices) |
---|
745 | { |
---|
746 | // Test the box against the ray |
---|
747 | Point Center, Extents; |
---|
748 | node->GetAABB()->GetCenter(Center); |
---|
749 | node->GetAABB()->GetExtents(Extents); |
---|
750 | if(!RayAABBOverlap(Center, Extents)) return; |
---|
751 | |
---|
752 | if(node->IsLeaf()) |
---|
753 | { |
---|
754 | mFlags |= OPC_CONTACT; |
---|
755 | box_indices.Add(node->GetPrimitives(), node->GetNbPrimitives()); |
---|
756 | } |
---|
757 | else |
---|
758 | { |
---|
759 | _RayStab(node->GetPos(), box_indices); |
---|
760 | _RayStab(node->GetNeg(), box_indices); |
---|
761 | } |
---|
762 | } |
---|