From 954d690b8a22523f1b4362c0147916c6b9356b20 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 12 Sep 2016 13:12:06 -0700 Subject: [PATCH] expose setting avatar collisionless --- interface/src/Application.cpp | 7 - interface/src/Menu.cpp | 2 +- interface/src/Menu.h | 2 +- interface/src/avatar/MyAvatar.cpp | 26 ++- interface/src/avatar/MyAvatar.h | 6 +- libraries/physics/src/CharacterController.cpp | 180 ++++++++++-------- libraries/physics/src/CharacterController.h | 13 +- 7 files changed, 127 insertions(+), 109 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8620b384ec..03fdd52585 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3667,13 +3667,6 @@ void Application::update(float deltaTime) { if (nearbyEntitiesAreReadyForPhysics()) { _physicsEnabled = true; getMyAvatar()->updateMotionBehaviorFromMenu(); - } else { - auto characterController = getMyAvatar()->getCharacterController(); - if (characterController) { - // if we have a character controller, disable it here so the avatar doesn't get stuck due to - // a non-loading collision hull. - characterController->setEnabled(false); - } } } } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index f58a593395..84d1c2cac6 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -502,7 +502,7 @@ Menu::Menu() { avatar, SLOT(updateMotionBehaviorFromMenu()), UNSPECIFIED_POSITION, "Developer"); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableCharacterController, 0, true, + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableAvatarCollisions, 0, true, avatar, SLOT(updateMotionBehaviorFromMenu()), UNSPECIFIED_POSITION, "Developer"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 9b916467d0..5e213f3974 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -97,7 +97,7 @@ namespace MenuOption { const QString DontRenderEntitiesAsScene = "Don't Render Entities as Scene"; const QString EchoLocalAudio = "Echo Local Audio"; const QString EchoServerAudio = "Echo Server Audio"; - const QString EnableCharacterController = "Enable avatar collisions"; + const QString EnableAvatarCollisions = "Enable Avatar Collisions"; const QString EnableInverseKinematics = "Enable Inverse Kinematics"; const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation"; const QString ExpandMyAvatarTiming = "Expand /myAvatar"; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c37fb0d972..df9e07ee78 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -137,8 +137,6 @@ MyAvatar::MyAvatar(RigPointer rig) : connect(DependencyManager::get().data(), &AddressManager::locationChangeRequired, this, static_cast(&MyAvatar::goToLocation)); - _characterController.setEnabled(true); - _bodySensorMatrix = deriveBodyFromHMDSensor(); using namespace recording; @@ -506,8 +504,8 @@ void MyAvatar::simulate(float deltaTime) { } }); _characterController.setFlyingAllowed(flyingAllowed); - if (!_characterController.isEnabled() && !ghostingAllowed) { - _characterController.setEnabled(true); + if (!ghostingAllowed && _characterController.getCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) { + _characterController.setCollisionGroup(BULLET_COLLISION_GROUP_MY_AVATAR); } } @@ -1270,7 +1268,8 @@ void MyAvatar::updateMotors() { _characterController.clearMotors(); glm::quat motorRotation; if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) { - if (_characterController.getState() == CharacterController::State::Hover) { + if (_characterController.getState() == CharacterController::State::Hover || + _characterController.getCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) { motorRotation = getHead()->getCameraOrientation(); } else { // non-hovering = walking: follow camera twist about vertical but not lift @@ -1316,6 +1315,7 @@ void MyAvatar::prepareForPhysicsSimulation() { qDebug() << "Warning: getParentVelocity failed" << getID(); parentVelocity = glm::vec3(); } + _characterController.handleChangedCollisionGroup(); _characterController.setParentVelocity(parentVelocity); _characterController.setPositionAndOrientation(getPosition(), getOrientation()); @@ -1933,13 +1933,13 @@ void MyAvatar::updateMotionBehaviorFromMenu() { _motionBehaviors &= ~AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED; } - setCharacterControllerEnabled(menu->isOptionChecked(MenuOption::EnableCharacterController)); + setAvatarCollisionsEnabled(menu->isOptionChecked(MenuOption::EnableAvatarCollisions)); } -void MyAvatar::setCharacterControllerEnabled(bool enabled) { +void MyAvatar::setAvatarCollisionsEnabled(bool enabled) { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "setCharacterControllerEnabled", Q_ARG(bool, enabled)); + QMetaObject::invokeMethod(this, "setAvatarCollisionsEnabled", Q_ARG(bool, enabled)); return; } @@ -1951,11 +1951,12 @@ void MyAvatar::setCharacterControllerEnabled(bool enabled) { ghostingAllowed = zone->getGhostingAllowed(); } } - _characterController.setEnabled(ghostingAllowed ? enabled : true); + int16_t group = enabled || !ghostingAllowed ? BULLET_COLLISION_GROUP_MY_AVATAR : BULLET_COLLISION_GROUP_COLLISIONLESS; + _characterController.setCollisionGroup(group); } -bool MyAvatar::getCharacterControllerEnabled() { - return _characterController.isEnabled(); +bool MyAvatar::getAvatarCollisionsEnabled() { + return _characterController.getCollisionGroup() != BULLET_COLLISION_GROUP_COLLISIONLESS; } void MyAvatar::clearDriveKeys() { @@ -2063,9 +2064,6 @@ void MyAvatar::lateUpdatePalms() { Avatar::updatePalms(); } - -static const float FOLLOW_TIME = 0.5f; - MyAvatar::FollowHelper::FollowHelper() { deactivate(); } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 85a2b72939..4b76c0defa 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -83,7 +83,7 @@ class MyAvatar : public Avatar { Q_PROPERTY(float energy READ getEnergy WRITE setEnergy) Q_PROPERTY(bool hmdLeanRecenterEnabled READ getHMDLeanRecenterEnabled WRITE setHMDLeanRecenterEnabled) - Q_PROPERTY(bool characterControllerEnabled READ getCharacterControllerEnabled WRITE setCharacterControllerEnabled) + Q_PROPERTY(bool avatarCollisionsEnabled READ getAvatarCollisionsEnabled WRITE setAvatarCollisionsEnabled) public: explicit MyAvatar(RigPointer rig); @@ -273,8 +273,8 @@ public: bool hasDriveInput() const; - Q_INVOKABLE void setCharacterControllerEnabled(bool enabled); - Q_INVOKABLE bool getCharacterControllerEnabled(); + Q_INVOKABLE void setAvatarCollisionsEnabled(bool enabled); + Q_INVOKABLE bool getAvatarCollisionsEnabled(); virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override; virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override; diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index af983deea9..331be5c9da 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -13,7 +13,6 @@ #include -#include "PhysicsCollisionGroups.h" #include "ObjectMotionState.h" #include "PhysicsLogging.h" @@ -62,10 +61,7 @@ CharacterController::CharacterMotor::CharacterMotor(const glm::vec3& vel, const } CharacterController::CharacterController() { - _halfHeight = 1.0f; - - _enabled = false; - + _halfHeight = 1.0f; _floorDistance = MAX_FALL_HEIGHT; _targetVelocity.setValue(0.0f, 0.0f, 0.0f); @@ -119,7 +115,7 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { // Before adding the RigidBody to the world we must save its oldGravity to the side // because adding an object to the world will overwrite it with the default gravity. btVector3 oldGravity = _rigidBody->getGravity(); - _dynamicsWorld->addRigidBody(_rigidBody, BULLET_COLLISION_GROUP_MY_AVATAR, BULLET_COLLISION_MASK_MY_AVATAR); + _dynamicsWorld->addRigidBody(_rigidBody, _collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR); _dynamicsWorld->addAction(this); // restore gravity settings _rigidBody->setGravity(oldGravity); @@ -199,7 +195,7 @@ void CharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) { // This mirrors the computation done in MyAvatar::FollowHelper::postPhysicsUpdate(). if (_following) { - // HACK these copied form elsewhere + // OUTOFBODY_HACK -- these consts were copied from elsewhere, and then tuned const float NORMAL_WALKING_SPEED = 0.5f; const float FOLLOW_TIME = 0.8f; const float FOLLOW_ROTATION_THRESHOLD = cosf(PI / 6.0f); @@ -282,15 +278,17 @@ void CharacterController::setState(State desiredState) { #ifdef DEBUG_STATE_CHANGE qCDebug(physics) << "CharacterController::setState" << stateToStr(desiredState) << "from" << stateToStr(_state) << "," << reason; #endif - if (desiredState == State::Hover && _state != State::Hover) { - // hover enter - if (_rigidBody) { + 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 (_rigidBody) { - _rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp); + } 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(DEFAULT_CHARACTER_GRAVITY * _currentUp); + } } } _state = desiredState; @@ -320,39 +318,45 @@ void CharacterController::setLocalBoundingBox(const glm::vec3& corner, const glm _pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION; } _pendingFlags |= PENDING_FLAG_UPDATE_SHAPE; - // only need to ADD back when we happen to be enabled - if (_enabled) { - _pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION; - } + _pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION; } // it's ok to change offset immediately -- there are no thread safety issues here _shapeLocalOffset = corner + 0.5f * _boxScale; } -void CharacterController::setEnabled(bool enabled) { - if (enabled != _enabled) { - if (enabled) { - // Don't bother clearing REMOVE bit since it might be paired with an UPDATE_SHAPE bit. - // Setting the ADD bit here works for all cases so we don't even bother checking other bits. - _pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION; - } else { - if (_dynamicsWorld) { - _pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION; - } - _pendingFlags &= ~ PENDING_FLAG_ADD_TO_SIMULATION; +void CharacterController::setCollisionGroup(int16_t group) { + if (_collisionGroup != group) { + _collisionGroup = group; + _pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_GROUP; + } +} + +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); + _dynamicsWorld->addRigidBody(_rigidBody, _collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR); + } + _pendingFlags &= ~PENDING_FLAG_UPDATE_COLLISION_GROUP; + + if (_state != State::Hover && _rigidBody) { + if (_collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) { + _rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f)); + } else { + _rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp); + } } - SET_STATE(State::Hover, "setEnabled"); - _enabled = enabled; } } void CharacterController::updateUpAxis(const glm::quat& rotation) { - btVector3 oldUp = _currentUp; _currentUp = quatRotate(glmToBullet(rotation), LOCAL_UP_AXIS); - if (_state != State::Hover) { - const btScalar MIN_UP_ERROR = 0.01f; - if (oldUp.distance(_currentUp) > MIN_UP_ERROR) { + if (_state != State::Hover && _rigidBody) { + if (_collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) { + _rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f)); + } else { _rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp); } } @@ -370,7 +374,7 @@ void CharacterController::setPositionAndOrientation( } void CharacterController::getPositionAndOrientation(glm::vec3& position, glm::quat& rotation) const { - if (_enabled && _rigidBody) { + if (_rigidBody) { const btTransform& avatarTransform = _rigidBody->getWorldTransform(); rotation = bulletToGLM(avatarTransform.getRotation()); position = bulletToGLM(avatarTransform.getOrigin()) - rotation * _shapeLocalOffset; @@ -438,7 +442,8 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel btScalar angle = motor.rotation.getAngle(); btVector3 velocity = worldVelocity.rotate(axis, -angle); - if (_state == State::Hover || motor.hTimescale == motor.vTimescale) { + if (_collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS || + _state == State::Hover || motor.hTimescale == motor.vTimescale) { // modify velocity btScalar tau = dt / motor.hTimescale; if (tau > 1.0f) { @@ -534,7 +539,7 @@ void CharacterController::computeNewVelocity(btScalar dt, glm::vec3& velocity) { } void CharacterController::preSimulation() { - if (_enabled && _dynamicsWorld) { + if (_dynamicsWorld) { quint64 now = usecTimestampNow(); // slam body to where it is supposed to be @@ -584,51 +589,61 @@ void CharacterController::preSimulation() { btVector3 actualHorizVelocity = velocity - velocity.dot(_currentUp) * _currentUp; bool flyingFast = _state == State::Hover && actualHorizVelocity.length() > (MAX_WALKING_SPEED * 0.75f); - 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"); + // OUTOFBODY_HACK -- disable normal state transitions while collisionless + 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"); + velocity += _jumpSpeed * _currentUp; + _rigidBody->setLinearVelocity(velocity); + } + break; + case State::InAir: { + if ((velocity.dot(_currentUp) <= (JUMP_SPEED / 2.0f)) && ((_floorDistance < FLY_TO_GROUND_THRESHOLD) || _hasSupport)) { + SET_STATE(State::Ground, "hit ground"); + } else { + 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"); + } + } + break; } - 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"); - velocity += _jumpSpeed * _currentUp; - _rigidBody->setLinearVelocity(velocity); + case State::Hover: + 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; } - break; - case State::InAir: { - if ((velocity.dot(_currentUp) <= (JUMP_SPEED / 2.0f)) && ((_floorDistance < FLY_TO_GROUND_THRESHOLD) || _hasSupport)) { - SET_STATE(State::Ground, "hit ground"); + } else { + // OUTOFBODY_HACK -- in collisionless state switch between Ground and Hover states + if (rayHasHit) { + SET_STATE(State::Ground, "collisionless above ground"); } else { - 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"); - } + SET_STATE(State::Hover, "collisionless in air"); } - break; - } - case State::Hover: - 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; } } @@ -664,7 +679,10 @@ void CharacterController::setFlyingAllowed(bool value) { _flyingAllowed = value; if (!_flyingAllowed && _state == State::Hover) { - SET_STATE(State::InAir, "flying not allowed"); + // OUTOFBODY_HACK -- disable normal state transitions while collisionless + if (_collisionGroup == BULLET_COLLISION_GROUP_MY_AVATAR) { + SET_STATE(State::InAir, "flying not allowed"); + } } } } diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 26ad481c46..2d8cf489de 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -19,12 +19,15 @@ #include #include +#include + #include "BulletUtil.h" 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 float DEFAULT_CHARACTER_GRAVITY = -5.0f; @@ -107,9 +110,15 @@ public: void setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale); + /* bool isEnabled() const { return _enabled; } // thread-safe void setEnabled(bool enabled); - bool isEnabledAndReady() const { return _enabled && _dynamicsWorld; } + */ + bool isEnabledAndReady() const { return _dynamicsWorld; } + + void setCollisionGroup(int16_t group); + int16_t getCollisionGroup() const { return _collisionGroup; } + void handleChangedCollisionGroup(); bool getRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation); @@ -170,7 +179,6 @@ protected: btVector3 _linearAcceleration; bool _following { false }; - std::atomic_bool _enabled; State _state; bool _isPushingUp; @@ -180,6 +188,7 @@ protected: uint32_t _previousFlags { 0 }; bool _flyingAllowed { true }; + int16_t _collisionGroup { BULLET_COLLISION_GROUP_MY_AVATAR }; }; #endif // hifi_CharacterControllerInterface_h