diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp old mode 100644 new mode 100755 index b9c7dc729d..5e4f02742e --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -270,7 +270,6 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { if (avatar->getSkeletonModel()->isLoaded()) { // remove the orb if it is there avatar->removeOrb(); - avatar->updateCollisionGroup(_myAvatar->getOtherAvatarsCollisionsEnabled()); if (avatar->needsPhysicsUpdate()) { _avatarsToChangeInPhysics.insert(avatar); } @@ -376,7 +375,6 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { } AvatarSharedPointer AvatarManager::newSharedAvatar(const QUuid& sessionUUID) { - auto otherAvatar = new OtherAvatar(qApp->thread()); otherAvatar->setSessionUUID(sessionUUID); auto nodeList = DependencyManager::get(); diff --git a/interface/src/avatar/AvatarMotionState.cpp b/interface/src/avatar/AvatarMotionState.cpp old mode 100644 new mode 100755 index 3fa59ea967..77fc81fa04 --- a/interface/src/avatar/AvatarMotionState.cpp +++ b/interface/src/avatar/AvatarMotionState.cpp @@ -15,7 +15,6 @@ #include #include - AvatarMotionState::AvatarMotionState(OtherAvatarPointer avatar, const btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) { assert(_avatar); _type = MOTIONSTATE_TYPE_AVATAR; @@ -172,7 +171,10 @@ QUuid AvatarMotionState::getSimulatorID() const { // virtual void AvatarMotionState::computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const { group = _collisionGroup; - mask = _collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS ? 0 : Physics::getDefaultCollisionMask(group); + mask = Physics::getDefaultCollisionMask(group); + if (!_avatar->getCollideWithOtherAvatars()) { + mask &= ~(BULLET_COLLISION_GROUP_MY_AVATAR | BULLET_COLLISION_GROUP_OTHER_AVATAR); + } } // virtual diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 84d6689e92..fec988b29f 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -205,12 +205,12 @@ MyAvatar::MyAvatar(QThread* thread) : if (recordingInterface->getPlayFromCurrentLocation()) { setRecordingBasis(); } - _previousCollisionGroup = _characterController.computeCollisionGroup(); + _previousCollisionMask = _characterController.computeCollisionMask(); _characterController.setCollisionless(true); } else { clearRecordingBasis(); useFullAvatarURL(_fullAvatarURLFromPreferences, _fullAvatarModelName); - if (_previousCollisionGroup != BULLET_COLLISION_GROUP_COLLISIONLESS) { + if (_previousCollisionMask != BULLET_COLLISION_MASK_COLLISIONLESS) { _characterController.setCollisionless(false); } } @@ -2534,7 +2534,7 @@ void MyAvatar::updateMotors() { float verticalMotorTimescale; if (_characterController.getState() == CharacterController::State::Hover || - _characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) { + _characterController.computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS) { horizontalMotorTimescale = FLYING_MOTOR_TIMESCALE; verticalMotorTimescale = FLYING_MOTOR_TIMESCALE; } else { @@ -2544,7 +2544,7 @@ void MyAvatar::updateMotors() { if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) { if (_characterController.getState() == CharacterController::State::Hover || - _characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) { + _characterController.computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS) { motorRotation = getMyHead()->getHeadOrientation(); } else { // non-hovering = walking: follow camera twist about vertical but not lift @@ -2599,7 +2599,7 @@ void MyAvatar::prepareForPhysicsSimulation() { qDebug() << "Warning: getParentVelocity failed" << getID(); parentVelocity = glm::vec3(); } - _characterController.handleChangedCollisionGroup(); + _characterController.handleChangedCollisionMask(); _characterController.setParentVelocity(parentVelocity); _characterController.setScaleFactor(getSensorToWorldScale()); @@ -3279,7 +3279,7 @@ void MyAvatar::updateOrientation(float deltaTime) { head->setBaseRoll(ROLL(euler)); } else { head->setBaseYaw(0.0f); - head->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime + head->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime + getDriveKey(DELTA_PITCH) * _pitchSpeed / PITCH_SPEED_DEFAULT); head->setBaseRoll(0.0f); } @@ -3325,7 +3325,7 @@ void MyAvatar::updateActionMotor(float deltaTime) { glm::vec3 direction = forward + right; if (state == CharacterController::State::Hover || - _characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) { + _characterController.computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS) { glm::vec3 up = (getDriveKey(TRANSLATE_Y)) * IDENTITY_UP; direction += up; } @@ -3881,7 +3881,7 @@ void MyAvatar::setCollisionsEnabled(bool enabled) { bool MyAvatar::getCollisionsEnabled() { // may return 'false' even though the collisionless option was requested // because the zone may disallow collisionless avatars - return _characterController.computeCollisionGroup() != BULLET_COLLISION_GROUP_COLLISIONLESS; + return _characterController.computeCollisionMask() != BULLET_COLLISION_MASK_COLLISIONLESS; } void MyAvatar::setOtherAvatarsCollisionsEnabled(bool enabled) { @@ -3890,7 +3890,11 @@ void MyAvatar::setOtherAvatarsCollisionsEnabled(bool enabled) { QMetaObject::invokeMethod(this, "setOtherAvatarsCollisionsEnabled", Q_ARG(bool, enabled)); return; } + bool change = _collideWithOtherAvatars != enabled; _collideWithOtherAvatars = enabled; + if (change) { + setCollisionWithOtherAvatarsFlags(); + } emit otherAvatarsCollisionsEnabledChanged(enabled); } @@ -3898,6 +3902,11 @@ bool MyAvatar::getOtherAvatarsCollisionsEnabled() { return _collideWithOtherAvatars; } +void MyAvatar::setCollisionWithOtherAvatarsFlags() { + _characterController.setCollideWithOtherAvatars(_collideWithOtherAvatars); + _characterController.setPendingFlagsUpdateCollisionMask(); +} + void MyAvatar::updateCollisionCapsuleCache() { glm::vec3 start, end; float radius; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h old mode 100644 new mode 100755 index 20dc2ae42b..af08955ca0 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -297,6 +297,8 @@ public: void reset(bool andRecenter = false, bool andReload = true, bool andHead = true); + void setCollisionWithOtherAvatarsFlags() override; + /**jsdoc * @function MyAvatar.resetSensorsAndBody */ @@ -1732,7 +1734,7 @@ private: SharedSoundPointer _collisionSound; MyCharacterController _characterController; - int32_t _previousCollisionGroup { BULLET_COLLISION_GROUP_MY_AVATAR }; + int32_t _previousCollisionMask { BULLET_COLLISION_MASK_MY_AVATAR }; AvatarWeakPointer _lookAtTargetAvatar; glm::vec3 _targetAvatarPosition; diff --git a/interface/src/avatar/MyCharacterController.cpp b/interface/src/avatar/MyCharacterController.cpp index 798dbc91ed..821b01c2c6 100755 --- a/interface/src/avatar/MyCharacterController.cpp +++ b/interface/src/avatar/MyCharacterController.cpp @@ -202,6 +202,29 @@ bool MyCharacterController::testRayShotgun(const glm::vec3& position, const glm: return result.hitFraction < 1.0f; } +int32_t MyCharacterController::computeCollisionMask() const { + int32_t collisionMask = BULLET_COLLISION_MASK_MY_AVATAR; + if (_collisionless && _collisionlessAllowed) { + collisionMask = BULLET_COLLISION_MASK_COLLISIONLESS; + } else if (!_collideWithOtherAvatars) { + collisionMask &= ~BULLET_COLLISION_GROUP_OTHER_AVATAR; + } + return collisionMask; +} + +void MyCharacterController::handleChangedCollisionMask() { + if (_pendingFlags & PENDING_FLAG_UPDATE_COLLISION_MASK) { + // ATM the easiest way to update collision groups/masks is to remove/re-add the RigidBody + if (_dynamicsWorld) { + _dynamicsWorld->removeRigidBody(_rigidBody); + int32_t collisionMask = computeCollisionMask(); + _dynamicsWorld->addRigidBody(_rigidBody, BULLET_COLLISION_GROUP_MY_AVATAR, collisionMask); + } + _pendingFlags &= ~PENDING_FLAG_UPDATE_COLLISION_MASK; + updateCurrentGravity(); + } +} + btConvexHullShape* MyCharacterController::computeShape() const { // HACK: the avatar collides using convex hull with a collision margin equal to // the old capsule radius. Two points define a capsule and additional points are diff --git a/interface/src/avatar/MyCharacterController.h b/interface/src/avatar/MyCharacterController.h old mode 100644 new mode 100755 index fd9caface2..76fe588e71 --- a/interface/src/avatar/MyCharacterController.h +++ b/interface/src/avatar/MyCharacterController.h @@ -42,6 +42,12 @@ public: void setDensity(btScalar density) { _density = density; } + int32_t computeCollisionMask() const override; + void handleChangedCollisionMask() override; + + bool _collideWithOtherAvatars{ true }; + void setCollideWithOtherAvatars(bool collideWithOtherAvatars) { _collideWithOtherAvatars = collideWithOtherAvatars; } + protected: void initRayShotgun(const btCollisionWorld* world); void updateMassProperties() override; diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp old mode 100644 new mode 100755 index 356b365f93..26d69841d0 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -187,7 +187,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { } } - bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS); + bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS); if (isFlying != _prevIsFlying) { const float FLY_TO_IDLE_HIPS_TRANSITION_TIME = 0.5f; _flyIdleTimer = FLY_TO_IDLE_HIPS_TRANSITION_TIME; @@ -198,7 +198,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // if hips are not under direct control, estimate the hips position. if (avatarHeadPose.isValid() && !(params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] & (uint8_t)Rig::ControllerFlags::Enabled)) { - bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS); + bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS); // timescale in seconds const float TRANS_HORIZ_TIMESCALE = 0.15f; diff --git a/interface/src/avatar/OtherAvatar.cpp b/interface/src/avatar/OtherAvatar.cpp old mode 100644 new mode 100755 index 1f4fdc5e52..754d914135 --- a/interface/src/avatar/OtherAvatar.cpp +++ b/interface/src/avatar/OtherAvatar.cpp @@ -135,17 +135,9 @@ void OtherAvatar::rebuildCollisionShape() { } } -void OtherAvatar::updateCollisionGroup(bool myAvatarCollide) { +void OtherAvatar::setCollisionWithOtherAvatarsFlags() { if (_motionState) { - bool collides = _motionState->getCollisionGroup() == BULLET_COLLISION_GROUP_OTHER_AVATAR && myAvatarCollide; - if (_collideWithOtherAvatars != collides) { - if (!myAvatarCollide) { - _collideWithOtherAvatars = false; - } - auto newCollisionGroup = _collideWithOtherAvatars ? BULLET_COLLISION_GROUP_OTHER_AVATAR : BULLET_COLLISION_GROUP_COLLISIONLESS; - _motionState->setCollisionGroup(newCollisionGroup); - _motionState->addDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); - } + _motionState->addDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); } } diff --git a/interface/src/avatar/OtherAvatar.h b/interface/src/avatar/OtherAvatar.h old mode 100644 new mode 100755 index a1dc5724a9..969f551783 --- a/interface/src/avatar/OtherAvatar.h +++ b/interface/src/avatar/OtherAvatar.h @@ -46,7 +46,9 @@ public: bool shouldBeInPhysicsSimulation() const; bool needsPhysicsUpdate() const; - void updateCollisionGroup(bool myAvatarCollide); + bool getCollideWithOtherAvatars() const { return _collideWithOtherAvatars; } + + void setCollisionWithOtherAvatarsFlags() override; void simulate(float deltaTime, bool inView) override; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp old mode 100644 new mode 100755 index 098caddde8..17dad715f9 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1172,6 +1172,9 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { sourceBuffer += sizeof(AvatarDataPacket::AdditionalFlags); + if (collideWithOtherAvatarsChanged) { + setCollisionWithOtherAvatarsFlags(); + } if (somethingChanged) { _additionalFlagsChanged = now; } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h old mode 100644 new mode 100755 index 2889e5ffa3..9128c2dbf9 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -496,6 +496,8 @@ public: /// \return number of bytes parsed virtual int parseDataFromBuffer(const QByteArray& buffer); + virtual void setCollisionWithOtherAvatarsFlags() {}; + // Body Rotation (degrees) float getBodyYaw() const; void setBodyYaw(float bodyYaw); diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 8fd6d4eada..d5ded6f909 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -109,7 +109,8 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { } _dynamicsWorld = nullptr; } - int32_t collisionGroup = computeCollisionGroup(); + int32_t collisionMask = computeCollisionMask(); + int32_t collisionGroup = BULLET_COLLISION_GROUP_MY_AVATAR; if (_rigidBody) { updateMassProperties(); } @@ -117,7 +118,7 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { // add to new world _dynamicsWorld = world; _pendingFlags &= ~PENDING_FLAG_JUMP; - _dynamicsWorld->addRigidBody(_rigidBody, collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR); + _dynamicsWorld->addRigidBody(_rigidBody, collisionGroup, collisionMask); _dynamicsWorld->addAction(this); // restore gravity settings because adding an object to the world overwrites its gravity setting _rigidBody->setGravity(_currentGravity * _currentUp); @@ -127,7 +128,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, collisionMask & (~ collisionGroup)); _ghost.setCollisionWorld(_dynamicsWorld); _ghost.setRadiusAndHalfHeight(_radius, _halfHeight); if (_rigidBody) { @@ -384,8 +385,8 @@ static const char* stateToStr(CharacterController::State state) { #endif // #ifdef DEBUG_STATE_CHANGE void CharacterController::updateCurrentGravity() { - int32_t collisionGroup = computeCollisionGroup(); - if (_state == State::Hover || collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) { + int32_t collisionMask = computeCollisionMask(); + if (_state == State::Hover || collisionMask == BULLET_COLLISION_MASK_COLLISIONLESS) { _currentGravity = 0.0f; } else { _currentGravity = _gravity; @@ -458,28 +459,7 @@ void CharacterController::setLocalBoundingBox(const glm::vec3& minCorner, const void CharacterController::setCollisionless(bool collisionless) { if (collisionless != _collisionless) { _collisionless = collisionless; - _pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_GROUP; - } -} - -int32_t CharacterController::computeCollisionGroup() const { - if (_collisionless) { - return _collisionlessAllowed ? BULLET_COLLISION_GROUP_COLLISIONLESS : BULLET_COLLISION_GROUP_MY_AVATAR; - } else { - return BULLET_COLLISION_GROUP_MY_AVATAR; - } -} - -void CharacterController::handleChangedCollisionGroup() { - if (_pendingFlags & PENDING_FLAG_UPDATE_COLLISION_GROUP) { - // ATM the easiest way to update collision groups is to remove/re-add the RigidBody - if (_dynamicsWorld) { - _dynamicsWorld->removeRigidBody(_rigidBody); - int32_t collisionGroup = computeCollisionGroup(); - _dynamicsWorld->addRigidBody(_rigidBody, collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR); - } - _pendingFlags &= ~PENDING_FLAG_UPDATE_COLLISION_GROUP; - updateCurrentGravity(); + _pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_MASK; } } @@ -567,8 +547,8 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel btScalar angle = motor.rotation.getAngle(); btVector3 velocity = worldVelocity.rotate(axis, -angle); - int32_t collisionGroup = computeCollisionGroup(); - if (collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS || + int32_t collisionMask = computeCollisionMask(); + if (collisionMask == BULLET_COLLISION_MASK_COLLISIONLESS || _state == State::Hover || motor.hTimescale == motor.vTimescale) { // modify velocity btScalar tau = dt / motor.hTimescale; @@ -708,11 +688,11 @@ void CharacterController::updateState() { btVector3 rayStart = _position; btScalar rayLength = _radius; - int32_t collisionGroup = computeCollisionGroup(); - if (collisionGroup == BULLET_COLLISION_GROUP_MY_AVATAR) { - rayLength += _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT; - } else { + int32_t collisionMask = computeCollisionMask(); + if (collisionMask == BULLET_COLLISION_MASK_COLLISIONLESS) { rayLength += MIN_HOVER_HEIGHT; + } else { + rayLength += _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT; } btVector3 rayEnd = rayStart - rayLength * _currentUp; @@ -746,69 +726,7 @@ void CharacterController::updateState() { // disable normal state transitions while collisionless const btScalar MAX_WALKING_SPEED = 2.65f; - if (collisionGroup == BULLET_COLLISION_GROUP_MY_AVATAR) { - switch (_state) { - case State::Ground: - if (!rayHasHit && !_hasSupport) { - SET_STATE(State::Hover, "no ground detected"); - } else if (_pendingFlags & PENDING_FLAG_JUMP && _jumpButtonDownCount != _takeoffJumpButtonID) { - _takeoffJumpButtonID = _jumpButtonDownCount; - _takeoffToInAirStartTime = now; - SET_STATE(State::Takeoff, "jump pressed"); - } else if (rayHasHit && !_hasSupport && _floorDistance > GROUND_TO_FLY_THRESHOLD) { - SET_STATE(State::InAir, "falling"); - } - break; - case State::Takeoff: - if (!rayHasHit && !_hasSupport) { - SET_STATE(State::Hover, "no ground"); - } else if ((now - _takeoffToInAirStartTime) > TAKE_OFF_TO_IN_AIR_PERIOD) { - SET_STATE(State::InAir, "takeoff done"); - - // compute jumpSpeed based on the scaled jump height for the default avatar in default gravity. - const float jumpHeight = std::max(_scaleFactor * DEFAULT_AVATAR_JUMP_HEIGHT, DEFAULT_AVATAR_MIN_JUMP_HEIGHT); - const float jumpSpeed = sqrtf(2.0f * -DEFAULT_AVATAR_GRAVITY * jumpHeight); - velocity += jumpSpeed * _currentUp; - _rigidBody->setLinearVelocity(velocity); - } - break; - case State::InAir: { - const float jumpHeight = std::max(_scaleFactor * DEFAULT_AVATAR_JUMP_HEIGHT, DEFAULT_AVATAR_MIN_JUMP_HEIGHT); - const float jumpSpeed = sqrtf(2.0f * -DEFAULT_AVATAR_GRAVITY * jumpHeight); - if ((velocity.dot(_currentUp) <= (jumpSpeed / 2.0f)) && ((_floorDistance < FLY_TO_GROUND_THRESHOLD) || _hasSupport)) { - SET_STATE(State::Ground, "hit ground"); - } else if (_flyingAllowed) { - btVector3 desiredVelocity = _targetVelocity; - if (desiredVelocity.length2() < MIN_TARGET_SPEED_SQUARED) { - desiredVelocity = btVector3(0.0f, 0.0f, 0.0f); - } - bool vertTargetSpeedIsNonZero = desiredVelocity.dot(_currentUp) > MIN_TARGET_SPEED; - if ((jumpButtonHeld || vertTargetSpeedIsNonZero) && (_takeoffJumpButtonID != _jumpButtonDownCount)) { - SET_STATE(State::Hover, "double jump button"); - } else if ((jumpButtonHeld || vertTargetSpeedIsNonZero) && (now - _jumpButtonDownStartTime) > JUMP_TO_HOVER_PERIOD) { - SET_STATE(State::Hover, "jump button held"); - } else if (_floorDistance > _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT) { - // Transition to hover if we are above the fall threshold - SET_STATE(State::Hover, "above fall threshold"); - } - } else if (!rayHasHit && !_hasSupport) { - SET_STATE(State::Hover, "no ground detected"); - } - break; - } - case State::Hover: - btScalar horizontalSpeed = (velocity - velocity.dot(_currentUp) * _currentUp).length(); - bool flyingFast = horizontalSpeed > (MAX_WALKING_SPEED * 0.75f); - if (!_flyingAllowed && rayHasHit) { - SET_STATE(State::InAir, "flying not allowed"); - } else if ((_floorDistance < MIN_HOVER_HEIGHT) && !jumpButtonHeld && !flyingFast) { - SET_STATE(State::InAir, "near ground"); - } else if (((_floorDistance < FLY_TO_GROUND_THRESHOLD) || _hasSupport) && !flyingFast) { - SET_STATE(State::Ground, "touching ground"); - } - break; - } - } else { + if (collisionMask == BULLET_COLLISION_MASK_COLLISIONLESS) { // when collisionless: only switch between State::Ground and State::Hover // and bypass state debugging if (rayHasHit) { @@ -820,6 +738,68 @@ void CharacterController::updateState() { } else { _state = State::Hover; } + } else { + switch (_state) { + case State::Ground: + if (!rayHasHit && !_hasSupport) { + SET_STATE(State::Hover, "no ground detected"); + } else if (_pendingFlags & PENDING_FLAG_JUMP && _jumpButtonDownCount != _takeoffJumpButtonID) { + _takeoffJumpButtonID = _jumpButtonDownCount; + _takeoffToInAirStartTime = now; + SET_STATE(State::Takeoff, "jump pressed"); + } else if (rayHasHit && !_hasSupport && _floorDistance > GROUND_TO_FLY_THRESHOLD) { + SET_STATE(State::InAir, "falling"); + } + break; + case State::Takeoff: + if (!rayHasHit && !_hasSupport) { + SET_STATE(State::Hover, "no ground"); + } else if ((now - _takeoffToInAirStartTime) > TAKE_OFF_TO_IN_AIR_PERIOD) { + SET_STATE(State::InAir, "takeoff done"); + + // compute jumpSpeed based on the scaled jump height for the default avatar in default gravity. + const float jumpHeight = std::max(_scaleFactor * DEFAULT_AVATAR_JUMP_HEIGHT, DEFAULT_AVATAR_MIN_JUMP_HEIGHT); + const float jumpSpeed = sqrtf(2.0f * -DEFAULT_AVATAR_GRAVITY * jumpHeight); + velocity += jumpSpeed * _currentUp; + _rigidBody->setLinearVelocity(velocity); + } + break; + case State::InAir: { + const float jumpHeight = std::max(_scaleFactor * DEFAULT_AVATAR_JUMP_HEIGHT, DEFAULT_AVATAR_MIN_JUMP_HEIGHT); + const float jumpSpeed = sqrtf(2.0f * -DEFAULT_AVATAR_GRAVITY * jumpHeight); + if ((velocity.dot(_currentUp) <= (jumpSpeed / 2.0f)) && ((_floorDistance < FLY_TO_GROUND_THRESHOLD) || _hasSupport)) { + SET_STATE(State::Ground, "hit ground"); + } else if (_flyingAllowed) { + btVector3 desiredVelocity = _targetVelocity; + if (desiredVelocity.length2() < MIN_TARGET_SPEED_SQUARED) { + desiredVelocity = btVector3(0.0f, 0.0f, 0.0f); + } + bool vertTargetSpeedIsNonZero = desiredVelocity.dot(_currentUp) > MIN_TARGET_SPEED; + if ((jumpButtonHeld || vertTargetSpeedIsNonZero) && (_takeoffJumpButtonID != _jumpButtonDownCount)) { + SET_STATE(State::Hover, "double jump button"); + } else if ((jumpButtonHeld || vertTargetSpeedIsNonZero) && (now - _jumpButtonDownStartTime) > JUMP_TO_HOVER_PERIOD) { + SET_STATE(State::Hover, "jump button held"); + } else if (_floorDistance > _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT) { + // Transition to hover if we are above the fall threshold + SET_STATE(State::Hover, "above fall threshold"); + } + } else if (!rayHasHit && !_hasSupport) { + SET_STATE(State::Hover, "no ground detected"); + } + break; + } + case State::Hover: + btScalar horizontalSpeed = (velocity - velocity.dot(_currentUp) * _currentUp).length(); + bool flyingFast = horizontalSpeed > (MAX_WALKING_SPEED * 0.75f); + if (!_flyingAllowed && rayHasHit) { + SET_STATE(State::InAir, "flying not allowed"); + } else if ((_floorDistance < MIN_HOVER_HEIGHT) && !jumpButtonHeld && !flyingFast) { + SET_STATE(State::InAir, "near ground"); + } else if (((_floorDistance < FLY_TO_GROUND_THRESHOLD) || _hasSupport) && !flyingFast) { + SET_STATE(State::Ground, "touching ground"); + } + break; + } } } @@ -866,6 +846,6 @@ void CharacterController::setFlyingAllowed(bool value) { void CharacterController::setCollisionlessAllowed(bool value) { if (value != _collisionlessAllowed) { _collisionlessAllowed = value; - _pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_GROUP; + _pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_MASK; } } diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h old mode 100644 new mode 100755 index 50db2bea12..cac37da0b9 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -30,7 +30,7 @@ const uint32_t PENDING_FLAG_ADD_TO_SIMULATION = 1U << 0; const uint32_t PENDING_FLAG_REMOVE_FROM_SIMULATION = 1U << 1; const uint32_t PENDING_FLAG_UPDATE_SHAPE = 1U << 2; const uint32_t PENDING_FLAG_JUMP = 1U << 3; -const uint32_t PENDING_FLAG_UPDATE_COLLISION_GROUP = 1U << 4; +const uint32_t PENDING_FLAG_UPDATE_COLLISION_MASK = 1U << 4; const uint32_t PENDING_FLAG_RECOMPUTE_FLYING = 1U << 5; const float DEFAULT_MIN_FLOOR_NORMAL_DOT_UP = cosf(PI / 3.0f); @@ -120,14 +120,16 @@ public: bool isStuck() const { return _isStuck; } void setCollisionless(bool collisionless); - int32_t computeCollisionGroup() const; - void handleChangedCollisionGroup(); + + virtual int32_t computeCollisionMask() const = 0; + virtual void handleChangedCollisionMask() = 0; bool getRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation); void setFlyingAllowed(bool value); void setCollisionlessAllowed(bool value); + void setPendingFlagsUpdateCollisionMask(){ _pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_MASK; } protected: #ifdef DEBUG_STATE_CHANGE