1. 编译bullet
修改配置:
2. 初始化物理世界
2.1 基本概念
1. btDefaultCollisionConfiguration
- 用途:这是 Bullet 物理引擎中用于配置碰撞检测系统的默认设置类。它负责初始化和配置碰撞检测所需的各种算法和数据结构。
- 功能:它提供了碰撞算法的默认实现,可以根据不同类型的碰撞对象(如凸体、网格)选择合适的算法。它还管理着用于碰撞检测过程中的内存分配。
2. btCollisionDispatcher
- 用途:这是一个碰撞分派器,用于管理和分配物体间的碰撞检测任务。
- 功能:当两个物体可能发生碰撞时,碰撞分派器确定是否需要进行碰撞检测,以及使用哪种碰撞检测算法。它依据物体的形状和碰撞配置来决定如何处理这些潜在的碰撞(依据包围盒)。
3. btBroadphaseInterface
- 用途:这是一个用于初步筛选碰撞对的接口,用于快速剔除那些不可能相互碰撞的物体。
- 功能:通过空间分割和快速检测方法(如AABB测试),该接口能够高效地缩小需要进行详细碰撞检测的物体对的数量。这极大地提高了整体的碰撞检测效率。
4. btSequentialImpulseConstraintSolver
- 用途:这是一个求解器,用于处理刚体间的约束和碰撞响应。
- 功能:它使用迭代方法来解决刚体之间的约束,如铰链、滑轮或接触约束。这个求解器计算出所需的力和冲量,以保持物体间的约束条件并适当地响应碰撞。
5. btCollisionWorld
- 用途:这是一个包含所有参与碰撞检测物体的容器。
- 功能:它负责组织和执行碰撞检测,但不处理物体的动力学(如力和运动)。这使得它适用于那些只需进行碰撞检测而无需物理模拟的场景。
6. btDiscreteDynamicsWorld
- 用途:这是 Bullet 中最常用的动力学世界类型,它既处理刚体的碰撞也处理它们的动力学。
- 功能:除了执行碰撞检测之外,这个世界还管理物体的运动,处理力的作用、质量、摩擦等物理属性。这使得它成为模拟真实世界物理行为(如在游戏和模拟中)的理想选择。
2.2 初始化碰撞世界
// 碰撞配置
btDefaultCollisionConfiguration* pCollisionConfig = new btDefaultCollisionConfiguration();
// 分派器
btCollisionDispatcher* pDispatcher = new btCollisionDispatcher(pCollisionConfig);
// 粗侧接口
btBroadphaseInterface* pBroadphase = new btDbvtBroadphase();
// 注册GImpact算法
btGImpactCollisionAlgorithm::registerAlgorithm(pDispatcher);
// 初始化碰撞世界
btCollisionWorld* pCollisionWorld = new btCollisionWorld(pDispatcher, pBroadphase, pCollisionConfig);
2.3 初始化动力学世界
// 碰撞配置
btDefaultCollisionConfiguration* pCollisionConfig = new btDefaultCollisionConfiguration();
// 分派器
btCollisionDispatcher* pDispatcher = new btCollisionDispatcher(pCollisionConfig);
// 设置世界的空间大小,限定刚体运动的空间范围
btVector3 worldAabbMin(-10000, -10000, -10000);
btVector3 worldAabbMax(10000, 10000, 10000);
// 设置最大刚体数
int maxProxies = 1024;
// 粗侧接口
btBroadphaseInterface* pBroadphase = new btAxisSweep3(worldAabbMin, worldAabbMax, maxProxies);
// 求解器
btSequentialImpulseConstraintSolver* solver = new btSequentialImpulseConstraintSolver();
// 注册GImpact算法
btGImpactCollisionAlgorithm::registerAlgorithm(pDispatcher);
// 初始化碰撞世界
btCollisionWorld* pCollisionWorld = new btCollisionWorld(pDispatcher, pBroadphase, pCollisionConfig);
3. 碰撞形体
3.1 btCollisionShape
1. 用途
btCollisionShape
是 Bullet 中所有碰撞形状的基类。它定义了物体在物理世界中的几何形状,这是进行碰撞检测的基础。
2. 特点
- 形状多样:支持多种类型的形状,包括球体(
btSphereShape
)、立方体(btBoxShape
)、圆柱体(btCylinderShape
)、锥体(btConeShape
)和更复杂的网格形状(btConvexHullShape
、btBvhTriangleMeshShape
等)。 - 可组合:可以通过
btCompoundShape
将多个简单形状组合成一个复杂的形状,以适应更复杂的物体。 - 用途广泛:被用于定义静态和动态物体的几何形状,以进行精确的碰撞检测和物理模拟。
3.2 创建立方体Mesh
btTriangleMesh* CreateBoxMesh(double halfExtents)
{
btTriangleMesh* pBoxMesh = new btTriangleMesh();
// 7----------------6
// /| |
// / 4----------------5
// / / /
// 3 / / <- 2 (hidden)
// |/ /
// 0----------------1
// right x | up y | front z
btVector3 p0 = btVector3(-halfExtents, -halfExtents, halfExtents);
btVector3 p1 = btVector3( halfExtents, -halfExtents, halfExtents);
btVector3 p2 = btVector3( halfExtents, halfExtents, halfExtents);
btVector3 p3 = btVector3(-halfExtents, halfExtents, halfExtents);
btVector3 p4 = btVector3(-halfExtents, -halfExtents, -halfExtents);
btVector3 p5 = btVector3( halfExtents, -halfExtents, -halfExtents);
btVector3 p6 = btVector3( halfExtents, halfExtents, -halfExtents);
btVector3 p7 = btVector3(-halfExtents, halfExtents, -halfExtents);
// front
pBoxMesh->addTriangle(p0, p1, p2);
pBoxMesh->addTriangle(p0, p2, p3);
// back
pBoxMesh->addTriangle(p4, p5, p6);
pBoxMesh->addTriangle(p4, p6, p7);
// right
pBoxMesh->addTriangle(p1, p5, p6);
pBoxMesh->addTriangle(p1, p6, p2);
// left
pBoxMesh->addTriangle(p0, p4, p7);
pBoxMesh->addTriangle(p0, p7, p3);
// top
pBoxMesh->addTriangle(p3, p2, p6);
pBoxMesh->addTriangle(p3, p6, p7);
// bottom
pBoxMesh->addTriangle(p0, p1, p5);
pBoxMesh->addTriangle(p0, p5, p4);
return pBoxMesh;
}
3.3 创建碰撞形体
btTriangleMesh* pBoxMesh = CreateBoxMesh(1.0);
// 形体 01
btGImpactMeshShape* gImpactShape01 = new btGImpactMeshShape(pBoxMesh);
gImpactShape01->updateBound();
// 形体 02
btGImpactMeshShape* gImpactShape02 = new btGImpactMeshShape(pBoxMesh);
gImpactShape02->updateBound();
4. 碰撞对象
4.1 btCollisionObject (碰撞对象)
4.1.1 基本概念
1. 定义与用途
btCollisionObject
是一个更通用的类,用于表示任何可以参与碰撞检测的物体。它是btRigidBody
和其他碰撞对象类的基类。
2. 主要特征
- 碰撞形状关联:同样与
btCollisionShape
关联,定义物体的碰撞边界。 - 灵活性:可以表示不仅仅是动态物体,也包括静态物体、触发器、感应区等。
- 状态和属性:存储有关物体的碰撞相关信息,如是否参与物理反应、摩擦系数、弹性系数等。
4.1.2 初始化
// 创建姿态
btTransform startTransform01;
startTransform01.setIdentity();
startTransform01.setOrigin(btVector3(0., 0., 0.));
btTransform startTransform02;
startTransform02.setIdentity();
startTransform02.setOrigin(btVector3(1., 0., 0.));
// 创建碰撞对象
btCollisionObject* btMeshObject01 = new btCollisionObject();
btMeshObject01->setCollisionShape(gImpactShape01);
btMeshObject01->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT);
btMeshObject01->setWorldTransform(startTransform01);
btCollisionObject* btMeshObject02 = new btCollisionObject();
btMeshObject02->setCollisionShape(gImpactShape02);
btMeshObject02->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT);
btMeshObject02->setWorldTransform(startTransform02);
// 将碰撞对象加入世界
pCollisionWorld->addCollisionObject(btMeshObject01);
pCollisionWorld->addCollisionObject(btMeshObject02);
4.2 btRigidBody (刚体)
4.2.1 基本概念
1. 定义与用途
btRigidBody
是表示动态物体的类,用于模拟具有质量和力学属性的物体。它是实现物理模拟中最常见的对象类型,如移动的车辆、掉落的物体等。
2. 主要特征
- 动力学属性:包括质量、惯性、速度(线性和角速度)等,这些属性影响物体的运动方式。
- 碰撞形状:与一个或多个
btCollisionShape
对象关联,这些形状定义了物体的碰撞边界。 - 运动和交互:可以响应力和扭矩,与其他物体发生碰撞,以及受到如重力等外部影响。
- 状态控制:可以设置为动态(可移动)或静态(不动的,如墙壁或地面)。
4.2.2 初始化
// 创建姿态
btTransform startTransform01;
startTransform01.setIdentity();
startTransform01.setOrigin(btVector3(0., 0., 0.));
btTransform startTransform02;
startTransform02.setIdentity();
startTransform02.setOrigin(btVector3(1., 0., 0.));
// 创建刚体
btScalar mass(1.f);
bool isDynamic = (mass != 0.f);
btVector3 localInertia(0, 0, 0);
if (isDynamic) gImpactShape01->calculateLocalInertia(mass, localInertia);
btDefaultMotionState* pMotionState01 = new btDefaultMotionState(startTransform01);
btRigidBody::btRigidBodyConstructionInfo rbInfo01 = btRigidBody::btRigidBodyConstructionInfo(mass, myMotionState, colShape, localInertia);
btRigidBody* pRigidBody01 = new btRigidBody(rbInfo01);
btVector3 localInertia(0, 0, 0);
if (isDynamic) gImpactShape02->calculateLocalInertia(mass, localInertia);
btDefaultMotionState* pMotionState02 = new btDefaultMotionState(startTransform02);
btRigidBody::btRigidBodyConstructionInfo rbInfo02 = btRigidBody::btRigidBodyConstructionInfo(mass, myMotionState, colShape, localInertia);
btRigidBody* pRigidBody02 = new btRigidBody(rbInfo02);
// 将碰撞对象加入世界
pCollisionWorld->addCollisionObject(pRigidBody01);
pCollisionWorld->addCollisionObject(pRigidBody02);
5. 碰撞检测
5.1 关键步骤
1. 精测阶段(Broadphase Detection)
- 目的:快速识别可能发生碰撞的物体对,以减少需要进行详细碰撞检测的物体数量。
- 方法:通常使用空间分割技术,如轴对齐包围盒(AABB)来快速筛选。在此阶段,只确定物体是否足够接近以致可能发生碰撞。
2. 粗侧阶段(Narrowphase Detection)
- 目的:在粗侧阶段确定的潜在碰撞对中进行详细的碰撞检测。
- 方法:计算精确的碰撞点、碰撞法线和穿透深度。这一步使用更精确的几何计算来确定物体是否真正相交,并在哪里相交。
3. 碰撞分派(Collision Dispatch)
- 目的:处理碰撞对,并决定对于每对碰撞物体使用哪种碰撞检测算法。
- 方法:根据物体的类型(如凸体、凹体、网格等)和碰撞形状,选择适当的碰撞算法。
4. 碰撞解算(Collision Resolution)
- 目的:计算碰撞的物理响应,如力、冲量的变化。
- 方法:这通常包括计算应用在物体上的反作用力和扭矩,以防止物体相互穿透,并模拟逼真的物理交互。
5. 集成到物理世界(Integration into the Physics World)
- 目的:将碰撞检测的结果应用到物理世界的状态更新中。
- 方法:根据碰撞解算的结果更新物体的位置、速度等状态。这可能包括处理约束和模拟物体的动力学行为。
6. 约束解算(Constraint Solving)
- 目的:如果有的话,解决物体间的约束条件。
- 方法:处理诸如铰链、滑轮、固定连接等约束,确保这些约束在物理世界中得到适当的维护。
5.2 代码示例
1. 碰撞世界
pCollisionWorld->performDiscreteCollisionDetection();
2. 动态世界
btScalar deltaTime = 1.f / 60.f;
int maxSubSteps = 10;
dynamicsWorld->stepSimulation(deltaTime, maxSubSteps);
5.3 碰撞世界与动态世界碰撞检测的区别
1. btCollisionWorld
btCollisionWorld
是 Bullet 物理引擎中处理碰撞检测的基础类。- 它提供了一个通用的环境,用于检测和记录物体之间的碰撞。
performDiscreteCollisionDetection()
方法在这个环境中执行碰撞检测,但不处理物体的运动或者动力学响应。它仅仅检测物体在特定时刻的碰撞状态。
2. btDiscreteDynamicsWorld
btDiscreteDynamicsWorld
是btCollisionWorld
的一个子类,它不仅处理碰撞检测,还处理物理模拟中的动力学方面。- 这包括物体的运动、力的应用、刚体之间的互动等。
- 在
btDiscreteDynamicsWorld
的上下文中,performDiscreteCollisionDetection()
方法不仅执行碰撞检测,还可能涉及碰撞的结果如何影响物体的动力学。例如,当两个物体碰撞时,它们可能会根据物理定律改变速度或方向。
6. 内存管理
#define SAFE_DELETE_PTR(ptr) if(ptr){delete ptr; ptr = nullptr;};
6.1 释放碰撞形体
碰撞形体需要在最后手动释放。
1. 保存形体的指针
// 保存shape
std::vector<btCollisionShape*> shapesCache;
shapesCache.emplace_back(gImpactShape01);
shapesCache.emplace_back(gImpactShape02);
2. 在结束时进行释放
// 释放 Shape
for (auto pShape : shapesCache)
{
SAFE_DELETE_PTR(pShape);
}
6.2 释放碰撞对象
// 释放 World 中的 Object
// 从后往前析构,避免未知错误,例如越界或者遗漏一些元素。
for (int idx = pCollisionWorld->getNumCollisionObjects() - 1; idx >= 0; idx--)
{
btCollisionObject* obj = pCollisionWorld->getCollisionObjectArray()[idx];
// 若为刚体,还需要释放内部的 btMotionState
btRigidBody* body = btRigidBody::upcast(obj);
if (body && body->getMotionState())
{
btMotionState* tempMotionState = body->getMotionState();
SAFE_DELETE_PTR(tempMotionState);
}
pCollisionWorld->removeCollisionObject(obj);
SAFE_DELETE_PTR(obj);
}
6.3 释放世界环境
碰撞世界必须首先进行释放,否则会报错。
SAFE_DELETE_PTR(pCollisionWorld);
SAFE_DELETE_PTR(pCollisionConfig);
SAFE_DELETE_PTR(pDispatcher);
SAFE_DELETE_PTR(pBroadphase);