better tracking of avatar gravity setting

This commit is contained in:
Andrew Meadows 2016-09-28 10:13:14 -07:00
parent 9fa9784135
commit d29386c43f
5 changed files with 53 additions and 48 deletions

View file

@ -61,7 +61,7 @@ void MyCharacterController::updateShapeIfNecessary() {
if (_state == State::Hover) { if (_state == State::Hover) {
_rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f)); _rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f));
} else { } else {
_rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp); _rigidBody->setGravity(_gravity * _currentUp);
} }
// KINEMATIC_CONTROLLER_HACK // KINEMATIC_CONTROLLER_HACK
if (_moveKinematically) { if (_moveKinematically) {

View file

@ -16,6 +16,7 @@
#include <NumericalConstants.h> #include <NumericalConstants.h>
#include "ObjectMotionState.h" #include "ObjectMotionState.h"
#include "PhysicsHelpers.h"
#include "PhysicsLogging.h" #include "PhysicsLogging.h"
const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f); const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f);
@ -112,13 +113,10 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
// add to new world // add to new world
_dynamicsWorld = world; _dynamicsWorld = world;
_pendingFlags &= ~PENDING_FLAG_JUMP; _pendingFlags &= ~PENDING_FLAG_JUMP;
// Before adding the RigidBody to the world we must save its oldGravity to the side
// because adding an object to the world will overwrite it with the default gravity.
btVector3 oldGravity = _rigidBody->getGravity();
_dynamicsWorld->addRigidBody(_rigidBody, _collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR); _dynamicsWorld->addRigidBody(_rigidBody, _collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR);
_dynamicsWorld->addAction(this); _dynamicsWorld->addAction(this);
// restore gravity settings // restore gravity settings because adding an object to the world overwrites its gravity setting
_rigidBody->setGravity(oldGravity); _rigidBody->setGravity(_gravity * _currentUp);
btCollisionShape* shape = _rigidBody->getCollisionShape(); btCollisionShape* shape = _rigidBody->getCollisionShape();
assert(shape && shape->getShapeType() == CAPSULE_SHAPE_PROXYTYPE); assert(shape && shape->getShapeType() == CAPSULE_SHAPE_PROXYTYPE);
_ghost.setCharacterCapsule(static_cast<btCapsuleShape*>(shape)); // KINEMATIC_CONTROLLER_HACK _ghost.setCharacterCapsule(static_cast<btCapsuleShape*>(shape)); // KINEMATIC_CONTROLLER_HACK
@ -130,7 +128,6 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
_ghost.setMaxStepHeight(0.75f * (_radius + _halfHeight)); // HACK _ghost.setMaxStepHeight(0.75f * (_radius + _halfHeight)); // HACK
_ghost.setMinWallAngle(PI / 4.0f); // HACK _ghost.setMinWallAngle(PI / 4.0f); // HACK
_ghost.setUpDirection(_currentUp); _ghost.setUpDirection(_currentUp);
_ghost.setGravity(DEFAULT_CHARACTER_GRAVITY);
_ghost.setWorldTransform(_rigidBody->getWorldTransform()); _ghost.setWorldTransform(_rigidBody->getWorldTransform());
} }
if (_dynamicsWorld) { if (_dynamicsWorld) {
@ -145,7 +142,7 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
} }
} }
bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld) { bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld, btScalar dt) {
_stepHeight = _minStepHeight; // clears last step obstacle _stepHeight = _minStepHeight; // clears last step obstacle
btDispatcher* dispatcher = collisionWorld->getDispatcher(); btDispatcher* dispatcher = collisionWorld->getDispatcher();
int numManifolds = dispatcher->getNumManifolds(); int numManifolds = dispatcher->getNumManifolds();
@ -220,14 +217,12 @@ void CharacterController::preStep(btCollisionWorld* collisionWorld) {
if (rayCallback.hasHit()) { if (rayCallback.hasHit()) {
_floorDistance = rayLength * rayCallback.m_closestHitFraction - _radius; _floorDistance = rayLength * rayCallback.m_closestHitFraction - _radius;
} }
_hasSupport = checkForSupport(collisionWorld);
} }
const btScalar MIN_TARGET_SPEED = 0.001f; const btScalar MIN_TARGET_SPEED = 0.001f;
const btScalar MIN_TARGET_SPEED_SQUARED = MIN_TARGET_SPEED * MIN_TARGET_SPEED; const btScalar MIN_TARGET_SPEED_SQUARED = MIN_TARGET_SPEED * MIN_TARGET_SPEED;
void CharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) { void CharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar dt) {
btVector3 velocity = _rigidBody->getLinearVelocity() - _parentVelocity; btVector3 velocity = _rigidBody->getLinearVelocity() - _parentVelocity;
if (_following) { if (_following) {
_followTimeAccumulator += dt; _followTimeAccumulator += dt;
@ -289,6 +284,8 @@ void CharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) {
btQuaternion endRot = angularDisplacement * startRot; btQuaternion endRot = angularDisplacement * startRot;
_rigidBody->setWorldTransform(btTransform(endRot, startPos)); _rigidBody->setWorldTransform(btTransform(endRot, startPos));
} }
_hasSupport = checkForSupport(collisionWorld, dt);
computeNewVelocity(dt, velocity); computeNewVelocity(dt, velocity);
if (_moveKinematically) { if (_moveKinematically) {
@ -298,7 +295,7 @@ void CharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) {
_ghost.setWorldTransform(transform); _ghost.setWorldTransform(transform);
_ghost.setMotorVelocity(_targetVelocity); _ghost.setMotorVelocity(_targetVelocity);
float overshoot = 1.0f * _radius; float overshoot = 1.0f * _radius;
_ghost.move(dt, overshoot); _ghost.move(dt, overshoot, _gravity);
transform.setOrigin(_ghost.getWorldTransform().getOrigin()); transform.setOrigin(_ghost.getWorldTransform().getOrigin());
_rigidBody->setWorldTransform(transform); _rigidBody->setWorldTransform(transform);
_rigidBody->setLinearVelocity(_ghost.getLinearVelocity()); _rigidBody->setLinearVelocity(_ghost.getLinearVelocity());
@ -360,7 +357,7 @@ void CharacterController::setState(State desiredState) {
if (_collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) { if (_collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) {
_rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f)); _rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f));
} else { } else {
_rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp); _rigidBody->setGravity(_gravity * _currentUp);
} }
} }
} }
@ -382,7 +379,7 @@ void CharacterController::setLocalBoundingBox(const glm::vec3& minCorner, const
if (glm::abs(radius - _radius) > FLT_EPSILON || glm::abs(halfHeight - _halfHeight) > FLT_EPSILON) { if (glm::abs(radius - _radius) > FLT_EPSILON || glm::abs(halfHeight - _halfHeight) > FLT_EPSILON) {
_radius = radius; _radius = radius;
_halfHeight = halfHeight; _halfHeight = halfHeight;
_minStepHeight = 0.02f; // HACK: hardcoded now but should be shape margin _minStepHeight = 0.041f; // HACK: hardcoded now but should be shape margin
_maxStepHeight = 0.75f * (_halfHeight + _radius); _maxStepHeight = 0.75f * (_halfHeight + _radius);
if (_dynamicsWorld) { if (_dynamicsWorld) {
@ -415,11 +412,8 @@ void CharacterController::handleChangedCollisionGroup() {
_pendingFlags &= ~PENDING_FLAG_UPDATE_COLLISION_GROUP; _pendingFlags &= ~PENDING_FLAG_UPDATE_COLLISION_GROUP;
if (_state != State::Hover && _rigidBody) { if (_state != State::Hover && _rigidBody) {
if (_collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) { _gravity = _collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS ? 0.0f : DEFAULT_CHARACTER_GRAVITY;
_rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f)); _rigidBody->setGravity(_gravity * _currentUp);
} else {
_rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp);
}
} }
} }
} }
@ -428,20 +422,14 @@ void CharacterController::updateUpAxis(const glm::quat& rotation) {
_currentUp = quatRotate(glmToBullet(rotation), LOCAL_UP_AXIS); _currentUp = quatRotate(glmToBullet(rotation), LOCAL_UP_AXIS);
_ghost.setUpDirection(_currentUp); _ghost.setUpDirection(_currentUp);
if (_state != State::Hover && _rigidBody) { if (_state != State::Hover && _rigidBody) {
if (_collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) { _rigidBody->setGravity(_gravity * _currentUp);
_rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f));
} else {
_rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp);
}
} }
} }
void CharacterController::setPositionAndOrientation( void CharacterController::setPositionAndOrientation(
const glm::vec3& position, const glm::vec3& position,
const glm::quat& orientation) { const glm::quat& orientation) {
// TODO: update gravity if up has changed
updateUpAxis(orientation); updateUpAxis(orientation);
_rotation = glmToBullet(orientation); _rotation = glmToBullet(orientation);
_position = glmToBullet(position + orientation * _shapeLocalOffset); _position = glmToBullet(position + orientation * _shapeLocalOffset);
} }
@ -555,10 +543,10 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel
// the motor pushes against step // the motor pushes against step
btVector3 leverArm = _stepPoint; btVector3 leverArm = _stepPoint;
motorVelocityWF = _stepNormal.cross(leverArm.cross(motorVelocityWF)); motorVelocityWF = _stepNormal.cross(leverArm.cross(motorVelocityWF));
btScalar distortedLength = motorVelocityWF.length(); btScalar doubleCrossLength = motorVelocityWF.length();
if (distortedLength > FLT_EPSILON) { if (doubleCrossLength > FLT_EPSILON) {
// scale the motor in the correct direction and rotate back to motor-frame // scale the motor in the correct direction and rotate back to motor-frame
motorVelocityWF *= (motorVelocity.length() / distortedLength); motorVelocityWF *= (motorVelocity.length() / doubleCrossLength);
motorVelocity += motorVelocityWF.rotate(axis, -angle); motorVelocity += motorVelocityWF.rotate(axis, -angle);
// make vTimescale as small as possible // make vTimescale as small as possible
vTimescale = glm::min(vTimescale, motor.hTimescale); vTimescale = glm::min(vTimescale, motor.hTimescale);

View file

@ -127,7 +127,7 @@ protected:
#endif #endif
void updateUpAxis(const glm::quat& rotation); void updateUpAxis(const glm::quat& rotation);
bool checkForSupport(btCollisionWorld* collisionWorld); bool checkForSupport(btCollisionWorld* collisionWorld, btScalar dt);
protected: protected:
struct CharacterMotor { struct CharacterMotor {
@ -176,7 +176,7 @@ protected:
btScalar _floorDistance; btScalar _floorDistance;
bool _hasSupport; bool _hasSupport;
btScalar _gravity; btScalar _gravity { DEFAULT_CHARACTER_GRAVITY };
btScalar _followTimeAccumulator { 0.0f }; btScalar _followTimeAccumulator { 0.0f };
btScalar _jumpSpeed; btScalar _jumpSpeed;

View file

@ -54,11 +54,6 @@ void CharacterGhostObject::setUpDirection(const btVector3& up) {
} }
} }
void CharacterGhostObject::setMotorVelocity(const btVector3& velocity) {
_motorVelocity = velocity;
_motorSpeed = _motorVelocity.length();
}
// override of btCollisionObject::setCollisionShape() // override of btCollisionObject::setCollisionShape()
void CharacterGhostObject::setCharacterCapsule(btCapsuleShape* capsule) { void CharacterGhostObject::setCharacterCapsule(btCapsuleShape* capsule) {
assert(capsule); assert(capsule);
@ -78,10 +73,11 @@ void CharacterGhostObject::setCollisionWorld(btCollisionWorld* world) {
} }
} }
void CharacterGhostObject::move(btScalar dt, btScalar overshoot) { void CharacterGhostObject::move(btScalar dt, btScalar overshoot, btScalar gravity) {
_onFloor = false; _onFloor = false;
_steppingUp = false;
assert(_world && _inWorld); assert(_world && _inWorld);
updateVelocity(dt); updateVelocity(dt, gravity);
// resolve any penetrations before sweeping // resolve any penetrations before sweeping
int32_t MAX_LOOPS = 4; int32_t MAX_LOOPS = 4;
@ -113,8 +109,9 @@ void CharacterGhostObject::move(btScalar dt, btScalar overshoot) {
updateTraction(startPosition); updateTraction(startPosition);
} }
btScalar speed = _linearVelocity.length();
btVector3 forwardSweep = dt * _linearVelocity; btVector3 forwardSweep = dt * _linearVelocity;
btScalar stepDistance = forwardSweep.length(); btScalar stepDistance = dt * speed;
btScalar MIN_SWEEP_DISTANCE = 0.0001f; btScalar MIN_SWEEP_DISTANCE = 0.0001f;
if (stepDistance < MIN_SWEEP_DISTANCE) { if (stepDistance < MIN_SWEEP_DISTANCE) {
// not moving, no need to sweep // not moving, no need to sweep
@ -146,6 +143,22 @@ void CharacterGhostObject::move(btScalar dt, btScalar overshoot) {
updateTraction(nextTransform.getOrigin()); updateTraction(nextTransform.getOrigin());
return; return;
} }
bool verticalOnly = btFabs(btFabs(_linearVelocity.dot(_upDirection)) - speed) < margin;
if (verticalOnly) {
// no need to step
nextTransform.setOrigin(startPosition + (result.m_closestHitFraction * stepDistance / longSweepDistance) * forwardSweep);
setWorldTransform(nextTransform);
if (result.m_hitNormalWorld.dot(_upDirection) > _maxWallNormalUpComponent) {
_floorNormal = result.m_hitNormalWorld;
_floorContact = result.m_hitPointWorld;
_steppingUp = false;
_onFloor = true;
_hovering = false;
}
updateTraction(nextTransform.getOrigin());
return;
}
// check if this hit is obviously unsteppable // check if this hit is obviously unsteppable
btVector3 hitFromBase = result.m_hitPointWorld - (startPosition - ((_radius + _halfHeight) * _upDirection)); btVector3 hitFromBase = result.m_hitPointWorld - (startPosition - ((_radius + _halfHeight) * _upDirection));
@ -189,6 +202,7 @@ void CharacterGhostObject::move(btScalar dt, btScalar overshoot) {
// can stand on future landing spot, so we interpolate toward it // can stand on future landing spot, so we interpolate toward it
_floorNormal = result.m_hitNormalWorld; _floorNormal = result.m_hitNormalWorld;
_floorContact = result.m_hitPointWorld; _floorContact = result.m_hitPointWorld;
_steppingUp = true;
_onFloor = true; _onFloor = true;
_hovering = false; _hovering = false;
nextTransform.setOrigin(startTransform.getOrigin() + result.m_closestHitFraction * downSweep); nextTransform.setOrigin(startTransform.getOrigin() + result.m_closestHitFraction * downSweep);
@ -337,11 +351,11 @@ void CharacterGhostObject::refreshOverlappingPairCache() {
_world->getBroadphase()->setAabb(getBroadphaseHandle(), minAabb, maxAabb, _world->getDispatcher()); _world->getBroadphase()->setAabb(getBroadphaseHandle(), minAabb, maxAabb, _world->getDispatcher());
} }
void CharacterGhostObject::updateVelocity(btScalar dt) { void CharacterGhostObject::updateVelocity(btScalar dt, btScalar gravity) {
if (_hovering) { if (_hovering) {
_linearVelocity *= 0.999f; // HACK damping _linearVelocity *= 0.999f; // HACK damping
} else { } else {
_linearVelocity += (dt * _gravity) * _upDirection; _linearVelocity += (dt * gravity) * _upDirection;
} }
} }
@ -371,7 +385,7 @@ void CharacterGhostObject::updateTraction(const btVector3& position) {
btVector3 pathDirection = leverArm.cross(_motorVelocity.cross(leverArm)); btVector3 pathDirection = leverArm.cross(_motorVelocity.cross(leverArm));
btScalar pathLength = pathDirection.length(); btScalar pathLength = pathDirection.length();
if (pathLength > FLT_EPSILON) { if (pathLength > FLT_EPSILON) {
_linearVelocity = (_motorSpeed / pathLength) * pathDirection; _linearVelocity = (_motorVelocity.length() / pathLength) * pathDirection;
} else { } else {
_linearVelocity = btVector3(0.0f, 0.0f, 0.0f); _linearVelocity = btVector3(0.0f, 0.0f, 0.0f);
} }

View file

@ -33,18 +33,18 @@ public:
void setRadiusAndHalfHeight(btScalar radius, btScalar halfHeight); void setRadiusAndHalfHeight(btScalar radius, btScalar halfHeight);
void setUpDirection(const btVector3& up); void setUpDirection(const btVector3& up);
void setMotorVelocity(const btVector3& velocity); void setMotorVelocity(const btVector3& velocity) { _motorVelocity = velocity; }
void setGravity(btScalar gravity) { _gravity = gravity; } // NOTE: we expect _gravity to be negative (in _upDirection)
void setMinWallAngle(btScalar angle) { _maxWallNormalUpComponent = cosf(angle); } void setMinWallAngle(btScalar angle) { _maxWallNormalUpComponent = cosf(angle); }
void setMaxStepHeight(btScalar height) { _maxStepHeight = height; } void setMaxStepHeight(btScalar height) { _maxStepHeight = height; }
void setLinearVelocity(const btVector3& velocity) { _linearVelocity = velocity; }
const btVector3& getLinearVelocity() const { return _linearVelocity; } const btVector3& getLinearVelocity() const { return _linearVelocity; }
void setCharacterCapsule(btCapsuleShape* capsule); void setCharacterCapsule(btCapsuleShape* capsule);
void setCollisionWorld(btCollisionWorld* world); void setCollisionWorld(btCollisionWorld* world);
void move(btScalar dt, btScalar overshoot); void move(btScalar dt, btScalar overshoot, btScalar gravity);
bool sweepTest(const btConvexShape* shape, bool sweepTest(const btConvexShape* shape,
const btTransform& start, const btTransform& start,
@ -52,6 +52,10 @@ public:
CharacterSweepResult& result) const; CharacterSweepResult& result) const;
bool isHovering() const { return _hovering; } bool isHovering() const { return _hovering; }
void setHovering(bool hovering) { _hovering = hovering; }
bool hasSupport() const { return _onFloor; }
bool isSteppingUp() const { return _steppingUp; }
protected: protected:
void removeFromWorld(); void removeFromWorld();
@ -63,7 +67,7 @@ protected:
bool resolvePenetration(int numTries); bool resolvePenetration(int numTries);
void refreshOverlappingPairCache(); void refreshOverlappingPairCache();
void updateVelocity(btScalar dt); void updateVelocity(btScalar dt, btScalar gravity);
void updateTraction(const btVector3& position); void updateTraction(const btVector3& position);
btScalar measureAvailableStepHeight() const; btScalar measureAvailableStepHeight() const;
void updateHoverState(const btVector3& position); void updateHoverState(const btVector3& position);
@ -78,8 +82,6 @@ protected:
//btScalar _distanceToFeet { 0.0f }; // input, distance from object center to lowest point on shape //btScalar _distanceToFeet { 0.0f }; // input, distance from object center to lowest point on shape
btScalar _halfHeight { 0.0f }; btScalar _halfHeight { 0.0f };
btScalar _radius { 0.0f }; btScalar _radius { 0.0f };
btScalar _motorSpeed { 0.0f }; // internal, cached for speed
btScalar _gravity { 0.0f }; // input, amplitude of gravity along _upDirection (should be negative)
btScalar _maxWallNormalUpComponent { 0.0f }; // input: max vertical component of wall normal btScalar _maxWallNormalUpComponent { 0.0f }; // input: max vertical component of wall normal
btScalar _maxStepHeight { 0.0f }; // input, max step height the character can climb btScalar _maxStepHeight { 0.0f }; // input, max step height the character can climb
btCapsuleShape* _characterShape { nullptr }; // input, shape of character btCapsuleShape* _characterShape { nullptr }; // input, shape of character
@ -89,6 +91,7 @@ protected:
bool _inWorld { false }; // internal, was added to world bool _inWorld { false }; // internal, was added to world
bool _hovering { false }; // internal, bool _hovering { false }; // internal,
bool _onFloor { false }; // output, is actually standing on floor bool _onFloor { false }; // output, is actually standing on floor
bool _steppingUp { false }; // output, future sweep hit a steppable ledge
bool _hasFloor { false }; // output, has floor underneath to fall on bool _hasFloor { false }; // output, has floor underneath to fall on
}; };