improved management of collisionless avatar state

This commit is contained in:
Andrew Meadows 2017-04-03 16:32:59 -07:00
parent 95a4bb9ef4
commit fe401c7488
3 changed files with 68 additions and 65 deletions

View file

@ -163,12 +163,14 @@ MyAvatar::MyAvatar(QThread* thread, RigPointer rig) :
if (recordingInterface->getPlayFromCurrentLocation()) {
setRecordingBasis();
}
_previousCollisionGroup = _characterController.getCollisionGroup();
_characterController.setCollisionGroup(BULLET_COLLISION_GROUP_COLLISIONLESS);
_previousCollisionGroup = _characterController.computeCollisionGroup();
_characterController.setCollisionless(true);
} else {
clearRecordingBasis();
useFullAvatarURL(_fullAvatarURLFromPreferences, _fullAvatarModelName);
_characterController.setCollisionGroup(_previousCollisionGroup);
if (_previousCollisionGroup != BULLET_COLLISION_GROUP_COLLISIONLESS) {
_characterController.setCollisionless(false);
}
}
auto audioIO = DependencyManager::get<AudioClient>();
@ -550,12 +552,12 @@ void MyAvatar::simulate(float deltaTime) {
EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr;
if (entityTree) {
bool flyingAllowed = true;
bool ghostingAllowed = true;
bool collisionlessAllowed = true;
entityTree->withWriteLock([&] {
std::shared_ptr<ZoneEntityItem> zone = entityTreeRenderer->myAvatarZone();
if (zone) {
flyingAllowed = zone->getFlyingAllowed();
ghostingAllowed = zone->getGhostingAllowed();
collisionlessAllowed = zone->getGhostingAllowed();
}
auto now = usecTimestampNow();
EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender();
@ -586,9 +588,7 @@ void MyAvatar::simulate(float deltaTime) {
}
});
_characterController.setFlyingAllowed(flyingAllowed);
if (!ghostingAllowed && _characterController.getCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) {
_characterController.setCollisionGroup(BULLET_COLLISION_GROUP_MY_AVATAR);
}
_characterController.setCollisionlessAllowed(collisionlessAllowed);
}
updateAvatarEntities();
@ -1448,7 +1448,7 @@ void MyAvatar::updateMotors() {
glm::quat motorRotation;
if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) {
if (_characterController.getState() == CharacterController::State::Hover ||
_characterController.getCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) {
_characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) {
motorRotation = getMyHead()->getCameraOrientation();
} else {
// non-hovering = walking: follow camera twist about vertical but not lift
@ -1884,7 +1884,7 @@ void MyAvatar::updateActionMotor(float deltaTime) {
glm::vec3 direction = forward + right;
CharacterController::State state = _characterController.getState();
if (state == CharacterController::State::Hover ||
_characterController.getCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) {
_characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) {
// we can fly --> support vertical motion
glm::vec3 up = (getDriveKey(TRANSLATE_Y)) * IDENTITY_UP;
direction += up;
@ -2207,20 +2207,13 @@ void MyAvatar::setCollisionsEnabled(bool enabled) {
return;
}
bool ghostingAllowed = true;
auto entityTreeRenderer = qApp->getEntities();
if (entityTreeRenderer) {
std::shared_ptr<ZoneEntityItem> zone = entityTreeRenderer->myAvatarZone();
if (zone) {
ghostingAllowed = zone->getGhostingAllowed();
}
}
int16_t group = (enabled || !ghostingAllowed) ? BULLET_COLLISION_GROUP_MY_AVATAR : BULLET_COLLISION_GROUP_COLLISIONLESS;
_characterController.setCollisionGroup(group);
_characterController.setCollisionless(!enabled);
}
bool MyAvatar::getCollisionsEnabled() {
return _characterController.getCollisionGroup() != BULLET_COLLISION_GROUP_COLLISIONLESS;
// may return 'false' even though the collisionless option was requested
// because the zone may disallow collisionless avatars
return _characterController.computeCollisionGroup() != BULLET_COLLISION_GROUP_COLLISIONLESS;
}
void MyAvatar::setCharacterControllerEnabled(bool enabled) {

View file

@ -111,11 +111,12 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
}
_dynamicsWorld = nullptr;
}
int16_t collisionGroup = computeCollisionGroup();
if (world && _rigidBody) {
// add to new world
_dynamicsWorld = world;
_pendingFlags &= ~PENDING_FLAG_JUMP;
_dynamicsWorld->addRigidBody(_rigidBody, _collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR);
_dynamicsWorld->addRigidBody(_rigidBody, collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR);
_dynamicsWorld->addAction(this);
// restore gravity settings because adding an object to the world overwrites its gravity setting
_rigidBody->setGravity(_gravity * _currentUp);
@ -123,7 +124,7 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
assert(shape && shape->getShapeType() == CONVEX_HULL_SHAPE_PROXYTYPE);
_ghost.setCharacterShape(static_cast<btConvexHullShape*>(shape));
}
_ghost.setCollisionGroupAndMask(_collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR & (~ _collisionGroup));
_ghost.setCollisionGroupAndMask(collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR & (~ collisionGroup));
_ghost.setCollisionWorld(_dynamicsWorld);
_ghost.setRadiusAndHalfHeight(_radius, _halfHeight);
_ghost.setWorldTransform(_rigidBody->getWorldTransform());
@ -331,20 +332,20 @@ void CharacterController::setState(State desiredState) {
#ifdef DEBUG_STATE_CHANGE
qCDebug(physics) << "CharacterController::setState" << stateToStr(desiredState) << "from" << stateToStr(_state) << "," << reason;
#endif
if (_rigidBody) {
if (desiredState == State::Hover && _state != State::Hover) {
// hover enter
_rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f));
} else if (_state == State::Hover && desiredState != State::Hover) {
// hover exit
if (_collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) {
_rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f));
} else {
_rigidBody->setGravity(_gravity * _currentUp);
}
}
}
_state = desiredState;
updateGravity();
}
}
void CharacterController::updateGravity() {
int16_t collisionGroup = computeCollisionGroup();
if (_state == State::Hover || collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) {
_gravity = 0.0f;
} else {
_gravity = DEFAULT_CHARACTER_GRAVITY;
}
if (_rigidBody) {
_rigidBody->setGravity(_gravity * _currentUp);
}
}
@ -379,11 +380,18 @@ void CharacterController::setLocalBoundingBox(const glm::vec3& minCorner, const
_shapeLocalOffset = minCorner + 0.5f * scale;
}
void CharacterController::setCollisionGroup(int16_t group) {
if (_collisionGroup != group) {
_collisionGroup = group;
void CharacterController::setCollisionless(bool collisionless) {
if (collisionless != _collisionless) {
_collisionless = collisionless;
_pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_GROUP;
_ghost.setCollisionGroupAndMask(_collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR & (~ _collisionGroup));
}
}
int16_t CharacterController::computeCollisionGroup() const {
if (_collisionless) {
return _collisionlessAllowed ? BULLET_COLLISION_GROUP_COLLISIONLESS : BULLET_COLLISION_GROUP_MY_AVATAR;
} else {
return BULLET_COLLISION_GROUP_MY_AVATAR;
}
}
@ -392,18 +400,11 @@ void CharacterController::handleChangedCollisionGroup() {
// ATM the easiest way to update collision groups is to remove/re-add the RigidBody
if (_dynamicsWorld) {
_dynamicsWorld->removeRigidBody(_rigidBody);
_dynamicsWorld->addRigidBody(_rigidBody, _collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR);
int16_t collisionGroup = computeCollisionGroup();
_dynamicsWorld->addRigidBody(_rigidBody, collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR);
}
_pendingFlags &= ~PENDING_FLAG_UPDATE_COLLISION_GROUP;
if (_collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) {
_gravity = 0.0f;
} else if (_state != State::Hover) {
_gravity = (_collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) ? 0.0f : DEFAULT_CHARACTER_GRAVITY;
}
if (_rigidBody) {
_rigidBody->setGravity(_gravity * _currentUp);
}
updateGravity();
}
}
@ -491,7 +492,8 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel
btScalar angle = motor.rotation.getAngle();
btVector3 velocity = worldVelocity.rotate(axis, -angle);
if (_collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS ||
int16_t collisionGroup = computeCollisionGroup();
if (collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS ||
_state == State::Hover || motor.hTimescale == motor.vTimescale) {
// modify velocity
btScalar tau = dt / motor.hTimescale;
@ -630,7 +632,8 @@ void CharacterController::updateState() {
btVector3 rayStart = _position;
btScalar rayLength = _radius;
if (_collisionGroup == BULLET_COLLISION_GROUP_MY_AVATAR) {
int16_t collisionGroup = computeCollisionGroup();
if (collisionGroup == BULLET_COLLISION_GROUP_MY_AVATAR) {
rayLength += MAX_FALL_HEIGHT;
} else {
rayLength += MIN_HOVER_HEIGHT;
@ -667,7 +670,7 @@ void CharacterController::updateState() {
// disable normal state transitions while collisionless
const btScalar MAX_WALKING_SPEED = 2.65f;
if (_collisionGroup == BULLET_COLLISION_GROUP_MY_AVATAR) {
if (collisionGroup == BULLET_COLLISION_GROUP_MY_AVATAR) {
switch (_state) {
case State::Ground:
if (!rayHasHit && !_hasSupport) {
@ -719,14 +722,15 @@ void CharacterController::updateState() {
}
} else {
// when collisionless: only switch between State::Ground and State::Hover
// and bypass state debugging
if (rayHasHit) {
if (velocity.length() > (MAX_WALKING_SPEED)) {
SET_STATE(State::Hover, "collisionless in air");
_state = State::Hover;
} else {
SET_STATE(State::Ground, "collisionless above ground");
_state = State::Ground;
}
} else {
SET_STATE(State::Hover, "collisionless in air");
_state = State::Hover;
}
}
}
@ -762,14 +766,17 @@ bool CharacterController::getRigidBodyLocation(glm::vec3& avatarRigidBodyPositio
}
void CharacterController::setFlyingAllowed(bool value) {
if (_flyingAllowed != value) {
if (value != _flyingAllowed) {
_flyingAllowed = value;
if (!_flyingAllowed && _state == State::Hover) {
// disable normal state transitions while collisionless
if (_collisionGroup == BULLET_COLLISION_GROUP_MY_AVATAR) {
SET_STATE(State::InAir, "flying not allowed");
}
SET_STATE(State::InAir, "flying not allowed");
}
}
}
void CharacterController::setCollisionlessAllowed(bool value) {
if (value != _collisionlessAllowed) {
_collisionlessAllowed = value;
_pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_GROUP;
}
}

View file

@ -113,13 +113,14 @@ public:
bool isEnabledAndReady() const { return _dynamicsWorld; }
void setCollisionGroup(int16_t group);
int16_t getCollisionGroup() const { return _collisionGroup; }
void setCollisionless(bool collisionless);
int16_t computeCollisionGroup() const;
void handleChangedCollisionGroup();
bool getRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation);
void setFlyingAllowed(bool value);
void setCollisionlessAllowed(bool value);
protected:
@ -129,6 +130,7 @@ protected:
void setState(State state);
#endif
void updateGravity();
void updateUpAxis(const glm::quat& rotation);
bool checkForSupport(btCollisionWorld* collisionWorld);
@ -198,7 +200,8 @@ protected:
uint32_t _previousFlags { 0 };
bool _flyingAllowed { true };
int16_t _collisionGroup { BULLET_COLLISION_GROUP_MY_AVATAR };
bool _collisionlessAllowed { true };
bool _collisionless { false };
};
#endif // hifi_CharacterController_h