diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 8c9baa7c43..f8576c3fda 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -192,6 +192,9 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::EnableAvatarCollisions, 0, true, avatar.get(), SLOT(updateMotionBehaviorFromMenu())); + addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::EnableFlying, 0, true, + avatar.get(), SLOT(setFlyingEnabled(bool))); + // Avatar > AvatarBookmarks related menus -- Note: the AvatarBookmarks class adds its own submenus here. auto avatarBookmarks = DependencyManager::get<AvatarBookmarks>(); avatarBookmarks->setupMenus(this, avatarMenu); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index f69860099d..cc8e67c8f7 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -94,6 +94,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 EnableFlying = "Enable Flying"; const QString EnableAvatarCollisions = "Enable Avatar Collisions"; const QString EnableInverseKinematics = "Enable Inverse Kinematics"; const QString EntityScriptServerLog = "Entity Script Server Log"; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 4a0d753695..e5c4f4b972 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -593,12 +593,12 @@ void MyAvatar::simulate(float deltaTime) { auto entityTreeRenderer = qApp->getEntities(); EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr; if (entityTree) { - bool flyingAllowed = true; + bool zoneAllowsFlying = true; bool collisionlessAllowed = true; entityTree->withWriteLock([&] { std::shared_ptr<ZoneEntityItem> zone = entityTreeRenderer->myAvatarZone(); if (zone) { - flyingAllowed = zone->getFlyingAllowed(); + zoneAllowsFlying = zone->getFlyingAllowed(); collisionlessAllowed = zone->getGhostingAllowed(); } auto now = usecTimestampNow(); @@ -629,7 +629,7 @@ void MyAvatar::simulate(float deltaTime) { entityTree->recurseTreeWithOperator(&moveOperator); } }); - _characterController.setFlyingAllowed(flyingAllowed); + _characterController.setFlyingAllowed(zoneAllowsFlying && _enableFlying); _characterController.setCollisionlessAllowed(collisionlessAllowed); } @@ -2500,6 +2500,30 @@ void MyAvatar::updateMotionBehaviorFromMenu() { setCollisionsEnabled(menu->isOptionChecked(MenuOption::EnableAvatarCollisions)); } +void MyAvatar::setFlyingEnabled(bool enabled) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setFlyingEnabled", Q_ARG(bool, enabled)); + return; + } + + _enableFlying = enabled; +} + +bool MyAvatar::isFlying() { + // Avatar is Flying, and is not Falling, or Taking off + return _characterController.getState() == CharacterController::State::Hover; +} + +bool MyAvatar::isInAir() { + // If Avatar is Hover, Falling, or Taking off, they are in Air. + return _characterController.getState() != CharacterController::State::Ground; +} + +bool MyAvatar::getFlyingEnabled() { + // May return true even if client is not allowed to fly in the zone. + return _enableFlying; +} + void MyAvatar::setCollisionsEnabled(bool enabled) { if (QThread::currentThread() != thread()) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 473bcb0de2..e2ea745ed0 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -507,6 +507,11 @@ public: bool hasDriveInput() const; + Q_INVOKABLE bool isFlying(); + Q_INVOKABLE bool isInAir(); + Q_INVOKABLE void setFlyingEnabled(bool enabled); + Q_INVOKABLE bool getFlyingEnabled(); + Q_INVOKABLE void setCollisionsEnabled(bool enabled); Q_INVOKABLE bool getCollisionsEnabled(); Q_INVOKABLE void setCharacterControllerEnabled(bool enabled); // deprecated @@ -569,6 +574,7 @@ public slots: void setEnableDebugDrawIKTargets(bool isEnabled); void setEnableDebugDrawIKConstraints(bool isEnabled); void setEnableDebugDrawIKChains(bool isEnabled); + bool getEnableMeshVisible() const { return _skeletonModel->isVisible(); } void setEnableMeshVisible(bool isEnabled); void setUseAnimPreAndPostRotations(bool isEnabled); @@ -641,6 +647,7 @@ private: std::array<float, MAX_DRIVE_KEYS> _driveKeys; std::bitset<MAX_DRIVE_KEYS> _disabledDriveKeys; + bool _enableFlying { true }; bool _wasPushing { false }; bool _isPushing { false }; bool _isBeingPushed { false }; @@ -790,7 +797,6 @@ private: ThreadSafeValueCache<controller::Pose> _rightArmControllerPoseInSensorFrameCache{ controller::Pose() }; bool _hmdLeanRecenterEnabled = true; - AnimPose _prePhysicsRoomPose; std::mutex _holdActionsMutex; std::vector<AvatarActionHold*> _holdActions;