1 /*************************************************************************
3 * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. *
4 * All rights reserved. Email: russ@q12.org Web: www.q12.org *
6 * This library is free software; you can redistribute it and/or *
7 * modify it under the terms of EITHER: *
8 * (1) The GNU Lesser General Public License as published by the Free *
9 * Software Foundation; either version 2.1 of the License, or (at *
10 * your option) any later version. The text of the GNU Lesser *
11 * General Public License is included with this library in the *
13 * (2) The BSD-style license that is included with this library in *
14 * the file LICENSE-BSD.TXT. *
16 * This library is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files *
19 * LICENSE.TXT and LICENSE-BSD.TXT for more details. *
21 *************************************************************************/
25 some useful collision utility stuff. this includes some API utility
26 functions that are defined in the public header files.
30 #include <ode/common.h>
31 #include <ode/collision.h>
32 #include <ode/odemath.h>
34 #include "collision_util.h"
36 //****************************************************************************
38 int dCollideSpheres (dVector3 p1
, dReal r1
,
39 dVector3 p2
, dReal r2
, dContactGeom
*c
)
41 // printf ("d=%.2f (%.2f %.2f %.2f) (%.2f %.2f %.2f) r1=%.2f r2=%.2f\n",
42 // d,p1[0],p1[1],p1[2],p2[0],p2[1],p2[2],r1,r2);
44 dReal d
= dCalcPointsDistance3(p1
,p2
);
45 if (d
> (r1
+ r2
)) return 0;
56 dReal d1
= dRecip (d
);
57 c
->normal
[0] = (p1
[0]-p2
[0])*d1
;
58 c
->normal
[1] = (p1
[1]-p2
[1])*d1
;
59 c
->normal
[2] = (p1
[2]-p2
[2])*d1
;
60 dReal k
= REAL(0.5) * (r2
- r1
- d
);
61 c
->pos
[0] = p1
[0] + c
->normal
[0]*k
;
62 c
->pos
[1] = p1
[1] + c
->normal
[1]*k
;
63 c
->pos
[2] = p1
[2] + c
->normal
[2]*k
;
64 c
->depth
= r1
+ r2
- d
;
70 void dLineClosestApproach (const dVector3 pa
, const dVector3 ua
,
71 const dVector3 pb
, const dVector3 ub
,
72 dReal
*alpha
, dReal
*beta
)
78 dReal uaub
= dCalcVectorDot3(ua
,ub
);
79 dReal q1
= dCalcVectorDot3(ua
,p
);
80 dReal q2
= -dCalcVectorDot3(ub
,p
);
81 dReal d
= 1-uaub
*uaub
;
82 if (d
<= REAL(0.0001)) {
83 // @@@ this needs to be made more robust
89 *alpha
= (q1
+ uaub
*q2
)*d
;
90 *beta
= (uaub
*q1
+ q2
)*d
;
95 // given two line segments A and B with endpoints a1-a2 and b1-b2, return the
96 // points on A and B that are closest to each other (in cp1 and cp2).
97 // in the case of parallel lines where there are multiple solutions, a
98 // solution involving the endpoint of at least one line will be returned.
99 // this will work correctly for zero length lines, e.g. if a1==a2 and/or
102 // the algorithm works by applying the voronoi clipping rule to the features
103 // of the line segments. the three features of each line segment are the two
104 // endpoints and the line between them. the voronoi clipping rule states that,
105 // for feature X on line A and feature Y on line B, the closest points PA and
106 // PB between X and Y are globally the closest points if PA is in V(Y) and
107 // PB is in V(X), where V(X) is the voronoi region of X.
109 void dClosestLineSegmentPoints (const dVector3 a1
, const dVector3 a2
,
110 const dVector3 b1
, const dVector3 b2
,
111 dVector3 cp1
, dVector3 cp2
)
113 dVector3 a1a2
,b1b2
,a1b1
,a1b2
,a2b1
,a2b2
,n
;
114 dReal la
,lb
,k
,da1
,da2
,da3
,da4
,db1
,db2
,db3
,db4
,det
;
116 #define SET2(a,b) a[0]=b[0]; a[1]=b[1]; a[2]=b[2];
117 #define SET3(a,b,op,c) a[0]=b[0] op c[0]; a[1]=b[1] op c[1]; a[2]=b[2] op c[2];
119 // check vertex-vertex features
124 da1
= dCalcVectorDot3(a1a2
,a1b1
);
125 db1
= dCalcVectorDot3(b1b2
,a1b1
);
126 if (da1
<= 0 && db1
>= 0) {
133 da2
= dCalcVectorDot3(a1a2
,a1b2
);
134 db2
= dCalcVectorDot3(b1b2
,a1b2
);
135 if (da2
<= 0 && db2
<= 0) {
142 da3
= dCalcVectorDot3(a1a2
,a2b1
);
143 db3
= dCalcVectorDot3(b1b2
,a2b1
);
144 if (da3
>= 0 && db3
>= 0) {
151 da4
= dCalcVectorDot3(a1a2
,a2b2
);
152 db4
= dCalcVectorDot3(b1b2
,a2b2
);
153 if (da4
>= 0 && db4
<= 0) {
159 // check edge-vertex features.
160 // if one or both of the lines has zero length, we will never get to here,
161 // so we do not have to worry about the following divisions by zero.
163 la
= dCalcVectorDot3(a1a2
,a1a2
);
164 if (da1
>= 0 && da3
<= 0) {
166 SET3 (n
,a1b1
,-,k
*a1a2
);
167 if (dCalcVectorDot3(b1b2
,n
) >= 0) {
168 SET3 (cp1
,a1
,+,k
*a1a2
);
174 if (da2
>= 0 && da4
<= 0) {
176 SET3 (n
,a1b2
,-,k
*a1a2
);
177 if (dCalcVectorDot3(b1b2
,n
) <= 0) {
178 SET3 (cp1
,a1
,+,k
*a1a2
);
184 lb
= dCalcVectorDot3(b1b2
,b1b2
);
185 if (db1
<= 0 && db2
>= 0) {
187 SET3 (n
,-a1b1
,-,k
*b1b2
);
188 if (dCalcVectorDot3(a1a2
,n
) >= 0) {
190 SET3 (cp2
,b1
,+,k
*b1b2
);
195 if (db3
<= 0 && db4
>= 0) {
197 SET3 (n
,-a2b1
,-,k
*b1b2
);
198 if (dCalcVectorDot3(a1a2
,n
) <= 0) {
200 SET3 (cp2
,b1
,+,k
*b1b2
);
205 // it must be edge-edge
207 k
= dCalcVectorDot3(a1a2
,b1b2
);
210 // this should never happen, but just in case...
216 dReal alpha
= (lb
*da1
- k
*db1
) * det
;
217 dReal beta
= ( k
*da1
- la
*db1
) * det
;
218 SET3 (cp1
,a1
,+,alpha
*a1a2
);
219 SET3 (cp2
,b1
,+,beta
*b1b2
);
226 // a simple root finding algorithm is used to find the value of 't' that
230 // |D(t)| = |p(t)-b(t)|
231 // where p(t) is a point on the line parameterized by t:
232 // p(t) = p1 + t*(p2-p1)
233 // and b(t) is that same point clipped to the boundary of the box. in box-
234 // relative coordinates d|D(t)|^2/dt is the sum of three x,y,z components
235 // each of which looks like this:
242 // t_lo and t_hi are the t values where the line passes through the planes
243 // corresponding to the sides of the box. the algorithm computes d|D(t)|^2/dt
244 // in a piecewise fashion from t=0 to t=1, stopping at the point where
245 // d|D(t)|^2/dt crosses from negative to positive.
247 void dClosestLineBoxPoints (const dVector3 p1
, const dVector3 p2
,
248 const dVector3 c
, const dMatrix3 R
,
250 dVector3 lret
, dVector3 bret
)
254 // compute the start and delta of the line p1-p2 relative to the box.
255 // we will do all subsequent computations in this box-relative coordinate
256 // system. we have to do a translation and rotation for each point.
258 tmp
[0] = p1
[0] - c
[0];
259 tmp
[1] = p1
[1] - c
[1];
260 tmp
[2] = p1
[2] - c
[2];
261 dMultiply1_331 (s
,R
,tmp
);
262 tmp
[0] = p2
[0] - p1
[0];
263 tmp
[1] = p2
[1] - p1
[1];
264 tmp
[2] = p2
[2] - p1
[2];
265 dMultiply1_331 (v
,R
,tmp
);
267 // mirror the line so that v has all components >= 0
269 for (i
=0; i
<3; i
++) {
284 // compute the half-sides of the box
286 h
[0] = REAL(0.5) * side
[0];
287 h
[1] = REAL(0.5) * side
[1];
288 h
[2] = REAL(0.5) * side
[2];
290 // region is -1,0,+1 depending on which side of the box planes each
291 // coordinate is on. tanchor is the next t value at which there is a
292 // transition, or the last one if there are no more.
296 // Denormals are a problem, because we divide by v[i], and then
297 // multiply that by 0. Alas, infinity times 0 is infinity (!)
298 // We also use v2[i], which is v[i] squared. Here's how the epsilons
300 // float epsilon = 1.175494e-038 (smallest non-denormal number)
301 // double epsilon = 2.225074e-308 (smallest non-denormal number)
302 // For single precision, choose an epsilon such that v[i] squared is
303 // not a denormal; this is for performance.
304 // For double precision, choose an epsilon such that v[i] is not a
305 // denormal; this is for correctness. (Jon Watte on mailinglist)
307 #if defined( dSINGLE )
308 const dReal tanchor_eps
= REAL(1e-19);
310 const dReal tanchor_eps
= REAL(1e-307);
313 // find the region and tanchor values for p1
314 for (i
=0; i
<3; i
++) {
315 if (v
[i
] > tanchor_eps
) {
318 tanchor
[i
] = (-h
[i
]-s
[i
])/v
[i
];
321 region
[i
] = (s
[i
] > h
[i
]);
322 tanchor
[i
] = (h
[i
]-s
[i
])/v
[i
];
327 tanchor
[i
] = 2; // this will never be a valid tanchor
331 // compute d|d|^2/dt for t=0. if it's >= 0 then p1 is the closest point
334 for (i
=0; i
<3; i
++) dd2dt
-= (region
[i
] ? v2
[i
] : 0) * tanchor
[i
];
335 if (dd2dt
>= 0) goto got_answer
;
338 // find the point on the line that is at the next clip plane boundary
340 for (i
=0; i
<3; i
++) {
341 if (tanchor
[i
] > t
&& tanchor
[i
] < 1 && tanchor
[i
] < next_t
)
345 // compute d|d|^2/dt for the next t
346 dReal next_dd2dt
= 0;
347 for (i
=0; i
<3; i
++) {
348 next_dd2dt
+= (region
[i
] ? v2
[i
] : 0) * (next_t
- tanchor
[i
]);
351 // if the sign of d|d|^2/dt has changed, solution = the crossover point
352 if (next_dd2dt
>= 0) {
353 dReal m
= (next_dd2dt
-dd2dt
)/(next_t
- t
);
358 // advance to the next anchor point / region
359 for (i
=0; i
<3; i
++) {
360 if (tanchor
[i
] == next_t
) {
361 tanchor
[i
] = (h
[i
]-s
[i
])/v
[i
];
373 // compute closest point on the line
374 for (i
=0; i
<3; i
++) lret
[i
] = p1
[i
] + t
*tmp
[i
]; // note: tmp=p2-p1
376 // compute closest point on the box
377 for (i
=0; i
<3; i
++) {
378 tmp
[i
] = sign
[i
] * (s
[i
] + t
*v
[i
]);
379 if (tmp
[i
] < -h
[i
]) tmp
[i
] = -h
[i
];
380 else if (tmp
[i
] > h
[i
]) tmp
[i
] = h
[i
];
382 dMultiply0_331 (s
,R
,tmp
);
383 for (i
=0; i
<3; i
++) bret
[i
] = s
[i
] + c
[i
];
387 // given boxes (p1,R1,side1) and (p1,R1,side1), return 1 if they intersect
390 int dBoxTouchesBox (const dVector3 p1
, const dMatrix3 R1
,
391 const dVector3 side1
, const dVector3 p2
,
392 const dMatrix3 R2
, const dVector3 side2
)
394 // two boxes are disjoint if (and only if) there is a separating axis
395 // perpendicular to a face from one box or perpendicular to an edge from
396 // either box. the following tests are derived from:
397 // "OBB Tree: A Hierarchical Structure for Rapid Interference Detection",
398 // S.Gottschalk, M.C.Lin, D.Manocha., Proc of ACM Siggraph 1996.
400 // Rij is R1'*R2, i.e. the relative rotation between R1 and R2.
403 dReal A1
,A2
,A3
,B1
,B2
,B3
,R11
,R12
,R13
,R21
,R22
,R23
,R31
,R32
,R33
,
404 Q11
,Q12
,Q13
,Q21
,Q22
,Q23
,Q31
,Q32
,Q33
;
406 // get vector from centers of box 1 to box 2, relative to box 1
407 p
[0] = p2
[0] - p1
[0];
408 p
[1] = p2
[1] - p1
[1];
409 p
[2] = p2
[2] - p1
[2];
410 dMultiply1_331 (pp
,R1
,p
); // get pp = p relative to body 1
412 // get side lengths / 2
413 A1
= side1
[0]*REAL(0.5); A2
= side1
[1]*REAL(0.5); A3
= side1
[2]*REAL(0.5);
414 B1
= side2
[0]*REAL(0.5); B2
= side2
[1]*REAL(0.5); B3
= side2
[2]*REAL(0.5);
416 // for the following tests, excluding computation of Rij, in the worst case,
417 // 15 compares, 60 adds, 81 multiplies, and 24 absolutes.
418 // notation: R1=[u1 u2 u3], R2=[v1 v2 v3]
420 // separating axis = u1,u2,u3
421 R11
= dCalcVectorDot3_44(R1
+0,R2
+0); R12
= dCalcVectorDot3_44(R1
+0,R2
+1); R13
= dCalcVectorDot3_44(R1
+0,R2
+2);
422 Q11
= dFabs(R11
); Q12
= dFabs(R12
); Q13
= dFabs(R13
);
423 if (dFabs(pp
[0]) > (A1
+ B1
*Q11
+ B2
*Q12
+ B3
*Q13
)) return 0;
424 R21
= dCalcVectorDot3_44(R1
+1,R2
+0); R22
= dCalcVectorDot3_44(R1
+1,R2
+1); R23
= dCalcVectorDot3_44(R1
+1,R2
+2);
425 Q21
= dFabs(R21
); Q22
= dFabs(R22
); Q23
= dFabs(R23
);
426 if (dFabs(pp
[1]) > (A2
+ B1
*Q21
+ B2
*Q22
+ B3
*Q23
)) return 0;
427 R31
= dCalcVectorDot3_44(R1
+2,R2
+0); R32
= dCalcVectorDot3_44(R1
+2,R2
+1); R33
= dCalcVectorDot3_44(R1
+2,R2
+2);
428 Q31
= dFabs(R31
); Q32
= dFabs(R32
); Q33
= dFabs(R33
);
429 if (dFabs(pp
[2]) > (A3
+ B1
*Q31
+ B2
*Q32
+ B3
*Q33
)) return 0;
431 // separating axis = v1,v2,v3
432 if (dFabs(dCalcVectorDot3_41(R2
+0,p
)) > (A1
*Q11
+ A2
*Q21
+ A3
*Q31
+ B1
)) return 0;
433 if (dFabs(dCalcVectorDot3_41(R2
+1,p
)) > (A1
*Q12
+ A2
*Q22
+ A3
*Q32
+ B2
)) return 0;
434 if (dFabs(dCalcVectorDot3_41(R2
+2,p
)) > (A1
*Q13
+ A2
*Q23
+ A3
*Q33
+ B3
)) return 0;
436 // separating axis = u1 x (v1,v2,v3)
437 if (dFabs(pp
[2]*R21
-pp
[1]*R31
) > A2
*Q31
+ A3
*Q21
+ B2
*Q13
+ B3
*Q12
) return 0;
438 if (dFabs(pp
[2]*R22
-pp
[1]*R32
) > A2
*Q32
+ A3
*Q22
+ B1
*Q13
+ B3
*Q11
) return 0;
439 if (dFabs(pp
[2]*R23
-pp
[1]*R33
) > A2
*Q33
+ A3
*Q23
+ B1
*Q12
+ B2
*Q11
) return 0;
441 // separating axis = u2 x (v1,v2,v3)
442 if (dFabs(pp
[0]*R31
-pp
[2]*R11
) > A1
*Q31
+ A3
*Q11
+ B2
*Q23
+ B3
*Q22
) return 0;
443 if (dFabs(pp
[0]*R32
-pp
[2]*R12
) > A1
*Q32
+ A3
*Q12
+ B1
*Q23
+ B3
*Q21
) return 0;
444 if (dFabs(pp
[0]*R33
-pp
[2]*R13
) > A1
*Q33
+ A3
*Q13
+ B1
*Q22
+ B2
*Q21
) return 0;
446 // separating axis = u3 x (v1,v2,v3)
447 if (dFabs(pp
[1]*R11
-pp
[0]*R21
) > A1
*Q21
+ A2
*Q11
+ B2
*Q33
+ B3
*Q32
) return 0;
448 if (dFabs(pp
[1]*R12
-pp
[0]*R22
) > A1
*Q22
+ A2
*Q12
+ B1
*Q33
+ B3
*Q31
) return 0;
449 if (dFabs(pp
[1]*R13
-pp
[0]*R23
) > A1
*Q23
+ A2
*Q13
+ B1
*Q32
+ B2
*Q31
) return 0;
454 //****************************************************************************
455 // other utility functions
457 void dInfiniteAABB (dxGeom
*geom
, dReal aabb
[6])
459 aabb
[0] = -dInfinity
;
461 aabb
[2] = -dInfinity
;
463 aabb
[4] = -dInfinity
;
468 //****************************************************************************
469 // Helpers for Croteam's collider - by Nguyen Binh
471 int dClipEdgeToPlane( dVector3
&vEpnt0
, dVector3
&vEpnt1
, const dVector4
& plPlane
)
473 // calculate distance of edge points to plane
474 dReal fDistance0
= dPointPlaneDistance( vEpnt0
,plPlane
);
475 dReal fDistance1
= dPointPlaneDistance( vEpnt1
,plPlane
);
477 // if both points are behind the plane
478 if ( fDistance0
< 0 && fDistance1
< 0 )
482 // if both points in front of the plane
484 else if ( fDistance0
> 0 && fDistance1
> 0 )
488 // if we have edge/plane intersection
489 } else if ((fDistance0
> 0 && fDistance1
< 0) || ( fDistance0
< 0 && fDistance1
> 0))
492 // find intersection point of edge and plane
493 dVector3 vIntersectionPoint
;
494 vIntersectionPoint
[0]= vEpnt0
[0]-(vEpnt0
[0]-vEpnt1
[0])*fDistance0
/(fDistance0
-fDistance1
);
495 vIntersectionPoint
[1]= vEpnt0
[1]-(vEpnt0
[1]-vEpnt1
[1])*fDistance0
/(fDistance0
-fDistance1
);
496 vIntersectionPoint
[2]= vEpnt0
[2]-(vEpnt0
[2]-vEpnt1
[2])*fDistance0
/(fDistance0
-fDistance1
);
498 // clamp correct edge to intersection point
499 if ( fDistance0
< 0 )
501 dVector3Copy(vIntersectionPoint
,vEpnt0
);
504 dVector3Copy(vIntersectionPoint
,vEpnt1
);
511 // clip polygon with plane and generate new polygon points
512 void dClipPolyToPlane( const dVector3 avArrayIn
[], const int ctIn
,
513 dVector3 avArrayOut
[], int &ctOut
,
514 const dVector4
&plPlane
)
516 // start with no output points
521 // for each edge in input polygon
522 for (int i1
=0; i1
<ctIn
; i0
=i1
, i1
++) {
525 // calculate distance of edge points to plane
526 dReal fDistance0
= dPointPlaneDistance( avArrayIn
[i0
],plPlane
);
527 dReal fDistance1
= dPointPlaneDistance( avArrayIn
[i1
],plPlane
);
529 // if first point is in front of plane
530 if( fDistance0
>= 0 ) {
532 avArrayOut
[ctOut
][0] = avArrayIn
[i0
][0];
533 avArrayOut
[ctOut
][1] = avArrayIn
[i0
][1];
534 avArrayOut
[ctOut
][2] = avArrayIn
[i0
][2];
538 // if points are on different sides
539 if( (fDistance0
> 0 && fDistance1
< 0) || ( fDistance0
< 0 && fDistance1
> 0) ) {
541 // find intersection point of edge and plane
542 dVector3 vIntersectionPoint
;
543 vIntersectionPoint
[0]= avArrayIn
[i0
][0] -
544 (avArrayIn
[i0
][0]-avArrayIn
[i1
][0])*fDistance0
/(fDistance0
-fDistance1
);
545 vIntersectionPoint
[1]= avArrayIn
[i0
][1] -
546 (avArrayIn
[i0
][1]-avArrayIn
[i1
][1])*fDistance0
/(fDistance0
-fDistance1
);
547 vIntersectionPoint
[2]= avArrayIn
[i0
][2] -
548 (avArrayIn
[i0
][2]-avArrayIn
[i1
][2])*fDistance0
/(fDistance0
-fDistance1
);
550 // emit intersection point
551 avArrayOut
[ctOut
][0] = vIntersectionPoint
[0];
552 avArrayOut
[ctOut
][1] = vIntersectionPoint
[1];
553 avArrayOut
[ctOut
][2] = vIntersectionPoint
[2];
560 void dClipPolyToCircle(const dVector3 avArrayIn
[], const int ctIn
,
561 dVector3 avArrayOut
[], int &ctOut
,
562 const dVector4
&plPlane
,dReal fRadius
)
564 // start with no output points
569 // for each edge in input polygon
570 for (int i1
=0; i1
<ctIn
; i0
=i1
, i1
++)
572 // calculate distance of edge points to plane
573 dReal fDistance0
= dPointPlaneDistance( avArrayIn
[i0
],plPlane
);
574 dReal fDistance1
= dPointPlaneDistance( avArrayIn
[i1
],plPlane
);
576 // if first point is in front of plane
577 if( fDistance0
>= 0 )
580 if (dVector3LengthSquare(avArrayIn
[i0
]) <= fRadius
*fRadius
)
582 avArrayOut
[ctOut
][0] = avArrayIn
[i0
][0];
583 avArrayOut
[ctOut
][1] = avArrayIn
[i0
][1];
584 avArrayOut
[ctOut
][2] = avArrayIn
[i0
][2];
589 // if points are on different sides
590 if( (fDistance0
> 0 && fDistance1
< 0) || ( fDistance0
< 0 && fDistance1
> 0) )
593 // find intersection point of edge and plane
594 dVector3 vIntersectionPoint
;
595 vIntersectionPoint
[0]= avArrayIn
[i0
][0] -
596 (avArrayIn
[i0
][0]-avArrayIn
[i1
][0])*fDistance0
/(fDistance0
-fDistance1
);
597 vIntersectionPoint
[1]= avArrayIn
[i0
][1] -
598 (avArrayIn
[i0
][1]-avArrayIn
[i1
][1])*fDistance0
/(fDistance0
-fDistance1
);
599 vIntersectionPoint
[2]= avArrayIn
[i0
][2] -
600 (avArrayIn
[i0
][2]-avArrayIn
[i1
][2])*fDistance0
/(fDistance0
-fDistance1
);
602 // emit intersection point
603 if (dVector3LengthSquare(avArrayIn
[i0
]) <= fRadius
*fRadius
)
605 avArrayOut
[ctOut
][0] = vIntersectionPoint
[0];
606 avArrayOut
[ctOut
][1] = vIntersectionPoint
[1];
607 avArrayOut
[ctOut
][2] = vIntersectionPoint
[2];