Bullet引擎的模拟循环
这里以bullet2.89版本为基础进行讲解。
btDiscreteDynamicsWorld类
btDiscreteDynamicsWorld负责整个物理世界的模拟流程。这个类包含物理世界的刚体数组、模拟岛和约束数组等。
// 在btDiscreteDynamicsWorld.h中
btDiscreteDynamicsWorld : public btDynamicsWorld
{
protected:
// ...
btConstraintSolver* m_constraintSolver; // 约束求解器
btSimulationIslandManager* m_islandManager; // 模拟岛管理器
btAlignedObjectArray<btTypedConstraint*> m_constraints; // 约束数组
btAlignedObjectArray<btRigidBody*> m_nonStaticRigidBodies; //刚体数组
btVector3 m_gravity;
// ...
}
StepSimulation函数
bullet的StepSimulation函数是
// timeStep: 本帧真实流逝时间(来自游戏主循环)。
// maxSubSteps: 单帧内允许执行的“最大物理子步数”.
// fixedTimeStep: 单次物理 step 的固定 dt
int btDiscreteDynamicsWorld::stepSimulation(btScalar timeStep, int maxSubSteps, btScalar fixedTimeStep)
在这个函数中,存在两种时间推进模式,两种的模式的启用条件是:maxSubSteps的数值。
- 固定时间步进(Fixed Timestep)。启用条件:
maxSubSteps >0,物理使用固定步长(如1/60s)。数值稳定,适合游戏和实时仿真。 - 可变时间步长(Variable Timestep)。启用条件
maxSubSteps ==0,使用可变的时间步模式。每帧只跑一次物理。不推荐使用。
int btDiscreteDynamicsWorld::stepSimulation(btScalar timeStep, int maxSubSteps, btScalar fixedTimeStep)
{
startProfiling(timeStep);
int numSimulationSubSteps = 0; // 本帧应执行的物理 step 次数
if (maxSubSteps)
{
//固定时间步
//fixed timestep with interpolation
m_fixedTimeStep = fixedTimeStep;
//m_localTime:尚未被物理消耗的时间
m_localTime += timeStep;
if (m_localTime >= fixedTimeStep)
{
numSimulationSubSteps = int(m_localTime / fixedTimeStep);
m_localTime -= numSimulationSubSteps * fixedTimeStep;
}
}
else
{
//可变时间步模式
//variable timestep
fixedTimeStep = timeStep;
m_localTime = m_latencyMotionStateInterpolation ? 0 : timeStep;
m_fixedTimeStep = 0;
if (btFuzzyZero(timeStep))
{
numSimulationSubSteps = 0;
maxSubSteps = 0;
}
else
{
numSimulationSubSteps = 1;
maxSubSteps = 1;
}
}
//process some debugging flags
if (getDebugDrawer())
{
btIDebugDraw* debugDrawer = getDebugDrawer();
gDisableDeactivation = (debugDrawer->getDebugMode() & btIDebugDraw::DBG_NoDeactivation) != 0;
}
if (numSimulationSubSteps)
{
//clamp the number of substeps, to prevent simulation grinding spiralling down to a halt
int clampedSimulationSteps = (numSimulationSubSteps > maxSubSteps) ? maxSubSteps : numSimulationSubSteps;
//保存上一帧状态
saveKinematicState(fixedTimeStep * clampedSimulationSteps);
//应用重力
applyGravity();
for (int i = 0; i < clampedSimulationSteps; i++)
{
//物理单步模拟
internalSingleStepSimulation(fixedTimeStep);
synchronizeMotionStates();
}
}
else
{
synchronizeMotionStates();
}
// 清理力
clearForces();
#ifndef BT_NO_PROFILE
CProfileManager::Increment_Frame_Counter();
#endif //BT_NO_PROFILE
return numSimulationSubSteps;
}
internalSingleStepSimulation
void btDiscreteDynamicsWorld::internalSingleStepSimulation(btScalar timeStep)
{
BT_PROFILE("internalSingleStepSimulation");
if (0 != m_internalPreTickCallback)
{
(*m_internalPreTickCallback)(this, timeStep);
}
///apply gravity, predict motion
// 1. 预测物无约束的运动
predictUnconstraintMotion(timeStep);
btDispatcherInfo& dispatchInfo = getDispatchInfo();
dispatchInfo.m_timeStep = timeStep;
dispatchInfo.m_stepCount = 0;
dispatchInfo.m_debugDraw = getDebugDrawer();
createPredictiveContacts(timeStep);
///perform collision detection
// 2. 碰撞检测
performDiscreteCollisionDetection();
// 3. 计算模拟岛
calculateSimulationIslands();
getSolverInfo().m_timeStep = timeStep;
///solve contact and other joint constraints
// 4. 约束求解
solveConstraints(getSolverInfo());
///CallbackTriggers();
///integrate transforms
// 5. 积分变换
integrateTransforms(timeStep);
///update vehicle simulation
// 6. 更新自定义行为
updateActions(timeStep);
// 7. 更新激活状态
updateActivationState(timeStep);
// 8. 系统回调
if (0 != m_internalTickCallback)
{
(*m_internalTickCallback)(this, timeStep);
}
}