diff options
Diffstat (limited to 'extern/ode/dist/ode/src/geom.cpp')
-rw-r--r-- | extern/ode/dist/ode/src/geom.cpp | 2207 |
1 files changed, 0 insertions, 2207 deletions
diff --git a/extern/ode/dist/ode/src/geom.cpp b/extern/ode/dist/ode/src/geom.cpp deleted file mode 100644 index 1818814a791..00000000000 --- a/extern/ode/dist/ode/src/geom.cpp +++ /dev/null @@ -1,2207 +0,0 @@ -/************************************************************************* - * * - * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * - * All rights reserved. Email: russ@q12.org Web: www.q12.org * - * * - * This library is free software; you can redistribute it and/or * - * modify it under the terms of EITHER: * - * (1) The GNU Lesser General Public License as published by the Free * - * Software Foundation; either version 2.1 of the License, or (at * - * your option) any later version. The text of the GNU Lesser * - * General Public License is included with this library in the * - * file LICENSE.TXT. * - * (2) The BSD-style license that is included with this library in * - * the file LICENSE-BSD.TXT. * - * * - * This library is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files * - * LICENSE.TXT and LICENSE-BSD.TXT for more details. * - * * - *************************************************************************/ - -/* - -the rule is that only the low level primitive collision functions should set -dContactGeom::g1 and dContactGeom::g2. - -*/ - -#define SHARED_GEOM_H_INCLUDED_FROM_DEFINING_FILE 1 -#include <ode/common.h> -#include <ode/geom.h> -#include <ode/rotation.h> -#include <ode/odemath.h> -#include <ode/memory.h> -#include <ode/misc.h> -#include <ode/objects.h> -#include <ode/matrix.h> -#include "objects.h" -#include "array.h" -#include "geom_internal.h" - -//**************************************************************************** -// collision utilities. - -// given a pointer `p' to a dContactGeom, return the dContactGeom at -// p + skip bytes. - -#define CONTACT(p,skip) ((dContactGeom*) (((char*)p) + (skip))) - - -// if the spheres (p1,r1) and (p2,r2) collide, set the contact `c' and -// return 1, else return 0. - -static int dCollideSpheres (dVector3 p1, dReal r1, - dVector3 p2, dReal r2, dContactGeom *c) -{ - // printf ("d=%.2f (%.2f %.2f %.2f) (%.2f %.2f %.2f) r1=%.2f r2=%.2f\n", - // d,p1[0],p1[1],p1[2],p2[0],p2[1],p2[2],r1,r2); - - dReal d = dDISTANCE (p1,p2); - if (d > (r1 + r2)) return 0; - if (d <= 0) { - c->pos[0] = p1[0]; - c->pos[1] = p1[1]; - c->pos[2] = p1[2]; - c->normal[0] = 1; - c->normal[1] = 0; - c->normal[2] = 0; - c->depth = r1 + r2; - } - else { - dReal d1 = dRecip (d); - c->normal[0] = (p1[0]-p2[0])*d1; - c->normal[1] = (p1[1]-p2[1])*d1; - c->normal[2] = (p1[2]-p2[2])*d1; - dReal k = REAL(0.5) * (r2 - r1 - d); - c->pos[0] = p1[0] + c->normal[0]*k; - c->pos[1] = p1[1] + c->normal[1]*k; - c->pos[2] = p1[2] + c->normal[2]*k; - c->depth = r1 + r2 - d; - } - return 1; -} - - -// given two lines -// qa = pa + alpha* ua -// qb = pb + beta * ub -// where pa,pb are two points, ua,ub are two unit length vectors, and alpha, -// beta go from [-inf,inf], return alpha and beta such that qa and qb are -// as close as possible - -static void lineClosestApproach (const dVector3 pa, const dVector3 ua, - const dVector3 pb, const dVector3 ub, - dReal *alpha, dReal *beta) -{ - dVector3 p; - p[0] = pb[0] - pa[0]; - p[1] = pb[1] - pa[1]; - p[2] = pb[2] - pa[2]; - dReal uaub = dDOT(ua,ub); - dReal q1 = dDOT(ua,p); - dReal q2 = -dDOT(ub,p); - dReal d = 1-uaub*uaub; - if (d <= 0) { - // @@@ this needs to be made more robust - *alpha = 0; - *beta = 0; - } - else { - d = dRecip(d); - *alpha = (q1 + uaub*q2)*d; - *beta = (uaub*q1 + q2)*d; - } -} - - -// given two line segments A and B with endpoints a1-a2 and b1-b2, return the -// points on A and B that are closest to each other (in cp1 and cp2). -// in the case of parallel lines where there are multiple solutions, a -// solution involving the endpoint of at least one line will be returned. -// this will work correctly for zero length lines, e.g. if a1==a2 and/or -// b1==b2. -// -// the algorithm works by applying the voronoi clipping rule to the features -// of the line segments. the three features of each line segment are the two -// endpoints and the line between them. the voronoi clipping rule states that, -// for feature X on line A and feature Y on line B, the closest points PA and -// PB between X and Y are globally the closest points if PA is in V(Y) and -// PB is in V(X), where V(X) is the voronoi region of X. - - -void dClosestLineSegmentPoints (dVector3 const a1, dVector3 const a2, - dVector3 const b1, dVector3 const b2, - dVector3 cp1, dVector3 cp2) -{ - dVector3 a1a2,b1b2,a1b1,a1b2,a2b1,a2b2,n; - dReal la,lb,k,da1,da2,da3,da4,db1,db2,db3,db4,det; - -#define SET2(a,b) a[0]=b[0]; a[1]=b[1]; a[2]=b[2]; -#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]; - - // check vertex-vertex features - - SET3 (a1a2,a2,-,a1); - SET3 (b1b2,b2,-,b1); - SET3 (a1b1,b1,-,a1); - da1 = dDOT(a1a2,a1b1); - db1 = dDOT(b1b2,a1b1); - if (da1 <= 0 && db1 >= 0) { - SET2 (cp1,a1); - SET2 (cp2,b1); - return; - } - - SET3 (a1b2,b2,-,a1); - da2 = dDOT(a1a2,a1b2); - db2 = dDOT(b1b2,a1b2); - if (da2 <= 0 && db2 <= 0) { - SET2 (cp1,a1); - SET2 (cp2,b2); - return; - } - - SET3 (a2b1,b1,-,a2); - da3 = dDOT(a1a2,a2b1); - db3 = dDOT(b1b2,a2b1); - if (da3 >= 0 && db3 >= 0) { - SET2 (cp1,a2); - SET2 (cp2,b1); - return; - } - - SET3 (a2b2,b2,-,a2); - da4 = dDOT(a1a2,a2b2); - db4 = dDOT(b1b2,a2b2); - if (da4 >= 0 && db4 <= 0) { - SET2 (cp1,a2); - SET2 (cp2,b2); - return; - } - - // check edge-vertex features. - // if one or both of the lines has zero length, we will never get to here, - // so we do not have to worry about the following divisions by zero. - - la = dDOT(a1a2,a1a2); - if (da1 >= 0 && da3 <= 0) { - k = da1 / la; - SET3 (n,a1b1,-,k*a1a2); - if (dDOT(b1b2,n) >= 0) { - SET3 (cp1,a1,+,k*a1a2); - SET2 (cp2,b1); - return; - } - } - - if (da2 >= 0 && da4 <= 0) { - k = da2 / la; - SET3 (n,a1b2,-,k*a1a2); - if (dDOT(b1b2,n) <= 0) { - SET3 (cp1,a1,+,k*a1a2); - SET2 (cp2,b2); - return; - } - } - - lb = dDOT(b1b2,b1b2); - if (db1 <= 0 && db2 >= 0) { - k = -db1 / lb; - SET3 (n,-a1b1,-,k*b1b2); - if (dDOT(a1a2,n) >= 0) { - SET2 (cp1,a1); - SET3 (cp2,b1,+,k*b1b2); - return; - } - } - - if (db3 <= 0 && db4 >= 0) { - k = -db3 / lb; - SET3 (n,-a2b1,-,k*b1b2); - if (dDOT(a1a2,n) <= 0) { - SET2 (cp1,a2); - SET3 (cp2,b1,+,k*b1b2); - return; - } - } - - // it must be edge-edge - - k = dDOT(a1a2,b1b2); - det = la*lb - k*k; - if (det <= 0) { - // this should never happen, but just in case... - SET2(cp1,a1); - SET2(cp2,b1); - return; - } - det = dRecip (det); - dReal alpha = (lb*da1 - k*db1) * det; - dReal beta = ( k*da1 - la*db1) * det; - SET3 (cp1,a1,+,alpha*a1a2); - SET3 (cp2,b1,+,beta*b1b2); - -# undef SET2 -# undef SET3 -} - - -// given a line segment p1-p2 and a box (center 'c', rotation 'R', side length -// vector 'side'), compute the points of closest approach between the box -// and the line. return these points in 'lret' (the point on the line) and -// 'bret' (the point on the box). if the line actually penetrates the box -// then the solution is not unique, but only one solution will be returned. -// in this case the solution points will coincide. -// -// a simple root finding algorithm is used to find the value of 't' that -// satisfies: -// d|D(t)|^2/dt = 0 -// where: -// |D(t)| = |p(t)-b(t)| -// where p(t) is a point on the line parameterized by t: -// p(t) = p1 + t*(p2-p1) -// and b(t) is that same point clipped to the boundary of the box. in box- -// relative coordinates d|D(t)|^2/dt is the sum of three x,y,z components -// each of which looks like this: -// -// t_lo / -// ______/ -->t -// / t_hi -// / -// -// t_lo and t_hi are the t values where the line passes through the planes -// corresponding to the sides of the box. the algorithm computes d|D(t)|^2/dt -// in a piecewise fashion from t=0 to t=1, stopping at the point where -// d|D(t)|^2/dt crosses from negative to positive. - -static void dClosestLineBoxPoints (const dVector3 p1, const dVector3 p2, - const dVector3 c, const dMatrix3 R, - const dVector3 side, - dVector3 lret, dVector3 bret) -{ - int i; - - // compute the start and delta of the line p1-p2 relative to the box. - // we will do all subsequent computations in this box-relative coordinate - // system. we have to do a translation and rotation for each point. - dVector3 tmp,s,v; - tmp[0] = p1[0] - c[0]; - tmp[1] = p1[1] - c[1]; - tmp[2] = p1[2] - c[2]; - dMULTIPLY1_331 (s,R,tmp); - tmp[0] = p2[0] - p1[0]; - tmp[1] = p2[1] - p1[1]; - tmp[2] = p2[2] - p1[2]; - dMULTIPLY1_331 (v,R,tmp); - - // mirror the line so that v has all components >= 0 - dVector3 sign; - for (i=0; i<3; i++) { - if (v[i] < 0) { - s[i] = -s[i]; - v[i] = -v[i]; - sign[i] = -1; - } - else sign[i] = 1; - } - - // compute v^2 - dVector3 v2; - v2[0] = v[0]*v[0]; - v2[1] = v[1]*v[1]; - v2[2] = v[2]*v[2]; - - // compute the half-sides of the box - dReal h[3]; - h[0] = REAL(0.5) * side[0]; - h[1] = REAL(0.5) * side[1]; - h[2] = REAL(0.5) * side[2]; - - // region is -1,0,+1 depending on which side of the box planes each - // coordinate is on. tanchor in the next t value at which there is a - // transition, or the last one if there are no more. - int region[3]; - dReal tanchor[3]; - - // find the region and tanchor values for p1 - for (i=0; i<3; i++) { - if (v[i] > 0) { - if (s[i] < -h[i]) { - region[i] = -1; - tanchor[i] = (-h[i]-s[i])/v[i]; - } - else { - region[i] = (s[i] > h[i]); - tanchor[i] = (h[i]-s[i])/v[i]; - } - } - else { - region[i] = 0; - tanchor[i] = 2; // this will never be a valid tanchor - } - } - - // compute d|d|^2/dt for t=0. if it's >= 0 then p1 is the closest point - dReal t=0; - dReal dd2dt = 0; - for (i=0; i<3; i++) dd2dt -= (region[i] ? v2[i] : 0) * tanchor[i]; - if (dd2dt >= 0) goto got_answer; - - do { - // find the point on the line that is at the next clip plane boundary - dReal next_t = 1; - for (i=0; i<3; i++) { - if (tanchor[i] > t && tanchor[i] < 1 && tanchor[i] < next_t) - next_t = tanchor[i]; - } - - // compute d|d|^2/dt for the next t - dReal next_dd2dt = 0; - for (i=0; i<3; i++) { - next_dd2dt += (region[i] ? v2[i] : 0) * (next_t - tanchor[i]); - } - - // if the sign of d|d|^2/dt has changed, solution = the crossover point - if (next_dd2dt >= 0) { - dReal m = (next_dd2dt-dd2dt)/(next_t - t); - t -= dd2dt/m; - goto got_answer; - } - - // advance to the next anchor point / region - for (i=0; i<3; i++) { - if (tanchor[i] == next_t) { - tanchor[i] = (h[i]-s[i])/v[i]; - region[i]++; - } - } - t = next_t; - dd2dt = next_dd2dt; - } - while (t < 1); - t = 1; - - got_answer: - - // compute closest point on the line - for (i=0; i<3; i++) lret[i] = p1[i] + t*tmp[i]; // note: tmp=p2-p1 - - // compute closest point on the box - for (i=0; i<3; i++) { - tmp[i] = sign[i] * (s[i] + t*v[i]); - if (tmp[i] < -h[i]) tmp[i] = -h[i]; - else if (tmp[i] > h[i]) tmp[i] = h[i]; - } - dMULTIPLY0_331 (s,R,tmp); - for (i=0; i<3; i++) bret[i] = s[i] + c[i]; -} - - -// given a box (R,side), `R' is the rotation matrix for the box, and `side' -// is a vector of x/y/z side lengths, return the size of the interval of the -// box projected along the given axis. if the axis has unit length then the -// return value will be the actual diameter, otherwise the result will be -// scaled by the axis length. - -static inline dReal boxDiameter (const dMatrix3 R, const dVector3 side, - const dVector3 axis) -{ - dVector3 q; - dMULTIPLY1_331 (q,R,axis); // transform axis to body-relative - return dFabs(q[0])*side[0] + dFabs(q[1])*side[1] + dFabs(q[2])*side[2]; -} - - -// given boxes (p1,R1,side1) and (p1,R1,side1), return 1 if they intersect -// or 0 if not. - -int dBoxTouchesBox (const dVector3 p1, const dMatrix3 R1, - const dVector3 side1, const dVector3 p2, - const dMatrix3 R2, const dVector3 side2) -{ - // two boxes are disjoint if (and only if) there is a separating axis - // perpendicular to a face from one box or perpendicular to an edge from - // either box. the following tests are derived from: - // "OBB Tree: A Hierarchical Structure for Rapid Interference Detection", - // S.Gottschalk, M.C.Lin, D.Manocha., Proc of ACM Siggraph 1996. - - // Rij is R1'*R2, i.e. the relative rotation between R1 and R2. - // Qij is abs(Rij) - dVector3 p,pp; - dReal A1,A2,A3,B1,B2,B3,R11,R12,R13,R21,R22,R23,R31,R32,R33, - Q11,Q12,Q13,Q21,Q22,Q23,Q31,Q32,Q33; - - // get vector from centers of box 1 to box 2, relative to box 1 - p[0] = p2[0] - p1[0]; - p[1] = p2[1] - p1[1]; - p[2] = p2[2] - p1[2]; - dMULTIPLY1_331 (pp,R1,p); // get pp = p relative to body 1 - - // get side lengths / 2 - A1 = side1[0]*REAL(0.5); A2 = side1[1]*REAL(0.5); A3 = side1[2]*REAL(0.5); - B1 = side2[0]*REAL(0.5); B2 = side2[1]*REAL(0.5); B3 = side2[2]*REAL(0.5); - - // for the following tests, excluding computation of Rij, in the worst case, - // 15 compares, 60 adds, 81 multiplies, and 24 absolutes. - // notation: R1=[u1 u2 u3], R2=[v1 v2 v3] - - // separating axis = u1,u2,u3 - R11 = dDOT44(R1+0,R2+0); R12 = dDOT44(R1+0,R2+1); R13 = dDOT44(R1+0,R2+2); - Q11 = dFabs(R11); Q12 = dFabs(R12); Q13 = dFabs(R13); - if (dFabs(pp[0]) > (A1 + B1*Q11 + B2*Q12 + B3*Q13)) return 0; - R21 = dDOT44(R1+1,R2+0); R22 = dDOT44(R1+1,R2+1); R23 = dDOT44(R1+1,R2+2); - Q21 = dFabs(R21); Q22 = dFabs(R22); Q23 = dFabs(R23); - if (dFabs(pp[1]) > (A2 + B1*Q21 + B2*Q22 + B3*Q23)) return 0; - R31 = dDOT44(R1+2,R2+0); R32 = dDOT44(R1+2,R2+1); R33 = dDOT44(R1+2,R2+2); - Q31 = dFabs(R31); Q32 = dFabs(R32); Q33 = dFabs(R33); - if (dFabs(pp[2]) > (A3 + B1*Q31 + B2*Q32 + B3*Q33)) return 0; - - // separating axis = v1,v2,v3 - if (dFabs(dDOT41(R2+0,p)) > (A1*Q11 + A2*Q21 + A3*Q31 + B1)) return 0; - if (dFabs(dDOT41(R2+1,p)) > (A1*Q12 + A2*Q22 + A3*Q32 + B2)) return 0; - if (dFabs(dDOT41(R2+2,p)) > (A1*Q13 + A2*Q23 + A3*Q33 + B3)) return 0; - - // separating axis = u1 x (v1,v2,v3) - if (dFabs(pp[2]*R21-pp[1]*R31) > A2*Q31 + A3*Q21 + B2*Q13 + B3*Q12) return 0; - if (dFabs(pp[2]*R22-pp[1]*R32) > A2*Q32 + A3*Q22 + B1*Q13 + B3*Q11) return 0; - if (dFabs(pp[2]*R23-pp[1]*R33) > A2*Q33 + A3*Q23 + B1*Q12 + B2*Q11) return 0; - - // separating axis = u2 x (v1,v2,v3) - if (dFabs(pp[0]*R31-pp[2]*R11) > A1*Q31 + A3*Q11 + B2*Q23 + B3*Q22) return 0; - if (dFabs(pp[0]*R32-pp[2]*R12) > A1*Q32 + A3*Q12 + B1*Q23 + B3*Q21) return 0; - if (dFabs(pp[0]*R33-pp[2]*R13) > A1*Q33 + A3*Q13 + B1*Q22 + B2*Q21) return 0; - - // separating axis = u3 x (v1,v2,v3) - if (dFabs(pp[1]*R11-pp[0]*R21) > A1*Q21 + A2*Q11 + B2*Q33 + B3*Q32) return 0; - if (dFabs(pp[1]*R12-pp[0]*R22) > A1*Q22 + A2*Q12 + B1*Q33 + B3*Q31) return 0; - if (dFabs(pp[1]*R13-pp[0]*R23) > A1*Q23 + A2*Q13 + B1*Q32 + B2*Q31) return 0; - - return 1; -} - - -// given two boxes (p1,R1,side1) and (p2,R2,side2), collide them together and -// generate contact points. this returns 0 if there is no contact otherwise -// it returns the number of contacts generated. -// `normal' returns the contact normal. -// `depth' returns the maximum penetration depth along that normal. -// `code' returns a number indicating the type of contact that was detected: -// 1,2,3 = box 2 intersects with a face of box 1 -// 4,5,6 = box 1 intersects with a face of box 2 -// 7..15 = edge-edge contact -// `maxc' is the maximum number of contacts allowed to be generated, i.e. -// the size of the `contact' array. -// `contact' and `skip' are the contact array information provided to the -// collision functions. this function only fills in the position and depth -// fields. -// -// @@@ some stuff to optimize here, reuse code in contact point calculations. - -extern "C" int dBoxBox (const dVector3 p1, const dMatrix3 R1, - const dVector3 side1, const dVector3 p2, - const dMatrix3 R2, const dVector3 side2, - dVector3 normal, dReal *depth, int *code, - int maxc, dContactGeom *contact, int skip) -{ - dVector3 p,pp,normalC; - const dReal *normalR = 0; - dReal A1,A2,A3,B1,B2,B3,R11,R12,R13,R21,R22,R23,R31,R32,R33, - Q11,Q12,Q13,Q21,Q22,Q23,Q31,Q32,Q33,s,s2,l; - int i,invert_normal; - - // get vector from centers of box 1 to box 2, relative to box 1 - p[0] = p2[0] - p1[0]; - p[1] = p2[1] - p1[1]; - p[2] = p2[2] - p1[2]; - dMULTIPLY1_331 (pp,R1,p); // get pp = p relative to body 1 - - // get side lengths / 2 - A1 = side1[0]*REAL(0.5); A2 = side1[1]*REAL(0.5); A3 = side1[2]*REAL(0.5); - B1 = side2[0]*REAL(0.5); B2 = side2[1]*REAL(0.5); B3 = side2[2]*REAL(0.5); - - // Rij is R1'*R2, i.e. the relative rotation between R1 and R2 - R11 = dDOT44(R1+0,R2+0); R12 = dDOT44(R1+0,R2+1); R13 = dDOT44(R1+0,R2+2); - R21 = dDOT44(R1+1,R2+0); R22 = dDOT44(R1+1,R2+1); R23 = dDOT44(R1+1,R2+2); - R31 = dDOT44(R1+2,R2+0); R32 = dDOT44(R1+2,R2+1); R33 = dDOT44(R1+2,R2+2); - - Q11 = dFabs(R11); Q12 = dFabs(R12); Q13 = dFabs(R13); - Q21 = dFabs(R21); Q22 = dFabs(R22); Q23 = dFabs(R23); - Q31 = dFabs(R31); Q32 = dFabs(R32); Q33 = dFabs(R33); - - // for all 15 possible separating axes: - // * see if the axis separates the boxes. if so, return 0. - // * find the depth of the penetration along the separating axis (s2) - // * if this is the largest depth so far, record it. - // the normal vector will be set to the separating axis with the smallest - // depth. note: normalR is set to point to a column of R1 or R2 if that is - // the smallest depth normal so far. otherwise normalR is 0 and normalC is - // set to a vector relative to body 1. invert_normal is 1 if the sign of - // the normal should be flipped. - -#define TEST(expr1,expr2,norm,cc) \ - s2 = dFabs(expr1) - (expr2); \ - if (s2 > 0) return 0; \ - if (s2 > s) { \ - s = s2; \ - normalR = norm; \ - invert_normal = ((expr1) < 0); \ - *code = (cc); \ - } - - s = -dInfinity; - invert_normal = 0; - *code = 0; - - // separating axis = u1,u2,u3 - TEST (pp[0],(A1 + B1*Q11 + B2*Q12 + B3*Q13),R1+0,1); - TEST (pp[1],(A2 + B1*Q21 + B2*Q22 + B3*Q23),R1+1,2); - TEST (pp[2],(A3 + B1*Q31 + B2*Q32 + B3*Q33),R1+2,3); - - // separating axis = v1,v2,v3 - TEST (dDOT41(R2+0,p),(A1*Q11 + A2*Q21 + A3*Q31 + B1),R2+0,4); - TEST (dDOT41(R2+1,p),(A1*Q12 + A2*Q22 + A3*Q32 + B2),R2+1,5); - TEST (dDOT41(R2+2,p),(A1*Q13 + A2*Q23 + A3*Q33 + B3),R2+2,6); - - // note: cross product axes need to be scaled when s is computed. - // normal (n1,n2,n3) is relative to box 1. -#undef TEST -#define TEST(expr1,expr2,n1,n2,n3,cc) \ - s2 = dFabs(expr1) - (expr2); \ - if (s2 > 0) return 0; \ - l = dSqrt ((n1)*(n1) + (n2)*(n2) + (n3)*(n3)); \ - if (l > 0) { \ - s2 /= l; \ - if (s2 > s) { \ - s = s2; \ - normalR = 0; \ - normalC[0] = (n1)/l; normalC[1] = (n2)/l; normalC[2] = (n3)/l; \ - invert_normal = ((expr1) < 0); \ - *code = (cc); \ - } \ - } - - // separating axis = u1 x (v1,v2,v3) - TEST(pp[2]*R21-pp[1]*R31,(A2*Q31+A3*Q21+B2*Q13+B3*Q12),0,-R31,R21,7); - TEST(pp[2]*R22-pp[1]*R32,(A2*Q32+A3*Q22+B1*Q13+B3*Q11),0,-R32,R22,8); - TEST(pp[2]*R23-pp[1]*R33,(A2*Q33+A3*Q23+B1*Q12+B2*Q11),0,-R33,R23,9); - - // separating axis = u2 x (v1,v2,v3) - TEST(pp[0]*R31-pp[2]*R11,(A1*Q31+A3*Q11+B2*Q23+B3*Q22),R31,0,-R11,10); - TEST(pp[0]*R32-pp[2]*R12,(A1*Q32+A3*Q12+B1*Q23+B3*Q21),R32,0,-R12,11); - TEST(pp[0]*R33-pp[2]*R13,(A1*Q33+A3*Q13+B1*Q22+B2*Q21),R33,0,-R13,12); - - // separating axis = u3 x (v1,v2,v3) - TEST(pp[1]*R11-pp[0]*R21,(A1*Q21+A2*Q11+B2*Q33+B3*Q32),-R21,R11,0,13); - TEST(pp[1]*R12-pp[0]*R22,(A1*Q22+A2*Q12+B1*Q33+B3*Q31),-R22,R12,0,14); - TEST(pp[1]*R13-pp[0]*R23,(A1*Q23+A2*Q13+B1*Q32+B2*Q31),-R23,R13,0,15); - -#undef TEST - - // if we get to this point, the boxes interpenetrate. compute the normal - // in global coordinates. - if (normalR) { - normal[0] = normalR[0]; - normal[1] = normalR[4]; - normal[2] = normalR[8]; - } - else { - dMULTIPLY0_331 (normal,R1,normalC); - } - if (invert_normal) { - normal[0] = -normal[0]; - normal[1] = -normal[1]; - normal[2] = -normal[2]; - } - *depth = -s; - - // compute contact point(s) - - if (*code > 6) { - // an edge from box 1 touches an edge from box 2. - // find a point pa on the intersecting edge of box 1 - dVector3 pa; - dReal sign; - for (i=0; i<3; i++) pa[i] = p1[i]; - sign = (dDOT14(normal,R1+0) > 0) ? REAL(1.0) : REAL(-1.0); - for (i=0; i<3; i++) pa[i] += sign * A1 * R1[i*4]; - sign = (dDOT14(normal,R1+1) > 0) ? REAL(1.0) : REAL(-1.0); - for (i=0; i<3; i++) pa[i] += sign * A2 * R1[i*4+1]; - sign = (dDOT14(normal,R1+2) > 0) ? REAL(1.0) : REAL(-1.0); - for (i=0; i<3; i++) pa[i] += sign * A3 * R1[i*4+2]; - - // find a point pb on the intersecting edge of box 2 - dVector3 pb; - for (i=0; i<3; i++) pb[i] = p2[i]; - sign = (dDOT14(normal,R2+0) > 0) ? REAL(-1.0) : REAL(1.0); - for (i=0; i<3; i++) pb[i] += sign * B1 * R2[i*4]; - sign = (dDOT14(normal,R2+1) > 0) ? REAL(-1.0) : REAL(1.0); - for (i=0; i<3; i++) pb[i] += sign * B2 * R2[i*4+1]; - sign = (dDOT14(normal,R2+2) > 0) ? REAL(-1.0) : REAL(1.0); - for (i=0; i<3; i++) pb[i] += sign * B3 * R2[i*4+2]; - - dReal alpha,beta; - dVector3 ua,ub; - for (i=0; i<3; i++) ua[i] = R1[((*code)-7)/3 + i*4]; - for (i=0; i<3; i++) ub[i] = R2[((*code)-7)%3 + i*4]; - - lineClosestApproach (pa,ua,pb,ub,&alpha,&beta); - for (i=0; i<3; i++) pa[i] += ua[i]*alpha; - for (i=0; i<3; i++) pb[i] += ub[i]*beta; - - for (i=0; i<3; i++) contact[0].pos[i] = REAL(0.5)*(pa[i]+pb[i]); - contact[0].depth = *depth; - return 1; - } - - // okay, we have a face-something intersection (because the separating - // axis is perpendicular to a face). - - // @@@ temporary: make deepest vertex on the "other" box the contact point. - // @@@ this kind of works, but we need multiple contact points for stability, - // @@@ especially for face-face contact. - - dVector3 vertex; - if (*code <= 3) { - // face from box 1 touches a vertex/edge/face from box 2. - dReal sign; - for (i=0; i<3; i++) vertex[i] = p2[i]; - sign = (dDOT14(normal,R2+0) > 0) ? REAL(-1.0) : REAL(1.0); - for (i=0; i<3; i++) vertex[i] += sign * B1 * R2[i*4]; - sign = (dDOT14(normal,R2+1) > 0) ? REAL(-1.0) : REAL(1.0); - for (i=0; i<3; i++) vertex[i] += sign * B2 * R2[i*4+1]; - sign = (dDOT14(normal,R2+2) > 0) ? REAL(-1.0) : REAL(1.0); - for (i=0; i<3; i++) vertex[i] += sign * B3 * R2[i*4+2]; - } - else { - // face from box 2 touches a vertex/edge/face from box 1. - dReal sign; - for (i=0; i<3; i++) vertex[i] = p1[i]; - sign = (dDOT14(normal,R1+0) > 0) ? REAL(1.0) : REAL(-1.0); - for (i=0; i<3; i++) vertex[i] += sign * A1 * R1[i*4]; - sign = (dDOT14(normal,R1+1) > 0) ? REAL(1.0) : REAL(-1.0); - for (i=0; i<3; i++) vertex[i] += sign * A2 * R1[i*4+1]; - sign = (dDOT14(normal,R1+2) > 0) ? REAL(1.0) : REAL(-1.0); - for (i=0; i<3; i++) vertex[i] += sign * A3 * R1[i*4+2]; - } - for (i=0; i<3; i++) contact[0].pos[i] = vertex[i]; - contact[0].depth = *depth; - return 1; -} - -//**************************************************************************** -// general support for geometry objects and classes - -struct dColliderEntry { - dColliderFn *fn; // collider function - int mode; // 1 = reverse o1 and o2, 2 = no function available -}; - -static dArray<dxGeomClass*> *classes=0; - -// function pointers and modes for n^2 class collider functions. this is an -// n*n matrix stored by row. the functions pointers are extracted from the -// class get-collider-function function. -static dArray<dColliderEntry> *colliders=0; - - -static inline void initCollisionArrays() -{ - if (classes==0) { - // old way: - // classes = (dArray<dxGeomClass*> *) dAllocNoFree (sizeof(dArrayBase)); - // classes->constructor(); - classes = new dArray<dxGeomClass*>; - classes->setSize (1); // force allocation of array data memory - dAllocDontReport (classes); - dAllocDontReport (classes->data()); - classes->setSize (0); - } - if (colliders==0) { - // old way: - // colliders=(dArray<dColliderEntry> *)dAllocNoFree (sizeof(dArrayBase)); - // colliders->constructor(); - colliders = new dArray<dColliderEntry>; - colliders->setSize (1); // force allocation of array data memory - dAllocDontReport (colliders); - dAllocDontReport (colliders->data()); - colliders->setSize (0); - } -} - - -int dCreateGeomClass (const dGeomClass *c) -{ - dUASSERT(c && c->bytes >= 0 && c->collider && c->aabb,"bad geom class"); - initCollisionArrays(); - - int n = classes->size(); - dxGeomClass *gc = (dxGeomClass*) dAlloc (sizeof(dxGeomClass)); - dAllocDontReport (gc); - gc->collider = c->collider; - gc->aabb = c->aabb; - gc->aabb_test = c->aabb_test; - gc->dtor = c->dtor; - gc->num = n; - gc->size = SIZEOF_DXGEOM + c->bytes; - classes->push (gc); - - // make room for n^2 class collider function pointers - these entries will - // be filled as dCollide() is called. - colliders->setSize ((n+1)*(n+1)); - memset (colliders->data(),0,(n+1)*(n+1)*sizeof(dColliderEntry)); - - return n; -} - - -int dCollide (dxGeom *o1, dxGeom *o2, int flags, dContactGeom *contact, - int skip) -{ - int i,c1,c2,a1,a2,count,swap; - dColliderFn *fn; - dAASSERT(o1 && o2 && contact); - dUASSERT(classes && colliders,"no registered geometry classes"); - - // no contacts if both geoms on the same body, and the body is not 0 - if (o1->body == o2->body && o1->body) return 0; - - dColliderEntry *colliders2 = colliders->data(); - c1 = o1->_class->num; - c2 = o2->_class->num; - a1 = c1 * classes->size() + c2; // address 1 in collider array - a2 = c2 * classes->size() + c1; // address 2 in collider array - swap = 0; // set to 1 to swap normals before returning - - // return if there are no collider functions available - if ((colliders2[a1].mode==2) || (colliders2[a2].mode==2)) return 0; - - if ((fn = colliders2[a1].fn)) { - swap = colliders2[a1].mode; - if (swap) count = (*fn) (o2,o1,flags,contact,skip); - else count = (*fn) (o1,o2,flags,contact,skip); - } - else if ((fn = (*classes)[c1]->collider (c2))) { - colliders2 [a2].fn = fn; - colliders2 [a2].mode = 1; - colliders2 [a1].fn = fn; // do mode=0 assignment second so that - colliders2 [a1].mode = 0; // diagonal entries will have mode 0 - count = (*fn) (o1,o2,flags,contact,skip); - swap = 0; - } - else if ((fn = (*classes)[c2]->collider (c1))) { - colliders2 [a1].fn = fn; - colliders2 [a1].mode = 1; - colliders2 [a2].fn = fn; // do mode=0 assignment second so that - colliders2 [a2].mode = 0; // diagonal entries will have mode 0 - count = (*fn) (o2,o1,flags,contact,skip); - swap = 1; - } - else { - colliders2[a1].mode = 2; - colliders2[a2].mode = 2; - return 0; - } - - if (swap) { - for (i=0; i<count; i++) { - dContactGeom *c = CONTACT(contact,skip*i); - c->normal[0] = -c->normal[0]; - c->normal[1] = -c->normal[1]; - c->normal[2] = -c->normal[2]; - dxGeom *tmp = c->g1; - c->g1 = c->g2; - c->g2 = tmp; - } - } - - return count; -} - - -int dGeomGetClass (dxGeom *g) -{ - dAASSERT (g); - return g->_class->num; -} - - -void dGeomSetData (dxGeom *g, void *data) -{ - dAASSERT (g); - g->data = data; -} - - -void *dGeomGetData (dxGeom *g) -{ - dAASSERT (g); - return g->data; -} - - -void dGeomSetBody (dxGeom *g, dBodyID b) -{ - dAASSERT (g); - if (b) { - if (!g->body) dFree (g->pos,sizeof(dxPosR)); - g->body = b; - g->pos = b->pos; - g->R = b->R; - } - else { - if (g->body) { - dxPosR *pr = (dxPosR*) dAlloc (sizeof(dxPosR)); - g->pos = pr->pos; - g->R = pr->R; - memcpy (g->pos,g->body->pos,sizeof(g->pos)); - memcpy (g->R,g->body->R,sizeof(g->R)); - g->body = 0; - } - } -} - - -dBodyID dGeomGetBody (dxGeom *g) -{ - dAASSERT (g); - return g->body; -} - - -void dGeomSetPosition (dxGeom *g, dReal x, dReal y, dReal z) -{ - dAASSERT (g); - if (g->body) dBodySetPosition (g->body,x,y,z); - else { - g->pos[0] = x; - g->pos[1] = y; - g->pos[2] = z; - } -} - - -void dGeomSetRotation (dxGeom *g, const dMatrix3 R) -{ - dAASSERT (g); - if (g->body) dBodySetRotation (g->body,R); - else memcpy (g->R,R,sizeof(dMatrix3)); -} - - -const dReal * dGeomGetPosition (dxGeom *g) -{ - dAASSERT (g); - return g->pos; -} - - -const dReal * dGeomGetRotation (dxGeom *g) -{ - dAASSERT (g); - return g->R; -} - - -// for external use only. use the CLASSDATA macro inside ODE. - -void * dGeomGetClassData (dxGeom *g) -{ - dAASSERT (g); - return (void*) CLASSDATA(g); -} - - -dxGeom * dCreateGeom (int classnum) -{ - dUASSERT (classes && colliders && classnum >= 0 && - classnum < classes->size(),"bad class number"); - int size = (*classes)[classnum]->size; - dxGeom *geom = (dxGeom*) dAlloc (size); - memset (geom,0,size); // everything is initially zeroed - - geom->_class = (*classes)[classnum]; - geom->data = 0; - geom->body = 0; - - dxPosR *pr = (dxPosR*) dAlloc (sizeof(dxPosR)); - geom->pos = pr->pos; - geom->R = pr->R; - dSetZero (geom->pos,4); - dRSetIdentity (geom->R); - - return geom; -} - - -void dGeomDestroy (dxGeom *g) -{ - dAASSERT (g); - if (g->spaceid) dSpaceRemove (g->spaceid,g); - if (g->_class->dtor) g->_class->dtor (g); - if (!g->body) dFree (g->pos,sizeof(dxPosR)); - dFree (g,g->_class->size); -} - - -void dGeomGetAABB (dxGeom *g, dReal aabb[6]) -{ - dAASSERT (g); - g->_class->aabb (g,aabb); -} - - -dReal *dGeomGetSpaceAABB (dxGeom *g) -{ - dAASSERT (g); - return g->space_aabb; -} - -//**************************************************************************** -// data for the standard classes - -struct dxSphere { - dReal radius; // sphere radius -}; - -struct dxBox { - dVector3 side; // side lengths (x,y,z) -}; - -struct dxCCylinder { // capped cylinder - dReal radius,lz; // radius, length along z axis */ -}; - -struct dxPlane { - dReal p[4]; -}; - -struct dxGeomGroup { - dArray<dxGeom*> parts; // all the geoms that make up the group -}; - -//**************************************************************************** -// primitive collision functions -// same interface as dCollide(). -// S=sphere, B=box, C=capped cylinder, P=plane, G=group, T=transform - -int dCollideSS (const dxGeom *o1, const dxGeom *o2, int flags, - dContactGeom *contact, int skip) -{ - dIASSERT (skip >= (int)sizeof(dContactGeom)); - dIASSERT (o1->_class->num == dSphereClass); - dIASSERT (o2->_class->num == dSphereClass); - dxSphere *s1 = (dxSphere*) CLASSDATA(o1); - dxSphere *s2 = (dxSphere*) CLASSDATA(o2); - contact->g1 = const_cast<dxGeom*> (o1); - contact->g2 = const_cast<dxGeom*> (o2); - return dCollideSpheres (o1->pos,s1->radius, - o2->pos,s2->radius,contact); -} - - -int dCollideSB (const dxGeom *o1, const dxGeom *o2, int flags, - dContactGeom *contact, int skip) -{ - // this is easy. get the sphere center `p' relative to the box, and then clip - // that to the boundary of the box (call that point `q'). if q is on the - // boundary of the box and |p-q| is <= sphere radius, they touch. - // if q is inside the box, the sphere is inside the box, so set a contact - // normal to push the sphere to the closest box edge. - - dVector3 l,t,p,q,r; - dReal depth; - int onborder = 0; - - dIASSERT (skip >= (int)sizeof(dContactGeom)); - dIASSERT (o1->_class->num == dSphereClass); - dIASSERT (o2->_class->num == dBoxClass); - dxSphere *sphere = (dxSphere*) CLASSDATA(o1); - dxBox *box = (dxBox*) CLASSDATA(o2); - - contact->g1 = const_cast<dxGeom*> (o1); - contact->g2 = const_cast<dxGeom*> (o2); - - p[0] = o1->pos[0] - o2->pos[0]; - p[1] = o1->pos[1] - o2->pos[1]; - p[2] = o1->pos[2] - o2->pos[2]; - - l[0] = box->side[0]*REAL(0.5); - t[0] = dDOT14(p,o2->R); - if (t[0] < -l[0]) { t[0] = -l[0]; onborder = 1; } - if (t[0] > l[0]) { t[0] = l[0]; onborder = 1; } - - l[1] = box->side[1]*REAL(0.5); - t[1] = dDOT14(p,o2->R+1); - if (t[1] < -l[1]) { t[1] = -l[1]; onborder = 1; } - if (t[1] > l[1]) { t[1] = l[1]; onborder = 1; } - - t[2] = dDOT14(p,o2->R+2); - l[2] = box->side[2]*REAL(0.5); - if (t[2] < -l[2]) { t[2] = -l[2]; onborder = 1; } - if (t[2] > l[2]) { t[2] = l[2]; onborder = 1; } - - if (!onborder) { - // sphere center inside box. find largest `t' value - dReal max = dFabs(t[0]); - int maxi = 0; - for (int i=1; i<3; i++) { - dReal tt = dFabs(t[i]); - if (tt > max) { - max = tt; - maxi = i; - } - } - // contact position = sphere center - contact->pos[0] = o1->pos[0]; - contact->pos[1] = o1->pos[1]; - contact->pos[2] = o1->pos[2]; - // contact normal aligned with box edge along largest `t' value - dVector3 tmp; - tmp[0] = 0; - tmp[1] = 0; - tmp[2] = 0; - tmp[maxi] = (t[maxi] > 0) ? REAL(1.0) : REAL(-1.0); - dMULTIPLY0_331 (contact->normal,o2->R,tmp); - // contact depth = distance to wall along normal plus radius - contact->depth = l[maxi] - max + sphere->radius; - return 1; - } - - t[3] = 0; //@@@ hmmm - dMULTIPLY0_331 (q,o2->R,t); - r[0] = p[0] - q[0]; - r[1] = p[1] - q[1]; - r[2] = p[2] - q[2]; - depth = sphere->radius - dSqrt(dDOT(r,r)); - if (depth < 0) return 0; - contact->pos[0] = q[0] + o2->pos[0]; - contact->pos[1] = q[1] + o2->pos[1]; - contact->pos[2] = q[2] + o2->pos[2]; - contact->normal[0] = r[0]; - contact->normal[1] = r[1]; - contact->normal[2] = r[2]; - dNormalize3 (contact->normal); - contact->depth = depth; - return 1; -} - - -int dCollideSP (const dxGeom *o1, const dxGeom *o2, int flags, - dContactGeom *contact, int skip) -{ - dIASSERT (skip >= (int)sizeof(dContactGeom)); - dIASSERT (o1->_class->num == dSphereClass); - dIASSERT (o2->_class->num == dPlaneClass); - contact->g1 = const_cast<dxGeom*> (o1); - contact->g2 = const_cast<dxGeom*> (o2); - dxSphere *sphere = (dxSphere*) CLASSDATA(o1); - dxPlane *plane = (dxPlane*) CLASSDATA(o2); - dReal k = dDOT (o1->pos,plane->p); - dReal depth = plane->p[3] - k + sphere->radius; - if (depth >= 0) { - contact->normal[0] = plane->p[0]; - contact->normal[1] = plane->p[1]; - contact->normal[2] = plane->p[2]; - contact->pos[0] = o1->pos[0] - plane->p[0] * sphere->radius; - contact->pos[1] = o1->pos[1] - plane->p[1] * sphere->radius; - contact->pos[2] = o1->pos[2] - plane->p[2] * sphere->radius; - contact->depth = depth; - return 1; - } - else return 0; -} - - -int dCollideBB (const dxGeom *o1, const dxGeom *o2, int flags, - dContactGeom *contact, int skip) -{ - dVector3 normal; - dReal depth; - int code; - dxBox *b1 = (dxBox*) CLASSDATA(o1); - dxBox *b2 = (dxBox*) CLASSDATA(o2); - int num = dBoxBox (o1->pos,o1->R,b1->side, o2->pos,o2->R,b2->side, - normal,&depth,&code,flags & NUMC_MASK,contact,skip); - for (int i=0; i<num; i++) { - CONTACT(contact,i*skip)->normal[0] = -normal[0]; - CONTACT(contact,i*skip)->normal[1] = -normal[1]; - CONTACT(contact,i*skip)->normal[2] = -normal[2]; - CONTACT(contact,i*skip)->g1 = const_cast<dxGeom*> (o1); - CONTACT(contact,i*skip)->g2 = const_cast<dxGeom*> (o2); - } - return num; -} - - -int dCollideBP (const dxGeom *o1, const dxGeom *o2, - int flags, dContactGeom *contact, int skip) -{ - dIASSERT (skip >= (int)sizeof(dContactGeom)); - dIASSERT (o1->_class->num == dBoxClass); - dIASSERT (o2->_class->num == dPlaneClass); - contact->g1 = const_cast<dxGeom*> (o1); - contact->g2 = const_cast<dxGeom*> (o2); - dxBox *box = (dxBox*) CLASSDATA(o1); - dxPlane *plane = (dxPlane*) CLASSDATA(o2); - int ret = 0; - - //@@@ problem: using 4-vector (plane->p) as 3-vector (normal). - const dReal *R = o1->R; // rotation of box - const dReal *n = plane->p; // normal vector - - // project sides lengths along normal vector, get absolute values - dReal Q1 = dDOT14(n,R+0); - dReal Q2 = dDOT14(n,R+1); - dReal Q3 = dDOT14(n,R+2); - dReal A1 = box->side[0] * Q1; - dReal A2 = box->side[1] * Q2; - dReal A3 = box->side[2] * Q3; - dReal B1 = dFabs(A1); - dReal B2 = dFabs(A2); - dReal B3 = dFabs(A3); - - // early exit test - dReal depth = plane->p[3] + REAL(0.5)*(B1+B2+B3) - dDOT(n,o1->pos); - if (depth < 0) return 0; - - // find number of contacts requested - int maxc = flags & NUMC_MASK; - if (maxc < 1) maxc = 1; - if (maxc > 3) maxc = 3; // no more than 3 contacts per box allowed - - // find deepest point - dVector3 p; - p[0] = o1->pos[0]; - p[1] = o1->pos[1]; - p[2] = o1->pos[2]; -#define FOO(i,op) \ - p[0] op REAL(0.5)*box->side[i] * R[0+i]; \ - p[1] op REAL(0.5)*box->side[i] * R[4+i]; \ - p[2] op REAL(0.5)*box->side[i] * R[8+i]; -#define BAR(i,iinc) if (A ## iinc > 0) { FOO(i,-=) } else { FOO(i,+=) } - BAR(0,1); - BAR(1,2); - BAR(2,3); -#undef FOO -#undef BAR - - // the deepest point is the first contact point - contact->pos[0] = p[0]; - contact->pos[1] = p[1]; - contact->pos[2] = p[2]; - contact->normal[0] = n[0]; - contact->normal[1] = n[1]; - contact->normal[2] = n[2]; - contact->depth = depth; - ret = 1; // ret is number of contact points found so far - if (maxc == 1) goto done; - - // get the second and third contact points by starting from `p' and going - // along the two sides with the smallest projected length. - -#define FOO(i,j,op) \ - CONTACT(contact,i*skip)->pos[0] = p[0] op box->side[j] * R[0+j]; \ - CONTACT(contact,i*skip)->pos[1] = p[1] op box->side[j] * R[4+j]; \ - CONTACT(contact,i*skip)->pos[2] = p[2] op box->side[j] * R[8+j]; -#define BAR(ctact,side,sideinc) \ - depth -= B ## sideinc; \ - if (depth < 0) goto done; \ - if (A ## sideinc > 0) { FOO(ctact,side,+) } else { FOO(ctact,side,-) } \ - CONTACT(contact,ctact*skip)->depth = depth; \ - ret++; - - CONTACT(contact,skip)->normal[0] = n[0]; - CONTACT(contact,skip)->normal[1] = n[1]; - CONTACT(contact,skip)->normal[2] = n[2]; - if (maxc == 3) { - CONTACT(contact,2*skip)->normal[0] = n[0]; - CONTACT(contact,2*skip)->normal[1] = n[1]; - CONTACT(contact,2*skip)->normal[2] = n[2]; - } - - if (B1 < B2) { - if (B3 < B1) goto use_side_3; else { - BAR(1,0,1); // use side 1 - if (maxc == 2) goto done; - if (B2 < B3) goto contact2_2; else goto contact2_3; - } - } - else { - if (B3 < B2) { - use_side_3: // use side 3 - BAR(1,2,3); - if (maxc == 2) goto done; - if (B1 < B2) goto contact2_1; else goto contact2_2; - } - else { - BAR(1,1,2); // use side 2 - if (maxc == 2) goto done; - if (B1 < B3) goto contact2_1; else goto contact2_3; - } - } - - contact2_1: BAR(2,0,1); goto done; - contact2_2: BAR(2,1,2); goto done; - contact2_3: BAR(2,2,3); goto done; -#undef FOO -#undef BAR - - done: - for (int i=0; i<ret; i++) { - CONTACT(contact,i*skip)->g1 = const_cast<dxGeom*> (o1); - CONTACT(contact,i*skip)->g2 = const_cast<dxGeom*> (o2); - } - return ret; -} - - -int dCollideCS (const dxGeom *o1, const dxGeom *o2, int flags, - dContactGeom *contact, int skip) -{ - dIASSERT (skip >= (int)sizeof(dContactGeom)); - dIASSERT (o1->_class->num == dCCylinderClass); - dIASSERT (o2->_class->num == dSphereClass); - contact->g1 = const_cast<dxGeom*> (o1); - contact->g2 = const_cast<dxGeom*> (o2); - dxCCylinder *ccyl = (dxCCylinder*) CLASSDATA(o1); - dxSphere *sphere = (dxSphere*) CLASSDATA(o2); - - // find the point on the cylinder axis that is closest to the sphere - dReal alpha = - o1->R[2] * (o2->pos[0] - o1->pos[0]) + - o1->R[6] * (o2->pos[1] - o1->pos[1]) + - o1->R[10] * (o2->pos[2] - o1->pos[2]); - dReal lz2 = ccyl->lz * REAL(0.5); - if (alpha > lz2) alpha = lz2; - if (alpha < -lz2) alpha = -lz2; - - // collide the spheres - dVector3 p; - p[0] = o1->pos[0] + alpha * o1->R[2]; - p[1] = o1->pos[1] + alpha * o1->R[6]; - p[2] = o1->pos[2] + alpha * o1->R[10]; - return dCollideSpheres (p,ccyl->radius,o2->pos,sphere->radius,contact); -} - - -int dCollideCB (const dxGeom *o1, const dxGeom *o2, int flags, - dContactGeom *contact, int skip) -{ - dIASSERT (skip >= (int)sizeof(dContactGeom)); - dIASSERT (o1->_class->num == dCCylinderClass); - dIASSERT (o2->_class->num == dBoxClass); - contact->g1 = const_cast<dxGeom*> (o1); - contact->g2 = const_cast<dxGeom*> (o2); - dxCCylinder *cyl = (dxCCylinder*) CLASSDATA(o1); - dxBox *box = (dxBox*) CLASSDATA(o2); - - // get p1,p2 = cylinder axis endpoints, get radius - dVector3 p1,p2; - dReal clen = cyl->lz * REAL(0.5); - p1[0] = o1->pos[0] + clen * o1->R[2]; - p1[1] = o1->pos[1] + clen * o1->R[6]; - p1[2] = o1->pos[2] + clen * o1->R[10]; - p2[0] = o1->pos[0] - clen * o1->R[2]; - p2[1] = o1->pos[1] - clen * o1->R[6]; - p2[2] = o1->pos[2] - clen * o1->R[10]; - dReal radius = cyl->radius; - - // copy out box center, rotation matrix, and side array - dReal *c = o2->pos; - dReal *R = o2->R; - dReal *side = box->side; - - // get the closest point between the cylinder axis and the box - dVector3 pl,pb; - dClosestLineBoxPoints (p1,p2,c,R,side,pl,pb); - - // generate contact point - return dCollideSpheres (pl,radius,pb,0,contact); -} - - -// this returns at most one contact point when the two cylinder's axes are not -// aligned, and at most two (for stability) when they are aligned. -// the algorithm minimizes the distance between two "sample spheres" that are -// positioned along the cylinder axes according to: -// sphere1 = pos1 + alpha1 * axis1 -// sphere2 = pos2 + alpha2 * axis2 -// alpha1 and alpha2 are limited to +/- half the length of the cylinders. -// the algorithm works by finding a solution that has both alphas free, or -// a solution that has one or both alphas fixed to the ends of the cylinder. - -int dCollideCC (const dxGeom *o1, const dxGeom *o2, - int flags, dContactGeom *contact, int skip) -{ - int i; - const dReal tolerance = REAL(1e-5); - - dIASSERT (skip >= (int)sizeof(dContactGeom)); - dIASSERT (o1->_class->num == dCCylinderClass); - dIASSERT (o2->_class->num == dCCylinderClass); - contact->g1 = const_cast<dxGeom*> (o1); - contact->g2 = const_cast<dxGeom*> (o2); - dxCCylinder *cyl1 = (dxCCylinder*) CLASSDATA(o1); - dxCCylinder *cyl2 = (dxCCylinder*) CLASSDATA(o2); - - // copy out some variables, for convenience - dReal lz1 = cyl1->lz * REAL(0.5); - dReal lz2 = cyl2->lz * REAL(0.5); - dReal *pos1 = o1->pos; - dReal *pos2 = o2->pos; - dReal axis1[3],axis2[3]; - axis1[0] = o1->R[2]; - axis1[1] = o1->R[6]; - axis1[2] = o1->R[10]; - axis2[0] = o2->R[2]; - axis2[1] = o2->R[6]; - axis2[2] = o2->R[10]; - - dReal alpha1,alpha2,sphere1[3],sphere2[3]; - int fix1 = 0; // 0 if alpha1 is free, +/-1 to fix at +/- lz1 - int fix2 = 0; // 0 if alpha2 is free, +/-1 to fix at +/- lz2 - - for (int count=0; count<9; count++) { - // find a trial solution by fixing or not fixing the alphas - if (fix1) { - if (fix2) { - // alpha1 and alpha2 are fixed, so the solution is easy - if (fix1 > 0) alpha1 = lz1; else alpha1 = -lz1; - if (fix2 > 0) alpha2 = lz2; else alpha2 = -lz2; - for (i=0; i<3; i++) sphere1[i] = pos1[i] + alpha1*axis1[i]; - for (i=0; i<3; i++) sphere2[i] = pos2[i] + alpha2*axis2[i]; - } - else { - // fix alpha1 but let alpha2 be free - if (fix1 > 0) alpha1 = lz1; else alpha1 = -lz1; - for (i=0; i<3; i++) sphere1[i] = pos1[i] + alpha1*axis1[i]; - alpha2 = (axis2[0]*(sphere1[0]-pos2[0]) + - axis2[1]*(sphere1[1]-pos2[1]) + - axis2[2]*(sphere1[2]-pos2[2])); - for (i=0; i<3; i++) sphere2[i] = pos2[i] + alpha2*axis2[i]; - } - } - else { - if (fix2) { - // fix alpha2 but let alpha1 be free - if (fix2 > 0) alpha2 = lz2; else alpha2 = -lz2; - for (i=0; i<3; i++) sphere2[i] = pos2[i] + alpha2*axis2[i]; - alpha1 = (axis1[0]*(sphere2[0]-pos1[0]) + - axis1[1]*(sphere2[1]-pos1[1]) + - axis1[2]*(sphere2[2]-pos1[2])); - for (i=0; i<3; i++) sphere1[i] = pos1[i] + alpha1*axis1[i]; - } - else { - // let alpha1 and alpha2 be free - // compute determinant of d(d^2)\d(alpha) jacobian - dReal a1a2 = dDOT (axis1,axis2); - dReal det = REAL(1.0)-a1a2*a1a2; - if (det < tolerance) { - // the cylinder axes (almost) parallel, so we will generate up to two - // contacts. the solution matrix is rank deficient so alpha1 and - // alpha2 are related by: - // alpha2 = alpha1 + (pos1-pos2)'*axis1 (if axis1==axis2) - // or alpha2 = -(alpha1 + (pos1-pos2)'*axis1) (if axis1==-axis2) - // first compute where the two cylinders overlap in alpha1 space: - if (a1a2 < 0) { - axis2[0] = -axis2[0]; - axis2[1] = -axis2[1]; - axis2[2] = -axis2[2]; - } - dReal q[3]; - for (i=0; i<3; i++) q[i] = pos1[i]-pos2[i]; - dReal k = dDOT (axis1,q); - dReal a1lo = -lz1; - dReal a1hi = lz1; - dReal a2lo = -lz2 - k; - dReal a2hi = lz2 - k; - dReal lo = (a1lo > a2lo) ? a1lo : a2lo; - dReal hi = (a1hi < a2hi) ? a1hi : a2hi; - if (lo <= hi) { - int num_contacts = flags & NUMC_MASK; - if (num_contacts >= 2 && lo < hi) { - // generate up to two contacts. if one of those contacts is - // not made, fall back on the one-contact strategy. - for (i=0; i<3; i++) sphere1[i] = pos1[i] + lo*axis1[i]; - for (i=0; i<3; i++) sphere2[i] = pos2[i] + (lo+k)*axis2[i]; - int n1 = dCollideSpheres (sphere1,cyl1->radius, - sphere2,cyl2->radius,contact); - if (n1) { - for (i=0; i<3; i++) sphere1[i] = pos1[i] + hi*axis1[i]; - for (i=0; i<3; i++) sphere2[i] = pos2[i] + (hi+k)*axis2[i]; - dContactGeom *c2 = CONTACT(contact,skip); - int n2 = dCollideSpheres (sphere1,cyl1->radius, - sphere2,cyl2->radius, c2); - if (n2) { - c2->g1 = const_cast<dxGeom*> (o1); - c2->g2 = const_cast<dxGeom*> (o2); - return 2; - } - } - } - - // just one contact to generate, so put it in the middle of - // the range - alpha1 = (lo + hi) * REAL(0.5); - alpha2 = alpha1 + k; - for (i=0; i<3; i++) sphere1[i] = pos1[i] + alpha1*axis1[i]; - for (i=0; i<3; i++) sphere2[i] = pos2[i] + alpha2*axis2[i]; - return dCollideSpheres (sphere1,cyl1->radius, - sphere2,cyl2->radius,contact); - } - else return 0; - } - det = REAL(1.0)/det; - dReal delta[3]; - for (i=0; i<3; i++) delta[i] = pos1[i] - pos2[i]; - dReal q1 = dDOT (delta,axis1); - dReal q2 = dDOT (delta,axis2); - alpha1 = det*(a1a2*q2-q1); - alpha2 = det*(q2-a1a2*q1); - for (i=0; i<3; i++) sphere1[i] = pos1[i] + alpha1*axis1[i]; - for (i=0; i<3; i++) sphere2[i] = pos2[i] + alpha2*axis2[i]; - } - } - - // if the alphas are outside their allowed ranges then fix them and - // try again - if (fix1==0) { - if (alpha1 < -lz1) { - fix1 = -1; - continue; - } - if (alpha1 > lz1) { - fix1 = 1; - continue; - } - } - if (fix2==0) { - if (alpha2 < -lz2) { - fix2 = -1; - continue; - } - if (alpha2 > lz2) { - fix2 = 1; - continue; - } - } - - // unfix the alpha variables if the local distance gradient indicates - // that we are not yet at the minimum - dReal tmp[3]; - for (i=0; i<3; i++) tmp[i] = sphere1[i] - sphere2[i]; - if (fix1) { - dReal gradient = dDOT (tmp,axis1); - if ((fix1 > 0 && gradient > 0) || (fix1 < 0 && gradient < 0)) { - fix1 = 0; - continue; - } - } - if (fix2) { - dReal gradient = -dDOT (tmp,axis2); - if ((fix2 > 0 && gradient > 0) || (fix2 < 0 && gradient < 0)) { - fix2 = 0; - continue; - } - } - return dCollideSpheres (sphere1,cyl1->radius,sphere2,cyl2->radius,contact); - } - // if we go through the loop too much, then give up. we should NEVER get to - // this point (i hope). - dMessage (0,"dCollideCC(): too many iterations"); - return 0; -} - - -int dCollideCP (const dxGeom *o1, const dxGeom *o2, int flags, - dContactGeom *contact, int skip) -{ - dIASSERT (skip >= (int)sizeof(dContactGeom)); - dIASSERT (o1->_class->num == dCCylinderClass); - dIASSERT (o2->_class->num == dPlaneClass); - dxCCylinder *ccyl = (dxCCylinder*) CLASSDATA(o1); - dxPlane *plane = (dxPlane*) CLASSDATA(o2); - - // collide the deepest capping sphere with the plane - dReal sign = (dDOT14 (plane->p,o1->R+2) > 0) ? REAL(-1.0) : REAL(1.0); - dVector3 p; - p[0] = o1->pos[0] + o1->R[2] * ccyl->lz * REAL(0.5) * sign; - p[1] = o1->pos[1] + o1->R[6] * ccyl->lz * REAL(0.5) * sign; - p[2] = o1->pos[2] + o1->R[10] * ccyl->lz * REAL(0.5) * sign; - - dReal k = dDOT (p,plane->p); - dReal depth = plane->p[3] - k + ccyl->radius; - if (depth < 0) return 0; - contact->normal[0] = plane->p[0]; - contact->normal[1] = plane->p[1]; - contact->normal[2] = plane->p[2]; - contact->pos[0] = p[0] - plane->p[0] * ccyl->radius; - contact->pos[1] = p[1] - plane->p[1] * ccyl->radius; - contact->pos[2] = p[2] - plane->p[2] * ccyl->radius; - contact->depth = depth; - - int ncontacts = 1; - if ((flags & NUMC_MASK) >= 2) { - // collide the other capping sphere with the plane - p[0] = o1->pos[0] - o1->R[2] * ccyl->lz * REAL(0.5) * sign; - p[1] = o1->pos[1] - o1->R[6] * ccyl->lz * REAL(0.5) * sign; - p[2] = o1->pos[2] - o1->R[10] * ccyl->lz * REAL(0.5) * sign; - - k = dDOT (p,plane->p); - depth = plane->p[3] - k + ccyl->radius; - if (depth >= 0) { - dContactGeom *c2 = CONTACT(contact,skip); - c2->normal[0] = plane->p[0]; - c2->normal[1] = plane->p[1]; - c2->normal[2] = plane->p[2]; - c2->pos[0] = p[0] - plane->p[0] * ccyl->radius; - c2->pos[1] = p[1] - plane->p[1] * ccyl->radius; - c2->pos[2] = p[2] - plane->p[2] * ccyl->radius; - c2->depth = depth; - ncontacts = 2; - } - } - - for (int i=0; i < ncontacts; i++) { - CONTACT(contact,i*skip)->g1 = const_cast<dxGeom*> (o1); - CONTACT(contact,i*skip)->g2 = const_cast<dxGeom*> (o2); - } - return ncontacts; -} - - -// this collides a group with another geom. the other geom can also be a -// group, but this case is not handled specially. - -int dCollideG (const dxGeom *o1, const dxGeom *o2, int flags, - dContactGeom *contact, int skip) -{ - dxGeomGroup *gr = (dxGeomGroup*) CLASSDATA(o1); - int numleft = flags & NUMC_MASK; - if (numleft == 0) numleft = 1; - flags &= ~NUMC_MASK; - int num=0,i=0; - while (i < gr->parts.size() && numleft > 0) { - int n = dCollide (gr->parts[i],const_cast<dxGeom*>(o2), - flags | numleft,contact,skip); - contact = CONTACT (contact,skip*n); - numleft -= n; - num += n; - i++; - } - return num; -} - -//**************************************************************************** -// standard classes - -SHAREDLIBEXPORT int dSphereClass = -1; -SHAREDLIBEXPORT int dBoxClass = -1; -SHAREDLIBEXPORT int dCCylinderClass = -1; -SHAREDLIBEXPORT int dPlaneClass = -1; - - -static dColliderFn * dSphereColliderFn (int num) -{ - if (num == dSphereClass) return (dColliderFn *) &dCollideSS; - if (num == dBoxClass) return (dColliderFn *) &dCollideSB; - if (num == dPlaneClass) return (dColliderFn *) &dCollideSP; - return 0; -} - - -static void dSphereAABB (dxGeom *geom, dReal aabb[6]) -{ - dxSphere *s = (dxSphere*) CLASSDATA(geom); - aabb[0] = geom->pos[0] - s->radius; - aabb[1] = geom->pos[0] + s->radius; - aabb[2] = geom->pos[1] - s->radius; - aabb[3] = geom->pos[1] + s->radius; - aabb[4] = geom->pos[2] - s->radius; - aabb[5] = geom->pos[2] + s->radius; -} - - -static dColliderFn * dBoxColliderFn (int num) -{ - if (num == dBoxClass) return (dColliderFn *) &dCollideBB; - if (num == dPlaneClass) return (dColliderFn *) &dCollideBP; - return 0; -} - - -static void dBoxAABB (dxGeom *geom, dReal aabb[6]) -{ - dxBox *b = (dxBox*) CLASSDATA(geom); - dReal xrange = REAL(0.5) * (dFabs (geom->R[0] * b->side[0]) + - dFabs (geom->R[1] * b->side[1]) + dFabs (geom->R[2] * b->side[2])); - dReal yrange = REAL(0.5) * (dFabs (geom->R[4] * b->side[0]) + - dFabs (geom->R[5] * b->side[1]) + dFabs (geom->R[6] * b->side[2])); - dReal zrange = REAL(0.5) * (dFabs (geom->R[8] * b->side[0]) + - dFabs (geom->R[9] * b->side[1]) + dFabs (geom->R[10] * b->side[2])); - aabb[0] = geom->pos[0] - xrange; - aabb[1] = geom->pos[0] + xrange; - aabb[2] = geom->pos[1] - yrange; - aabb[3] = geom->pos[1] + yrange; - aabb[4] = geom->pos[2] - zrange; - aabb[5] = geom->pos[2] + zrange; -} - - -static dColliderFn * dCCylinderColliderFn (int num) -{ - if (num == dSphereClass) return (dColliderFn *) &dCollideCS; - if (num == dPlaneClass) return (dColliderFn *) &dCollideCP; - if (num == dCCylinderClass) return (dColliderFn *) &dCollideCC; - if (num == dBoxClass) return (dColliderFn *) &dCollideCB; - return 0; -} - - -static void dCCylinderAABB (dxGeom *geom, dReal aabb[6]) -{ - dxCCylinder *c = (dxCCylinder*) CLASSDATA(geom); - dReal xrange = dFabs(geom->R[2] * c->lz) * REAL(0.5) + c->radius; - dReal yrange = dFabs(geom->R[6] * c->lz) * REAL(0.5) + c->radius; - dReal zrange = dFabs(geom->R[10] * c->lz) * REAL(0.5) + c->radius; - aabb[0] = geom->pos[0] - xrange; - aabb[1] = geom->pos[0] + xrange; - aabb[2] = geom->pos[1] - yrange; - aabb[3] = geom->pos[1] + yrange; - aabb[4] = geom->pos[2] - zrange; - aabb[5] = geom->pos[2] + zrange; -} - - -dColliderFn * dPlaneColliderFn (int num) -{ - return 0; -} - - -static void dPlaneAABB (dxGeom *geom, dReal aabb[6]) -{ - // @@@ planes that have normal vectors aligned along an axis can use a - // @@@ less comprehensive bounding box. - aabb[0] = -dInfinity; - aabb[1] = dInfinity; - aabb[2] = -dInfinity; - aabb[3] = dInfinity; - aabb[4] = -dInfinity; - aabb[5] = dInfinity; -} - - -dxGeom *dCreateSphere (dSpaceID space, dReal radius) -{ - dAASSERT (radius > 0); - if (dSphereClass == -1) { - dGeomClass c; - c.bytes = sizeof (dxSphere); - c.collider = &dSphereColliderFn; - c.aabb = &dSphereAABB; - c.aabb_test = 0; - c.dtor = 0; - dSphereClass = dCreateGeomClass (&c); - } - - dxGeom *g = dCreateGeom (dSphereClass); - if (space) dSpaceAdd (space,g); - dxSphere *s = (dxSphere*) CLASSDATA(g); - s->radius = radius; - return g; -} - - -dxGeom *dCreateBox (dSpaceID space, dReal lx, dReal ly, dReal lz) -{ - dAASSERT (lx > 0 && ly > 0 && lz > 0); - if (dBoxClass == -1) { - dGeomClass c; - c.bytes = sizeof (dxBox); - c.collider = &dBoxColliderFn; - c.aabb = &dBoxAABB; - c.aabb_test = 0; - c.dtor = 0; - dBoxClass = dCreateGeomClass (&c); - } - - dxGeom *g = dCreateGeom (dBoxClass); - if (space) dSpaceAdd (space,g); - dxBox *b = (dxBox*) CLASSDATA(g); - b->side[0] = lx; - b->side[1] = ly; - b->side[2] = lz; - return g; -} - - -dxGeom * dCreateCCylinder (dSpaceID space, dReal radius, dReal length) -{ - dAASSERT (radius > 0 && length > 0); - if (dCCylinderClass == -1) { - dGeomClass c; - c.bytes = sizeof (dxCCylinder); - c.collider = &dCCylinderColliderFn; - c.aabb = &dCCylinderAABB; - c.aabb_test = 0; - c.dtor = 0; - dCCylinderClass = dCreateGeomClass (&c); - } - - dxGeom *g = dCreateGeom (dCCylinderClass); - if (space) dSpaceAdd (space,g); - dxCCylinder *c = (dxCCylinder*) CLASSDATA(g); - c->radius = radius; - c->lz = length; - return g; -} - - -dxGeom *dCreatePlane (dSpaceID space, - dReal a, dReal b, dReal c, dReal d) -{ - if (dPlaneClass == -1) { - dGeomClass c; - c.bytes = sizeof (dxPlane); - c.collider = &dPlaneColliderFn; - c.aabb = &dPlaneAABB; - c.aabb_test = 0; - c.dtor = 0; - dPlaneClass = dCreateGeomClass (&c); - } - - dxGeom *g = dCreateGeom (dPlaneClass); - if (space) dSpaceAdd (space,g); - dxPlane *p = (dxPlane*) CLASSDATA(g); - - // make sure plane normal has unit length - dReal l = a*a + b*b + c*c; - if (l > 0) { - l = dRecipSqrt(l); - p->p[0] = a*l; - p->p[1] = b*l; - p->p[2] = c*l; - p->p[3] = d*l; - } - else { - p->p[0] = 1; - p->p[1] = 0; - p->p[2] = 0; - p->p[3] = 0; - } - return g; -} - - -void dGeomSphereSetRadius (dGeomID g, dReal radius) -{ - dUASSERT (g && g->_class->num == dSphereClass,"argument not a sphere"); - dAASSERT (radius > 0); - dxSphere *s = (dxSphere*) CLASSDATA(g); - s->radius = radius; -} - - -void dGeomBoxSetLengths (dGeomID g, dReal lx, dReal ly, dReal lz) -{ - dUASSERT (g && g->_class->num == dBoxClass,"argument not a box"); - dAASSERT (lx > 0 && ly > 0 && lz > 0); - dxBox *b = (dxBox*) CLASSDATA(g); - b->side[0] = lx; - b->side[1] = ly; - b->side[2] = lz; -} - - -void dGeomPlaneSetParams (dGeomID g, dReal a, dReal b, dReal c, dReal d) -{ - dUASSERT (g && g->_class->num == dPlaneClass,"argument not a plane"); - dxPlane *p = (dxPlane*) CLASSDATA(g); - p->p[0] = a; - p->p[1] = b; - p->p[2] = c; - p->p[3] = d; -} - - -void dGeomCCylinderSetParams (dGeomID g, dReal radius, dReal length) -{ - dUASSERT (g && g->_class->num == dCCylinderClass,"argument not a ccylinder"); - dAASSERT (radius > 0 && length > 0); - dxCCylinder *c = (dxCCylinder*) CLASSDATA(g); - c->radius = radius; - c->lz = length; -} - - -dReal dGeomSphereGetRadius (dGeomID g) -{ - dUASSERT (g && g->_class->num == dSphereClass,"argument not a sphere"); - dxSphere *s = (dxSphere*) CLASSDATA(g); - return s->radius; -} - - -void dGeomBoxGetLengths (dGeomID g, dVector3 result) -{ - dUASSERT (g && g->_class->num == dBoxClass,"argument not a box"); - dxBox *b = (dxBox*) CLASSDATA(g); - result[0] = b->side[0]; - result[1] = b->side[1]; - result[2] = b->side[2]; -} - - -void dGeomPlaneGetParams (dGeomID g, dVector4 result) -{ - dUASSERT (g && g->_class->num == dPlaneClass,"argument not a plane"); - dxPlane *p = (dxPlane*) CLASSDATA(g); - result[0] = p->p[0]; - result[1] = p->p[1]; - result[2] = p->p[2]; - result[3] = p->p[3]; -} - - -void dGeomCCylinderGetParams (dGeomID g, dReal *radius, dReal *length) -{ - dUASSERT (g && g->_class->num == dCCylinderClass,"argument not a ccylinder"); - dxCCylinder *c = (dxCCylinder*) CLASSDATA(g); - *radius = c->radius; - *length = c->lz; -} - -//**************************************************************************** -// geom group - -int dGeomGroupClass = -1; - -static dColliderFn * dGeomGroupColliderFn (int num) -{ - return (dColliderFn *) &dCollideG; -} - - -static void dGeomGroupAABB (dxGeom *geom, dReal aabb[6]) -{ - dxGeomGroup *gr = (dxGeomGroup*) CLASSDATA(geom); - aabb[0] = dInfinity; - aabb[1] = -dInfinity; - aabb[2] = dInfinity; - aabb[3] = -dInfinity; - aabb[4] = dInfinity; - aabb[5] = -dInfinity; - int i,j; - for (i=0; i < gr->parts.size(); i++) { - dReal aabb2[6]; - gr->parts[i]->_class->aabb (gr->parts[i],aabb2); - for (j=0; j<6; j += 2) if (aabb2[j] < aabb[j]) aabb[j] = aabb2[j]; - for (j=1; j<6; j += 2) if (aabb2[j] > aabb[j]) aabb[j] = aabb2[j]; - } -} - - -static void dGeomGroupDtor (dxGeom *geom) -{ - dxGeomGroup *gr = (dxGeomGroup*) CLASSDATA(geom); - gr->parts.~dArray(); -} - - -dxGeom *dCreateGeomGroup (dSpaceID space) -{ - if (dGeomGroupClass == -1) { - dGeomClass c; - c.bytes = sizeof (dxGeomGroup); - c.collider = &dGeomGroupColliderFn; - c.aabb = &dGeomGroupAABB; - c.aabb_test = 0; - c.dtor = &dGeomGroupDtor; - dGeomGroupClass = dCreateGeomClass (&c); - } - - dxGeom *g = dCreateGeom (dGeomGroupClass); - if (space) dSpaceAdd (space,g); - dxGeomGroup *gr = (dxGeomGroup*) CLASSDATA(g); - gr->parts.constructor(); - return g; -} - - -void dGeomGroupAdd (dxGeom *g, dxGeom *x) -{ - dUASSERT (g && g->_class->num == dGeomGroupClass,"argument not a geomgroup"); - dxGeomGroup *gr = (dxGeomGroup*) CLASSDATA(g); - gr->parts.push (x); -} - - -void dGeomGroupRemove (dxGeom *g, dxGeom *x) -{ - dUASSERT (g && g->_class->num == dGeomGroupClass,"argument not a geomgroup"); - dxGeomGroup *gr = (dxGeomGroup*) CLASSDATA(g); - for (int i=0; i < gr->parts.size(); i++) { - if (gr->parts[i] == x) { - gr->parts.remove (i); - return; - } - } -} - - -int dGeomGroupGetNumGeoms (dxGeom *g) -{ - dUASSERT (g && g->_class->num == dGeomGroupClass,"argument not a geomgroup"); - dxGeomGroup *gr = (dxGeomGroup*) CLASSDATA(g); - return gr->parts.size(); -} - - -dxGeom * dGeomGroupGetGeom (dxGeom *g, int i) -{ - dUASSERT (g && g->_class->num == dGeomGroupClass,"argument not a geomgroup"); - dxGeomGroup *gr = (dxGeomGroup*) CLASSDATA(g); - dAASSERT (i >= 0 && i < gr->parts.size()); - return gr->parts[i]; -} - -//**************************************************************************** -// transformed geom - -int dGeomTransformClass = -1; - -struct dxGeomTransform { - dxGeom *obj; // object that is being transformed - int cleanup; // 1 to destroy obj when destroyed - int infomode; // 1 to put Tx geom in dContactGeom g1 - dVector3 final_pos; // final tx (body tx + relative tx) of the object. - dMatrix3 final_R; // this is only set if the AABB function is called -}; // by space collision before the collide fn is called - - -// compute final pos and R for the encapsulated geom object - -static void compute_final_tx (const dxGeom *g) -{ - dxGeomTransform *tr = (dxGeomTransform*) CLASSDATA(g); - dMULTIPLY0_331 (tr->final_pos,g->R,tr->obj->pos); - tr->final_pos[0] += g->pos[0]; - tr->final_pos[1] += g->pos[1]; - tr->final_pos[2] += g->pos[2]; - dMULTIPLY0_333 (tr->final_R,g->R,tr->obj->R); -} - - - -// this collides a transformed geom with another geom. the other geom can -// also be a transformed geom, but this case is not handled specially. - -int dCollideT (const dxGeom *o1, const dxGeom *o2, int flags, - dContactGeom *contact, int skip) -{ - dxGeomTransform *tr = (dxGeomTransform*) CLASSDATA(o1); - if (!tr->obj) return 0; - dUASSERT (tr->obj->spaceid==0, - "GeomTransform encapsulated object must not be in a space"); - dUASSERT (tr->obj->body==0, - "GeomTransform encapsulated object must not be attach to a body"); - - // backup the relative pos and R pointers of the encapsulated geom object, - // and the body pointer - dReal *posbak = tr->obj->pos; - dReal *Rbak = tr->obj->R; - dxBody *bodybak = tr->obj->body; - - // compute temporary pos and R for the encapsulated geom object - if (!o1->space_aabb) compute_final_tx (o1); - tr->obj->pos = tr->final_pos; - tr->obj->R = tr->final_R; - tr->obj->body = o1->body; - - // do the collision - int n = dCollide (tr->obj,const_cast<dxGeom*>(o2),flags,contact,skip); - - // if required, adjust the 'g1' values in the generated contacts so that - // thay indicated the GeomTransform object instead of the encapsulated - // object. - if (tr->infomode) { - for (int i=0; i<n; i++) { - dContactGeom *c = CONTACT(contact,skip*i); - c->g1 = const_cast<dxGeom*> (o1); - } - } - - // restore the pos, R and body - tr->obj->pos = posbak; - tr->obj->R = Rbak; - tr->obj->body = bodybak; - return n; -} - - -static dColliderFn * dGeomTransformColliderFn (int num) -{ - return (dColliderFn *) &dCollideT; -} - - -static void dGeomTransformAABB (dxGeom *geom, dReal aabb[6]) -{ - dxGeomTransform *tr = (dxGeomTransform*) CLASSDATA(geom); - if (!tr->obj) { - dSetZero (aabb,6); - return; - } - - // backup the relative pos and R pointers of the encapsulated geom object - dReal *posbak = tr->obj->pos; - dReal *Rbak = tr->obj->R; - - // compute temporary pos and R for the encapsulated geom object - compute_final_tx (geom); - tr->obj->pos = tr->final_pos; - tr->obj->R = tr->final_R; - - // compute the AABB - tr->obj->_class->aabb (tr->obj,aabb); - - // restore the pos and R - tr->obj->pos = posbak; - tr->obj->R = Rbak; -} - - -static void dGeomTransformDtor (dxGeom *geom) -{ - dxGeomTransform *tr = (dxGeomTransform*) CLASSDATA(geom); - if (tr->obj && tr->cleanup) { - dGeomDestroy (tr->obj); - } -} - - -dxGeom *dCreateGeomTransform (dSpaceID space) -{ - if (dGeomTransformClass == -1) { - dGeomClass c; - c.bytes = sizeof (dxGeomTransform); - c.collider = &dGeomTransformColliderFn; - c.aabb = &dGeomTransformAABB; - c.aabb_test = 0; - c.dtor = dGeomTransformDtor; - dGeomTransformClass = dCreateGeomClass (&c); - } - - dxGeom *g = dCreateGeom (dGeomTransformClass); - if (space) dSpaceAdd (space,g); - - dxGeomTransform *tr = (dxGeomTransform*) CLASSDATA(g); - tr->obj = 0; - tr->cleanup = 0; - tr->infomode = 0; - dSetZero (tr->final_pos,4); - dRSetIdentity (tr->final_R); - - return g; -} - - -void dGeomTransformSetGeom (dxGeom *g, dxGeom *obj) -{ - dUASSERT (g && g->_class->num == dGeomTransformClass, - "argument not a geom transform"); - dxGeomTransform *tr = (dxGeomTransform*) CLASSDATA(g); - if (tr->obj && tr->cleanup) { - dGeomDestroy (tr->obj); - } - tr->obj = obj; -} - - -dxGeom * dGeomTransformGetGeom (dxGeom *g) -{ - dUASSERT (g && g->_class->num == dGeomTransformClass, - "argument not a geom transform"); - dxGeomTransform *tr = (dxGeomTransform*) CLASSDATA(g); - return tr->obj; -} - - -void dGeomTransformSetCleanup (dGeomID g, int mode) -{ - dUASSERT (g && g->_class->num == dGeomTransformClass, - "argument not a geom transform"); - dxGeomTransform *tr = (dxGeomTransform*) CLASSDATA(g); - tr->cleanup = mode; -} - - -int dGeomTransformGetCleanup (dGeomID g) -{ - dUASSERT (g && g->_class->num == dGeomTransformClass, - "argument not a geom transform"); - dxGeomTransform *tr = (dxGeomTransform*) CLASSDATA(g); - return tr->cleanup; -} - - -void dGeomTransformSetInfo (dGeomID g, int mode) -{ - dUASSERT (g && g->_class->num == dGeomTransformClass, - "argument not a geom transform"); - dxGeomTransform *tr = (dxGeomTransform*) CLASSDATA(g); - tr->infomode = mode; -} - - -int dGeomTransformGetInfo (dGeomID g) -{ - dUASSERT (g && g->_class->num == dGeomTransformClass, - "argument not a geom transform"); - dxGeomTransform *tr = (dxGeomTransform*) CLASSDATA(g); - return tr->infomode; -} - -//**************************************************************************** -// other utility functions - -void dInfiniteAABB (dxGeom *geom, dReal aabb[6]) -{ - aabb[0] = -dInfinity; - aabb[1] = dInfinity; - aabb[2] = -dInfinity; - aabb[3] = dInfinity; - aabb[4] = -dInfinity; - aabb[5] = dInfinity; -} - - -void dCloseODE() -{ - if (colliders) { - delete colliders; - colliders = 0; - } - if (classes) { - for (int i=0; i < classes->size(); i++) { - dFree ((*classes)[i], sizeof (dxGeomClass)); - } - delete classes; - classes = 0; - } - - // reset geom class vars - dSphereClass = -1; - dBoxClass = -1; - dCCylinderClass = -1; - dPlaneClass = -1; - dGeomGroupClass = -1; - dGeomTransformClass = -1; - - // if you're using contrib code you may want to uncomment the following: - // dTriListClass = -1; - // dRayClass = -1; -} |