2 * OPCODE - Optimized Collision Detection
3 * http://www.codercorner.com/Opcode.htm
5 * Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com
7 This software is provided 'as-is', without any express or implied warranty.
8 In no event will the authors be held liable for any damages arising from the use of this software.
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it freely,
11 subject to the following restrictions:
13 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
14 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
15 3. This notice may not be removed or altered from any source distribution.
18 // Following code from Magic-Software (http://www.magic-software.com/)
19 // A bit modified for Opcode
21 inline_ float OPC_PointAABBSqrDist(const Point& point, const Point& center, const Point& extents)
23 // Compute coordinates of point in box coordinate system
24 Point Closest = point - center;
26 float SqrDistance = 0.0f;
28 if(Closest.x < -extents.x)
30 float Delta = Closest.x + extents.x;
31 SqrDistance += Delta*Delta;
33 else if(Closest.x > extents.x)
35 float Delta = Closest.x - extents.x;
36 SqrDistance += Delta*Delta;
39 if(Closest.y < -extents.y)
41 float Delta = Closest.y + extents.y;
42 SqrDistance += Delta*Delta;
44 else if(Closest.y > extents.y)
46 float Delta = Closest.y - extents.y;
47 SqrDistance += Delta*Delta;
50 if(Closest.z < -extents.z)
52 float Delta = Closest.z + extents.z;
53 SqrDistance += Delta*Delta;
55 else if(Closest.z > extents.z)
57 float Delta = Closest.z - extents.z;
58 SqrDistance += Delta*Delta;
63 static void Face(int i0, int i1, int i2, Point& rkPnt, const Point& rkDir, const Point& extents, const Point& rkPmE, float* pfLParam, float& rfSqrDistance)
66 float fLSqr, fInv, fTmp, fParam, fT, fDelta;
68 kPpE[i1] = rkPnt[i1] + extents[i1];
69 kPpE[i2] = rkPnt[i2] + extents[i2];
70 if(rkDir[i0]*kPpE[i1] >= rkDir[i1]*rkPmE[i0])
72 if(rkDir[i0]*kPpE[i2] >= rkDir[i2]*rkPmE[i0])
74 // v[i1] >= -e[i1], v[i2] >= -e[i2] (distance = 0)
77 rkPnt[i0] = extents[i0];
78 fInv = 1.0f/rkDir[i0];
79 rkPnt[i1] -= rkDir[i1]*rkPmE[i0]*fInv;
80 rkPnt[i2] -= rkDir[i2]*rkPmE[i0]*fInv;
81 *pfLParam = -rkPmE[i0]*fInv;
86 // v[i1] >= -e[i1], v[i2] < -e[i2]
87 fLSqr = rkDir[i0]*rkDir[i0] + rkDir[i2]*rkDir[i2];
88 fTmp = fLSqr*kPpE[i1] - rkDir[i1]*(rkDir[i0]*rkPmE[i0] + rkDir[i2]*kPpE[i2]);
89 if(fTmp <= 2.0f*fLSqr*extents[i1])
92 fLSqr += rkDir[i1]*rkDir[i1];
94 fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*fTmp + rkDir[i2]*kPpE[i2];
95 fParam = -fDelta/fLSqr;
96 rfSqrDistance += rkPmE[i0]*rkPmE[i0] + fTmp*fTmp + kPpE[i2]*kPpE[i2] + fDelta*fParam;
101 rkPnt[i0] = extents[i0];
102 rkPnt[i1] = fT - extents[i1];
103 rkPnt[i2] = -extents[i2];
108 fLSqr += rkDir[i1]*rkDir[i1];
109 fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*rkPmE[i1] + rkDir[i2]*kPpE[i2];
110 fParam = -fDelta/fLSqr;
111 rfSqrDistance += rkPmE[i0]*rkPmE[i0] + rkPmE[i1]*rkPmE[i1] + kPpE[i2]*kPpE[i2] + fDelta*fParam;
116 rkPnt[i0] = extents[i0];
117 rkPnt[i1] = extents[i1];
118 rkPnt[i2] = -extents[i2];
125 if ( rkDir[i0]*kPpE[i2] >= rkDir[i2]*rkPmE[i0] )
127 // v[i1] < -e[i1], v[i2] >= -e[i2]
128 fLSqr = rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1];
129 fTmp = fLSqr*kPpE[i2] - rkDir[i2]*(rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1]);
130 if(fTmp <= 2.0f*fLSqr*extents[i2])
133 fLSqr += rkDir[i2]*rkDir[i2];
134 fTmp = kPpE[i2] - fT;
135 fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*fTmp;
136 fParam = -fDelta/fLSqr;
137 rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + fTmp*fTmp + fDelta*fParam;
142 rkPnt[i0] = extents[i0];
143 rkPnt[i1] = -extents[i1];
144 rkPnt[i2] = fT - extents[i2];
149 fLSqr += rkDir[i2]*rkDir[i2];
150 fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*rkPmE[i2];
151 fParam = -fDelta/fLSqr;
152 rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + rkPmE[i2]*rkPmE[i2] + fDelta*fParam;
157 rkPnt[i0] = extents[i0];
158 rkPnt[i1] = -extents[i1];
159 rkPnt[i2] = extents[i2];
165 // v[i1] < -e[i1], v[i2] < -e[i2]
166 fLSqr = rkDir[i0]*rkDir[i0]+rkDir[i2]*rkDir[i2];
167 fTmp = fLSqr*kPpE[i1] - rkDir[i1]*(rkDir[i0]*rkPmE[i0] + rkDir[i2]*kPpE[i2]);
170 // v[i1]-edge is closest
171 if ( fTmp <= 2.0f*fLSqr*extents[i1] )
174 fLSqr += rkDir[i1]*rkDir[i1];
175 fTmp = kPpE[i1] - fT;
176 fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*fTmp + rkDir[i2]*kPpE[i2];
177 fParam = -fDelta/fLSqr;
178 rfSqrDistance += rkPmE[i0]*rkPmE[i0] + fTmp*fTmp + kPpE[i2]*kPpE[i2] + fDelta*fParam;
183 rkPnt[i0] = extents[i0];
184 rkPnt[i1] = fT - extents[i1];
185 rkPnt[i2] = -extents[i2];
190 fLSqr += rkDir[i1]*rkDir[i1];
191 fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*rkPmE[i1] + rkDir[i2]*kPpE[i2];
192 fParam = -fDelta/fLSqr;
193 rfSqrDistance += rkPmE[i0]*rkPmE[i0] + rkPmE[i1]*rkPmE[i1] + kPpE[i2]*kPpE[i2] + fDelta*fParam;
198 rkPnt[i0] = extents[i0];
199 rkPnt[i1] = extents[i1];
200 rkPnt[i2] = -extents[i2];
206 fLSqr = rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1];
207 fTmp = fLSqr*kPpE[i2] - rkDir[i2]*(rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1]);
210 // v[i2]-edge is closest
211 if(fTmp <= 2.0f*fLSqr*extents[i2])
214 fLSqr += rkDir[i2]*rkDir[i2];
215 fTmp = kPpE[i2] - fT;
216 fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*fTmp;
217 fParam = -fDelta/fLSqr;
218 rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + fTmp*fTmp + fDelta*fParam;
223 rkPnt[i0] = extents[i0];
224 rkPnt[i1] = -extents[i1];
225 rkPnt[i2] = fT - extents[i2];
230 fLSqr += rkDir[i2]*rkDir[i2];
231 fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*rkPmE[i2];
232 fParam = -fDelta/fLSqr;
233 rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + rkPmE[i2]*rkPmE[i2] + fDelta*fParam;
238 rkPnt[i0] = extents[i0];
239 rkPnt[i1] = -extents[i1];
240 rkPnt[i2] = extents[i2];
246 // (v[i1],v[i2])-corner is closest
247 fLSqr += rkDir[i2]*rkDir[i2];
248 fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*kPpE[i2];
249 fParam = -fDelta/fLSqr;
250 rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + kPpE[i2]*kPpE[i2] + fDelta*fParam;
255 rkPnt[i0] = extents[i0];
256 rkPnt[i1] = -extents[i1];
257 rkPnt[i2] = -extents[i2];
263 static void CaseNoZeros(Point& rkPnt, const Point& rkDir, const Point& extents, float* pfLParam, float& rfSqrDistance)
265 Point kPmE(rkPnt.x - extents.x, rkPnt.y - extents.y, rkPnt.z - extents.z);
267 float fProdDxPy, fProdDyPx, fProdDzPx, fProdDxPz, fProdDzPy, fProdDyPz;
269 fProdDxPy = rkDir.x*kPmE.y;
270 fProdDyPx = rkDir.y*kPmE.x;
271 if(fProdDyPx >= fProdDxPy)
273 fProdDzPx = rkDir.z*kPmE.x;
274 fProdDxPz = rkDir.x*kPmE.z;
275 if(fProdDzPx >= fProdDxPz)
277 // line intersects x = e0
278 Face(0, 1, 2, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance);
282 // line intersects z = e2
283 Face(2, 0, 1, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance);
288 fProdDzPy = rkDir.z*kPmE.y;
289 fProdDyPz = rkDir.y*kPmE.z;
290 if(fProdDzPy >= fProdDyPz)
292 // line intersects y = e1
293 Face(1, 2, 0, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance);
297 // line intersects z = e2
298 Face(2, 0, 1, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance);
303 static void Case0(int i0, int i1, int i2, Point& rkPnt, const Point& rkDir, const Point& extents, float* pfLParam, float& rfSqrDistance)
305 float fPmE0 = rkPnt[i0] - extents[i0];
306 float fPmE1 = rkPnt[i1] - extents[i1];
307 float fProd0 = rkDir[i1]*fPmE0;
308 float fProd1 = rkDir[i0]*fPmE1;
309 float fDelta, fInvLSqr, fInv;
313 // line intersects P[i0] = e[i0]
314 rkPnt[i0] = extents[i0];
316 float fPpE1 = rkPnt[i1] + extents[i1];
317 fDelta = fProd0 - rkDir[i0]*fPpE1;
320 fInvLSqr = 1.0f/(rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1]);
321 rfSqrDistance += fDelta*fDelta*fInvLSqr;
324 rkPnt[i1] = -extents[i1];
325 *pfLParam = -(rkDir[i0]*fPmE0+rkDir[i1]*fPpE1)*fInvLSqr;
332 fInv = 1.0f/rkDir[i0];
333 rkPnt[i1] -= fProd0*fInv;
334 *pfLParam = -fPmE0*fInv;
340 // line intersects P[i1] = e[i1]
341 rkPnt[i1] = extents[i1];
343 float fPpE0 = rkPnt[i0] + extents[i0];
344 fDelta = fProd1 - rkDir[i1]*fPpE0;
347 fInvLSqr = 1.0f/(rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1]);
348 rfSqrDistance += fDelta*fDelta*fInvLSqr;
351 rkPnt[i0] = -extents[i0];
352 *pfLParam = -(rkDir[i0]*fPpE0+rkDir[i1]*fPmE1)*fInvLSqr;
359 fInv = 1.0f/rkDir[i1];
360 rkPnt[i0] -= fProd1*fInv;
361 *pfLParam = -fPmE1*fInv;
366 if(rkPnt[i2] < -extents[i2])
368 fDelta = rkPnt[i2] + extents[i2];
369 rfSqrDistance += fDelta*fDelta;
370 rkPnt[i2] = -extents[i2];
372 else if ( rkPnt[i2] > extents[i2] )
374 fDelta = rkPnt[i2] - extents[i2];
375 rfSqrDistance += fDelta*fDelta;
376 rkPnt[i2] = extents[i2];
380 static void Case00(int i0, int i1, int i2, Point& rkPnt, const Point& rkDir, const Point& extents, float* pfLParam, float& rfSqrDistance)
385 *pfLParam = (extents[i0] - rkPnt[i0])/rkDir[i0];
387 rkPnt[i0] = extents[i0];
389 if(rkPnt[i1] < -extents[i1])
391 fDelta = rkPnt[i1] + extents[i1];
392 rfSqrDistance += fDelta*fDelta;
393 rkPnt[i1] = -extents[i1];
395 else if(rkPnt[i1] > extents[i1])
397 fDelta = rkPnt[i1] - extents[i1];
398 rfSqrDistance += fDelta*fDelta;
399 rkPnt[i1] = extents[i1];
402 if(rkPnt[i2] < -extents[i2])
404 fDelta = rkPnt[i2] + extents[i2];
405 rfSqrDistance += fDelta*fDelta;
406 rkPnt[i1] = -extents[i2];
408 else if(rkPnt[i2] > extents[i2])
410 fDelta = rkPnt[i2] - extents[i2];
411 rfSqrDistance += fDelta*fDelta;
412 rkPnt[i2] = extents[i2];
416 static void Case000(Point& rkPnt, const Point& extents, float& rfSqrDistance)
420 if(rkPnt.x < -extents.x)
422 fDelta = rkPnt.x + extents.x;
423 rfSqrDistance += fDelta*fDelta;
424 rkPnt.x = -extents.x;
426 else if(rkPnt.x > extents.x)
428 fDelta = rkPnt.x - extents.x;
429 rfSqrDistance += fDelta*fDelta;
433 if(rkPnt.y < -extents.y)
435 fDelta = rkPnt.y + extents.y;
436 rfSqrDistance += fDelta*fDelta;
437 rkPnt.y = -extents.y;
439 else if(rkPnt.y > extents.y)
441 fDelta = rkPnt.y - extents.y;
442 rfSqrDistance += fDelta*fDelta;
446 if(rkPnt.z < -extents.z)
448 fDelta = rkPnt.z + extents.z;
449 rfSqrDistance += fDelta*fDelta;
450 rkPnt.z = -extents.z;
452 else if(rkPnt.z > extents.z)
454 fDelta = rkPnt.z - extents.z;
455 rfSqrDistance += fDelta*fDelta;
460 static float SqrDistance(const Ray& rkLine, const Point& center, const Point& extents, float* pfLParam)
462 // compute coordinates of line in box coordinate system
463 Point kDiff = rkLine.mOrig - center;
465 Point kDir = rkLine.mDir;
467 // Apply reflections so that direction vector has nonnegative components.
483 float fSqrDistance = 0.0f;
489 if(kDir.z>0.0f) CaseNoZeros(kPnt, kDir, extents, pfLParam, fSqrDistance); // (+,+,+)
490 else Case0(0, 1, 2, kPnt, kDir, extents, pfLParam, fSqrDistance); // (+,+,0)
494 if(kDir.z>0.0f) Case0(0, 2, 1, kPnt, kDir, extents, pfLParam, fSqrDistance); // (+,0,+)
495 else Case00(0, 1, 2, kPnt, kDir, extents, pfLParam, fSqrDistance); // (+,0,0)
502 if(kDir.z>0.0f) Case0(1, 2, 0, kPnt, kDir, extents, pfLParam, fSqrDistance); // (0,+,+)
503 else Case00(1, 0, 2, kPnt, kDir, extents, pfLParam, fSqrDistance); // (0,+,0)
507 if(kDir.z>0.0f) Case00(2, 0, 1, kPnt, kDir, extents, pfLParam, fSqrDistance); // (0,0,+)
510 Case000(kPnt, extents, fSqrDistance); // (0,0,0)
511 if(pfLParam) *pfLParam = 0.0f;
518 inline_ float OPC_SegmentOBBSqrDist(const Segment& segment, const Point& c0, const Point& e0)
521 float fSqrDistance = SqrDistance(Ray(segment.GetOrigin(), segment.ComputeDirection()), c0, e0, &fLP);
524 if(fLP<=1.0f) return fSqrDistance;
525 else return OPC_PointAABBSqrDist(segment.mP1, c0, e0);
527 else return OPC_PointAABBSqrDist(segment.mP0, c0, e0);
530 inline_ BOOL LSSCollider::LSSAABBOverlap(const Point& center, const Point& extents)
535 float s2 = OPC_SegmentOBBSqrDist(mSeg, center, extents);
536 if(s2<mRadius2) return TRUE;