BGE Fix: apply velocity clamping on every physics subtick

This patch uses the Bullet "internal tick callback" functionality to
ensure that velocity clamping is performed after every physics update.
This makes a difference when physics subticks > 1, as in that case the
too-high velocity could have impacted the simulation.

This patch follows the examples at [1] and [2]; the latter example
also explains that the way we limit velocity in the BGE (before this
patch) is wrong.

[1] http://bulletphysics.org/mediawiki-1.5.8/index.php/Simulation_Tick_Callbacks
[2] http://www.bulletphysics.org/mediawiki-1.5.8/index.php/Code_Snippets#I_want_to_cap_the_speed_of_my_spaceship;

Reviewed by: panzergame

Differential Revision: https://developer.blender.org/D1364
This commit is contained in:
Sybren A. Stüvel 2015-06-18 14:43:30 +02:00
parent 32319dd106
commit c5c2883ce0
4 changed files with 51 additions and 13 deletions

View File

@ -697,6 +697,24 @@ CcdPhysicsController::~CcdPhysicsController()
}
}
void CcdPhysicsController::SimulationTick(float timestep)
{
btRigidBody *body = GetRigidBody();
if (!body && body->isStaticObject())
return;
// Clamp linear velocity
if (m_cci.m_clamp_vel_max > 0.0f || m_cci.m_clamp_vel_min > 0.0f) {
const btVector3 &linvel = body->getLinearVelocity();
btScalar len = linvel.length();
if (m_cci.m_clamp_vel_max > 0.0f && len > m_cci.m_clamp_vel_max)
body->setLinearVelocity(linvel * (m_cci.m_clamp_vel_max / len));
else if (m_cci.m_clamp_vel_min > 0.0f && !btFuzzyZero(len) && len < m_cci.m_clamp_vel_min)
body->setLinearVelocity(linvel * (m_cci.m_clamp_vel_min / len));
}
}
/**
* SynchronizeMotionStates ynchronizes dynas, kinematic and deformable entities (and do 'late binding')
@ -732,19 +750,6 @@ bool CcdPhysicsController::SynchronizeMotionStates(float time)
if (body && !body->isStaticObject())
{
if ((m_cci.m_clamp_vel_max>0.0) || (m_cci.m_clamp_vel_min>0.0))
{
const btVector3& linvel = body->getLinearVelocity();
float len= linvel.length();
if ((m_cci.m_clamp_vel_max>0.0) && (len > m_cci.m_clamp_vel_max))
body->setLinearVelocity(linvel * (m_cci.m_clamp_vel_max / len));
else if ((m_cci.m_clamp_vel_min>0.0) && btFuzzyZero(len)==0 && (len < m_cci.m_clamp_vel_min))
body->setLinearVelocity(linvel * (m_cci.m_clamp_vel_min / len));
}
const btTransform& xform = body->getCenterOfMassTransform();
const btMatrix3x3& worldOri = xform.getBasis();
const btVector3& worldPos = xform.getOrigin();

View File

@ -579,6 +579,13 @@ protected:
* SynchronizeMotionStates ynchronizes dynas, kinematic and deformable entities (and do 'late binding')
*/
virtual bool SynchronizeMotionStates(float time);
/**
* Called for every physics simulation step. Use this method for
* things like limiting linear and angular velocity.
*/
void SimulationTick(float timestep);
/**
* WriteMotionStateToDynamics ynchronizes dynas, kinematic and deformable entities (and do 'late binding')
*/

View File

@ -452,6 +452,7 @@ m_scalingPropagated(false)
SetSolverType(1);//issues with quickstep and memory allocations
// m_dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,m_broadphase,m_solver,m_collisionConfiguration);
m_dynamicsWorld = new btSoftRigidDynamicsWorld(dispatcher,m_broadphase,m_solver,m_collisionConfiguration);
m_dynamicsWorld->setInternalTickCallback(&CcdPhysicsEnvironment::StaticSimulationSubtickCallback, this);
//m_dynamicsWorld->getSolverInfo().m_linearSlop = 0.01f;
//m_dynamicsWorld->getSolverInfo().m_solverMode= SOLVER_USE_WARMSTARTING + SOLVER_USE_2_FRICTION_DIRECTIONS + SOLVER_RANDMIZE_ORDER + SOLVER_USE_FRICTION_WARMSTARTING;
@ -677,6 +678,22 @@ void CcdPhysicsEnvironment::DebugDrawWorld()
m_dynamicsWorld->debugDrawWorld();
}
void CcdPhysicsEnvironment::StaticSimulationSubtickCallback(btDynamicsWorld *world, btScalar timeStep)
{
// Get the pointer to the CcdPhysicsEnvironment associated with this Bullet world.
CcdPhysicsEnvironment *this_ = static_cast<CcdPhysicsEnvironment*>(world->getWorldUserInfo());
this_->SimulationSubtickCallback(timeStep);
}
void CcdPhysicsEnvironment::SimulationSubtickCallback(btScalar timeStep)
{
std::set<CcdPhysicsController*>::iterator it;
for (it = m_controllers.begin(); it != m_controllers.end(); it++) {
(*it)->SimulationTick(timeStep);
}
}
bool CcdPhysicsEnvironment::ProceedDeltaTime(double curTime,float timeStep,float interval)
{
std::set<CcdPhysicsController*>::iterator it;

View File

@ -52,6 +52,7 @@ class btBroadphaseInterface;
struct btDbvtBroadphase;
class btOverlappingPairCache;
class btIDebugDraw;
class btDynamicsWorld;
class PHY_IVehicle;
class CcdOverlapFilterCallBack;
class CcdShapeConstructionInfo;
@ -129,6 +130,14 @@ protected:
/// Perform an integration step of duration 'timeStep'.
virtual bool ProceedDeltaTime(double curTime,float timeStep,float interval);
/**
* Called by Bullet for every physical simulation (sub)tick.
* Our constructor registers this callback to Bullet, which stores a pointer to 'this' in
* the btDynamicsWorld::getWorldUserInfo() pointer.
*/
static void StaticSimulationSubtickCallback(btDynamicsWorld *world, btScalar timeStep);
void SimulationSubtickCallback(btScalar timeStep);
virtual void DebugDrawWorld();
// virtual bool proceedDeltaTimeOneStep(float timeStep);