Updated character controller with a state enumeration

Also, adjusted checkForSupport logic such that very slanted walls are not considered support.
This commit is contained in:
Anthony J. Thibault 2016-01-29 11:57:14 -08:00
parent e9fd439ffd
commit 61c55ebf6c
8 changed files with 62 additions and 58 deletions

View file

@ -1510,7 +1510,8 @@ void MyAvatar::updatePosition(float deltaTime) {
// rotate velocity into camera frame // rotate velocity into camera frame
glm::quat rotation = getHead()->getCameraOrientation(); glm::quat rotation = getHead()->getCameraOrientation();
glm::vec3 localVelocity = glm::inverse(rotation) * _targetVelocity; glm::vec3 localVelocity = glm::inverse(rotation) * _targetVelocity;
glm::vec3 newLocalVelocity = applyKeyboardMotor(deltaTime, localVelocity, isHovering()); bool isHovering = _characterController.getState() == CharacterController::State::Hover;
glm::vec3 newLocalVelocity = applyKeyboardMotor(deltaTime, localVelocity, isHovering);
newLocalVelocity = applyScriptedMotor(deltaTime, newLocalVelocity); newLocalVelocity = applyScriptedMotor(deltaTime, newLocalVelocity);
// rotate back into world-frame // rotate back into world-frame
@ -1579,10 +1580,6 @@ bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float
return false; return false;
} }
bool MyAvatar::isHovering() const {
return _characterController.isHovering();
}
void MyAvatar::increaseSize() { void MyAvatar::increaseSize() {
if ((1.0f + SCALING_RATIO) * _targetScale < MAX_AVATAR_SCALE) { if ((1.0f + SCALING_RATIO) * _targetScale < MAX_AVATAR_SCALE) {
_targetScale *= (1.0f + SCALING_RATIO); _targetScale *= (1.0f + SCALING_RATIO);

View file

@ -239,8 +239,6 @@ public:
glm::quat getCustomListenOrientation() { return _customListenOrientation; } glm::quat getCustomListenOrientation() { return _customListenOrientation; }
void setCustomListenOrientation(glm::quat customListenOrientation) { _customListenOrientation = customListenOrientation; } void setCustomListenOrientation(glm::quat customListenOrientation) { _customListenOrientation = customListenOrientation; }
bool isHovering() const;
public slots: public slots:
void increaseSize(); void increaseSize();
void decreaseSize(); void decreaseSize();

View file

@ -67,7 +67,7 @@ void MyCharacterController::updateShapeIfNecessary() {
_rigidBody->setAngularFactor(0.0f); _rigidBody->setAngularFactor(0.0f);
_rigidBody->setWorldTransform(btTransform(glmToBullet(_avatar->getOrientation()), _rigidBody->setWorldTransform(btTransform(glmToBullet(_avatar->getOrientation()),
glmToBullet(_avatar->getPosition()))); glmToBullet(_avatar->getPosition())));
if (_isHovering) { 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(DEFAULT_CHARACTER_GRAVITY * _currentUp);

View file

@ -72,6 +72,18 @@ void SkeletonModel::initJointStates() {
emit skeletonLoaded(); emit skeletonLoaded();
} }
Rig::CharacterControllerState convertCharacterControllerState(CharacterController::State state) {
switch (state) {
default:
case CharacterController::State::Ground:
return Rig::CharacterControllerState::Ground;
case CharacterController::State::InAir:
return Rig::CharacterControllerState::InAir;
case CharacterController::State::Hover:
return Rig::CharacterControllerState::Hover;
};
}
// Called within Model::simulate call, below. // Called within Model::simulate call, below.
void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
Head* head = _owningAvatar->getHead(); Head* head = _owningAvatar->getHead();
@ -133,13 +145,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
_rig->updateFromHandParameters(handParams, deltaTime); _rig->updateFromHandParameters(handParams, deltaTime);
Rig::CharacterControllerState ccState = Rig::CharacterControllerState::Ground; Rig::CharacterControllerState ccState = convertCharacterControllerState(myAvatar->getCharacterController()->getState());
if (myAvatar->getCharacterController()->isHovering()) {
ccState = Rig::CharacterControllerState::Hover;
} else if (myAvatar->getCharacterController()->isJumping()) {
ccState = Rig::CharacterControllerState::Jump;
}
_rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation(), ccState); _rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation(), ccState);
// evaluate AnimGraph animation and update jointStates. // evaluate AnimGraph animation and update jointStates.

View file

@ -577,11 +577,11 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
_desiredStateAge = 0.0f; _desiredStateAge = 0.0f;
} }
_desiredState = RigRole::Hover; _desiredState = RigRole::Hover;
} else if (ccState == CharacterControllerState::Jump) { } else if (ccState == CharacterControllerState::InAir) {
if (_desiredState != RigRole::Jump) { if (_desiredState != RigRole::InAir) {
_desiredStateAge = 0.0f; _desiredStateAge = 0.0f;
} }
_desiredState = RigRole::Jump; _desiredState = RigRole::InAir;
} else { } else {
float moveThresh; float moveThresh;
if (_state != RigRole::Move) { if (_state != RigRole::Move) {
@ -719,7 +719,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
_animVars.set("isNotFlying", false); _animVars.set("isNotFlying", false);
_animVars.set("isInAir", false); _animVars.set("isInAir", false);
_animVars.set("isNotInAir", true); _animVars.set("isNotInAir", true);
} else if (_state == RigRole::Jump) { } else if (_state == RigRole::InAir) {
// jumping in-air // jumping in-air
_animVars.set("isMovingForward", false); _animVars.set("isMovingForward", false);
_animVars.set("isMovingBackward", false); _animVars.set("isMovingBackward", false);

View file

@ -75,7 +75,7 @@ public:
enum class CharacterControllerState { enum class CharacterControllerState {
Ground = 0, Ground = 0,
Jump, InAir,
Hover Hover
}; };
@ -278,7 +278,7 @@ public:
Turn, Turn,
Move, Move,
Hover, Hover,
Jump InAir
}; };
RigRole _state { RigRole::Idle }; RigRole _state { RigRole::Idle };
RigRole _desiredState { RigRole::Idle }; RigRole _desiredState { RigRole::Idle };

View file

@ -51,10 +51,7 @@ CharacterController::CharacterController() {
_followDesiredBodyTransform.setIdentity(); _followDesiredBodyTransform.setIdentity();
_followTimeRemaining = 0.0f; _followTimeRemaining = 0.0f;
_jumpSpeed = JUMP_SPEED; _jumpSpeed = JUMP_SPEED;
_isOnGround = false; _state = State::Hover;
_isJumping = false;
_isFalling = false;
_isHovering = true;
_isPushingUp = false; _isPushingUp = false;
_jumpToHoverStart = 0; _jumpToHoverStart = 0;
_followTime = 0.0f; _followTime = 0.0f;
@ -107,6 +104,8 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
} }
} }
static const float COS_PI_OVER_THREE = cosf(PI / 3.0f);
bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld) const { bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld) const {
int numManifolds = collisionWorld->getDispatcher()->getNumManifolds(); int numManifolds = collisionWorld->getDispatcher()->getNumManifolds();
for (int i = 0; i < numManifolds; i++) { for (int i = 0; i < numManifolds; i++) {
@ -119,8 +118,10 @@ bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld) cons
btManifoldPoint& pt = contactManifold->getContactPoint(j); btManifoldPoint& pt = contactManifold->getContactPoint(j);
// check to see if contact point is touching the bottom sphere of the capsule. // check to see if contact point is touching the bottom sphere of the capsule.
// and the contact normal is not slanted too much.
float contactPointY = (obA == _rigidBody) ? pt.m_localPointA.getY() : pt.m_localPointB.getY(); float contactPointY = (obA == _rigidBody) ? pt.m_localPointA.getY() : pt.m_localPointB.getY();
if (contactPointY < -_halfHeight) { btVector3 normal = (obA == _rigidBody) ? pt.m_normalWorldOnB : -pt.m_normalWorldOnB;
if (contactPointY < -_halfHeight && normal.dot(_currentUp) > COS_PI_OVER_THREE) {
return true; return true;
} }
} }
@ -165,7 +166,7 @@ void CharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) {
} }
const btScalar MIN_SPEED = 0.001f; const btScalar MIN_SPEED = 0.001f;
if (_isHovering) { if (_state == State::Hover) {
if (desiredSpeed < MIN_SPEED) { if (desiredSpeed < MIN_SPEED) {
if (actualSpeed < MIN_SPEED) { if (actualSpeed < MIN_SPEED) {
_rigidBody->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f)); _rigidBody->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f));
@ -255,9 +256,9 @@ void CharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) {
void CharacterController::jump() { void CharacterController::jump() {
// check for case where user is holding down "jump" key... // check for case where user is holding down "jump" key...
// we'll eventually tansition to "hover" // we'll eventually transition to "hover"
if (!_isJumping) { if (_state != State::InAir) {
if (!_isHovering) { if (_state != State::Hover) {
_jumpToHoverStart = usecTimestampNow(); _jumpToHoverStart = usecTimestampNow();
_pendingFlags |= PENDING_FLAG_JUMP; _pendingFlags |= PENDING_FLAG_JUMP;
} }
@ -266,7 +267,7 @@ void CharacterController::jump() {
const quint64 JUMP_TO_HOVER_PERIOD = 75 * (USECS_PER_SECOND / 100); const quint64 JUMP_TO_HOVER_PERIOD = 75 * (USECS_PER_SECOND / 100);
if (now - _jumpToHoverStart > JUMP_TO_HOVER_PERIOD) { if (now - _jumpToHoverStart > JUMP_TO_HOVER_PERIOD) {
_isPushingUp = true; _isPushingUp = true;
setHovering(true); setState(State::Hover);
} }
} }
} }
@ -276,19 +277,19 @@ bool CharacterController::onGround() const {
return _floorDistance < FLOOR_PROXIMITY_THRESHOLD || _hasSupport; return _floorDistance < FLOOR_PROXIMITY_THRESHOLD || _hasSupport;
} }
void CharacterController::setHovering(bool hover) { void CharacterController::setState(State desiredState) {
if (hover != _isHovering) { if (desiredState == State::Hover && _state != State::Hover) {
_isHovering = hover; // hover enter
_isJumping = false;
if (_rigidBody) { if (_rigidBody) {
if (hover) {
_rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f)); _rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f));
} else { }
} else if (_state == State::Hover && desiredState != State::Hover) {
// hover exit
if (_rigidBody) {
_rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp); _rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp);
} }
} }
} _state = desiredState;
} }
void CharacterController::setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale) { void CharacterController::setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale) {
@ -335,9 +336,8 @@ void CharacterController::setEnabled(bool enabled) {
_pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION; _pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION;
} }
_pendingFlags &= ~ PENDING_FLAG_ADD_TO_SIMULATION; _pendingFlags &= ~ PENDING_FLAG_ADD_TO_SIMULATION;
_isOnGround = false;
} }
setHovering(true); setState(State::Hover);
_enabled = enabled; _enabled = enabled;
} }
} }
@ -345,7 +345,7 @@ void CharacterController::setEnabled(bool enabled) {
void CharacterController::updateUpAxis(const glm::quat& rotation) { void CharacterController::updateUpAxis(const glm::quat& rotation) {
btVector3 oldUp = _currentUp; btVector3 oldUp = _currentUp;
_currentUp = quatRotate(glmToBullet(rotation), LOCAL_UP_AXIS); _currentUp = quatRotate(glmToBullet(rotation), LOCAL_UP_AXIS);
if (!_isHovering) { if (_state != State::Hover) {
const btScalar MIN_UP_ERROR = 0.01f; const btScalar MIN_UP_ERROR = 0.01f;
if (oldUp.distance(_currentUp) > MIN_UP_ERROR) { if (oldUp.distance(_currentUp) > MIN_UP_ERROR) {
_rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp); _rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp);
@ -425,23 +425,23 @@ void CharacterController::preSimulation() {
if (rayCallback.hasHit()) { if (rayCallback.hasHit()) {
_floorDistance = rayLength * rayCallback.m_closestHitFraction - _radius; _floorDistance = rayLength * rayCallback.m_closestHitFraction - _radius;
const btScalar MIN_HOVER_HEIGHT = 3.0f; const btScalar MIN_HOVER_HEIGHT = 3.0f;
if (_isHovering && _floorDistance < MIN_HOVER_HEIGHT && !_isPushingUp) { if (_state == State::Hover && _floorDistance < MIN_HOVER_HEIGHT && !_isPushingUp) {
setHovering(false); setState(State::InAir);
} }
// TODO: use collision events rather than ray-trace test to disable jumping // TODO: use collision events rather than ray-trace test to disable jumping
const btScalar JUMP_PROXIMITY_THRESHOLD = 0.1f * _radius; const btScalar JUMP_PROXIMITY_THRESHOLD = 0.1f * _radius;
if (_floorDistance < JUMP_PROXIMITY_THRESHOLD) { if (_floorDistance < JUMP_PROXIMITY_THRESHOLD || _hasSupport) {
_isJumping = false; setState(State::Ground);
} }
} else if (!_hasSupport) { } else if (!_hasSupport) {
_floorDistance = FLT_MAX; _floorDistance = FLT_MAX;
setHovering(true); setState(State::Hover);
} }
if (_pendingFlags & PENDING_FLAG_JUMP) { if (_pendingFlags & PENDING_FLAG_JUMP) {
_pendingFlags &= ~ PENDING_FLAG_JUMP; _pendingFlags &= ~ PENDING_FLAG_JUMP;
if (onGround()) { if (onGround()) {
_isJumping = true; setState(State::InAir);
btVector3 velocity = _rigidBody->getLinearVelocity(); btVector3 velocity = _rigidBody->getLinearVelocity();
velocity += _jumpSpeed * _currentUp; velocity += _jumpSpeed * _currentUp;
_rigidBody->setLinearVelocity(velocity); _rigidBody->setLinearVelocity(velocity);

View file

@ -75,9 +75,13 @@ public:
glm::vec3 getLinearVelocity() const; glm::vec3 getLinearVelocity() const;
bool isJumping() const { return _isJumping; } enum class State {
bool isHovering() const { return _isHovering; } Ground = 0,
void setHovering(bool enabled); InAir,
Hover
};
State getState() const { return _state; }
void setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale); void setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale);
@ -87,6 +91,8 @@ public:
bool getRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation); bool getRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation);
protected: protected:
void setState(State state);
void updateUpAxis(const glm::quat& rotation); void updateUpAxis(const glm::quat& rotation);
bool checkForSupport(btCollisionWorld* collisionWorld) const; bool checkForSupport(btCollisionWorld* collisionWorld) const;
@ -117,10 +123,7 @@ protected:
btQuaternion _followAngularDisplacement; btQuaternion _followAngularDisplacement;
bool _enabled; bool _enabled;
bool _isOnGround; State _state;
bool _isJumping;
bool _isFalling;
bool _isHovering;
bool _isPushingUp; bool _isPushingUp;
btDynamicsWorld* _dynamicsWorld { nullptr }; btDynamicsWorld* _dynamicsWorld { nullptr };