diff --git a/interface/resources/avatar/animations/settle_sitturnleft_to_sitidle.fbx b/interface/resources/avatar/animations/settle_sitturnleft_to_sitidle.fbx new file mode 100644 index 0000000000..8e6c3fc007 Binary files /dev/null and b/interface/resources/avatar/animations/settle_sitturnleft_to_sitidle.fbx differ diff --git a/interface/resources/avatar/animations/settle_sitturnright_to_sitidle.fbx b/interface/resources/avatar/animations/settle_sitturnright_to_sitidle.fbx new file mode 100644 index 0000000000..e997a913da Binary files /dev/null and b/interface/resources/avatar/animations/settle_sitturnright_to_sitidle.fbx differ diff --git a/interface/resources/avatar/animations/sitting_turn_left.fbx b/interface/resources/avatar/animations/sitting_turn_left.fbx new file mode 100644 index 0000000000..e0863327f4 Binary files /dev/null and b/interface/resources/avatar/animations/sitting_turn_left.fbx differ diff --git a/interface/resources/avatar/animations/sitting_turn_right.fbx b/interface/resources/avatar/animations/sitting_turn_right.fbx new file mode 100644 index 0000000000..54b1d9608e Binary files /dev/null and b/interface/resources/avatar/animations/sitting_turn_right.fbx differ diff --git a/interface/resources/avatar/avatar-animation.json b/interface/resources/avatar/avatar-animation.json index 59b00684d3..3ee0f6f9c4 100644 --- a/interface/resources/avatar/avatar-animation.json +++ b/interface/resources/avatar/avatar-animation.json @@ -684,139 +684,295 @@ "children": [ { "children": [ + { + "children": [ + ], + "data": { + "endFrame": 800, + "loopFlag": true, + "startFrame": 0, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle.fbx" + }, + "id": "seatedIdle01", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 800, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle02.fbx" + }, + "id": "seatedIdle02", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 800, + "loopFlag": true, + "startFrame": 0, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle03.fbx" + }, + "id": "seatedIdle03", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 800, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle04.fbx" + }, + "id": "seatedIdle04", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 332, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle05.fbx" + }, + "id": "seatedIdle05", + "type": "clip" + } ], "data": { - "endFrame": 800, - "loopFlag": true, - "startFrame": 0, + "currentState": "seatedIdle01", + "endFrame": 30, + "randomSwitchTimeMax": 40, + "randomSwitchTimeMin": 10, + "startFrame": 10, + "states": [ + { + "easingType": "easeInOutQuad", + "id": "seatedIdle01", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 1, + "resume": true, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedIdle02", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 1, + "resume": true, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedIdle03", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 1, + "resume": true, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedIdle04", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 1, + "resume": true, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedIdle05", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 1, + "resume": true, + "transitions": [ + ] + } + ], "timeScale": 1, - "url": "qrc:///avatar/animations/sitting_idle.fbx" + "triggerRandomSwitch": "seatedIdleSwitch", + "triggerTimeMax": 10 }, - "id": "seatedIdle01", - "type": "clip" + "id": "masterSeatedIdleRand", + "type": "randomSwitchStateMachine" }, { "children": [ ], "data": { - "endFrame": 800, + "endFrame": 200, "loopFlag": true, "startFrame": 1, "timeScale": 1, - "url": "qrc:///avatar/animations/sitting_idle02.fbx" + "url": "qrc:///avatar/animations/sitting_turn_left.fbx" }, - "id": "seatedIdle02", + "id": "seatedTurnLeft", "type": "clip" }, { "children": [ ], "data": { - "endFrame": 800, - "loopFlag": true, - "startFrame": 0, - "timeScale": 1, - "url": "qrc:///avatar/animations/sitting_idle03.fbx" - }, - "id": "seatedIdle03", - "type": "clip" - }, - { - "children": [ - ], - "data": { - "endFrame": 800, + "endFrame": 200, "loopFlag": true, "startFrame": 1, "timeScale": 1, - "url": "qrc:///avatar/animations/sitting_idle04.fbx" + "url": "qrc:///avatar/animations/sitting_turn_right.fbx" }, - "id": "seatedIdle04", + "id": "seatedTurnRight", "type": "clip" }, { "children": [ ], "data": { - "endFrame": 332, - "loopFlag": true, + "endFrame": 45, + "loopFlag": false, "startFrame": 1, "timeScale": 1, - "url": "qrc:///avatar/animations/sitting_idle05.fbx" + "url": "qrc:///avatar/animations/settle_sitturnright_to_sitidle.fbx" }, - "id": "seatedIdle05", + "id": "seatedTurnRight_to_Idle", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 45, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/settle_sitturnleft_to_sitidle.fbx" + }, + "id": "seatedTurnLeft_to_Idle", "type": "clip" } ], "data": { - "currentState": "seatedIdle01", - "endFrame": 30, - "randomSwitchTimeMax": 40, - "randomSwitchTimeMin": 10, - "startFrame": 10, + "currentState": "masterSeatedIdleRand", "states": [ { "easingType": "easeInOutQuad", - "id": "seatedIdle01", - "interpDuration": 30, - "interpTarget": 30, - "interpType": "evaluateBoth", - "priority": 1, - "resume": true, + "id": "masterSeatedIdleRand", + "interpDuration": 15, + "interpTarget": 15, + "interpType": "snapshotPrev", "transitions": [ + { + "state": "seatedTurnLeft", + "var": "isSeatedTurningLeft" + }, + { + "state": "seatedTurnRight", + "var": "isSeatedTurningRight" + } ] }, { "easingType": "easeInOutQuad", - "id": "seatedIdle02", - "interpDuration": 30, - "interpTarget": 30, + "id": "seatedTurnLeft", + "interpDuration": 22, + "interpTarget": 22, "interpType": "evaluateBoth", - "priority": 1, - "resume": true, "transitions": [ + { + "state": "seatedTurnLeft_to_Idle", + "var": "isSeatedNotTurning" + }, + { + "state": "seatedTurnRight", + "var": "isSeatedTurningRight" + } ] }, { "easingType": "easeInOutQuad", - "id": "seatedIdle03", - "interpDuration": 30, - "interpTarget": 30, + "id": "seatedTurnRight", + "interpDuration": 22, + "interpTarget": 22, "interpType": "evaluateBoth", - "priority": 1, - "resume": true, "transitions": [ + { + "state": "seatedTurnLeft", + "var": "isSeatedTurningLeft" + }, + { + "state": "seatedTurnRight_to_Idle", + "var": "isSeatedNotTurning" + } ] }, { "easingType": "easeInOutQuad", - "id": "seatedIdle04", - "interpDuration": 30, - "interpTarget": 30, + "id": "seatedTurnRight_to_Idle", + "interpDuration": 22, + "interpTarget": 22, "interpType": "evaluateBoth", - "priority": 1, - "resume": true, "transitions": [ + { + "state": "seatedTurnLeft", + "var": "isSeatedTurningLeft" + }, + { + "state": "seatedTurnRight", + "var": "isSeatedTurningRight" + }, + { + "state": "masterSeatedIdleRand", + "var": "seatedTurnRight_to_IdleOnDone" + } ] }, { "easingType": "easeInOutQuad", - "id": "seatedIdle05", - "interpDuration": 30, - "interpTarget": 30, + "id": "seatedTurnLeft_to_Idle", + "interpDuration": 22, + "interpTarget": 22, "interpType": "evaluateBoth", - "priority": 1, - "resume": true, "transitions": [ + { + "state": "seatedTurnRight", + "var": "isSeatedTurningRight" + }, + { + "state": "seatedTurnLeft", + "var": "isSeatedTurningLeft" + }, + { + "state": "masterSeatedIdleRand", + "var": "seatedTurnLeft_to_IdleOnDone" + } ] } - ], - "timeScale": 1, - "triggerRandomSwitch": "seatedIdleSwitch", - "triggerTimeMax": 10 + ] }, "id": "masterSeatedIdle", - "type": "randomSwitchStateMachine" + "type": "stateMachine" }, { "children": [ @@ -1082,6 +1238,14 @@ { "randomSwitchState": "masterSeatedIdle", "var": "seatedFidgetShakeLegsOnDone" + }, + { + "randomSwitchState": "masterSeatedIdle", + "var": "isSeatedTurningRight" + }, + { + "randomSwitchState": "masterSeatedIdle", + "var": "isSeatedTurningLeft" } ] } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index d0eb59c9a1..4e7b8a07f1 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3508,8 +3508,46 @@ void MyAvatar::updateOrientation(float deltaTime) { } setWorldOrientation(glm::slerp(getWorldOrientation(), faceRotation, blend)); } else if (isRotatingWhileSeated) { - float rotatingWhileSeatedYaw = -getDriveKey(TRANSLATE_X) * _yawSpeed * deltaTime; - setWorldOrientation(getWorldOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, rotatingWhileSeatedYaw, 0.0f)))); + float direction = -getDriveKey(TRANSLATE_X); + float seatedTargetSpeed = direction * _yawSpeed * deltaTime; //deg/renderframe + + const float SEATED_ROTATION_ACCEL_SCALE = 3.5; + + float blend = deltaTime * SEATED_ROTATION_ACCEL_SCALE; + if (blend > 1.0f) { + blend = 1.0f; + } + + //init, accelerate or clamp rotation at target speed + if (fabsf(_seatedBodyYawDelta) > 0.0f) { + if (fabsf(_seatedBodyYawDelta) >= fabsf(seatedTargetSpeed)) { + _seatedBodyYawDelta = seatedTargetSpeed; + } else { + _seatedBodyYawDelta += blend * direction; + } + } else { + _seatedBodyYawDelta = blend * direction; + } + + setWorldOrientation(getWorldOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, _seatedBodyYawDelta, 0.0f)))); + + } else if (_seatedBodyYawDelta != 0.0f) { + //decelerate from seated rotation + const float ROTATION_DECAY_TIMESCALE = 0.25f; + float attenuation = 1.0f - deltaTime / ROTATION_DECAY_TIMESCALE; + if (attenuation < 0.0f) { + attenuation = 0.0f; + } + _seatedBodyYawDelta *= attenuation; + + float MINIMUM_ROTATION_RATE = 2.0f; + if (fabsf(_seatedBodyYawDelta) < MINIMUM_ROTATION_RATE * deltaTime) { + _seatedBodyYawDelta = 0.0f; + } + + setWorldOrientation(getWorldOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, _seatedBodyYawDelta, 0.0f)))); + } else { + _seatedBodyYawDelta = 0.0f; } } @@ -3568,20 +3606,20 @@ void MyAvatar::updateOrientation(float deltaTime) { const float DEFAULT_REORIENT_ANGLE = 65.0f; const float FIRST_PERSON_REORIENT_ANGLE = 95.0f; - const float TRIGGER_REORIENT_ANGLE = 45.0f; + const float TRIGGER_REORIENT_ANGLE = 135.0f; const float FIRST_PERSON_TRIGGER_REORIENT_ANGLE = 65.0f; glm::vec3 ajustedYawVector = cameraYawVector; - float limitAngle = 0.0f; - float triggerAngle = -glm::sin(glm::radians(TRIGGER_REORIENT_ANGLE)); + float triggerAngle = glm::cos(glm::radians(TRIGGER_REORIENT_ANGLE)); + float limitAngle = triggerAngle; if (mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT) { - limitAngle = glm::sin(glm::radians(90.0f - FIRST_PERSON_TRIGGER_REORIENT_ANGLE)); + limitAngle = glm::cos(glm::radians(FIRST_PERSON_TRIGGER_REORIENT_ANGLE)); triggerAngle = limitAngle; } float reorientAngle = mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT ? FIRST_PERSON_REORIENT_ANGLE : DEFAULT_REORIENT_ANGLE; + if (frontBackDot < 0.0f) { + ajustedYawVector = (leftRightDot < 0.0f ? -avatarVectorRight : avatarVectorRight); + } if (frontBackDot < limitAngle) { - if (frontBackDot < 0.0f) { - ajustedYawVector = (leftRightDot < 0.0f ? -avatarVectorRight : avatarVectorRight); - } if (!isRotatingWhileSeated) { if (frontBackDot < triggerAngle) { _shouldTurnToFaceCamera = true; @@ -3589,6 +3627,7 @@ void MyAvatar::updateOrientation(float deltaTime) { } } else { setWorldOrientation(previousOrientation); + _seatedBodyYawDelta = 0.0f; } } else if (frontBackDot > glm::sin(glm::radians(reorientAngle))) { _shouldTurnToFaceCamera = false; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 01ff217aeb..06fe558964 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1093,6 +1093,12 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos const float TURN_ENTER_SPEED_THRESHOLD = 0.5f; // rad/sec const float TURN_EXIT_SPEED_THRESHOLD = 0.2f; // rad/sec + //stategraph vars based on input + const float INPUT_DEADZONE_THRESHOLD = 0.05f; + const float SLOW_SPEED_THRESHOLD = 1.5f; + const float HAS_MOMENTUM_THRESHOLD = 2.2f; + const float RESET_MOMENTUM_THRESHOLD = 0.05f; + if (ccState == CharacterControllerState::Hover) { if (_desiredState != RigRole::Hover) { _desiredStateAge = 0.0f; @@ -1245,6 +1251,9 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotInAir", true); _animVars.set("isSeated", false); _animVars.set("isNotSeated", true); + _animVars.set("isSeatedTurningRight", false); + _animVars.set("isSeatedTurningLeft", false); + _animVars.set("isSeatedNotTurning", false); } else if (_state == RigRole::Turn) { if (turningSpeed > 0.0f) { @@ -1275,6 +1284,9 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotInAir", true); _animVars.set("isSeated", false); _animVars.set("isNotSeated", true); + _animVars.set("isSeatedTurningRight", false); + _animVars.set("isSeatedTurningLeft", false); + _animVars.set("isSeatedNotTurning", false); } else if (_state == RigRole::Idle) { // default anim vars to notMoving and notTurning @@ -1298,6 +1310,9 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotInAir", true); _animVars.set("isSeated", false); _animVars.set("isNotSeated", true); + _animVars.set("isSeatedTurningRight", false); + _animVars.set("isSeatedTurningLeft", false); + _animVars.set("isSeatedNotTurning", false); } else if (_state == RigRole::Hover) { // flying. @@ -1321,6 +1336,9 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotInAir", true); _animVars.set("isSeated", false); _animVars.set("isNotSeated", true); + _animVars.set("isSeatedTurningRight", false); + _animVars.set("isSeatedTurningLeft", false); + _animVars.set("isSeatedNotTurning", false); } else if (_state == RigRole::Takeoff) { // jumping in-air @@ -1352,6 +1370,9 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotInAir", false); _animVars.set("isSeated", false); _animVars.set("isNotSeated", true); + _animVars.set("isSeatedTurningRight", false); + _animVars.set("isSeatedTurningLeft", false); + _animVars.set("isSeatedNotTurning", false); } else if (_state == RigRole::InAir) { // jumping in-air @@ -1372,6 +1393,9 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotTakeoff", true); _animVars.set("isSeated", false); _animVars.set("isNotSeated", true); + _animVars.set("isSeatedTurningRight", false); + _animVars.set("isSeatedTurningLeft", false); + _animVars.set("isSeatedNotTurning", false); bool inAirRun = forwardSpeed > 0.1f; if (inAirRun) { @@ -1394,6 +1418,23 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("inAirAlpha", alpha); } else if (_state == RigRole::Seated) { + if (fabsf(_previousControllerParameters.inputX) <= INPUT_DEADZONE_THRESHOLD) { + // seated not turning + _animVars.set("isSeatedTurningRight", false); + _animVars.set("isSeatedTurningLeft", false); + _animVars.set("isSeatedNotTurning", true); + } else if (_previousControllerParameters.inputX > 0.0f) { + // seated turning right + _animVars.set("isSeatedTurningRight", true); + _animVars.set("isSeatedTurningLeft", false); + _animVars.set("isSeatedNotTurning", false); + } else { + // seated turning left + _animVars.set("isSeatedTurningRight", false); + _animVars.set("isSeatedTurningLeft", true); + _animVars.set("isSeatedNotTurning", false); + } + _animVars.set("isMovingForward", false); _animVars.set("isMovingBackward", false); _animVars.set("isMovingRight", false); @@ -1435,11 +1476,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _lastEnableInverseKinematics = _enableInverseKinematics; - //stategraph vars based on input - const float INPUT_DEADZONE_THRESHOLD = 0.05f; - const float SLOW_SPEED_THRESHOLD = 1.5f; - const float HAS_MOMENTUM_THRESHOLD = 2.2f; - const float RESET_MOMENTUM_THRESHOLD = 0.05f; + if (fabsf(_previousControllerParameters.inputX) <= INPUT_DEADZONE_THRESHOLD && fabsf(_previousControllerParameters.inputZ) <= INPUT_DEADZONE_THRESHOLD) { diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 031b2f1309..e262bda526 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -662,6 +662,7 @@ protected: std::vector> _attachmentsToDelete; float _bodyYawDelta { 0.0f }; // degrees/sec + float _seatedBodyYawDelta{ 0.0f }; // degrees/renderframe // These position histories and derivatives are in the world-frame. // The derivatives are the MEASURED results of all external and internal forces