Sphere - AABB Collision Detaction
AABB盒-球体之间的碰撞检测:
问题描述:给定一个AABB盒子,以及一个以速度v前进的球体,求出该球体第一次与AABB盒发生接触的时间t。由于画3维的球体和盒子很麻烦,下面的图示中我都直接用2维的图形代替了,在后面给出的算法中会使用3维的坐标:
首先看一下球体和盒碰撞发生时球的圆形会出现在哪些区域如图、:
黑色部分代表的是原始的AABB盒子红色部分代表的是被Expand后的区域,形象地说就是将AABB盒子的边长扩展大球体的半径,然后把4个角磨成球状。。。。,如果是3D
情况下就是将边打成圆柱形的1/4轮廓,以及将顶点打成球体,这时候实际上每条边和他的2个顶点组成了一个胶囊体。
显而易见当球体的球心位于该红色区域内的时候,球体和盒子是相交的,其实丛数学意义上来说该区域就是盒子和球体的Minkwoski和。
因此计算一个以速度v移动的球体和盒子第一次发生接触的时间实际上就变成了计算球心的运动线段和红色部分第一次发生接触的时间t:
如图蓝色的点代表球心初始位置,箭头代表速度方向,箭头和红色轮廓的交点就是第一次接触时间。
所以现在问题变为只要求出线段(C0 , C0 +Vt)和红色区域第一次接触的时间,那么这个时间就是球体和盒子第一次发生碰撞的时间。
那么怎么计算这个时间呢,首先如果这个区域,实际上非常简单以2维情况为例子,首先可以把红色部分扩展为一个盒子(也就是在原来黑色盒子的基础上扩展3条边的半径r即可),首先计算线段和这个扩展后的AABB盒子的交点,线段和AABB盒的交点算法非常简单,这里不说了。得到交点后做进一步判断:
如果交点位于绿色部分区域的话,那么这个交点就是第一次发生接触的位置,如果处于蓝线的区域部分那么需要做额外的判断也就是在求一次线段和以该顶点为圆心,R为半径的球体的交点。如果相交那么该交点就是第一次碰撞的时间否则不发生碰撞,转到3维空间就是
1:交点出现在顶点的Voronoi区域:这时候求该顶点的沿,X,Y,Z轴3个方向的3个胶囊体和线段做一次相交测试取时间最短的。如果线段和3胶囊都不相交那么没有碰撞发生
2:交点发生在边的Voronoi区域上这时候求线段和该边的所在胶囊的交点
其余情况不用做额外判断。关于胶囊和线段交点的推导以后再写,。打字太累了。
球体和AABB碰撞检测的基本代码如下:
bool GFSphereAABBIntersector::FindCollision(const GFAabb &StaBox,const GFSphere&MoveBall,const GFVECTOR_3D&Speed,float Timeinterval,float&collidetime)
{
GFAabb ExtBox = StaBox;
//Extend the ExtBox figure by the Sphere Radius
float Radius = MoveBall.GetRadius();
ExtBox.InCreaseExt(Radius);
//Calculate Segment
GFPOINT_3D SegStart = MoveBall.m_Center;
GFVECTOR_3D d = Speed * Timeinterval;
GFPOINT_3D SegEnd = SegStart + d;
//Intersect
float tmin , tmax;
bool Intersected = GFLineAABBIntersector::InterSectSegMent(SegStart, SegEnd, ExtBox, tmin , tmax);
if( Intersected == false)
return false;
//Now the Segment Intersected the Extended Box We should check Whetehr the
//Intersect Point P is in the Original Box's Vertex or Edge's Voroni Region
GFPOINT_3D p = SegStart + d * tmin;//This is the Intersect Point
int u = 0, v = 0;
if(p.x < StaBox.GetMinX()) u |= 1;
if(p.x > StaBox.GetMaxX()) v |= 1;
if(p.y < StaBox.GetMinY()) u |= 2;
if(p.y > StaBox.GetMaxY()) v |= 2;
if(p.z < StaBox.GetMinZ()) u |= 4;
if(p.z > StaBox.GetMaxZ()) v |= 4;
int m = u + v;
//Prepare some useful varaiable to be used
GFPOINT_3D DestPoint = MoveBall.m_Center + Speed * Timeinterval;
GFCapsule Capsule;
Capsule.m_p1 = BoxCorner(StaBox , v);
Capsule.m_radius = MoveBall.GetRadius();
int intersectnum ;
//if all 3Bits set P is in a Vertex Voroni Region
//then we need to test the 3 Capsule meeting in this Region to Find the cd TIme
if(m == 0)
{
collidetime = Timeinterval * tmin;
return true;
}
if(m == 7)
{
float min = FLT_MAX;
//Test First Capsule In Vertex Region Conjunction
Capsule.m_p2 = BoxCorner(StaBox, v ^ 1);
intersectnum = GFLineQuadricInterSector::SegMentCapsuleIntersector(MoveBall.m_Center , DestPoint , Capsule , tmin , tmax);
if( intersectnum >0 )
min = (tmin < min ? tmin : min);
//Test Second Capsule In Vertex Region Conjunction
Capsule.m_p2 = BoxCorner(StaBox, v ^ 2);
intersectnum = GFLineQuadricInterSector::SegMentCapsuleIntersector(MoveBall.m_Center , DestPoint , Capsule , tmin , tmax);
if( intersectnum >0 )
min = (tmin < min ? tmin : min);
//Test Second Capsule In Vertex Region Conjunction
Capsule.m_p2 = BoxCorner(StaBox, v ^ 4);
intersectnum = GFLineQuadricInterSector::SegMentCapsuleIntersector(MoveBall.m_Center , DestPoint , Capsule , tmin , tmax);
if( intersectnum >0 )
min = (tmin < min ? tmin : min);
if( FLT_MAX == min)//No intersect
return false;
collidetime = Timeinterval * min;
return true;
}
//if
if((m & (m-1))== 0)
{
collidetime = Timeinterval * tmin;
return true;
}
//
Capsule.m_p2 = BoxCorner(StaBox, u ^ 7);
intersectnum = GFLineQuadricInterSector::SegMentCapsuleIntersector(MoveBall.m_Center , DestPoint , Capsule , tmin , tmax);
if( intersectnum > 0)
{
collidetime = Timeinterval * tmin;
return true;
}
else
return false;
}
fish.shadow song 若愚.影歌
posted on 2008-11-08 14:30 fish.shadow song(若愚.影歌) 阅读(1374) 评论(0) 编辑 收藏 举报