OpenSimMirror/libraries/ode-0.9/contrib/dCylinder/dCylinder.cpp

1446 lines
38 KiB
C++

#include <ode/ode.h>
#include "dCylinder.h"
// given a pointer `p' to a dContactGeom, return the dContactGeom at
// p + skip bytes.
struct dxCylinder { // cylinder
dReal radius,lz; // radius, length along y axis //
};
int dCylinderClassUser = -1;
#define NUMC_MASK (0xffff)
#define CONTACT(p,skip) ((dContactGeom*) (((char*)p) + (skip)))
/////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////circleIntersection//////////////////////////////////////////////////
//this does following:
//takes two circles as normals to planes n1,n2, center points cp1,cp2,and radiuses r1,r2
//finds line on which circles' planes intersect
//finds four points O1,O2 - intersection between the line and sphere with center cp1 radius r1
// O3,O4 - intersection between the line and sphere with center cp2 radius r2
//returns false if there is no intersection
//computes distances O1-O3, O1-O4, O2-O3, O2-O4
//in "point" returns mean point between intersection points with smallest distance
/////////////////////////////////////////////////////////////////////////////////////////////////
inline bool circleIntersection(const dReal* n1,const dReal* cp1,dReal r1,const dReal* n2,const dReal* cp2,dReal r2,dVector3 point){
dReal c1=dDOT14(cp1,n1);
dReal c2=dDOT14(cp2,n2);
dReal cos=dDOT44(n1,n2);
dReal cos_2=cos*cos;
dReal sin_2=1-cos_2;
dReal p1=(c1-c2*cos)/sin_2;
dReal p2=(c2-c1*cos)/sin_2;
dVector3 lp={p1*n1[0]+p2*n2[0],p1*n1[4]+p2*n2[4],p1*n1[8]+p2*n2[8]};
dVector3 n;
dCROSS144(n,=,n1,n2);
dVector3 LC1={lp[0]-cp1[0],lp[1]-cp1[1],lp[2]-cp1[2]};
dVector3 LC2={lp[0]-cp2[0],lp[1]-cp2[1],lp[2]-cp2[2]};
dReal A,B,C,B_A,B_A_2,D;
dReal t1,t2,t3,t4;
A=dDOT(n,n);
B=dDOT(LC1,n);
C=dDOT(LC1,LC1)-r1*r1;
B_A=B/A;
B_A_2=B_A*B_A;
D=B_A_2-C;
if(D<0.f){ //somewhat strange solution
//- it is needed to set some
//axis to sepparate cylinders
//when their edges approach
t1=-B_A+sqrtf(-D);
t2=-B_A-sqrtf(-D);
// return false;
}
else{
t1=-B_A-sqrtf(D);
t2=-B_A+sqrtf(D);
}
B=dDOT(LC2,n);
C=dDOT(LC2,LC2)-r2*r2;
B_A=B/A;
B_A_2=B_A*B_A;
D=B_A_2-C;
if(D<0.f) {
t3=-B_A+sqrtf(-D);
t4=-B_A-sqrtf(-D);
// return false;
}
else{
t3=-B_A-sqrtf(D);
t4=-B_A+sqrtf(D);
}
dVector3 O1={lp[0]+n[0]*t1,lp[1]+n[1]*t1,lp[2]+n[2]*t1};
dVector3 O2={lp[0]+n[0]*t2,lp[1]+n[1]*t2,lp[2]+n[2]*t2};
dVector3 O3={lp[0]+n[0]*t3,lp[1]+n[1]*t3,lp[2]+n[2]*t3};
dVector3 O4={lp[0]+n[0]*t4,lp[1]+n[1]*t4,lp[2]+n[2]*t4};
dVector3 L1_3={O3[0]-O1[0],O3[1]-O1[1],O3[2]-O1[2]};
dVector3 L1_4={O4[0]-O1[0],O4[1]-O1[1],O4[2]-O1[2]};
dVector3 L2_3={O3[0]-O2[0],O3[1]-O2[1],O3[2]-O2[2]};
dVector3 L2_4={O4[0]-O2[0],O4[1]-O2[1],O4[2]-O2[2]};
dReal l1_3=dDOT(L1_3,L1_3);
dReal l1_4=dDOT(L1_4,L1_4);
dReal l2_3=dDOT(L2_3,L2_3);
dReal l2_4=dDOT(L2_4,L2_4);
if (l1_3<l1_4)
if(l2_3<l2_4)
if(l1_3<l2_3)
{
//l1_3;
point[0]=0.5f*(O1[0]+O3[0]);
point[1]=0.5f*(O1[1]+O3[1]);
point[2]=0.5f*(O1[2]+O3[2]);
}
else{
//l2_3;
point[0]=0.5f*(O2[0]+O3[0]);
point[1]=0.5f*(O2[1]+O3[1]);
point[2]=0.5f*(O2[2]+O3[2]);
}
else
if(l1_3<l2_4)
{
//l1_3;
point[0]=0.5f*(O1[0]+O3[0]);
point[1]=0.5f*(O1[1]+O3[1]);
point[2]=0.5f*(O1[2]+O3[2]);
}
else{
//l2_4;
point[0]=0.5f*(O2[0]+O4[0]);
point[1]=0.5f*(O2[1]+O4[1]);
point[2]=0.5f*(O2[2]+O4[2]);
}
else
if(l2_3<l2_4)
if(l1_4<l2_3)
{
//l1_4;
point[0]=0.5f*(O1[0]+O4[0]);
point[1]=0.5f*(O1[1]+O4[1]);
point[2]=0.5f*(O1[2]+O4[2]);
}
else{
//l2_3;
point[0]=0.5f*(O2[0]+O3[0]);
point[1]=0.5f*(O2[1]+O3[1]);
point[2]=0.5f*(O2[2]+O3[2]);
}
else
if(l1_4<l2_4)
{
//l1_4;
point[0]=0.5f*(O1[0]+O4[0]);
point[1]=0.5f*(O1[1]+O4[1]);
point[2]=0.5f*(O1[2]+O4[2]);
}
else{
//l2_4;
point[0]=0.5f*(O2[0]+O4[0]);
point[1]=0.5f*(O2[1]+O4[1]);
point[2]=0.5f*(O2[2]+O4[2]);
}
return true;
}
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;
}
}
// @@@ some stuff to optimize here, reuse code in contact point calculations.
extern "C" int dCylBox (const dVector3 p1, const dMatrix3 R1,
const dReal radius,const dReal lz, 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 B1,B2,B3,R11,R12,R13,R21,R22,R23,R31,R32,R33,
Q11,Q12,Q13,Q21,Q22,Q23,Q31,Q32,Q33,s,s2,l,sQ21,sQ22,sQ23;
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 =radius; A2 = lz*REAL(0.5); A3 = radius;
dReal hlz=lz/2.f;
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);
// * see if the axis separates the box with cylinder. 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 = cylinder ax u2
//used when a box vertex touches a flat face of the cylinder
TEST (pp[1],(hlz + B1*Q21 + B2*Q22 + B3*Q23),R1+1,0);
// separating axis = box axis v1,v2,v3
//used when cylinder edge touches box face
//there is two ways to compute sQ: sQ21=sqrtf(1.f-Q21*Q21); or sQ21=sqrtf(Q23*Q23+Q22*Q22);
//if we did not need Q23 and Q22 the first way might be used to quiken the routine but then it need to
//check if Q21<=1.f, becouse it may slightly exeed 1.f.
sQ21=sqrtf(Q23*Q23+Q22*Q22);
TEST (dDOT41(R2+0,p),(radius*sQ21 + hlz*Q21 + B1),R2+0,1);
sQ22=sqrtf(Q23*Q23+Q21*Q21);
TEST (dDOT41(R2+1,p),(radius*sQ22 + hlz*Q22 + B2),R2+1,2);
sQ23=sqrtf(Q22*Q22+Q21*Q21);
TEST (dDOT41(R2+2,p),(radius*sQ23 + hlz*Q23 + B3),R2+2,3);
#undef TEST
#define TEST(expr1,expr2,n1,n2,n3,cc) \
s2 = dFabs(expr1) - (expr2); \
if (s2 > 0) return 0; \
if (s2 > s) { \
s = s2; \
normalR = 0; \
normalC[0] = (n1); normalC[1] = (n2); normalC[2] = (n3); \
invert_normal = ((expr1) < 0); \
*code = (cc); \
}
// separating axis is a normal to the cylinder axis passing across the nearest box vertex
//used when a box vertex touches the lateral surface of the cylinder
dReal proj,boxProj,cos,sin,cos1,cos3;
dVector3 tAx,Ax,pb;
{
//making Ax which is perpendicular to cyl ax to box position//
proj=dDOT14(p2,R1+1)-dDOT14(p1,R1+1);
Ax[0]=p2[0]-p1[0]-R1[1]*proj;
Ax[1]=p2[1]-p1[1]-R1[5]*proj;
Ax[2]=p2[2]-p1[2]-R1[9]*proj;
dNormalize3(Ax);
//using Ax find box vertex which is nearest to the cylinder axis
dReal sign;
for (i=0; i<3; i++) pb[i] = p2[i];
sign = (dDOT14(Ax,R2+0) > 0) ? REAL(-1.0) : REAL(1.0);
for (i=0; i<3; i++) pb[i] += sign * B1 * R2[i*4];
sign = (dDOT14(Ax,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(Ax,R2+2) > 0) ? REAL(-1.0) : REAL(1.0);
for (i=0; i<3; i++) pb[i] += sign * B3 * R2[i*4+2];
//building axis which is normal to cylinder ax to the nearest box vertex
proj=dDOT14(pb,R1+1)-dDOT14(p1,R1+1);
Ax[0]=pb[0]-p1[0]-R1[1]*proj;
Ax[1]=pb[1]-p1[1]-R1[5]*proj;
Ax[2]=pb[2]-p1[2]-R1[9]*proj;
dNormalize3(Ax);
}
boxProj=dFabs(dDOT14(Ax,R2+0)*B1)+
dFabs(dDOT14(Ax,R2+1)*B2)+
dFabs(dDOT14(Ax,R2+2)*B3);
TEST(p[0]*Ax[0]+p[1]*Ax[1]+p[2]*Ax[2],(radius+boxProj),Ax[0],Ax[1],Ax[2],4);
//next three test used to handle collisions between cylinder circles and box ages
proj=dDOT14(p1,R2+0)-dDOT14(p2,R2+0);
tAx[0]=-p1[0]+p2[0]+R2[0]*proj;
tAx[1]=-p1[1]+p2[1]+R2[4]*proj;
tAx[2]=-p1[2]+p2[2]+R2[8]*proj;
dNormalize3(tAx);
//now tAx is normal to first ax of the box to cylinder center
//making perpendicular to tAx lying in the plane which is normal to the cylinder axis
//it is tangent in the point where projection of tAx on cylinder's ring intersect edge circle
cos=dDOT14(tAx,R1+0);
sin=dDOT14(tAx,R1+2);
tAx[0]=R1[2]*cos-R1[0]*sin;
tAx[1]=R1[6]*cos-R1[4]*sin;
tAx[2]=R1[10]*cos-R1[8]*sin;
//use cross between tAx and first ax of the box as separating axix
dCROSS114(Ax,=,tAx,R2+0);
dNormalize3(Ax);
boxProj=dFabs(dDOT14(Ax,R2+1)*B2)+
dFabs(dDOT14(Ax,R2+0)*B1)+
dFabs(dDOT14(Ax,R2+2)*B3);
cos=dFabs(dDOT14(Ax,R1+1));
cos1=dDOT14(Ax,R1+0);
cos3=dDOT14(Ax,R1+2);
sin=sqrtf(cos1*cos1+cos3*cos3);
TEST(p[0]*Ax[0]+p[1]*Ax[1]+p[2]*Ax[2],(sin*radius+cos*hlz+boxProj),Ax[0],Ax[1],Ax[2],5);
//same thing with the second axis of the box
proj=dDOT14(p1,R2+1)-dDOT14(p2,R2+1);
tAx[0]=-p1[0]+p2[0]+R2[1]*proj;
tAx[1]=-p1[1]+p2[1]+R2[5]*proj;
tAx[2]=-p1[2]+p2[2]+R2[9]*proj;
dNormalize3(tAx);
cos=dDOT14(tAx,R1+0);
sin=dDOT14(tAx,R1+2);
tAx[0]=R1[2]*cos-R1[0]*sin;
tAx[1]=R1[6]*cos-R1[4]*sin;
tAx[2]=R1[10]*cos-R1[8]*sin;
dCROSS114(Ax,=,tAx,R2+1);
dNormalize3(Ax);
boxProj=dFabs(dDOT14(Ax,R2+0)*B1)+
dFabs(dDOT14(Ax,R2+1)*B2)+
dFabs(dDOT14(Ax,R2+2)*B3);
cos=dFabs(dDOT14(Ax,R1+1));
cos1=dDOT14(Ax,R1+0);
cos3=dDOT14(Ax,R1+2);
sin=sqrtf(cos1*cos1+cos3*cos3);
TEST(p[0]*Ax[0]+p[1]*Ax[1]+p[2]*Ax[2],(sin*radius+cos*hlz+boxProj),Ax[0],Ax[1],Ax[2],6);
//same thing with the third axis of the box
proj=dDOT14(p1,R2+2)-dDOT14(p2,R2+2);
Ax[0]=-p1[0]+p2[0]+R2[2]*proj;
Ax[1]=-p1[1]+p2[1]+R2[6]*proj;
Ax[2]=-p1[2]+p2[2]+R2[10]*proj;
dNormalize3(tAx);
cos=dDOT14(tAx,R1+0);
sin=dDOT14(tAx,R1+2);
tAx[0]=R1[2]*cos-R1[0]*sin;
tAx[1]=R1[6]*cos-R1[4]*sin;
tAx[2]=R1[10]*cos-R1[8]*sin;
dCROSS114(Ax,=,tAx,R2+2);
dNormalize3(Ax);
boxProj=dFabs(dDOT14(Ax,R2+1)*B2)+
dFabs(dDOT14(Ax,R2+2)*B3)+
dFabs(dDOT14(Ax,R2+0)*B1);
cos=dFabs(dDOT14(Ax,R1+1));
cos1=dDOT14(Ax,R1+0);
cos3=dDOT14(Ax,R1+2);
sin=sqrtf(cos1*cos1+cos3*cos3);
TEST(p[0]*Ax[0]+p[1]*Ax[1]+p[2]*Ax[2],(sin*radius+cos*hlz+boxProj),Ax[0],Ax[1],Ax[2],7);
#undef TEST
// note: cross product axes need to be scaled when s is computed.
// normal (n1,n2,n3) is relative to box 1.
#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); \
} \
}
//crosses between cylinder axis and box axes
// separating axis = u2 x (v1,v2,v3)
TEST(pp[0]*R31-pp[2]*R11,(radius+B2*Q23+B3*Q22),R31,0,-R11,8);
TEST(pp[0]*R32-pp[2]*R12,(radius+B1*Q23+B3*Q21),R32,0,-R12,9);
TEST(pp[0]*R33-pp[2]*R13,(radius+B1*Q22+B2*Q21),R33,0,-R13,10);
#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 {
if(*code>7) dMULTIPLY0_331 (normal,R1,normalC);
else {normal[0] =normalC[0];normal[1] = normalC[1];normal[2] = normalC[2];}
}
if (invert_normal) {
normal[0] = -normal[0];
normal[1] = -normal[1];
normal[2] = -normal[2];
}
*depth = -s;
// compute contact point(s)
if (*code > 7) {
//find point on the cylinder pa deepest along normal
dVector3 pa;
dReal sign, cos1,cos3,factor;
for (i=0; i<3; i++) pa[i] = p1[i];
cos1 = dDOT14(normal,R1+0);
cos3 = dDOT14(normal,R1+2) ;
factor=sqrtf(cos1*cos1+cos3*cos3);
cos1/=factor;
cos3/=factor;
for (i=0; i<3; i++) pa[i] += cos1 * radius * R1[i*4];
sign = (dDOT14(normal,R1+1) > 0) ? REAL(1.0) : REAL(-1.0);
for (i=0; i<3; i++) pa[i] += sign * hlz * R1[i*4+1];
for (i=0; i<3; i++) pa[i] += cos3 * radius * R1[i*4+2];
// find vertex of the box deepest along normal
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[1 + i*4];
for (i=0; i<3; i++) ub[i] = R2[*code-8 + 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;
}
if(*code==4){
for (i=0; i<3; i++) contact[0].pos[i] = pb[i];
contact[0].depth = *depth;
return 1;
}
dVector3 vertex;
if (*code == 0) {
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 {
dReal sign,cos1,cos3,factor;
for (i=0; i<3; i++) vertex[i] = p1[i];
cos1 = dDOT14(normal,R1+0) ;
cos3 = dDOT14(normal,R1+2);
factor=sqrtf(cos1*cos1+cos3*cos3);
factor= factor ? factor : 1.f;
cos1/=factor;
cos3/=factor;
for (i=0; i<3; i++) vertex[i] += cos1 * radius * R1[i*4];
sign = (dDOT14(normal,R1+1) > 0) ? REAL(1.0) : REAL(-1.0);
for (i=0; i<3; i++) vertex[i] += sign * hlz * R1[i*4+1];
for (i=0; i<3; i++) vertex[i] += cos3 * radius * R1[i*4+2];
}
for (i=0; i<3; i++) contact[0].pos[i] = vertex[i];
contact[0].depth = *depth;
return 1;
}
//****************************************************************************
extern "C" int dCylCyl (const dVector3 p1, const dMatrix3 R1,
const dReal radius1,const dReal lz1, const dVector3 p2,
const dMatrix3 R2, const dReal radius2,const dReal lz2,
dVector3 normal, dReal *depth, int *code,
int maxc, dContactGeom *contact, int skip)
{
dVector3 p,pp1,pp2,normalC;
const dReal *normalR = 0;
dReal hlz1,hlz2,s,s2;
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 (pp1,R1,p); // get pp1 = p relative to body 1
dMULTIPLY1_331 (pp2,R2,p);
// get side lengths / 2
hlz1 = lz1*REAL(0.5);
hlz2 = lz2*REAL(0.5);
dReal proj,cos,sin,cos1,cos3;
#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;
cos=dFabs(dDOT44(R1+1,R2+1));
sin=sqrtf(1.f-(cos>1.f ? 1.f : cos));
TEST (pp1[1],(hlz1 + radius2*sin + hlz2*cos ),R1+1,0);//pp
TEST (pp2[1],(radius1*sin + hlz1*cos + hlz2),R2+1,1);
// note: cross product axes need to be scaled when s is computed.
#undef TEST
#define TEST(expr1,expr2,n1,n2,n3,cc) \
s2 = dFabs(expr1) - (expr2); \
if (s2 > 0) return 0; \
if (s2 > s) { \
s = s2; \
normalR = 0; \
normalC[0] = (n1); normalC[1] = (n2); normalC[2] = (n3); \
invert_normal = ((expr1) < 0); \
*code = (cc); \
}
dVector3 tAx,Ax,pa,pb;
//cross between cylinders' axes
dCROSS144(Ax,=,R1+1,R2+1);
dNormalize3(Ax);
TEST(p[0]*Ax[0]+p[1]*Ax[1]+p[2]*Ax[2],radius1+radius2,Ax[0],Ax[1],Ax[2],6);
{
dReal sign, factor;
//making ax which is perpendicular to cyl1 ax passing across cyl2 position//
//(project p on cyl1 flat surface )
for (i=0; i<3; i++) pb[i] = p2[i];
//cos1 = dDOT14(p,R1+0);
//cos3 = dDOT14(p,R1+2) ;
tAx[0]=pp1[0]*R1[0]+pp1[2]*R1[2];
tAx[1]=pp1[0]*R1[4]+pp1[2]*R1[6];
tAx[2]=pp1[0]*R1[8]+pp1[2]*R1[10];
dNormalize3(tAx);
//find deepest point pb of cyl2 on opposite direction of tAx
cos1 = dDOT14(tAx,R2+0);
cos3 = dDOT14(tAx,R2+2) ;
factor=sqrtf(cos1*cos1+cos3*cos3);
cos1/=factor;
cos3/=factor;
for (i=0; i<3; i++) pb[i] -= cos1 * radius2 * R2[i*4];
sign = (dDOT14(tAx,R2+1) > 0) ? REAL(1.0) : REAL(-1.0);
for (i=0; i<3; i++) pb[i] -= sign * hlz2 * R2[i*4+1];
for (i=0; i<3; i++) pb[i] -= cos3 * radius2 * R2[i*4+2];
//making perpendicular to cyl1 ax passing across pb
proj=dDOT14(pb,R1+1)-dDOT14(p1,R1+1);
Ax[0]=pb[0]-p1[0]-R1[1]*proj;
Ax[1]=pb[1]-p1[1]-R1[5]*proj;
Ax[2]=pb[2]-p1[2]-R1[9]*proj;
}
dNormalize3(Ax);
cos=dFabs(dDOT14(Ax,R2+1));
cos1=dDOT14(Ax,R2+0);
cos3=dDOT14(Ax,R2+2);
sin=sqrtf(cos1*cos1+cos3*cos3);
TEST(p[0]*Ax[0]+p[1]*Ax[1]+p[2]*Ax[2],radius1+cos*hlz2+sin*radius2,Ax[0],Ax[1],Ax[2],3);
{
dReal sign, factor;
for (i=0; i<3; i++) pa[i] = p1[i];
//making ax which is perpendicular to cyl2 ax passing across cyl1 position//
//(project p on cyl2 flat surface )
//cos1 = dDOT14(p,R2+0);
//cos3 = dDOT14(p,R2+2) ;
tAx[0]=pp2[0]*R2[0]+pp2[2]*R2[2];
tAx[1]=pp2[0]*R2[4]+pp2[2]*R2[6];
tAx[2]=pp2[0]*R2[8]+pp2[2]*R2[10];
dNormalize3(tAx);
cos1 = dDOT14(tAx,R1+0);
cos3 = dDOT14(tAx,R1+2) ;
factor=sqrtf(cos1*cos1+cos3*cos3);
cos1/=factor;
cos3/=factor;
//find deepest point pa of cyl2 on direction of tAx
for (i=0; i<3; i++) pa[i] += cos1 * radius1 * R1[i*4];
sign = (dDOT14(tAx,R1+1) > 0) ? REAL(1.0) : REAL(-1.0);
for (i=0; i<3; i++) pa[i] += sign * hlz1 * R1[i*4+1];
for (i=0; i<3; i++) pa[i] += cos3 * radius1 * R1[i*4+2];
proj=dDOT14(pa,R2+1)-dDOT14(p2,R2+1);
Ax[0]=pa[0]-p2[0]-R2[1]*proj;
Ax[1]=pa[1]-p2[1]-R2[5]*proj;
Ax[2]=pa[2]-p2[2]-R2[9]*proj;
}
dNormalize3(Ax);
cos=dFabs(dDOT14(Ax,R1+1));
cos1=dDOT14(Ax,R1+0);
cos3=dDOT14(Ax,R1+2);
sin=sqrtf(cos1*cos1+cos3*cos3);
TEST(p[0]*Ax[0]+p[1]*Ax[1]+p[2]*Ax[2],radius2+cos*hlz1+sin*radius1,Ax[0],Ax[1],Ax[2],4);
////test circl
//@ this needed to set right normal when cylinders edges intersect
//@ the most precise axis for this test may be found as a line between nearest points of two
//@ circles. But it needs comparatively a lot of computation.
//@ I use a trick which lets not to solve quadric equation.
//@ In the case when cylinder eidges touches the test below rather accurate.
//@ I still not sure about problems with sepparation but they have not been revealed during testing.
dVector3 point;
{
dVector3 ca,cb;
dReal sign;
for (i=0; i<3; i++) ca[i] = p1[i];
for (i=0; i<3; i++) cb[i] = p2[i];
//find two nearest flat rings
sign = (pp1[1] > 0) ? REAL(1.0) : REAL(-1.0);
for (i=0; i<3; i++) ca[i] += sign * hlz1 * R1[i*4+1];
sign = (pp2[1] > 0) ? REAL(1.0) : REAL(-1.0);
for (i=0; i<3; i++) cb[i] -= sign * hlz2 * R2[i*4+1];
dVector3 tAx,tAx1;
circleIntersection(R1+1,ca,radius1,R2+1,cb,radius2,point);
Ax[0]=point[0]-ca[0];
Ax[1]=point[1]-ca[1];
Ax[2]=point[2]-ca[2];
cos1 = dDOT14(Ax,R1+0);
cos3 = dDOT14(Ax,R1+2) ;
tAx[0]=cos3*R1[0]-cos1*R1[2];
tAx[1]=cos3*R1[4]-cos1*R1[6];
tAx[2]=cos3*R1[8]-cos1*R1[10];
Ax[0]=point[0]-cb[0];
Ax[1]=point[1]-cb[1];
Ax[2]=point[2]-cb[2];
cos1 = dDOT14(Ax,R2+0);
cos3 = dDOT14(Ax,R2+2) ;
tAx1[0]=cos3*R2[0]-cos1*R2[2];
tAx1[1]=cos3*R2[4]-cos1*R2[6];
tAx1[2]=cos3*R2[8]-cos1*R2[10];
dCROSS(Ax,=,tAx,tAx1);
dNormalize3(Ax);
dReal cyl1Pr,cyl2Pr;
cos=dFabs(dDOT14(Ax,R1+1));
cos1=dDOT14(Ax,R1+0);
cos3=dDOT14(Ax,R1+2);
sin=sqrtf(cos1*cos1+cos3*cos3);
cyl1Pr=cos*hlz1+sin*radius1;
cos=dFabs(dDOT14(Ax,R2+1));
cos1=dDOT14(Ax,R2+0);
cos3=dDOT14(Ax,R2+2);
sin=sqrtf(cos1*cos1+cos3*cos3);
cyl2Pr=cos*hlz2+sin*radius2;
TEST(p[0]*Ax[0]+p[1]*Ax[1]+p[2]*Ax[2],cyl1Pr+cyl2Pr,Ax[0],Ax[1],Ax[2],5);
}
#undef TEST
// if we get to this point, the cylinders interpenetrate. compute the normal
// in global coordinates.
if (normalR) {
normal[0] = normalR[0];
normal[1] = normalR[4];
normal[2] = normalR[8];
}
else {
normal[0] =normalC[0];normal[1] = normalC[1];normal[2] = normalC[2];
}
if (invert_normal) {
normal[0] = -normal[0];
normal[1] = -normal[1];
normal[2] = -normal[2];
}
*depth = -s;
// compute contact point(s)
if(*code==3){
for (i=0; i<3; i++) contact[0].pos[i] = pb[i];
contact[0].depth = *depth;
return 1;
}
if(*code==4){
for (i=0; i<3; i++) contact[0].pos[i] = pa[i];
contact[0].depth = *depth;
return 1;
}
if(*code==5){
for (i=0; i<3; i++) contact[0].pos[i] = point[i];
contact[0].depth = *depth;
return 1;
}
if (*code == 6) {
dVector3 pa;
dReal sign, cos1,cos3,factor;
for (i=0; i<3; i++) pa[i] = p1[i];
cos1 = dDOT14(normal,R1+0);
cos3 = dDOT14(normal,R1+2) ;
factor=sqrtf(cos1*cos1+cos3*cos3);
cos1/=factor;
cos3/=factor;
for (i=0; i<3; i++) pa[i] += cos1 * radius1 * R1[i*4];
sign = (dDOT14(normal,R1+1) > 0) ? REAL(1.0) : REAL(-1.0);
for (i=0; i<3; i++) pa[i] += sign * hlz1 * R1[i*4+1];
for (i=0; i<3; i++) pa[i] += cos3 * radius1 * R1[i*4+2];
// find a point pb on the intersecting edge of cylinder 2
dVector3 pb;
for (i=0; i<3; i++) pb[i] = p2[i];
cos1 = dDOT14(normal,R2+0);
cos3 = dDOT14(normal,R2+2) ;
factor=sqrtf(cos1*cos1+cos3*cos3);
cos1/=factor;
cos3/=factor;
for (i=0; i<3; i++) pb[i] -= cos1 * radius2 * R2[i*4];
sign = (dDOT14(normal,R2+1) > 0) ? REAL(1.0) : REAL(-1.0);
for (i=0; i<3; i++) pb[i] -= sign * hlz2 * R2[i*4+1];
for (i=0; i<3; i++) pb[i] -= cos3 * radius2 * R2[i*4+2];
dReal alpha,beta;
dVector3 ua,ub;
for (i=0; i<3; i++) ua[i] = R1[1 + i*4];
for (i=0; i<3; i++) ub[i] = R2[1 + 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 point on the "other" cylinder the contact point.
// @@@ this kind of works, but we need multiple contact points for stability,
// @@@ especially for face-face contact.
dVector3 vertex;
if (*code == 0) {
// flat face from cylinder 1 touches a edge/face from cylinder 2.
dReal sign,cos1,cos3,factor;
for (i=0; i<3; i++) vertex[i] = p2[i];
cos1 = dDOT14(normal,R2+0) ;
cos3 = dDOT14(normal,R2+2);
factor=sqrtf(cos1*cos1+cos3*cos3);
cos1/=factor;
cos3/=factor;
for (i=0; i<3; i++) vertex[i] -= cos1 * radius2 * R2[i*4];
sign = (dDOT14(normal,R1+1) > 0) ? REAL(1.0) : REAL(-1.0);
for (i=0; i<3; i++) vertex[i] -= sign * hlz2 * R2[i*4+1];
for (i=0; i<3; i++) vertex[i] -= cos3 * radius2 * R2[i*4+2];
}
else {
// flat face from cylinder 2 touches a edge/face from cylinder 1.
dReal sign,cos1,cos3,factor;
for (i=0; i<3; i++) vertex[i] = p1[i];
cos1 = dDOT14(normal,R1+0) ;
cos3 = dDOT14(normal,R1+2);
factor=sqrtf(cos1*cos1+cos3*cos3);
cos1/=factor;
cos3/=factor;
for (i=0; i<3; i++) vertex[i] += cos1 * radius1 * R1[i*4];
sign = (dDOT14(normal,R1+1) > 0) ? REAL(1.0) : REAL(-1.0);
for (i=0; i<3; i++) vertex[i] += sign * hlz1 * R1[i*4+1];
for (i=0; i<3; i++) vertex[i] += cos3 * radius1 * R1[i*4+2];
}
for (i=0; i<3; i++) contact[0].pos[i] = vertex[i];
contact[0].depth = *depth;
return 1;
}
//****************************************************************************
int dCollideCylS (dxGeom *o1, dxGeom *o2, int flags,
dContactGeom *contact, int skip)
{
dIASSERT (skip >= (int)sizeof(dContactGeom));
dIASSERT (dGeomGetClass(o2) == dSphereClass);
dIASSERT (dGeomGetClass(o1) == dCylinderClassUser);
const dReal* p1=dGeomGetPosition(o1);
const dReal* p2=dGeomGetPosition(o2);
const dReal* R=dGeomGetRotation(o1);
dVector3 p,normalC,normal;
const dReal *normalR = 0;
dReal cylRadius;
dReal hl;
dGeomCylinderGetParams(o1,&cylRadius,&hl);
dReal sphereRadius;
sphereRadius=dGeomSphereGetRadius(o2);
int i,invert_normal;
// get vector from centers of cyl to shere
p[0] = p2[0] - p1[0];
p[1] = p2[1] - p1[1];
p[2] = p2[2] - p1[2];
dReal s,s2;
unsigned char code;
#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 cyl ax
TEST (dDOT14(p,R+1),sphereRadius+hl,R+1,2);
// note: cross product axes need to be scaled when s is computed.
// normal (n1,n2,n3) is relative to
#undef TEST
#define TEST(expr1,expr2,n1,n2,n3,cc) \
s2 = dFabs(expr1) - (expr2); \
if (s2 > 0) return 0; \
if (s2 > s) { \
s = s2; \
normalR = 0; \
normalC[0] = (n1); normalC[1] = (n2); normalC[2] = (n3); \
invert_normal = ((expr1) < 0); \
code = (cc); \
}
//making ax which is perpendicular to cyl1 ax to sphere center//
dReal proj,cos,sin,cos1,cos3;
dVector3 Ax;
proj=dDOT14(p2,R+1)-dDOT14(p1,R+1);
Ax[0]=p2[0]-p1[0]-R[1]*proj;
Ax[1]=p2[1]-p1[1]-R[5]*proj;
Ax[2]=p2[2]-p1[2]-R[9]*proj;
dNormalize3(Ax);
TEST(dDOT(p,Ax),sphereRadius+cylRadius,Ax[0],Ax[1],Ax[2],9);
Ax[0]=p[0];
Ax[1]=p[1];
Ax[2]=p[2];
dNormalize3(Ax);
dVector3 pa;
dReal sign, factor;
for (i=0; i<3; i++) pa[i] = p1[i];
cos1 = dDOT14(Ax,R+0);
cos3 = dDOT14(Ax,R+2) ;
factor=sqrtf(cos1*cos1+cos3*cos3);
cos1/=factor;
cos3/=factor;
for (i=0; i<3; i++) pa[i] += cos1 * cylRadius * R[i*4];
sign = (dDOT14(normal,R+1) > 0) ? REAL(1.0) : REAL(-1.0);
for (i=0; i<3; i++) pa[i] += sign * hl * R[i*4+1];
for (i=0; i<3; i++) pa[i] += cos3 * cylRadius * R[i*4+2];
Ax[0]=p2[0]-pa[0];
Ax[1]=p2[1]-pa[1];
Ax[2]=p2[2]-pa[2];
dNormalize3(Ax);
cos=dFabs(dDOT14(Ax,R+1));
cos1=dDOT14(Ax,R+0);
cos3=dDOT14(Ax,R+2);
sin=sqrtf(cos1*cos1+cos3*cos3);
TEST(dDOT(p,Ax),sphereRadius+cylRadius*sin+hl*cos,Ax[0],Ax[1],Ax[2],14);
#undef TEST
if (normalR) {
normal[0] = normalR[0];
normal[1] = normalR[4];
normal[2] = normalR[8];
}
else {
normal[0] = normalC[0];
normal[1] = normalC[1];
normal[2] = normalC[2];
}
if (invert_normal) {
normal[0] = -normal[0];
normal[1] = -normal[1];
normal[2] = -normal[2];
}
// compute contact point(s)
contact->depth=-s;
contact->normal[0]=-normal[0];
contact->normal[1]=-normal[1];
contact->normal[2]=-normal[2];
contact->g1=const_cast<dxGeom*> (o1);
contact->g2=const_cast<dxGeom*> (o2);
contact->pos[0]=p2[0]-normal[0]*sphereRadius;
contact->pos[1]=p2[1]-normal[1]*sphereRadius;
contact->pos[2]=p2[2]-normal[2]*sphereRadius;
return 1;
}
int dCollideCylB (dxGeom *o1, dxGeom *o2, int flags,
dContactGeom *contact, int skip)
{
dVector3 normal;
dReal depth;
int code;
dReal cylRadius,cylLength;
dVector3 boxSides;
dGeomCylinderGetParams(o1,&cylRadius,&cylLength);
dGeomBoxGetLengths(o2,boxSides);
int num = dCylBox(dGeomGetPosition(o1),dGeomGetRotation(o1),cylRadius,cylLength,
dGeomGetPosition(o2),dGeomGetRotation(o2),boxSides,
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 dCollideCylCyl (dxGeom *o1, dxGeom *o2, int flags,
dContactGeom *contact, int skip)
{
dVector3 normal;
dReal depth;
int code;
dReal cylRadius1,cylRadius2;
dReal cylLength1,cylLength2;
dGeomCylinderGetParams(o1,&cylRadius1,&cylLength1);
dGeomCylinderGetParams(o2,&cylRadius2,&cylLength2);
int num = dCylCyl (dGeomGetPosition(o1),dGeomGetRotation(o1),cylRadius1,cylLength1,
dGeomGetPosition(o2),dGeomGetRotation(o2),cylRadius2,cylLength2,
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;
}
struct dxPlane {
dReal p[4];
};
int dCollideCylPlane
(
dxGeom *o1, dxGeom *o2, int flags,
dContactGeom *contact, int skip){
dIASSERT (skip >= (int)sizeof(dContactGeom));
dIASSERT (dGeomGetClass(o1) == dCylinderClassUser);
dIASSERT (dGeomGetClass(o2) == dPlaneClass);
contact->g1 = const_cast<dxGeom*> (o1);
contact->g2 = const_cast<dxGeom*> (o2);
unsigned int ret = 0;
dReal radius;
dReal hlz;
dGeomCylinderGetParams(o1,&radius,&hlz);
hlz /= 2;
const dReal *R = dGeomGetRotation(o1);// rotation of cylinder
const dReal* p = dGeomGetPosition(o1);
dVector4 n; // normal vector
dReal pp;
dGeomPlaneGetParams (o2, n);
pp=n[3];
dReal cos1,sin1;
cos1=dFabs(dDOT14(n,R+1));
cos1=cos1<REAL(1.) ? cos1 : REAL(1.); //cos1 may slightly exeed 1.f
sin1=sqrtf(REAL(1.)-cos1*cos1);
//////////////////////////////
dReal sidePr=cos1*hlz+sin1*radius;
dReal dist=-pp+dDOT(n,p);
dReal outDepth=sidePr-dist;
if(outDepth<0.f) return 0;
dVector3 pos;
/////////////////////////////////////////// from geom.cpp dCollideBP
dReal Q1 = dDOT14(n,R+0);
dReal Q2 = dDOT14(n,R+1);
dReal Q3 = dDOT14(n,R+2);
dReal factor =sqrtf(Q1*Q1+Q3*Q3);
factor= factor ? factor :1.f;
dReal A1 = radius * Q1/factor;
dReal A2 = hlz*Q2;
dReal A3 = radius * Q3/factor;
pos[0]=p[0];
pos[1]=p[1];
pos[2]=p[2];
pos[0]-= A1*R[0];
pos[1]-= A1*R[4];
pos[2]-= A1*R[8];
pos[0]-= A3*R[2];
pos[1]-= A3*R[6];
pos[2]-= A3*R[10];
pos[0]-= A2>0 ? hlz*R[1]:-hlz*R[1];
pos[1]-= A2>0 ? hlz*R[5]:-hlz*R[5];
pos[2]-= A2>0 ? hlz*R[9]:-hlz*R[9];
contact->pos[0] = pos[0];
contact->pos[1] = pos[1];
contact->pos[2] = pos[2];
contact->depth = outDepth;
ret=1;
if(dFabs(Q2)>M_SQRT1_2){
CONTACT(contact,ret*skip)->pos[0]=pos[0]+2.f*A1*R[0];
CONTACT(contact,ret*skip)->pos[1]=pos[1]+2.f*A1*R[4];
CONTACT(contact,ret*skip)->pos[2]=pos[2]+2.f*A1*R[8];
CONTACT(contact,ret*skip)->depth=outDepth-dFabs(Q1*2.f*A1);
if(CONTACT(contact,ret*skip)->depth>0.f)
ret++;
CONTACT(contact,ret*skip)->pos[0]=pos[0]+2.f*A3*R[2];
CONTACT(contact,ret*skip)->pos[1]=pos[1]+2.f*A3*R[6];
CONTACT(contact,ret*skip)->pos[2]=pos[2]+2.f*A3*R[10];
CONTACT(contact,ret*skip)->depth=outDepth-dFabs(Q3*2.f*A3);
if(CONTACT(contact,ret*skip)->depth>0.f) ret++;
} else {
CONTACT(contact,ret*skip)->pos[0]=pos[0]+2.f*(A2>0 ? hlz*R[1]:-hlz*R[1]);
CONTACT(contact,ret*skip)->pos[1]=pos[1]+2.f*(A2>0 ? hlz*R[5]:-hlz*R[5]);
CONTACT(contact,ret*skip)->pos[2]=pos[2]+2.f*(A2>0 ? hlz*R[9]:-hlz*R[9]);
CONTACT(contact,ret*skip)->depth=outDepth-dFabs(Q2*2.f*A2);
if(CONTACT(contact,ret*skip)->depth>0.f) ret++;
}
for (unsigned int i=0; i<ret; i++) {
CONTACT(contact,i*skip)->g1 = const_cast<dxGeom*> (o1);
CONTACT(contact,i*skip)->g2 = const_cast<dxGeom*> (o2);
CONTACT(contact,i*skip)->normal[0] =n[0];
CONTACT(contact,i*skip)->normal[1] =n[1];
CONTACT(contact,i*skip)->normal[2] =n[2];
}
return ret;
}
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
q[0] = start[0] - (p[0] + k*R[0*4+1]);
q[1] = start[1] - (p[1] + k*R[1*4+1]);
q[2] = start[2] - (p[2] + k*R[2*4+1]);
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];
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;
}
static dColliderFn * dCylinderColliderFn (int num)
{
if (num == dBoxClass) return (dColliderFn *) &dCollideCylB;
else if (num == dSphereClass) return (dColliderFn *) &dCollideCylS;
else if (num == dCylinderClassUser) return (dColliderFn *) &dCollideCylCyl;
else if (num == dPlaneClass) return (dColliderFn *) &dCollideCylPlane;
else if (num == dRayClass) return (dColliderFn *) &dCollideCylRay;
return 0;
}
static void dCylinderAABB (dxGeom *geom, dReal aabb[6])
{
dReal radius,lz;
dGeomCylinderGetParams(geom,&radius,&lz);
const dReal* R= dGeomGetRotation(geom);
const dReal* pos= dGeomGetPosition(geom);
dReal xrange = dFabs (R[0] *radius) +
REAL(0.5) *dFabs (R[1] * lz) + dFabs (R[2] * radius);
dReal yrange = dFabs (R[4] *radius) +
REAL(0.5) * dFabs (R[5] * lz) + dFabs (R[6] * radius);
dReal zrange = dFabs (R[8] * radius) +
REAL(0.5) *dFabs (R[9] * lz) + dFabs (R[10] * 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;
}
dxGeom *dCreateCylinder (dSpaceID space, dReal r, dReal lz)
{
dAASSERT (r > 0 && lz > 0);
if (dCylinderClassUser == -1)
{
dGeomClass c;
c.bytes = sizeof (dxCylinder);
c.collider = &dCylinderColliderFn;
c.aabb = &dCylinderAABB;
c.aabb_test = 0;
c.dtor = 0;
dCylinderClassUser=dCreateGeomClass (&c);
}
dGeomID g = dCreateGeom (dCylinderClassUser);
if (space) dSpaceAdd (space,g);
dxCylinder *c = (dxCylinder*) dGeomGetClassData(g);
c->radius = r;
c->lz = lz;
return g;
}
void dGeomCylinderSetParams (dGeomID g, dReal radius, dReal length)
{
dUASSERT (g && dGeomGetClass(g) == dCylinderClassUser,"argument not a cylinder");
dAASSERT (radius > 0 && length > 0);
dxCylinder *c = (dxCylinder*) dGeomGetClassData(g);
c->radius = radius;
c->lz = length;
}
void dGeomCylinderGetParams (dGeomID g, dReal *radius, dReal *length)
{
dUASSERT (g && dGeomGetClass(g) == dCylinderClassUser ,"argument not a cylinder");
dxCylinder *c = (dxCylinder*) dGeomGetClassData(g);
*radius = c->radius;
*length = c->lz;
}
/*
void dMassSetCylinder (dMass *m, dReal density,
dReal radius, dReal length)
{
dAASSERT (m);
dMassSetZero (m);
dReal M = length*M_PI*radius*radius*density;
m->mass = M;
m->_I(0,0) = M/REAL(4.0) * (ly*ly + lz*lz);
m->_I(1,1) = M/REAL(12.0) * (lx*lx + lz*lz);
m->_I(2,2) = M/REAL(4.0) * (lx*lx + ly*ly);
# ifndef dNODEBUG
checkMass (m);
# endif
}
*/