CharacterController: Improve ground support detection

In addition to the existing line probe ground check, also check if the bottom sphere of the capsule is contact with any geometry before going into hover mode.
This should prevent going into the fly animation when standing or walking on collision shapes with small gaps between elements.
This commit is contained in:
Anthony J. Thibault 2016-01-25 21:54:35 -08:00
parent b55856da08
commit bc9621baab
2 changed files with 29 additions and 2 deletions

View file

@ -60,6 +60,7 @@ CharacterController::CharacterController() {
_followTime = 0.0f;
_followLinearDisplacement = btVector3(0, 0, 0);
_followAngularDisplacement = btQuaternion::getIdentity();
_hasSupport = false;
_pendingFlags = PENDING_FLAG_UPDATE_SHAPE;
@ -106,6 +107,28 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
}
}
bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld) const {
int numManifolds = collisionWorld->getDispatcher()->getNumManifolds();
for (int i = 0; i < numManifolds; i++) {
btPersistentManifold* contactManifold = collisionWorld->getDispatcher()->getManifoldByIndexInternal(i);
const btCollisionObject* obA = static_cast<const btCollisionObject*>(contactManifold->getBody0());
const btCollisionObject* obB = static_cast<const btCollisionObject*>(contactManifold->getBody1());
if (obA == _rigidBody || obB == _rigidBody) {
int numContacts = contactManifold->getNumContacts();
for (int j = 0; j < numContacts; j++) {
btManifoldPoint& pt = contactManifold->getContactPoint(j);
// check to see if contact point is touching the bottom sphere of the capsule.
float contactPointY = (obA == _rigidBody) ? pt.m_localPointA.getY() : pt.m_localPointB.getY();
if (contactPointY < -_halfHeight) {
return true;
}
}
}
}
return false;
}
void CharacterController::preStep(btCollisionWorld* collisionWorld) {
// trace a ray straight down to see if we're standing on the ground
const btTransform& xform = _rigidBody->getWorldTransform();
@ -125,6 +148,8 @@ void CharacterController::preStep(btCollisionWorld* collisionWorld) {
if (rayCallback.hasHit()) {
_floorDistance = rayLength * rayCallback.m_closestHitFraction - _radius;
}
_hasSupport = checkForSupport(collisionWorld);
}
void CharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) {
@ -248,7 +273,7 @@ void CharacterController::jump() {
bool CharacterController::onGround() const {
const btScalar FLOOR_PROXIMITY_THRESHOLD = 0.3f * _radius;
return _floorDistance < FLOOR_PROXIMITY_THRESHOLD;
return _floorDistance < FLOOR_PROXIMITY_THRESHOLD || _hasSupport;
}
void CharacterController::setHovering(bool hover) {
@ -400,7 +425,7 @@ void CharacterController::preSimulation() {
if (_floorDistance < JUMP_PROXIMITY_THRESHOLD) {
_isJumping = false;
}
} else {
} else if (!_hasSupport) {
_floorDistance = FLT_MAX;
setHovering(true);
}

View file

@ -86,6 +86,7 @@ public:
protected:
void updateUpAxis(const glm::quat& rotation);
bool checkForSupport(btCollisionWorld* collisionWorld) const;
protected:
btVector3 _currentUp;
@ -104,6 +105,7 @@ protected:
btScalar _radius;
btScalar _floorDistance;
bool _hasSupport;
btScalar _gravity;