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.
19 //! This macro quickly finds the min & max values among 3 variables
20 #define FINDMINMAX(x0, x1, x2, min, max) \
28 inline_ BOOL planeBoxOverlap(const Point& normal, const float d, const Point& maxbox)
31 for(udword q=0;q<=2;q++)
33 if(normal[q]>0.0f) { vmin[q]=-maxbox[q]; vmax[q]=maxbox[q]; }
34 else { vmin[q]=maxbox[q]; vmax[q]=-maxbox[q]; }
36 if((normal|vmin)+d>0.0f) return FALSE;
37 if((normal|vmax)+d>=0.0f) return TRUE;
43 #define AXISTEST_X01(a, b, fa, fb) \
44 min = a*v0.y - b*v0.z; \
45 max = a*v2.y - b*v2.z; \
46 if(min>max) {const float tmp=max; max=min; min=tmp; } \
47 rad = fa * extents.y + fb * extents.z; \
48 if(min>rad || max<-rad) return FALSE;
51 #define AXISTEST_X2(a, b, fa, fb) \
52 min = a*v0.y - b*v0.z; \
53 max = a*v1.y - b*v1.z; \
54 if(min>max) {const float tmp=max; max=min; min=tmp; } \
55 rad = fa * extents.y + fb * extents.z; \
56 if(min>rad || max<-rad) return FALSE;
59 #define AXISTEST_Y02(a, b, fa, fb) \
60 min = b*v0.z - a*v0.x; \
61 max = b*v2.z - a*v2.x; \
62 if(min>max) {const float tmp=max; max=min; min=tmp; } \
63 rad = fa * extents.x + fb * extents.z; \
64 if(min>rad || max<-rad) return FALSE;
67 #define AXISTEST_Y1(a, b, fa, fb) \
68 min = b*v0.z - a*v0.x; \
69 max = b*v1.z - a*v1.x; \
70 if(min>max) {const float tmp=max; max=min; min=tmp; } \
71 rad = fa * extents.x + fb * extents.z; \
72 if(min>rad || max<-rad) return FALSE;
75 #define AXISTEST_Z12(a, b, fa, fb) \
76 min = a*v1.x - b*v1.y; \
77 max = a*v2.x - b*v2.y; \
78 if(min>max) {const float tmp=max; max=min; min=tmp; } \
79 rad = fa * extents.x + fb * extents.y; \
80 if(min>rad || max<-rad) return FALSE;
83 #define AXISTEST_Z0(a, b, fa, fb) \
84 min = a*v0.x - b*v0.y; \
85 max = a*v1.x - b*v1.y; \
86 if(min>max) {const float tmp=max; max=min; min=tmp; } \
87 rad = fa * extents.x + fb * extents.y; \
88 if(min>rad || max<-rad) return FALSE;
90 // compute triangle edges
91 // - edges lazy evaluated to take advantage of early exits
92 // - fabs precomputed (half less work, possible since extents are always >0)
93 // - customized macros to take advantage of the null component
94 // - axis vector discarded, possibly saves useless movs
95 #define IMPLEMENT_CLASS3_TESTS \
99 const float fey0 = fabsf(e0.y); \
100 const float fez0 = fabsf(e0.z); \
101 AXISTEST_X01(e0.z, e0.y, fez0, fey0); \
102 const float fex0 = fabsf(e0.x); \
103 AXISTEST_Y02(e0.z, e0.x, fez0, fex0); \
104 AXISTEST_Z12(e0.y, e0.x, fey0, fex0); \
106 const float fey1 = fabsf(e1.y); \
107 const float fez1 = fabsf(e1.z); \
108 AXISTEST_X01(e1.z, e1.y, fez1, fey1); \
109 const float fex1 = fabsf(e1.x); \
110 AXISTEST_Y02(e1.z, e1.x, fez1, fex1); \
111 AXISTEST_Z0(e1.y, e1.x, fey1, fex1); \
113 const Point e2 = mLeafVerts[0] - mLeafVerts[2]; \
114 const float fey2 = fabsf(e2.y); \
115 const float fez2 = fabsf(e2.z); \
116 AXISTEST_X2(e2.z, e2.y, fez2, fey2); \
117 const float fex2 = fabsf(e2.x); \
118 AXISTEST_Y1(e2.z, e2.x, fez2, fex2); \
119 AXISTEST_Z12(e2.y, e2.x, fey2, fex2);
121 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
123 * Triangle-Box overlap test using the separating axis theorem.
124 * This is the code from Tomas Möller, a bit optimized:
125 * - with some more lazy evaluation (faster path on PC)
126 * - with a tiny bit of assembly
127 * - with "SAT-lite" applied if needed
128 * - and perhaps with some more minor modifs...
130 * \param center [in] box center
131 * \param extents [in] box extents
132 * \return true if triangle & box overlap
134 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
135 inline_ BOOL AABBTreeCollider::TriBoxOverlap(const Point& center, const Point& extents)
140 // use separating axis theorem to test overlap between triangle and box
141 // need to test for overlap in these directions:
142 // 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle
143 // we do not even need to test these)
144 // 2) normal of the triangle
145 // 3) crossproduct(edge from tri, {x,y,z}-directin)
146 // this gives 3x3=9 more tests
148 // move everything so that the boxcenter is in (0,0,0)
150 v0.x = mLeafVerts[0].x - center.x;
151 v1.x = mLeafVerts[1].x - center.x;
152 v2.x = mLeafVerts[2].x - center.x;
154 // First, test overlap in the {x,y,z}-directions
156 // find min, max of the triangle in x-direction, and test for overlap in X
157 if(FCMin3(v0.x, v1.x, v2.x)>extents.x) return FALSE;
158 if(FCMax3(v0.x, v1.x, v2.x)<-extents.x) return FALSE;
161 v0.y = mLeafVerts[0].y - center.y;
162 v1.y = mLeafVerts[1].y - center.y;
163 v2.y = mLeafVerts[2].y - center.y;
165 if(FCMin3(v0.y, v1.y, v2.y)>extents.y) return FALSE;
166 if(FCMax3(v0.y, v1.y, v2.y)<-extents.y) return FALSE;
169 v0.z = mLeafVerts[0].z - center.z;
170 v1.z = mLeafVerts[1].z - center.z;
171 v2.z = mLeafVerts[2].z - center.z;
173 if(FCMin3(v0.z, v1.z, v2.z)>extents.z) return FALSE;
174 if(FCMax3(v0.z, v1.z, v2.z)<-extents.z) return FALSE;
177 // Find min, max of the triangle in x-direction, and test for overlap in X
178 FINDMINMAX(v0.x, v1.x, v2.x, min, max);
179 if(min>extents.x || max<-extents.x) return FALSE;
182 v0.y = mLeafVerts[0].y - center.y;
183 v1.y = mLeafVerts[1].y - center.y;
184 v2.y = mLeafVerts[2].y - center.y;
186 FINDMINMAX(v0.y, v1.y, v2.y, min, max);
187 if(min>extents.y || max<-extents.y) return FALSE;
190 v0.z = mLeafVerts[0].z - center.z;
191 v1.z = mLeafVerts[1].z - center.z;
192 v2.z = mLeafVerts[2].z - center.z;
194 FINDMINMAX(v0.z, v1.z, v2.z, min, max);
195 if(min>extents.z || max<-extents.z) return FALSE;
197 // 2) Test if the box intersects the plane of the triangle
198 // compute plane equation of triangle: normal*x+d=0
199 // ### could be precomputed since we use the same leaf triangle several times
200 const Point e0 = v1 - v0;
201 const Point e1 = v2 - v1;
202 const Point normal = e0 ^ e1;
203 const float d = -normal|v0;
204 if(!planeBoxOverlap(normal, d, extents)) return FALSE;
206 // 3) "Class III" tests
209 IMPLEMENT_CLASS3_TESTS
214 //! A dedicated version where the box is constant
215 inline_ BOOL OBBCollider::TriBoxOverlap()
218 mNbVolumePrimTests++;
221 const Point& extents = mBoxExtents;
222 const Point& v0 = mLeafVerts[0];
223 const Point& v1 = mLeafVerts[1];
224 const Point& v2 = mLeafVerts[2];
226 // use separating axis theorem to test overlap between triangle and box
227 // need to test for overlap in these directions:
228 // 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle
229 // we do not even need to test these)
230 // 2) normal of the triangle
231 // 3) crossproduct(edge from tri, {x,y,z}-directin)
232 // this gives 3x3=9 more tests
234 // Box center is already in (0,0,0)
236 // First, test overlap in the {x,y,z}-directions
238 // find min, max of the triangle in x-direction, and test for overlap in X
239 if(FCMin3(v0.x, v1.x, v2.x)>mBoxExtents.x) return FALSE;
240 if(FCMax3(v0.x, v1.x, v2.x)<-mBoxExtents.x) return FALSE;
242 if(FCMin3(v0.y, v1.y, v2.y)>mBoxExtents.y) return FALSE;
243 if(FCMax3(v0.y, v1.y, v2.y)<-mBoxExtents.y) return FALSE;
245 if(FCMin3(v0.z, v1.z, v2.z)>mBoxExtents.z) return FALSE;
246 if(FCMax3(v0.z, v1.z, v2.z)<-mBoxExtents.z) return FALSE;
249 // Find min, max of the triangle in x-direction, and test for overlap in X
250 FINDMINMAX(v0.x, v1.x, v2.x, min, max);
251 if(min>mBoxExtents.x || max<-mBoxExtents.x) return FALSE;
253 FINDMINMAX(v0.y, v1.y, v2.y, min, max);
254 if(min>mBoxExtents.y || max<-mBoxExtents.y) return FALSE;
256 FINDMINMAX(v0.z, v1.z, v2.z, min, max);
257 if(min>mBoxExtents.z || max<-mBoxExtents.z) return FALSE;
259 // 2) Test if the box intersects the plane of the triangle
260 // compute plane equation of triangle: normal*x+d=0
261 // ### could be precomputed since we use the same leaf triangle several times
262 const Point e0 = v1 - v0;
263 const Point e1 = v2 - v1;
264 const Point normal = e0 ^ e1;
265 const float d = -normal|v0;
266 if(!planeBoxOverlap(normal, d, mBoxExtents)) return FALSE;
268 // 3) "Class III" tests - here we always do full tests since the box is a primitive (not a BV)
270 IMPLEMENT_CLASS3_TESTS
275 //! ...and another one, jeez
276 inline_ BOOL AABBCollider::TriBoxOverlap()
279 mNbVolumePrimTests++;
282 const Point& center = mBox.mCenter;
283 const Point& extents = mBox.mExtents;
285 // use separating axis theorem to test overlap between triangle and box
286 // need to test for overlap in these directions:
287 // 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle
288 // we do not even need to test these)
289 // 2) normal of the triangle
290 // 3) crossproduct(edge from tri, {x,y,z}-directin)
291 // this gives 3x3=9 more tests
293 // move everything so that the boxcenter is in (0,0,0)
295 v0.x = mLeafVerts[0].x - center.x;
296 v1.x = mLeafVerts[1].x - center.x;
297 v2.x = mLeafVerts[2].x - center.x;
299 // First, test overlap in the {x,y,z}-directions
301 // find min, max of the triangle in x-direction, and test for overlap in X
302 if(FCMin3(v0.x, v1.x, v2.x)>extents.x) return FALSE;
303 if(FCMax3(v0.x, v1.x, v2.x)<-extents.x) return FALSE;
306 v0.y = mLeafVerts[0].y - center.y;
307 v1.y = mLeafVerts[1].y - center.y;
308 v2.y = mLeafVerts[2].y - center.y;
310 if(FCMin3(v0.y, v1.y, v2.y)>extents.y) return FALSE;
311 if(FCMax3(v0.y, v1.y, v2.y)<-extents.y) return FALSE;
314 v0.z = mLeafVerts[0].z - center.z;
315 v1.z = mLeafVerts[1].z - center.z;
316 v2.z = mLeafVerts[2].z - center.z;
318 if(FCMin3(v0.z, v1.z, v2.z)>extents.z) return FALSE;
319 if(FCMax3(v0.z, v1.z, v2.z)<-extents.z) return FALSE;
322 // Find min, max of the triangle in x-direction, and test for overlap in X
323 FINDMINMAX(v0.x, v1.x, v2.x, min, max);
324 if(min>extents.x || max<-extents.x) return FALSE;
327 v0.y = mLeafVerts[0].y - center.y;
328 v1.y = mLeafVerts[1].y - center.y;
329 v2.y = mLeafVerts[2].y - center.y;
331 FINDMINMAX(v0.y, v1.y, v2.y, min, max);
332 if(min>extents.y || max<-extents.y) return FALSE;
335 v0.z = mLeafVerts[0].z - center.z;
336 v1.z = mLeafVerts[1].z - center.z;
337 v2.z = mLeafVerts[2].z - center.z;
339 FINDMINMAX(v0.z, v1.z, v2.z, min, max);
340 if(min>extents.z || max<-extents.z) return FALSE;
342 // 2) Test if the box intersects the plane of the triangle
343 // compute plane equation of triangle: normal*x+d=0
344 // ### could be precomputed since we use the same leaf triangle several times
345 const Point e0 = v1 - v0;
346 const Point e1 = v2 - v1;
347 const Point normal = e0 ^ e1;
348 const float d = -normal|v0;
349 if(!planeBoxOverlap(normal, d, extents)) return FALSE;
351 // 3) "Class III" tests - here we always do full tests since the box is a primitive (not a BV)
353 IMPLEMENT_CLASS3_TESTS