diff --git a/interface/resources/avatar/avatar-animation.json b/interface/resources/avatar/avatar-animation.json index f455389009..0131829471 100644 --- a/interface/resources/avatar/avatar-animation.json +++ b/interface/resources/avatar/avatar-animation.json @@ -4048,6 +4048,19 @@ }, "id": "LANDRUN", "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 105, + "loopFlag": false, + "startFrame": 100, + "timeScale": 1, + "url": "qrc:///avatar/animations/idle.fbx" + }, + "id": "seatedToIdle", + "type": "clip" } ], "data": { @@ -4065,60 +4078,8 @@ "interpType": "evaluateBoth", "transitions": [ { - "state": "idle", - "var": "isNotMoving" - }, - { - "state": "WALKFWD", - "var": "isMovingForward" - }, - { - "state": "WALKBWD", - "var": "isMovingBackward" - }, - { - "state": "STRAFERIGHT", - "var": "isMovingRight" - }, - { - "state": "STRAFELEFT", - "var": "isMovingLeft" - }, - { - "state": "turnRight", - "var": "isTurningRight" - }, - { - "state": "turnLeft", - "var": "isTurningLeft" - }, - { - "state": "fly", - "var": "isFlying" - }, - { - "state": "takeoffStand", - "var": "isTakeoffStand" - }, - { - "state": "TAKEOFFRUN", - "var": "isTakeoffRun" - }, - { - "state": "inAirStand", - "var": "isInAirStand" - }, - { - "state": "INAIRRUN", - "var": "isInAirRun" - }, - { - "state": "strafeRightHmd", - "var": "isMovingRightHmd" - }, - { - "state": "strafeLeftHmd", - "var": "isMovingLeftHmd" + "state": "seatedToIdle", + "var": "isNotSeated" } ] }, @@ -5008,6 +4969,18 @@ "var": "landRunOnDone" } ] + }, + { + "id": "seatedToIdle", + "interpDuration": 10, + "interpTarget": 1, + "interpType": "snapshotPrev", + "transitions": [ + { + "state": "idle", + "var": "seatedToIdleOnDone" + } + ] } ] }, diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 28f6644d7f..0ff2e055b7 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -6245,31 +6245,43 @@ void MyAvatar::setSitDriveKeysStatus(bool enabled) { } void MyAvatar::beginSit(const glm::vec3& position, const glm::quat& rotation) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "beginSit", Q_ARG(glm::vec3, position), Q_ARG(glm::quat, rotation)); + return; + } + _characterController.setSeated(true); - setCollisionsEnabled(false); + setCollisionsEnabled(false); setHMDLeanRecenterEnabled(false); // Disable movement setSitDriveKeysStatus(false); centerBody(); int hipIndex = getJointIndex("Hips"); clearPinOnJoint(hipIndex); - goToLocation(position, true, rotation, false, false); pinJoint(hipIndex, position, rotation); } void MyAvatar::endSit(const glm::vec3& position, const glm::quat& rotation) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "endSit", Q_ARG(glm::vec3, position), Q_ARG(glm::quat, rotation)); + return; + } + if (_characterController.getSeated()) { clearPinOnJoint(getJointIndex("Hips")); _characterController.setSeated(false); setCollisionsEnabled(true); setHMDLeanRecenterEnabled(true); centerBody(); - goToLocation(position, true, rotation, false, false); + slamPosition(position); + setWorldOrientation(rotation); + + // the jump key is used to exit the chair. We add a delay here to prevent + // the avatar from jumping right as they exit the chair. float TIME_BEFORE_DRIVE_ENABLED_MS = 150.0f; QTimer::singleShot(TIME_BEFORE_DRIVE_ENABLED_MS, [this]() { // Enable movement again setSitDriveKeysStatus(true); }); } - -} \ No newline at end of file +} diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index d0204219ac..211c54def8 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1144,6 +1144,11 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _desiredStateAge = STATE_CHANGE_HYSTERESIS_TIMER; } + // Skip hysterisis timer for sit transitions. + if (_desiredState == RigRole::Seated || _state == RigRole::Seated) { + _desiredStateAge = STATE_CHANGE_HYSTERESIS_TIMER; + } + if ((_desiredStateAge >= STATE_CHANGE_HYSTERESIS_TIMER) && _desiredState != _state) { _state = _desiredState; _desiredStateAge = 0.0f; @@ -1223,6 +1228,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isInAirRun", false); _animVars.set("isNotInAir", true); _animVars.set("isSeated", false); + _animVars.set("isNotSeated", true); } else if (_state == RigRole::Turn) { if (turningSpeed > 0.0f) { @@ -1252,6 +1258,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isInAirRun", false); _animVars.set("isNotInAir", true); _animVars.set("isSeated", false); + _animVars.set("isNotSeated", true); } else if (_state == RigRole::Idle) { // default anim vars to notMoving and notTurning @@ -1274,6 +1281,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isInAirRun", false); _animVars.set("isNotInAir", true); _animVars.set("isSeated", false); + _animVars.set("isNotSeated", true); } else if (_state == RigRole::Hover) { // flying. @@ -1296,6 +1304,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isInAirRun", false); _animVars.set("isNotInAir", true); _animVars.set("isSeated", false); + _animVars.set("isNotSeated", true); } else if (_state == RigRole::Takeoff) { // jumping in-air @@ -1326,6 +1335,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isInAirRun", false); _animVars.set("isNotInAir", false); _animVars.set("isSeated", false); + _animVars.set("isNotSeated", true); } else if (_state == RigRole::InAir) { // jumping in-air @@ -1345,6 +1355,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isTakeoffRun", false); _animVars.set("isNotTakeoff", true); _animVars.set("isSeated", false); + _animVars.set("isNotSeated", true); bool inAirRun = forwardSpeed > 0.1f; if (inAirRun) { @@ -1386,6 +1397,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isInAirRun", false); _animVars.set("isNotInAir", true); _animVars.set("isSeated", true); + _animVars.set("isNotSeated", false); } t += deltaTime; @@ -1821,8 +1833,10 @@ void Rig::updateFeet(bool leftFootEnabled, bool rightFootEnabled, bool headEnabl int hipsIndex = indexOfJoint("Hips"); const float KNEE_POLE_VECTOR_BLEND_FACTOR = 0.85f; - if (headEnabled) { - // always do IK if head is enabled + bool isSeated = _state == RigRole::Seated; + + if (headEnabled && !isSeated) { + // enable leg IK if head is enabled and we arent sitting down. _animVars.set("leftFootIKEnabled", true); _animVars.set("rightFootIKEnabled", true); } else { diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 9d88dd47ea..7f363dd36f 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -131,7 +131,7 @@ AvatarTransit::Status AvatarTransit::update(float deltaTime, const glm::vec3& av } else { _lastPosition = avatarPosition; _status = Status::ABORT_TRANSIT; - } + } } _lastPosition = avatarPosition; _status = updatePosition(deltaTime); @@ -143,11 +143,18 @@ AvatarTransit::Status AvatarTransit::update(float deltaTime, const glm::vec3& av return _status; } +void AvatarTransit::slamPosition(const glm::vec3& avatarPosition) { + // used to instantly teleport between two points without triggering a change in status. + _lastPosition = avatarPosition; + _endPosition = avatarPosition; +} + void AvatarTransit::reset() { _lastPosition = _endPosition; _currentPosition = _endPosition; _isActive = false; } + void AvatarTransit::start(float deltaTime, const glm::vec3& startPosition, const glm::vec3& endPosition, const AvatarTransit::TransitConfig& config) { _startPosition = startPosition; _endPosition = endPosition; @@ -192,8 +199,8 @@ AvatarTransit::Status AvatarTransit::updatePosition(float deltaTime) { status = Status::PRE_TRANSIT; if (_currentTime == 0) { status = Status::STARTED; - } - } else if (nextTime < _totalTime - _postTransitTime){ + } + } else if (nextTime < _totalTime - _postTransitTime) { status = Status::TRANSITING; if (_currentTime <= _preTransitTime) { status = Status::START_TRANSIT; @@ -519,10 +526,10 @@ void Avatar::relayJointDataToChildren() { * * "avatar" or ""The rate at which the avatar is updated even if not in view. * "avatarInView"The rate at which the avatar is updated if in view. - * "skeletonModel"The rate at which the skeleton model is being updated, even if there are no + * "skeletonModel"The rate at which the skeleton model is being updated, even if there are no * joint data available. * "jointData"The rate at which joint data are being updated. - * ""When no rate name is specified, the "avatar" update rate is + * ""When no rate name is specified, the "avatar" update rate is * provided. * * @@ -557,6 +564,7 @@ void Avatar::slamPosition(const glm::vec3& newPosition) { _positionDeltaAccumulator = glm::vec3(0.0f); setWorldVelocity(glm::vec3(0.0f)); _lastVelocity = glm::vec3(0.0f); + _transit.slamPosition(newPosition); } void Avatar::updateAttitude(const glm::quat& orientation) { @@ -1533,7 +1541,7 @@ void Avatar::setModelURLFinished(bool success) { QMetaObject::invokeMethod(_skeletonModel.get(), "setURL", Qt::QueuedConnection, Q_ARG(QUrl, AvatarData::defaultFullAvatarModelUrl())); } else { - qCWarning(avatars_renderer) << "Avatar model failed to load... attempts:" + qCWarning(avatars_renderer) << "Avatar model failed to load... attempts:" << _skeletonModel->getResourceDownloadAttempts() << "out of:" << MAX_SKELETON_DOWNLOAD_ATTEMPTS; } } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index ee0fb6f433..7bb15ecbf7 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -93,6 +93,7 @@ public: AvatarTransit() {}; Status update(float deltaTime, const glm::vec3& avatarPosition, const TransitConfig& config); + void slamPosition(const glm::vec3& avatarPosition); Status getStatus() { return _status; } bool isActive() { return _isActive; } glm::vec3 getCurrentPosition() { return _currentPosition; }