diff -r 000000000000 -r 2f259fa3e83a ode/src/capsule.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ode/src/capsule.cpp Tue Feb 02 01:00:49 2010 +0200 @@ -0,0 +1,340 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001-2003 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. * + * * + *************************************************************************/ + +/* + +standard ODE geometry primitives: public API and pairwise collision functions. + +the rule is that only the low level primitive collision functions should set +dContactGeom::g1 and dContactGeom::g2. + +*/ + +#include +#include +#include +#include +#include +#include "collision_kernel.h" +#include "collision_std.h" +#include "collision_util.h" + +//**************************************************************************** +// capped cylinder public API + +dxCapsule::dxCapsule (dSpaceID space, dReal _radius, dReal _length) : + dxGeom (space,1) +{ + type = dCapsuleClass; + radius = _radius; + lz = _length; +} + + +void dxCapsule::computeAABB() +{ + const dMatrix3& R = final_posr->R; + const dVector3& pos = final_posr->pos; + + dReal xrange = dMUL(dFabs(dMUL(R[2],lz)),REAL(0.5)) + radius; + dReal yrange = dMUL(dFabs(dMUL(R[6],lz)),REAL(0.5)) + radius; + dReal zrange = dMUL(dFabs(dMUL(R[10],lz)),REAL(0.5)) + radius; + aabb[0] = pos[0] - xrange; + aabb[1] = pos[0] + xrange; + aabb[2] = pos[1] - yrange; + aabb[3] = pos[1] + yrange; + aabb[4] = pos[2] - zrange; + aabb[5] = pos[2] + zrange; +} + + +EXPORT_C dGeomID dCreateCapsule (dSpaceID space, dReal radius, dReal length) +{ + return new dxCapsule (space,radius,length); +} + + +EXPORT_C void dGeomCapsuleSetParams (dGeomID g, dReal radius, dReal length) +{ + dxCapsule *c = (dxCapsule*) g; + c->radius = radius; + c->lz = length; + dGeomMoved (g); +} + + +EXPORT_C void dGeomCapsuleGetParams (dGeomID g, dReal *radius, dReal *length) +{ + dxCapsule *c = (dxCapsule*) g; + *radius = c->radius; + *length = c->lz; +} + + +EXPORT_C dReal dGeomCapsulePointDepth (dGeomID g, dReal x, dReal y, dReal z) +{ + g->recomputePosr(); + dxCapsule *c = (dxCapsule*) g; + + const dReal* R = g->final_posr->R; + const dReal* pos = g->final_posr->pos; + + dVector3 a; + a[0] = x - pos[0]; + a[1] = y - pos[1]; + a[2] = z - pos[2]; + dReal beta = dDOT14(a,R+2); + dReal lz2 = dMUL(c->lz,REAL(0.5)); + if (beta < -lz2) beta = -lz2; + else if (beta > lz2) beta = lz2; + a[0] = c->final_posr->pos[0] + dMUL(beta,R[0*4+2]); + a[1] = c->final_posr->pos[1] + dMUL(beta,R[1*4+2]); + a[2] = c->final_posr->pos[2] + dMUL(beta,R[2*4+2]); + return c->radius - + dSqrt (dMUL((x-a[0]),(x-a[0])) + dMUL((y-a[1]),(y-a[1])) + dMUL((z-a[2]),(z-a[2]))); +} + + + +int dCollideCapsuleSphere (dxGeom *o1, dxGeom *o2, int /*flags*/, + dContactGeom *contact, int /*skip*/) +{ + dxCapsule *ccyl = (dxCapsule*) o1; + dxSphere *sphere = (dxSphere*) o2; + + contact->g1 = o1; + contact->g2 = o2; + + // find the point on the cylinder axis that is closest to the sphere + dReal alpha = + dMUL(o1->final_posr->R[2],(o2->final_posr->pos[0] - o1->final_posr->pos[0])) + + dMUL(o1->final_posr->R[6],(o2->final_posr->pos[1] - o1->final_posr->pos[1])) + + dMUL(o1->final_posr->R[10],(o2->final_posr->pos[2] - o1->final_posr->pos[2])); + dReal lz2 = dMUL(ccyl->lz,REAL(0.5)); + if (alpha > lz2) alpha = lz2; + if (alpha < -lz2) alpha = -lz2; + + // collide the spheres + dVector3 p; + p[0] = o1->final_posr->pos[0] + dMUL(alpha,o1->final_posr->R[2]); + p[1] = o1->final_posr->pos[1] + dMUL(alpha,o1->final_posr->R[6]); + p[2] = o1->final_posr->pos[2] + dMUL(alpha,o1->final_posr->R[10]); + return dCollideSpheres (p,ccyl->radius,o2->final_posr->pos,sphere->radius,contact); +} + + +int dCollideCapsuleBox (dxGeom *o1, dxGeom *o2, int /*flags*/, + dContactGeom *contact, int /*skip*/) +{ + dxCapsule *cyl = (dxCapsule*) o1; + dxBox *box = (dxBox*) o2; + + contact->g1 = o1; + contact->g2 = o2; + + // get p1,p2 = cylinder axis endpoints, get radius + dVector3 p1,p2; + dReal clen = dMUL(cyl->lz,REAL(0.5)); + p1[0] = o1->final_posr->pos[0] + dMUL(clen,o1->final_posr->R[2]); + p1[1] = o1->final_posr->pos[1] + dMUL(clen,o1->final_posr->R[6]); + p1[2] = o1->final_posr->pos[2] + dMUL(clen,o1->final_posr->R[10]); + p2[0] = o1->final_posr->pos[0] - dMUL(clen,o1->final_posr->R[2]); + p2[1] = o1->final_posr->pos[1] - dMUL(clen,o1->final_posr->R[6]); + p2[2] = o1->final_posr->pos[2] - dMUL(clen,o1->final_posr->R[10]); + dReal radius = cyl->radius; + + // copy out box center, rotation matrix, and side array + dReal *c = o2->final_posr->pos; + dReal *R = o2->final_posr->R; + const 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); +} + + +int dCollideCapsuleCapsule (dxGeom *o1, dxGeom *o2, + int flags, dContactGeom *contact, int skip) +{ + int i; + const dReal tolerance = REAL(2e-5); + + dxCapsule *cyl1 = (dxCapsule*) o1; + dxCapsule *cyl2 = (dxCapsule*) o2; + + contact->g1 = o1; + contact->g2 = o2; + + // copy out some variables, for convenience + dReal lz1 = dMUL(cyl1->lz,REAL(0.5)); + dReal lz2 = dMUL(cyl2->lz,REAL(0.5)); + dReal *pos1 = o1->final_posr->pos; + dReal *pos2 = o2->final_posr->pos; + dReal axis1[3],axis2[3]; + axis1[0] = o1->final_posr->R[2]; + axis1[1] = o1->final_posr->R[6]; + axis1[2] = o1->final_posr->R[10]; + axis2[0] = o2->final_posr->R[2]; + axis2[1] = o2->final_posr->R[6]; + axis2[2] = o2->final_posr->R[10]; + + // if the cylinder axes are close to parallel, we'll try to detect up to + // two contact points along the body of the cylinder. if we can't find any + // points then we'll fall back to the closest-points algorithm. note that + // we are not treating this special case for reasons of degeneracy, but + // because we want two contact points in some situations. the closet-points + // algorithm is robust in all casts, but it can return only one contact. + + dVector3 sphere1,sphere2; + dReal a1a2 = dDOT (axis1,axis2); + dReal det = REAL(1.0)-dMUL(a1a2,a1a2); + if (det < tolerance) { + // the cylinder axes (almost) parallel, so we will generate up to two + // contacts. alpha1 and alpha2 (line position parameters) 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] + dMUL(lo,axis1[i]); + for (i=0; i<3; i++) sphere2[i] = pos2[i] + dMUL((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] + dMUL(hi,axis1[i]); + for (i=0; i<3; i++) sphere2[i] = pos2[i] + dMUL((hi+k),axis2[i]); + dContactGeom *c2 = CONTACT(contact,skip); + int n2 = dCollideSpheres (sphere1,cyl1->radius, + sphere2,cyl2->radius, c2); + if (n2) { + c2->g1 = o1; + c2->g2 = o2; + return 2; + } + } + } + + // just one contact to generate, so put it in the middle of + // the range + dReal alpha1 = dMUL((lo + hi),REAL(0.5)); + dReal alpha2 = alpha1 + k; + for (i=0; i<3; i++) sphere1[i] = pos1[i] + dMUL(alpha1,axis1[i]); + for (i=0; i<3; i++) sphere2[i] = pos2[i] + dMUL(alpha2,axis2[i]); + return dCollideSpheres (sphere1,cyl1->radius, + sphere2,cyl2->radius,contact); + } + } + + // use the closest point algorithm + dVector3 a1,a2,b1,b2; + a1[0] = o1->final_posr->pos[0] + dMUL(axis1[0],lz1); + a1[1] = o1->final_posr->pos[1] + dMUL(axis1[1],lz1); + a1[2] = o1->final_posr->pos[2] + dMUL(axis1[2],lz1); + a2[0] = o1->final_posr->pos[0] - dMUL(axis1[0],lz1); + a2[1] = o1->final_posr->pos[1] - dMUL(axis1[1],lz1); + a2[2] = o1->final_posr->pos[2] - dMUL(axis1[2],lz1); + b1[0] = o2->final_posr->pos[0] + dMUL(axis2[0],lz2); + b1[1] = o2->final_posr->pos[1] + dMUL(axis2[1],lz2); + b1[2] = o2->final_posr->pos[2] + dMUL(axis2[2],lz2); + b2[0] = o2->final_posr->pos[0] - dMUL(axis2[0],lz2); + b2[1] = o2->final_posr->pos[1] - dMUL(axis2[1],lz2); + b2[2] = o2->final_posr->pos[2] - dMUL(axis2[2],lz2); + + dClosestLineSegmentPoints (a1,a2,b1,b2,sphere1,sphere2); + return dCollideSpheres (sphere1,cyl1->radius,sphere2,cyl2->radius,contact); +} + + +int dCollideCapsulePlane (dxGeom *o1, dxGeom *o2, int flags, + dContactGeom *contact, int skip) +{ + dxCapsule *ccyl = (dxCapsule*) o1; + dxPlane *plane = (dxPlane*) o2; + + // collide the deepest capping sphere with the plane + dReal sign = (dDOT14 (plane->p,o1->final_posr->R+2) > 0) ? REAL(-1.0) : REAL(1.0); + dVector3 p; + p[0] = o1->final_posr->pos[0] + dMUL(o1->final_posr->R[2],dMUL(ccyl->lz,dMUL(REAL(0.5),sign))); + p[1] = o1->final_posr->pos[1] + dMUL(o1->final_posr->R[6],dMUL(ccyl->lz,dMUL(REAL(0.5),sign))); + p[2] = o1->final_posr->pos[2] + dMUL(o1->final_posr->R[10],dMUL(ccyl->lz,dMUL(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] - dMUL(plane->p[0],ccyl->radius); + contact->pos[1] = p[1] - dMUL(plane->p[1],ccyl->radius); + contact->pos[2] = p[2] - dMUL(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->final_posr->pos[0] - dMUL(o1->final_posr->R[2],dMUL(ccyl->lz,dMUL(REAL(0.5),sign))); + p[1] = o1->final_posr->pos[1] - dMUL(o1->final_posr->R[6],dMUL(ccyl->lz,dMUL(REAL(0.5),sign))); + p[2] = o1->final_posr->pos[2] - dMUL(o1->final_posr->R[10],dMUL(ccyl->lz,dMUL(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] - dMUL(plane->p[0],ccyl->radius); + c2->pos[1] = p[1] - dMUL(plane->p[1],ccyl->radius); + c2->pos[2] = p[2] - dMUL(plane->p[2],ccyl->radius); + c2->depth = depth; + ncontacts = 2; + } + } + + for (int i=0; i < ncontacts; i++) { + CONTACT(contact,i*skip)->g1 = o1; + CONTACT(contact,i*skip)->g2 = o2; + } + return ncontacts; +} +