diff --git a/interface/resources/avatar/animations/sitting_idle.fbx b/interface/resources/avatar/animations/sitting_idle.fbx new file mode 100644 index 0000000000..ee03d942cd Binary files /dev/null and b/interface/resources/avatar/animations/sitting_idle.fbx differ diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index acb199bb97..c2b9a97509 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -6144,3 +6144,81 @@ void MyAvatar::sendPacket(const QUuid& entityID) const { }); } } + +QStringList MyAvatar::getSitRolesToOverride() { + auto allRoles = getAnimationRoles(); + auto sitRolesToOverride = QStringList(); + for (auto role : allRoles) { + if (!(role.startsWith("right") || role.startsWith("left"))) { + sitRolesToOverride.append(role); + } + } + return sitRolesToOverride; +} + +void MyAvatar::setSitDriveKeysStatus(bool enabled) { + const std::vector DISABLED_DRIVE_KEYS_DURING_SIT = { + DriveKeys::TRANSLATE_X, + DriveKeys::TRANSLATE_Y, + DriveKeys::TRANSLATE_Z, + DriveKeys::STEP_TRANSLATE_X, + DriveKeys::STEP_TRANSLATE_Y, + DriveKeys::STEP_TRANSLATE_Z + }; + for (auto key : DISABLED_DRIVE_KEYS_DURING_SIT) { + if (enabled) { + enableDriveKey(key); + } else { + disableDriveKey(key); + } + } +} + +void MyAvatar::beginSit(const glm::vec3& position, const glm::quat& rotation) { + const QString ANIMATION_URL = PathUtils::defaultScriptsLocation().toString() + "/resources/animations/sittingIdle.fbx"; + const int ANIMATION_FPS = 30; + const int ANIMATION_FIRST_FRAME = 1; + const int ANIMATION_LAST_FRAME = 350; + _characterController.setSeated(true); + setCollisionsEnabled(false); + setHMDLeanRecenterEnabled(false); + /* + auto sitRolesToOverride = getSitRolesToOverride(); + for (auto& role: sitRolesToOverride) { // restore roles to prevent overlap + if (_isSeated) { + restoreRoleAnimation(role); + } + overrideRoleAnimation(role, ANIMATION_URL, ANIMATION_FPS, true, + ANIMATION_FIRST_FRAME, ANIMATION_LAST_FRAME); + } + */ + + // Disable movement + setSitDriveKeysStatus(false); + centerBody(); + int hipIndex = getJointIndex("Hips"); + clearPinOnJoint(hipIndex); + goToLocation(position, true, rotation, false, false); + pinJoint(hipIndex, position, rotation); + _isSeated = true; +} + +void MyAvatar::endSit(const glm::vec3& position, const glm::quat& rotation) { + clearPinOnJoint(getJointIndex("Hips")); + /* + auto sitRolesToOverride = getSitRolesToOverride(); + for (auto& role : sitRolesToOverride) { + restoreRoleAnimation(role); + } + */ + _characterController.setSeated(false); + setCollisionsEnabled(true); + setHMDLeanRecenterEnabled(true); + centerBody(); + float STANDUP_BUMP = 0.225f; + glm::vec3 currentPosition = getWorldPosition(); + currentPosition.y = currentPosition.y + STANDUP_BUMP; + setWorldPosition(currentPosition); + // Enable movement again + setSitDriveKeysStatus(true); +} \ No newline at end of file diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index d092122863..24fda81969 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1835,6 +1835,22 @@ public: */ Q_INVOKABLE QVariantList getCollidingFlowJoints(); + /**jsdoc + * Starts a sitting action for the avatar + * @function MyAvatar.beginSit + * @param {Vec3} position - The point in space where the avatar will sit. + * @param {Quat} rotation - Initial absolute orientation of the avatar once is seated. + */ + Q_INVOKABLE void beginSit(const glm::vec3& position, const glm::quat& rotation); + + /**jsdoc + * Ends a sitting action for the avatar + * @function MyAvatar.endSit + * @param {Vec3} position - The position of the avatar when standing up. + * @param {Quat} rotation - The absolute rotation of the avatar once the sitting action ends. + */ + Q_INVOKABLE void endSit(const glm::vec3& position, const glm::quat& rotation); + int getOverrideJointCount() const; bool getFlowActive() const; bool getNetworkGraphActive() const; @@ -2490,6 +2506,8 @@ private: virtual void updatePalms() override {} void lateUpdatePalms(); + QStringList getSitRolesToOverride(); + void setSitDriveKeysStatus(bool enabled); void clampTargetScaleToDomainLimits(); void clampScaleChangeToDomainLimits(float desiredScale); @@ -2506,6 +2524,7 @@ private: bool _isBeingPushed { false }; bool _isBraking { false }; bool _isAway { false }; + bool _isSeated { false }; float _boomLength { ZOOM_DEFAULT }; float _yawSpeed; // degrees/sec diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index df46b428e7..141166a150 100755 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -32,6 +32,8 @@ Rig::CharacterControllerState convertCharacterControllerState(CharacterControlle return Rig::CharacterControllerState::InAir; case CharacterController::State::Hover: return Rig::CharacterControllerState::Hover; + case CharacterController::State::Seated: + return Rig::CharacterControllerState::Seated; }; } diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 0f0c67b846..4722201c05 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1092,6 +1092,11 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _desiredStateAge = 0.0f; } _desiredState = RigRole::Takeoff; + } else if (ccState == CharacterControllerState::Seated) { + if (_desiredState != RigRole::Seated) { + _desiredStateAge = 0.0f; + } + _desiredState = RigRole::Seated; } else { float moveThresh; if (_state != RigRole::Move) { @@ -1216,6 +1221,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isInAirStand", false); _animVars.set("isInAirRun", false); _animVars.set("isNotInAir", true); + _animVars.set("isSeated", false); } else if (_state == RigRole::Turn) { if (turningSpeed > 0.0f) { @@ -1244,6 +1250,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isInAirStand", false); _animVars.set("isInAirRun", false); _animVars.set("isNotInAir", true); + _animVars.set("isSeated", false); } else if (_state == RigRole::Idle) { // default anim vars to notMoving and notTurning @@ -1265,6 +1272,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isInAirStand", false); _animVars.set("isInAirRun", false); _animVars.set("isNotInAir", true); + _animVars.set("isSeated", false); } else if (_state == RigRole::Hover) { // flying. @@ -1286,6 +1294,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isInAirStand", false); _animVars.set("isInAirRun", false); _animVars.set("isNotInAir", true); + _animVars.set("isSeated", false); } else if (_state == RigRole::Takeoff) { // jumping in-air @@ -1315,6 +1324,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isInAirStand", false); _animVars.set("isInAirRun", false); _animVars.set("isNotInAir", false); + _animVars.set("isSeated", false); } else if (_state == RigRole::InAir) { // jumping in-air @@ -1333,6 +1343,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isTakeoffStand", false); _animVars.set("isTakeoffRun", false); _animVars.set("isNotTakeoff", true); + _animVars.set("isSeated", false); bool inAirRun = forwardSpeed > 0.1f; if (inAirRun) { @@ -1354,6 +1365,26 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos float alpha = glm::clamp((-workingVelocity.y * sensorToWorldScale) / jumpSpeed, -1.0f, 1.0f) + 1.0f; _animVars.set("inAirAlpha", alpha); + } else if (_state == RigRole::Seated) { + _animVars.set("isMovingForward", false); + _animVars.set("isMovingBackward", false); + _animVars.set("isMovingRight", false); + _animVars.set("isMovingLeft", false); + _animVars.set("isMovingRightHmd", false); + _animVars.set("isMovingLeftHmd", false); + _animVars.set("isNotMoving", false); + _animVars.set("isTurningRight", false); + _animVars.set("isTurningLeft", false); + _animVars.set("isNotTurning", true); + _animVars.set("isFlying", false); + _animVars.set("isNotFlying", true); + _animVars.set("isTakeoffStand", false); + _animVars.set("isTakeoffRun", false); + _animVars.set("isNotTakeoff", true); + _animVars.set("isInAirStand", false); + _animVars.set("isInAirRun", false); + _animVars.set("isNotInAir", true); + _animVars.set("isSeated", true); } t += deltaTime; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 9baf4644f2..8056d1eff1 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -107,7 +107,8 @@ public: Ground = 0, Takeoff, InAir, - Hover + Hover, + Seated }; Rig(); @@ -336,7 +337,8 @@ protected: Move, Hover, Takeoff, - InAir + InAir, + Seated }; RigRole _state { RigRole::Idle }; RigRole _desiredState { RigRole::Idle }; diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index f0f1b9c837..0eb5e29e94 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -384,6 +384,8 @@ static const char* stateToStr(CharacterController::State state) { return "InAir"; case CharacterController::State::Hover: return "Hover"; + case CharacterController::State::Seated: + return "Seated"; default: return "Unknown"; } @@ -739,9 +741,11 @@ void CharacterController::updateState() { // disable normal state transitions while collisionless const btScalar MAX_WALKING_SPEED = 2.65f; if (collisionMask == BULLET_COLLISION_MASK_COLLISIONLESS) { - // when collisionless: only switch between State::Ground and State::Hover + // when collisionless: only switch between State::Ground, State::Hover and State::Seated // and bypass state debugging - if (rayHasHit) { + if (_isSeated) { + _state = State::Seated; + } else if (rayHasHit) { if (velocity.length() > (MAX_WALKING_SPEED)) { _state = State::Hover; } else { diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index c825f3a10b..3dafcda219 100755 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -111,7 +111,8 @@ public: Ground = 0, Takeoff, InAir, - Hover + Hover, + Seated }; State getState() const { return _state; } @@ -135,6 +136,7 @@ public: void setCollisionlessAllowed(bool value); void setPendingFlagsUpdateCollisionMask(){ _pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_MASK; } + void setSeated(bool isSeated) { _isSeated = isSeated; } protected: #ifdef DEBUG_STATE_CHANGE @@ -208,6 +210,7 @@ protected: State _state; bool _isPushingUp; bool _isStuck { false }; + bool _isSeated { false }; btDynamicsWorld* _dynamicsWorld { nullptr }; btRigidBody* _rigidBody { nullptr };