[ODE] Flat ended cylinder
Patrik Stellmann
patrik at volleynet.de
Fri Jul 25 05:38:02 2003
This is a multi-part message in MIME format.
--------------020006020207010206000100
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit
>PS> I also noticed that the cylinder-ray collider doesn't care about the end
>PS> of the cylinder but seems to assume the cylinder has infinite length!?
>
>Cylinder - Ray is Olivier Michel's work. But as far as I know it works
>as it should.
>
I finally took the time to debug the code: when calculating the
intersection of the ray with the cylinder caps only the length of the
ray is checked but not if the intersection point is also within the
circle. I added this check and it works now fine for me - probably not
the most efficient way but I didn't feel like trying to understand the
rest of the code and find out if I could better reuse existing data...
However, I attached the modified function (only changed code within the
markers).
Patrik
--------------020006020207010206000100
Content-Type: text/plain;
name="CylRay.c"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="CylRay.c"
int dCollideCylRay(dxGeom *o1, dxGeom *o2, int flags,
dContactGeom *contact, int skip) {
dIASSERT (skip >= (int)sizeof(dContactGeom));
dIASSERT (dGeomGetClass(o1) == dCylinderClassUser);
dIASSERT (dGeomGetClass(o2) == dRayClass);
contact->g1 = const_cast<dxGeom*> (o1);
contact->g2 = const_cast<dxGeom*> (o2);
dReal radius;
dReal lz;
dGeomCylinderGetParams(o1,&radius,&lz);
dReal lz2=lz*REAL(0.5);
const dReal *R = dGeomGetRotation(o1); // rotation of the cylinder
const dReal *p = dGeomGetPosition(o1); // position of the cylinder
dVector3 start,dir;
dGeomRayGet(o2,start,dir); // position and orientation of the ray
dReal length = dGeomRayGetLength(o2);
// compute some useful info
dVector3 cs,q,r;
dReal C,k;
cs[0] = start[0] - p[0];
cs[1] = start[1] - p[1];
cs[2] = start[2] - p[2];
k = dDOT41(R+1,cs); // position of ray start along cyl axis (Y)
q[0] = k*R[0*4+1] - cs[0];
q[1] = k*R[1*4+1] - cs[1];
q[2] = k*R[2*4+1] - cs[2];
C = dDOT(q,q) - radius*radius;
// if C < 0 then ray start position within infinite extension of cylinder
// if ray start position is inside the cylinder
int inside_cyl=0;
if (C<0 && !(k<-lz2 || k>lz2)) inside_cyl=1;
// compute ray collision with infinite cylinder, except for the case where
// the ray is outside the cylinder but within the infinite cylinder
// (it that case the ray can only hit endcaps)
if (!inside_cyl && C < 0) {
// set k to cap position to check
if (k < 0) k = -lz2; else k = lz2;
}
else {
dReal uv = dDOT41(R+1,dir);
r[0] = uv*R[0*4+1] - dir[0];
r[1] = uv*R[1*4+1] - dir[1];
r[2] = uv*R[2*4+1] - dir[2];
dReal A = dDOT(r,r);
dReal B = 2*dDOT(q,r);
k = B*B-4*A*C;
if (k < 0) {
// the ray does not intersect the infinite cylinder, but if the ray is
// inside and parallel to the cylinder axis it may intersect the end
// caps. set k to cap position to check.
if (!inside_cyl) return 0;
if (uv < 0) k = -lz2; else k = lz2;
}
else {
k = dSqrt(k);
A = dRecip (2*A);
dReal alpha = (-B-k)*A;
if (alpha < 0) {
alpha = (-B+k)*A;
if (alpha<0) return 0;
}
if (alpha>length) return 0;
// the ray intersects the infinite cylinder. check to see if the
// intersection point is between the caps
contact->pos[0] = start[0] + alpha*dir[0];
contact->pos[1] = start[1] + alpha*dir[1];
contact->pos[2] = start[2] + alpha*dir[2];
q[0] = contact->pos[0] - p[0];
q[1] = contact->pos[1] - p[1];
q[2] = contact->pos[2] - p[2];
k = dDOT14(q,R+1);
dReal nsign = inside_cyl ? -1 : 1;
if (k >= -lz2 && k <= lz2) {
contact->normal[0] = nsign * (contact->pos[0] -
(p[0] + k*R[0*4+1]));
contact->normal[1] = nsign * (contact->pos[1] -
(p[1] + k*R[1*4+1]));
contact->normal[2] = nsign * (contact->pos[2] -
(p[2] + k*R[2*4+1]));
dNormalize3 (contact->normal);
contact->depth = alpha;
return 1;
}
// the infinite cylinder intersection point is not between the caps.
// set k to cap position to check.
if (k < 0) k = -lz2; else k = lz2;
}
}
// check for ray intersection with the caps. k must indicate the cap
// position to check
// perform a ray plan interesection
// R+1 is the plan normal
// <MODIFIED>
cs[0] = (p[0] + k*R[0*4+1]);
cs[1] = (p[1] + k*R[1*4+1]);
cs[2] = (p[2] + k*R[2*4+1]);
q[0] = start[0] - cs[0];
q[1] = start[1] - cs[1];
q[2] = start[2] - cs[2];
// </MODIFIED>
dReal alpha = -dDOT14(q,R+1);
dReal k2 = dDOT14(dir,R+1);
if (k2==0) return 0; // ray parallel to the plane
alpha/=k2;
if (alpha<0 || alpha>length) return 0; // too short
contact->pos[0]=start[0]+alpha*dir[0];
contact->pos[1]=start[1]+alpha*dir[1];
contact->pos[2]=start[2]+alpha*dir[2];
// <MODIFIED>
// check distance of contact pos from cap-position with radidus
cs[0] -= contact->pos[0];
cs[1] -= contact->pos[1];
cs[2] -= contact->pos[2];
if (dSqrt(dDOT(cs, cs)) > radius) return 0;
// *******************
// </MODIFIED>
dReal nsign = (k<0)?-1:1;
contact->normal[0]=nsign*R[0*4+1];
contact->normal[1]=nsign*R[1*4+1];
contact->normal[2]=nsign*R[2*4+1];
contact->depth=alpha;
return 1;
}
--------------020006020207010206000100--