Peridyno提供了多种几何基本单元之间的碰撞检测算法函数。用户可以通过位置信息对几何基本单元进行初始化,进而调用相关函数进行位置关系的判断。
Peridyno提供了13种几何基本单元的构造以及相关的碰撞检测算法,具体实现可以参考src/Framework/Topology/Primitive3D.inl。
TPoint3D定义了三维空间中的一个点。既通过点坐标进行初始化,
Coord3D position = Coord(0,1,0);
Point3D point0(position); //使用坐标三元数进行初始化
Point3D point1(0,1,0);//使用三个坐标进行初始化
也可以用另一个TPoint3D进行初始化
Point3D point0(0,1,0);
Point3D point1(point_0);
TAlignedBox3D定义了轴对齐包围盒,是几何单元计算轴对齐包围盒的方法aabb()的返回对象。它可以通过两个三维点坐标v0、v1(v0的三个坐标值均需要小于v1对应坐标值)进行初始化
Coord v0(0,0,0);
Coord v1(1,1,1);
AABB aabb(v0, v1); //v0的三个坐标值均需要小于v1对应坐标值
也可以通过另一个TAlignedBox3D进行初始化
AABB aabb1;
...
AABB aabb2(aabb1);
TAlignedBox3D中提供的方法如下:
intersect intersect计算了该AABB与另一个aabb之间是否相交以。返回值为布尔类型,代表两个AABB是否相交。如果发生相交,那么将交叠的AABB以传引用的方式返回。
例如
AABB aabb1, aabb2;
...
AABB interBox;
if(aabb1.intersect(aabb2, interBox)) //如果aabb1和aabb2发生交叉,返回1并且将交叉的AABB存入interBox
{
...
}
isValid
isValid返回值为布尔类型,判断v0的三个坐标值均需要小于v1对应坐标值,
return v1[0] > v0[0] && v1[1] > v0[1] && v1[2] > v0[2];
使用方法如
AABB aabb;
...
if(aabb.isValid()) //判断aabb的v0的三个坐标值是否不大于v1的对应坐标值
{
...
}
v1[i] - v0[i]
,例如AABB aabb;
...
Real l_x = aabb.length(0);//x轴的边长
Real l_y = aabb.length(1);//y轴的边长
Real l_z = aabb.length(2);//z轴的边长
TSphere3D定义了三维空间中的一个球体。既通过球心坐标和球半径进行初始化,
Coord3D center = Coord(0,1,0);
Real radius = 0.5f;
Sphere3D sphere(center, radius);
也可以用另一个TSphere3D进行初始化
Sphere3D sphere0(center, radius);
Sphere3D sphere1(sphere0);
TSphere3D中提供的方法如下:
aabb
aabb计算了该球体的轴对齐包围盒,主要应用于宽阶段碰撞检测,例如
AABB box;
switch (eleType)
{
case ET_SPHERE:
{
box = spheres[tId].aabb(); //计算该球体的轴对齐包围盒
break;
}
...
}
volume
volume计算了球体的体积,可以用于初始化例子质量等,例如
mass = sphere.volume() * density; //计算该球体的体积
TTet3D定义了三维空间中的四面体,初始化方法包括使用四个顶点坐标进行初始化
Coord3D p1,p2,p3,p4;
...
Tet3D tet(p1,p2,p3,p4);
以及使用另一个TTet3D进行初始化
Tet3D tet0;
...
Tet3D tet1(tet0);
TTet3D中提供的方法如下:
aabb
aabb计算了该四面体的轴对齐包围盒,主要应用于宽阶段碰撞检测,例如
AABB box;
TTet3D tet;
...
box = tet.aabb(); //计算该球体的轴对齐包围盒
TTet3D tet;
...
Real volume_tet = tet.volume(); //计算该球体的轴对齐包围盒
circumcenter
circumcenter计算该四面体的外接圆圆心,返回值为一个TPoint3D类型的变量。
计算方法可参考http://rodolphe-vaillant.fr/entry/127/find-a-tetrahedron-circumcenter。
TTet3D tet;
...
TPoint3D circumcenter_tet = tet.circumcenter();
barycenter
barycenter计算该四面体的重心,返回值为一个TPoint3D类型的变量。
TTet3D tet;
...
TPoint3D barycenter_tet = tet.barycenter();
TOrientedBox3D定义了三维空间中的长方体,有三种初始化方法。
第一种初始化方法使用长方体的中心、三个轴的方向以及三个轴方向边长的一半初始化。
Coord3D center;//长方体中心坐标
Coord3D dir0, dir1, dir2;//均为三维向量,应两两相互垂直,分别代表长方体边的三个方向
Coord3D ext;//三维向量,第1、2、3维的数值均应非负,分别代表dir0、dir1、dir2方向边长的一半
...
Box3D obb(center, dir0, dir1, dir2, ext);
第二种初始化方法使用长方体的中心、用四元数表达的旋转角度以及三个轴方向边长的一半初始化。
Coord3D center;//长方体中心坐标
Quat rot;//四元数代表的三个轴方向的旋转角
Coord3D ext;//三维向量,第1、2、3维的数值均应非负,分别代表dir0、dir1、dir2方向边长的一半
...
Box3D obb(center, rot, ext);
这里,长方体的三条边以(1,0,0),(0,1,0),(0,0,1)为初始方向,使用四元数进行旋转后即为长方体边的方向,具体可参考实现
auto mat = rot.toMatrix3x3();
u = mat.col(0);
v = mat.col(1);
w = mat.col(2);
第三种初始化方法为使用另一个TOrientedBox3D进行初始化
Box3D Box0;
...
Box3D Box1(Box0);
TOrientedBox3D中提供的方法如下:
aabb
aabb计算了该长方体的轴对齐包围盒,主要应用于宽阶段碰撞检测,例如
AABB box;
Box3D obb;
...
box = obb.aabb(); //计算该球体的轴对齐包围盒
AABB box;
Box3D obb;
...
box = obb.volume(); //计算该球体的轴对齐包围盒
Peridyno可以检测几何基本单元之间的接触点、接触点法向和穿透距离,具体实现可以参考src/Framework/Collision/CollisionDetectionAlgorithm.inl。
进行一次碰撞检测包含如下步骤:
TManifold<Real> manifold;
其中TManifold在src/Framework/Collision/CollisionData.h中的定义如下,
template<typename Real>
struct TManifold
{
public:
Vector<Real, 3> normal; //接触点法向
TContact<Real> contacts[8]; //接触点位置以及穿透距离
int contactCount = 0; //两个几何基本单元的接触点数量
};
其中contacts的定义如下,存储接触点的坐标以及穿透距离
template<typename Real>
class TContact
{
public:
Vector<Real, 3> position; // 接触点坐标
Real penetration; // 穿透距离
};
调用CollisionDetection::request进行碰撞检测
定义好TManifold后,可以调用CollisionDetection::request系列函数进行碰撞检测。CollisionDetection::request为定义在src/Framework/Collision/CollisionDetectionAlgorithm.h中的一系列函数,其功能为计算并返回两个几何单元几何单元1和几何单元2之间的碰撞信息,存储在manifold中。它可以在CPU和GPU上调用,调用方法为
CollisionDetection<Real>::request(manifold, 几何单元1, 几何单元2);
例如,如果希望进行长方体(OBB)和四面体的碰撞检测,可以进行如下操作
Tet3D tetA; //创建一个四面体
tetA.v[0] = tetPos0; //初始化四面体顶点位置
tetA.v[1] = tetPos1;
tetA.v[2] = tetPos2;
tetA.v[3] = tetPos3;
Box3D boxB;//创建一个长方体
boxB.center = center; //长方体中心点位置
boxB.extent = halfLength;//长方体三个方向的边长
Mat3f rot = rotation.toMatrix3x3(); //用长方体的旋转角初始化长方体的边方向
boxB.u = rot * Vec3f(1, 0, 0);
boxB.v = rot * Vec3f(0, 1, 0);
boxB.w = rot * Vec3f(0, 0, 1);
TManifold<Real> manifold; //定义manifold用于存储穿透信息
CollisionDetection<Real>::request(manifold, tetA, boxB); //调用request,将穿透信息存储在manifold中
目前CollisionDetection::request支持的几何单元类型如下
DYN_FUNC static void request(Manifold& m, const OBox3D box0, const OBox3D box1); //长方体之间的碰撞检测
DYN_FUNC static void request(Manifold& m, const Sphere3D& sphere, const OBox3D& box); //长方体和球体之间的碰撞检测
DYN_FUNC static void request(Manifold& m, const OBox3D& box, const Sphere3D& sphere);//长方体和球体之间的碰撞检测
DYN_FUNC static void request(Manifold& m, const Sphere3D& sphere0, const Sphere3D& sphere1);//球体之间的碰撞检测
DYN_FUNC static void request(Manifold& m, const Tet3D& tet0, const Tet3D& tet1);//四面体之间的碰撞检测
DYN_FUNC static void request(Manifold& m, const Tet3D& tet, const OBox3D& box);//长方体和四面体之间的碰撞检测
DYN_FUNC static void request(Manifold& m, const OBox3D& box, const Tet3D& tet);//长方体和四面体之间的碰撞检测
DYN_FUNC static void request(Manifold& m, const Sphere3D& sphere, const Tet3D& tet);//球体和四面体之间的碰撞检测
DYN_FUNC static void request(Manifold& m, const Tet3D& tet, const Sphere3D& sphere);//球体和四面体之间的碰撞检测
对于长方体与四面体之间的碰撞检测,peridyno中采用分离轴定理(http://www.randygaul.net/2014/05/22/deriving-obb-to-obb-intersection-sat/)进行碰撞检测。
在调用后,几何单元之间的碰撞信息被存储在TManifold中,可以通过访问TManifold中的信息获得碰撞接触点数量、接触点位置、接触点法向量和穿透距离。其中,接触点法向量由几何单元1指向几何单元2;穿透距离为一个负数,其绝对值代表两个该接触点沿接触点法向量的穿透的深度;如果两个集合体没有发生碰撞,那么接触点数量为0。
例如,可以遍历TManifold中的contacts获得每个接触点的穿透深度。
for (int n = 0; n < manifold.contactCount; n++)//遍历每个穿透点
{
...
cp.pos1 = manifold.contacts[n].position; //获取穿透点位置
cp.normal1 = -manifold.normal;//获取穿透法向量,对于相同的一对几何单元,每个接触点的穿透的法向量都相同
cp.interpenetration = -manifold.contacts[n].penetration;//获取穿透深度
...
}