Box2D射线和AABB碰撞检测

来源:互联网 发布:联邦止咳水 淘宝 编辑:程序博客网 时间:2024/05/14 03:58

box2d使用了一种叫做slab的碰撞检测算法。所谓slab是指两个平行平面之间的空间,由此我们可以把3D空间中的AABB盒子看做是由AABB的3组平行面形成的3个方向的slab的交集。根据这个定义,我们可以得到以下两个结论:

1.如果一个点在AABB中,那么这个点必定同时在这3个slab中。

2.如果一条射线和AABB相交,那么这条射线和3个slab的相交部分必定有重合部分。

这很容易理解,如果射线和3个slab的相交线段没有重合,那么这些线段就不可能同时存在于3个slab中,也就不可能在AABB盒子中。下图展示了2D空间中射线R1和R2与AABB相交的情形。R1在x-slab和y-slab中的线段没有重合部分,因此R1和AABB不相交。R2在x-slab和y-slab中的线段有重合部分,因此R2和AABB相交。


根据上述原理,检查2D中射线和AABB的碰撞,只需要检查射线和x-slab,y-slab的交线是否有重合。

首先我们需要得到射线和slab边界平面的交点。射线可以用参数方程表示为R(t) = P0 + t·d , (其中P0为射线起点,d为射线的方向向量),平面由隐式定义方程X· n = D, (其中X为平面上的点,n为平面法向量,D为原点到平面的距离)给出。将平面方程中的X用P0 + t·d替换解得交点的参数t=(D−P0·n)/(d·n).由于AABB的slab平面都分别和两个坐标轴平行,公式可以进一步简化:设P0=(px,py,pz), d=(dx,dy,dz), t和x-slab面的交点的参数计算公式可化简为t=(D-px)/dx,而此处的D就是AABB的边界面x坐标。

/// Ray-cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1).  struct b2RayCastInput  {      b2Vec2 p1, p2;      float32 maxFraction;  };    /// Ray-cast output data. The ray hits at p1 + fraction * (p2 - p1), where p1 and p2  /// come from b2RayCastInput.  struct b2RayCastOutput  {      b2Vec2 normal;      float32 fraction;  };    bool b2AABB::RayCast(b2RayCastOutput* output, const b2RayCastInput& input) const  {       float32 tmin = -b2_maxFloat;       float32 tmax = b2_maxFloat;         b2Vec2 p = input.p1;       b2Vec2 d = input.p2 - input.p1;       b2Vec2 absD = b2Abs(d);         b2Vec2 normal;         for (int32 i = 0; i < 2; ++i)       {           if (absD(i) < b2_epsilon)           {               // Parallel.               if (p(i) < lowerBound(i) || upperBound(i) < p(i))               {                  return false ;               }           }           else           {               float32 inv_d = 1.0f / d(i);               float32 t1 = (lowerBound(i) - p(i)) * inv_d;               float32 t2 = (upperBound(i) - p(i)) * inv_d;                 // Sign of the normal vector.               float32 s = -1.0f;                 if (t1 > t2)               {                   b2Swap(t1, t2);                   s = 1.0f;               }                 // Push the min up               if (t1 > tmin)               {                   normal.SetZero();                   normal(i) = s;                   tmin = t1;               }                 // Pull the max down               tmax = b2Min(tmax, t2);                 if (tmin > tmax)               {                   return false ;               }           }       }         // Does the ray start inside the box?       // Does the ray intersect beyond the max fraction?       if (tmin < 0.0f || input.maxFraction < tmin)       {           return false ;       }         // Intersection.       output->fraction = tmin;       output->normal = normal;       return true ;  }  


0 0
原创粉丝点击