From fe401c7488ddeb9da58c224452153445848726dc Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 3 Apr 2017 16:32:59 -0700 Subject: [PATCH] improved management of collisionless avatar state --- interface/src/avatar/MyAvatar.cpp | 35 +++----- libraries/physics/src/CharacterController.cpp | 89 ++++++++++--------- libraries/physics/src/CharacterController.h | 9 +- 3 files changed, 68 insertions(+), 65 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 45b4930b3e..333c33ba3b 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -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(); @@ -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 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 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) { diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index f98710ecc3..7d08675e9d 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -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(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; + } +} diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 8323284315..fe0362cdc7 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -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