Bullet Basic Example 示例
代码参见:bullet3/examples/BasicDemo
在 Bullet 自带的例程中,仿真主要包括以下三步:
(1)example->initPhysics();
首先,初始化仿真相关的环境。建立仿真相关对象,添加场景中的物体,等等。
(2)example->stepSimulation(1.f / 60.f);
执行仿真步骤。
(3)example->exitPhysics();
最后,清理仿真相关的对象。
(0)仿真场景相关的类极其组成
在仿真场景中,主要涉及到的类有:
btAlignedObjectArray<btCollisionShape*> m_collisionShapes;
btBroadphaseInterface* m_broadphase;
btCollisionDispatcher* m_dispatcher;
btConstraintSolver* m_solver;
btDefaultCollisionConfiguration* m_collisionConfiguration;
btDiscreteDynamicsWorld* m_dynamicsWorld;
(1)初始化仿真环境
所谓初始化仿真环境,一方面需要建立仿真场景相关的类,另一方面需要向仿真场景中添加所需要的物体。比如,在 BasicExample::initPhysics()
中,首先需要建立空的 EmptyDynamicWord
,即:
///collision configuration contains default setup for memory, collision setup
m_collisionConfiguration = new btDefaultCollisionConfiguration();
//m_collisionConfiguration->setConvexConvexMultipointIterations();
///use the default collision dispatcher. For parallel processing you can use a diffent dispatcher (see Extras/BulletMultiThreaded)
m_dispatcher = new btCollisionDispatcher(m_collisionConfiguration);
m_broadphase = new btDbvtBroadphase();
///the default constraint solver. For parallel processing you can use a different solver (see Extras/BulletMultiThreaded)
btSequentialImpulseConstraintSolver* sol = new btSequentialImpulseConstraintSolver;
m_solver = sol;
m_dynamicsWorld = new btDiscreteDynamicsWorld(m_dispatcher, m_broadphase, m_solver, m_collisionConfiguration);
m_dynamicsWorld->setGravity(btVector3(0, -10, 0));
接下来,需要这个 EmptyDynamicWord
中添加物体,即
///create a few basic rigid bodies
btBoxShape* groundShape = createBoxShape(btVector3(btScalar(50.), btScalar(50.), btScalar(50.)));
//groundShape->initializePolyhedralFeatures();
//btCollisionShape* groundShape = new btStaticPlaneShape(btVector3(0,1,0),50);
m_collisionShapes.push_back(groundShape);
btTransform groundTransform;
groundTransform.setIdentity();
groundTransform.setOrigin(btVector3(0, -50, 0));
{
btScalar mass(0.);
createRigidBody(mass, groundTransform, groundShape, btVector4(0, 0, 1, 1));
}
其中, createRigidBody(...)
的内容为:
btVector3 localInertia(0, 0, 0);
if (isDynamic)
shape->calculateLocalInertia(mass, localInertia);
//using motionstate is recommended, it provides interpolation capabilities, and only synchronizes 'active' objects
btDefaultMotionState* myMotionState = new btDefaultMotionState(startTransform);
btRigidBody::btRigidBodyConstructionInfo cInfo(mass, myMotionState, shape, localInertia);
btRigidBody* body = new btRigidBody(cInfo);
//body->setContactProcessingThreshold(m_defaultContactProcessingThreshold);
body->setUserIndex(-1);
m_dynamicsWorld->addRigidBody(body);
(2)执行仿真步骤
这个就比较清晰了,可以认为是一个大的步骤 m_dynamicsWorld->stepSimulation(deltaTime);
(3)结束仿真并清理仿真场景
大致就是清理各种 new 出来的对象,这里就不再赘述了。
通过这个简单的示例,Bullet 总的框架就比较清晰了。可以认为,通过 btDiscreteDynamicsWorld
类创建了一个空的仿真场景。向场景中添加物体、以及物体之间的约束(比如关节等),再由 btDiscreteDynamicsWorld::stepSimulation(t)
进行仿真计算。
那么,这里就需要进一步弄清楚两个问题,(1)仿真场景中相关的类是什么样的构成,比如物体类 btBoxShape
、碰撞对象类 btCollisionShape
、以及碰撞检测类 btCollisionDispatcher
btBroadphaseInterface
btDefaultCollisionConfiguration
、约束类 btTypedConstraint
btConstraintSolver
、等等是怎样构成的;(2)仿真步骤是如何一步一步执行的,即 btDiscreteDynamicsWorld::stepSimulation(t)
里面包含了怎样一步一步的小步骤。