Merge pull request #14713 from raveenajain/collideWithOtherAvatars_scale

20565 Avatar::_collideWithOtherAvatars does not scale
This commit is contained in:
Shannon Romano 2019-01-25 11:06:36 -08:00 committed by GitHub
commit 28edb472fc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 147 additions and 126 deletions

2
interface/src/avatar/AvatarManager.cpp Normal file → Executable file
View file

@ -270,7 +270,6 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
if (avatar->getSkeletonModel()->isLoaded()) { if (avatar->getSkeletonModel()->isLoaded()) {
// remove the orb if it is there // remove the orb if it is there
avatar->removeOrb(); avatar->removeOrb();
avatar->updateCollisionGroup(_myAvatar->getOtherAvatarsCollisionsEnabled());
if (avatar->needsPhysicsUpdate()) { if (avatar->needsPhysicsUpdate()) {
_avatarsToChangeInPhysics.insert(avatar); _avatarsToChangeInPhysics.insert(avatar);
} }
@ -376,7 +375,6 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
} }
AvatarSharedPointer AvatarManager::newSharedAvatar(const QUuid& sessionUUID) { AvatarSharedPointer AvatarManager::newSharedAvatar(const QUuid& sessionUUID) {
auto otherAvatar = new OtherAvatar(qApp->thread()); auto otherAvatar = new OtherAvatar(qApp->thread());
otherAvatar->setSessionUUID(sessionUUID); otherAvatar->setSessionUUID(sessionUUID);
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();

6
interface/src/avatar/AvatarMotionState.cpp Normal file → Executable file
View file

@ -15,7 +15,6 @@
#include <PhysicsEngine.h> #include <PhysicsEngine.h>
#include <PhysicsHelpers.h> #include <PhysicsHelpers.h>
AvatarMotionState::AvatarMotionState(OtherAvatarPointer avatar, const btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) { AvatarMotionState::AvatarMotionState(OtherAvatarPointer avatar, const btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) {
assert(_avatar); assert(_avatar);
_type = MOTIONSTATE_TYPE_AVATAR; _type = MOTIONSTATE_TYPE_AVATAR;
@ -172,7 +171,10 @@ QUuid AvatarMotionState::getSimulatorID() const {
// virtual // virtual
void AvatarMotionState::computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const { void AvatarMotionState::computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const {
group = _collisionGroup; 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 // virtual

View file

@ -205,12 +205,12 @@ MyAvatar::MyAvatar(QThread* thread) :
if (recordingInterface->getPlayFromCurrentLocation()) { if (recordingInterface->getPlayFromCurrentLocation()) {
setRecordingBasis(); setRecordingBasis();
} }
_previousCollisionGroup = _characterController.computeCollisionGroup(); _previousCollisionMask = _characterController.computeCollisionMask();
_characterController.setCollisionless(true); _characterController.setCollisionless(true);
} else { } else {
clearRecordingBasis(); clearRecordingBasis();
useFullAvatarURL(_fullAvatarURLFromPreferences, _fullAvatarModelName); useFullAvatarURL(_fullAvatarURLFromPreferences, _fullAvatarModelName);
if (_previousCollisionGroup != BULLET_COLLISION_GROUP_COLLISIONLESS) { if (_previousCollisionMask != BULLET_COLLISION_MASK_COLLISIONLESS) {
_characterController.setCollisionless(false); _characterController.setCollisionless(false);
} }
} }
@ -2534,7 +2534,7 @@ void MyAvatar::updateMotors() {
float verticalMotorTimescale; float verticalMotorTimescale;
if (_characterController.getState() == CharacterController::State::Hover || if (_characterController.getState() == CharacterController::State::Hover ||
_characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) { _characterController.computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS) {
horizontalMotorTimescale = FLYING_MOTOR_TIMESCALE; horizontalMotorTimescale = FLYING_MOTOR_TIMESCALE;
verticalMotorTimescale = FLYING_MOTOR_TIMESCALE; verticalMotorTimescale = FLYING_MOTOR_TIMESCALE;
} else { } else {
@ -2544,7 +2544,7 @@ void MyAvatar::updateMotors() {
if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) { if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) {
if (_characterController.getState() == CharacterController::State::Hover || if (_characterController.getState() == CharacterController::State::Hover ||
_characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) { _characterController.computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS) {
motorRotation = getMyHead()->getHeadOrientation(); motorRotation = getMyHead()->getHeadOrientation();
} else { } else {
// non-hovering = walking: follow camera twist about vertical but not lift // non-hovering = walking: follow camera twist about vertical but not lift
@ -2599,7 +2599,7 @@ void MyAvatar::prepareForPhysicsSimulation() {
qDebug() << "Warning: getParentVelocity failed" << getID(); qDebug() << "Warning: getParentVelocity failed" << getID();
parentVelocity = glm::vec3(); parentVelocity = glm::vec3();
} }
_characterController.handleChangedCollisionGroup(); _characterController.handleChangedCollisionMask();
_characterController.setParentVelocity(parentVelocity); _characterController.setParentVelocity(parentVelocity);
_characterController.setScaleFactor(getSensorToWorldScale()); _characterController.setScaleFactor(getSensorToWorldScale());
@ -3279,7 +3279,7 @@ void MyAvatar::updateOrientation(float deltaTime) {
head->setBaseRoll(ROLL(euler)); head->setBaseRoll(ROLL(euler));
} else { } else {
head->setBaseYaw(0.0f); 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); + getDriveKey(DELTA_PITCH) * _pitchSpeed / PITCH_SPEED_DEFAULT);
head->setBaseRoll(0.0f); head->setBaseRoll(0.0f);
} }
@ -3325,7 +3325,7 @@ void MyAvatar::updateActionMotor(float deltaTime) {
glm::vec3 direction = forward + right; glm::vec3 direction = forward + right;
if (state == CharacterController::State::Hover || 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; glm::vec3 up = (getDriveKey(TRANSLATE_Y)) * IDENTITY_UP;
direction += up; direction += up;
} }
@ -3881,7 +3881,7 @@ void MyAvatar::setCollisionsEnabled(bool enabled) {
bool MyAvatar::getCollisionsEnabled() { bool MyAvatar::getCollisionsEnabled() {
// may return 'false' even though the collisionless option was requested // may return 'false' even though the collisionless option was requested
// because the zone may disallow collisionless avatars // 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) { void MyAvatar::setOtherAvatarsCollisionsEnabled(bool enabled) {
@ -3890,7 +3890,11 @@ void MyAvatar::setOtherAvatarsCollisionsEnabled(bool enabled) {
QMetaObject::invokeMethod(this, "setOtherAvatarsCollisionsEnabled", Q_ARG(bool, enabled)); QMetaObject::invokeMethod(this, "setOtherAvatarsCollisionsEnabled", Q_ARG(bool, enabled));
return; return;
} }
bool change = _collideWithOtherAvatars != enabled;
_collideWithOtherAvatars = enabled; _collideWithOtherAvatars = enabled;
if (change) {
setCollisionWithOtherAvatarsFlags();
}
emit otherAvatarsCollisionsEnabledChanged(enabled); emit otherAvatarsCollisionsEnabledChanged(enabled);
} }
@ -3898,6 +3902,11 @@ bool MyAvatar::getOtherAvatarsCollisionsEnabled() {
return _collideWithOtherAvatars; return _collideWithOtherAvatars;
} }
void MyAvatar::setCollisionWithOtherAvatarsFlags() {
_characterController.setCollideWithOtherAvatars(_collideWithOtherAvatars);
_characterController.setPendingFlagsUpdateCollisionMask();
}
void MyAvatar::updateCollisionCapsuleCache() { void MyAvatar::updateCollisionCapsuleCache() {
glm::vec3 start, end; glm::vec3 start, end;
float radius; float radius;

4
interface/src/avatar/MyAvatar.h Normal file → Executable file
View file

@ -297,6 +297,8 @@ public:
void reset(bool andRecenter = false, bool andReload = true, bool andHead = true); void reset(bool andRecenter = false, bool andReload = true, bool andHead = true);
void setCollisionWithOtherAvatarsFlags() override;
/**jsdoc /**jsdoc
* @function MyAvatar.resetSensorsAndBody * @function MyAvatar.resetSensorsAndBody
*/ */
@ -1732,7 +1734,7 @@ private:
SharedSoundPointer _collisionSound; SharedSoundPointer _collisionSound;
MyCharacterController _characterController; MyCharacterController _characterController;
int32_t _previousCollisionGroup { BULLET_COLLISION_GROUP_MY_AVATAR }; int32_t _previousCollisionMask { BULLET_COLLISION_MASK_MY_AVATAR };
AvatarWeakPointer _lookAtTargetAvatar; AvatarWeakPointer _lookAtTargetAvatar;
glm::vec3 _targetAvatarPosition; glm::vec3 _targetAvatarPosition;

View file

@ -202,6 +202,29 @@ bool MyCharacterController::testRayShotgun(const glm::vec3& position, const glm:
return result.hitFraction < 1.0f; 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 { btConvexHullShape* MyCharacterController::computeShape() const {
// HACK: the avatar collides using convex hull with a collision margin equal to // 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 // the old capsule radius. Two points define a capsule and additional points are

6
interface/src/avatar/MyCharacterController.h Normal file → Executable file
View file

@ -42,6 +42,12 @@ public:
void setDensity(btScalar density) { _density = density; } void setDensity(btScalar density) { _density = density; }
int32_t computeCollisionMask() const override;
void handleChangedCollisionMask() override;
bool _collideWithOtherAvatars{ true };
void setCollideWithOtherAvatars(bool collideWithOtherAvatars) { _collideWithOtherAvatars = collideWithOtherAvatars; }
protected: protected:
void initRayShotgun(const btCollisionWorld* world); void initRayShotgun(const btCollisionWorld* world);
void updateMassProperties() override; void updateMassProperties() override;

4
interface/src/avatar/MySkeletonModel.cpp Normal file → Executable file
View file

@ -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) { if (isFlying != _prevIsFlying) {
const float FLY_TO_IDLE_HIPS_TRANSITION_TIME = 0.5f; const float FLY_TO_IDLE_HIPS_TRANSITION_TIME = 0.5f;
_flyIdleTimer = FLY_TO_IDLE_HIPS_TRANSITION_TIME; _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 hips are not under direct control, estimate the hips position.
if (avatarHeadPose.isValid() && !(params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] & (uint8_t)Rig::ControllerFlags::Enabled)) { 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 // timescale in seconds
const float TRANS_HORIZ_TIMESCALE = 0.15f; const float TRANS_HORIZ_TIMESCALE = 0.15f;

12
interface/src/avatar/OtherAvatar.cpp Normal file → Executable file
View file

@ -135,17 +135,9 @@ void OtherAvatar::rebuildCollisionShape() {
} }
} }
void OtherAvatar::updateCollisionGroup(bool myAvatarCollide) { void OtherAvatar::setCollisionWithOtherAvatarsFlags() {
if (_motionState) { if (_motionState) {
bool collides = _motionState->getCollisionGroup() == BULLET_COLLISION_GROUP_OTHER_AVATAR && myAvatarCollide; _motionState->addDirtyFlags(Simulation::DIRTY_COLLISION_GROUP);
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);
}
} }
} }

4
interface/src/avatar/OtherAvatar.h Normal file → Executable file
View file

@ -46,7 +46,9 @@ public:
bool shouldBeInPhysicsSimulation() const; bool shouldBeInPhysicsSimulation() const;
bool needsPhysicsUpdate() const; bool needsPhysicsUpdate() const;
void updateCollisionGroup(bool myAvatarCollide); bool getCollideWithOtherAvatars() const { return _collideWithOtherAvatars; }
void setCollisionWithOtherAvatarsFlags() override;
void simulate(float deltaTime, bool inView) override; void simulate(float deltaTime, bool inView) override;

3
libraries/avatars/src/AvatarData.cpp Normal file → Executable file
View file

@ -1172,6 +1172,9 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
sourceBuffer += sizeof(AvatarDataPacket::AdditionalFlags); sourceBuffer += sizeof(AvatarDataPacket::AdditionalFlags);
if (collideWithOtherAvatarsChanged) {
setCollisionWithOtherAvatarsFlags();
}
if (somethingChanged) { if (somethingChanged) {
_additionalFlagsChanged = now; _additionalFlagsChanged = now;
} }

2
libraries/avatars/src/AvatarData.h Normal file → Executable file
View file

@ -496,6 +496,8 @@ public:
/// \return number of bytes parsed /// \return number of bytes parsed
virtual int parseDataFromBuffer(const QByteArray& buffer); virtual int parseDataFromBuffer(const QByteArray& buffer);
virtual void setCollisionWithOtherAvatarsFlags() {};
// Body Rotation (degrees) // Body Rotation (degrees)
float getBodyYaw() const; float getBodyYaw() const;
void setBodyYaw(float bodyYaw); void setBodyYaw(float bodyYaw);

View file

@ -109,7 +109,8 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
} }
_dynamicsWorld = nullptr; _dynamicsWorld = nullptr;
} }
int32_t collisionGroup = computeCollisionGroup(); int32_t collisionMask = computeCollisionMask();
int32_t collisionGroup = BULLET_COLLISION_GROUP_MY_AVATAR;
if (_rigidBody) { if (_rigidBody) {
updateMassProperties(); updateMassProperties();
} }
@ -117,7 +118,7 @@ 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;
_dynamicsWorld->addRigidBody(_rigidBody, collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR); _dynamicsWorld->addRigidBody(_rigidBody, collisionGroup, collisionMask);
_dynamicsWorld->addAction(this); _dynamicsWorld->addAction(this);
// restore gravity settings because adding an object to the world overwrites its gravity setting // restore gravity settings because adding an object to the world overwrites its gravity setting
_rigidBody->setGravity(_currentGravity * _currentUp); _rigidBody->setGravity(_currentGravity * _currentUp);
@ -127,7 +128,7 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
assert(shape && shape->getShapeType() == CONVEX_HULL_SHAPE_PROXYTYPE); assert(shape && shape->getShapeType() == CONVEX_HULL_SHAPE_PROXYTYPE);
_ghost.setCharacterShape(static_cast<btConvexHullShape*>(shape)); _ghost.setCharacterShape(static_cast<btConvexHullShape*>(shape));
} }
_ghost.setCollisionGroupAndMask(collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR & (~ collisionGroup)); _ghost.setCollisionGroupAndMask(collisionGroup, collisionMask & (~ collisionGroup));
_ghost.setCollisionWorld(_dynamicsWorld); _ghost.setCollisionWorld(_dynamicsWorld);
_ghost.setRadiusAndHalfHeight(_radius, _halfHeight); _ghost.setRadiusAndHalfHeight(_radius, _halfHeight);
if (_rigidBody) { if (_rigidBody) {
@ -384,8 +385,8 @@ static const char* stateToStr(CharacterController::State state) {
#endif // #ifdef DEBUG_STATE_CHANGE #endif // #ifdef DEBUG_STATE_CHANGE
void CharacterController::updateCurrentGravity() { void CharacterController::updateCurrentGravity() {
int32_t collisionGroup = computeCollisionGroup(); int32_t collisionMask = computeCollisionMask();
if (_state == State::Hover || collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) { if (_state == State::Hover || collisionMask == BULLET_COLLISION_MASK_COLLISIONLESS) {
_currentGravity = 0.0f; _currentGravity = 0.0f;
} else { } else {
_currentGravity = _gravity; _currentGravity = _gravity;
@ -458,28 +459,7 @@ void CharacterController::setLocalBoundingBox(const glm::vec3& minCorner, const
void CharacterController::setCollisionless(bool collisionless) { void CharacterController::setCollisionless(bool collisionless) {
if (collisionless != _collisionless) { if (collisionless != _collisionless) {
_collisionless = collisionless; _collisionless = collisionless;
_pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_GROUP; _pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_MASK;
}
}
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();
} }
} }
@ -567,8 +547,8 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel
btScalar angle = motor.rotation.getAngle(); btScalar angle = motor.rotation.getAngle();
btVector3 velocity = worldVelocity.rotate(axis, -angle); btVector3 velocity = worldVelocity.rotate(axis, -angle);
int32_t collisionGroup = computeCollisionGroup(); int32_t collisionMask = computeCollisionMask();
if (collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS || if (collisionMask == BULLET_COLLISION_MASK_COLLISIONLESS ||
_state == State::Hover || motor.hTimescale == motor.vTimescale) { _state == State::Hover || motor.hTimescale == motor.vTimescale) {
// modify velocity // modify velocity
btScalar tau = dt / motor.hTimescale; btScalar tau = dt / motor.hTimescale;
@ -708,11 +688,11 @@ void CharacterController::updateState() {
btVector3 rayStart = _position; btVector3 rayStart = _position;
btScalar rayLength = _radius; btScalar rayLength = _radius;
int32_t collisionGroup = computeCollisionGroup(); int32_t collisionMask = computeCollisionMask();
if (collisionGroup == BULLET_COLLISION_GROUP_MY_AVATAR) { if (collisionMask == BULLET_COLLISION_MASK_COLLISIONLESS) {
rayLength += _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT;
} else {
rayLength += MIN_HOVER_HEIGHT; rayLength += MIN_HOVER_HEIGHT;
} else {
rayLength += _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT;
} }
btVector3 rayEnd = rayStart - rayLength * _currentUp; btVector3 rayEnd = rayStart - rayLength * _currentUp;
@ -746,69 +726,7 @@ void CharacterController::updateState() {
// disable normal state transitions while collisionless // disable normal state transitions while collisionless
const btScalar MAX_WALKING_SPEED = 2.65f; const btScalar MAX_WALKING_SPEED = 2.65f;
if (collisionGroup == BULLET_COLLISION_GROUP_MY_AVATAR) { if (collisionMask == BULLET_COLLISION_MASK_COLLISIONLESS) {
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 {
// when collisionless: only switch between State::Ground and State::Hover // when collisionless: only switch between State::Ground and State::Hover
// and bypass state debugging // and bypass state debugging
if (rayHasHit) { if (rayHasHit) {
@ -820,6 +738,68 @@ void CharacterController::updateState() {
} else { } else {
_state = State::Hover; _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) { void CharacterController::setCollisionlessAllowed(bool value) {
if (value != _collisionlessAllowed) { if (value != _collisionlessAllowed) {
_collisionlessAllowed = value; _collisionlessAllowed = value;
_pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_GROUP; _pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_MASK;
} }
} }

8
libraries/physics/src/CharacterController.h Normal file → Executable file
View file

@ -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_REMOVE_FROM_SIMULATION = 1U << 1;
const uint32_t PENDING_FLAG_UPDATE_SHAPE = 1U << 2; const uint32_t PENDING_FLAG_UPDATE_SHAPE = 1U << 2;
const uint32_t PENDING_FLAG_JUMP = 1U << 3; 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 uint32_t PENDING_FLAG_RECOMPUTE_FLYING = 1U << 5;
const float DEFAULT_MIN_FLOOR_NORMAL_DOT_UP = cosf(PI / 3.0f); const float DEFAULT_MIN_FLOOR_NORMAL_DOT_UP = cosf(PI / 3.0f);
@ -120,14 +120,16 @@ public:
bool isStuck() const { return _isStuck; } bool isStuck() const { return _isStuck; }
void setCollisionless(bool collisionless); 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); bool getRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation);
void setFlyingAllowed(bool value); void setFlyingAllowed(bool value);
void setCollisionlessAllowed(bool value); void setCollisionlessAllowed(bool value);
void setPendingFlagsUpdateCollisionMask(){ _pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_MASK; }
protected: protected:
#ifdef DEBUG_STATE_CHANGE #ifdef DEBUG_STATE_CHANGE