From 46b3a7fd2313a811d1d8618ce42fe8a1ba364ec3 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 2 Sep 2015 17:28:06 -0700 Subject: [PATCH] Improved default avatar.json state machine. Now triggers 7 states. Idle, WalkFwd, WalkBwd, StrafeLeft, StrafeRight, TurnLeft & TurnRight. As well as variable speed walking to match current velocity. --- interface/src/avatar/MyAvatar.cpp | 2 +- libraries/animation/src/AnimStateMachine.cpp | 4 +- libraries/animation/src/Rig.cpp | 144 ++++++++++++------- tests/animation/src/data/avatar.json | 85 ++++++++++- 4 files changed, 181 insertions(+), 54 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 8e07aa3442..c89a583f32 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1225,7 +1225,7 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { // https://gist.github.com/hyperlogic/7d6a0892a7319c69e2b9 // python2 -m SimpleHTTPServer& //auto graphUrl = QUrl("http://localhost:8000/avatar.json"); - auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/7d6a0892a7319c69e2b9/raw/a6e3754beef1524f95bae178066bae3b2f839952/avatar.json"); + auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/7d6a0892a7319c69e2b9/raw/e2cb37aee601b6fba31d60eac3f6ae3ef72d4a66/avatar.json"); _skeletonModel.initAnimGraph(graphUrl, _skeletonModel.getGeometry()->getFBXGeometry()); } diff --git a/libraries/animation/src/AnimStateMachine.cpp b/libraries/animation/src/AnimStateMachine.cpp index eccfcfa6e6..5de379dd33 100644 --- a/libraries/animation/src/AnimStateMachine.cpp +++ b/libraries/animation/src/AnimStateMachine.cpp @@ -52,7 +52,9 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, fl if (_duringInterp) { _alpha += _alphaVel * dt; if (_alpha < 1.0f) { - ::blend(_poses.size(), &_prevPoses[0], &_nextPoses[0], _alpha, &_poses[0]); + if (_poses.size() > 0) { + ::blend(_poses.size(), &_prevPoses[0], &_nextPoses[0], _alpha, &_poses[0]); + } } else { _duringInterp = false; _prevPoses.clear(); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 89ab062c02..c96a588e5c 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -416,52 +416,107 @@ glm::mat4 Rig::getJointVisibleTransform(int jointIndex) const { void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) { + glm::vec3 front = worldRotation * IDENTITY_FRONT; + + // at the moment worldVelocity comes from the Avatar physics body, which is not always correct when + // moving in the HMD, so let's compute our own veloicty. + glm::vec3 worldVel = (worldPosition - _lastPosition) / deltaTime; + glm::vec3 localVel = glm::inverse(worldRotation) * worldVel; + float forwardSpeed = glm::dot(localVel, IDENTITY_FRONT); + float lateralSpeed = glm::dot(localVel, IDENTITY_RIGHT); + float turningSpeed = glm::orientedAngle(front, _lastFront, IDENTITY_UP) / deltaTime; + if (_enableAnimGraph) { + + // sine wave LFO var for testing. static float t = 0.0f; _animVars.set("sine", static_cast(0.5 * sin(t) + 0.5)); - if (glm::length(worldVelocity) > 0.07f) { - _animVars.set("isMoving", true); - _animVars.set("isNotMoving", false); + // default anim vars to notMoving and notTurning + _animVars.set("isMovingForward", false); + _animVars.set("isMovingBackward", false); + _animVars.set("isMovingLeft", false); + _animVars.set("isMovingRight", false); + _animVars.set("isNotMoving", true); + _animVars.set("isTurningLeft", false); + _animVars.set("isTurningRight", false); + _animVars.set("isNotTurning", true); + + const float ANIM_WALK_SPEED = 1.4f; // m/s + _animVars.set("walkTimeScale", glm::clamp(0.1f, 2.0f, glm::length(localVel) / ANIM_WALK_SPEED)); + + const float MOVE_SPEED_THRESHOLD = 0.01f; // m/sec + const float TURN_SPEED_THRESHOLD = 0.5f; // rad/sec + if (glm::length(localVel) > MOVE_SPEED_THRESHOLD) { + if (fabs(forwardSpeed) > fabs(lateralSpeed)) { + if (forwardSpeed > 0.0f) { + // forward + _animVars.set("isMovingForward", true); + _animVars.set("isNotMoving", false); + + } else { + // backward + _animVars.set("isMovingBackward", true); + _animVars.set("isNotMoving", false); + } + } else { + if (lateralSpeed > 0.0f) { + // right + _animVars.set("isMovingRight", true); + _animVars.set("isNotMoving", false); + } else { + // left + _animVars.set("isMovingLeft", true); + _animVars.set("isNotMoving", false); + } + } } else { - _animVars.set("isMoving", false); - _animVars.set("isNotMoving", true); + if (fabs(turningSpeed) > TURN_SPEED_THRESHOLD) { + if (turningSpeed > 0.0f) { + // turning right + _animVars.set("isTurningRight", true); + _animVars.set("isNotTurning", false); + } else { + // turning left + _animVars.set("isTurningLeft", true); + _animVars.set("isNotTurning", false); + } + } else { + // idle + } } t += deltaTime; } - if (!_enableRig) { - return; + if (_enableRig) { + bool isMoving = false; + auto updateRole = [&](const QString& role, bool isOn) { + isMoving = isMoving || isOn; + if (isOn) { + if (!isRunningRole(role)) { + qCDebug(animation) << "Rig STARTING" << role; + startAnimationByRole(role); + } + } else { + if (isRunningRole(role)) { + qCDebug(animation) << "Rig stopping" << role; + stopAnimationByRole(role); + } + } + }; + + updateRole("walk", forwardSpeed > 0.01f); + updateRole("backup", forwardSpeed < -0.01f); + bool isTurning = std::abs(turningSpeed) > 0.5f; + updateRole("rightTurn", isTurning && (turningSpeed > 0)); + updateRole("leftTurn", isTurning && (turningSpeed < 0)); + bool isStrafing = !isTurning && (std::abs(lateralSpeed) > 0.01f); + updateRole("rightStrafe", isStrafing && (lateralSpeed > 0.0f)); + updateRole("leftStrafe", isStrafing && (lateralSpeed < 0.0f)); + updateRole("idle", !isMoving); // Must be last, as it makes isMoving bogus. } - bool isMoving = false; - glm::vec3 front = worldRotation * IDENTITY_FRONT; - float forwardSpeed = glm::dot(worldVelocity, front); - float rightLateralSpeed = glm::dot(worldVelocity, worldRotation * IDENTITY_RIGHT); - float rightTurningSpeed = glm::orientedAngle(front, _lastFront, IDENTITY_UP) / deltaTime; - auto updateRole = [&](const QString& role, bool isOn) { - isMoving = isMoving || isOn; - if (isOn) { - if (!isRunningRole(role)) { - qCDebug(animation) << "Rig STARTING" << role; - startAnimationByRole(role); - } - } else { - if (isRunningRole(role)) { - qCDebug(animation) << "Rig stopping" << role; - stopAnimationByRole(role); - } - } - }; - updateRole("walk", forwardSpeed > 0.01f); - updateRole("backup", forwardSpeed < -0.01f); - bool isTurning = std::abs(rightTurningSpeed) > 0.5f; - updateRole("rightTurn", isTurning && (rightTurningSpeed > 0)); - updateRole("leftTurn", isTurning && (rightTurningSpeed < 0)); - bool isStrafing = !isTurning && (std::abs(rightLateralSpeed) > 0.01f); - updateRole("rightStrafe", isStrafing && (rightLateralSpeed > 0.0f)); - updateRole("leftStrafe", isStrafing && (rightLateralSpeed < 0.0f)); - updateRole("idle", !isMoving); // Must be last, as it makes isMoving bogus. + _lastFront = front; _lastPosition = worldPosition; } @@ -487,13 +542,6 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { setJointRotationInConstrainedFrame((int)i, glm::inverse(_animSkeleton->getRelativeBindPose(i).rot) * poses[i].rot, PRIORITY, false); } - for (int i = 0; i < _jointStates.size(); i++) { - updateJointState(i, rootTransform); - } - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].resetTransformChanged(); - } - } else { // First normalize the fades so that they sum to 1.0. @@ -538,13 +586,13 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { handle->setMix(mix); handle->simulate(deltaTime); } + } - for (int i = 0; i < _jointStates.size(); i++) { - updateJointState(i, rootTransform); - } - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].resetTransformChanged(); - } + for (int i = 0; i < _jointStates.size(); i++) { + updateJointState(i, rootTransform); + } + for (int i = 0; i < _jointStates.size(); i++) { + _jointStates[i].resetTransformChanged(); } } diff --git a/tests/animation/src/data/avatar.json b/tests/animation/src/data/avatar.json index 0988e40ca8..24967979ea 100644 --- a/tests/animation/src/data/avatar.json +++ b/tests/animation/src/data/avatar.json @@ -11,7 +11,12 @@ "interpTarget": 6, "interpDuration": 6, "transitions": [ - { "var": "isMoving", "state": "walkFwd" } + { "var": "isMovingForward", "state": "walkFwd" }, + { "var": "isMovingBackward", "state": "walkBwd" }, + { "var": "isMovingRight", "state": "strafeRight" }, + { "var": "isMovingLeft", "state": "strafeLeft" }, + { "var": "isTurningRight", "state": "turnRight" }, + { "var": "isTurningLeft", "state": "turnLeft" } ] }, { @@ -19,7 +24,77 @@ "interpTarget": 6, "interpDuration": 6, "transitions": [ - { "var": "isNotMoving", "state": "idle" } + { "var": "isNotMoving", "state": "idle" }, + { "var": "isMovingBackward", "state": "walkBwd" }, + { "var": "isMovingRight", "state": "strafeRight" }, + { "var": "isMovingLeft", "state": "strafeLeft" }, + { "var": "isTurningRight", "state": "turnRight" }, + { "var": "isTurningLeft", "state": "turnLeft" } + ] + }, + { + "id": "walkBwd", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "isNotMoving", "state": "idle" }, + { "var": "isMovingForward", "state": "walkFwd" }, + { "var": "isMovingRight", "state": "strafeRight" }, + { "var": "isMovingLeft", "state": "strafeLeft" }, + { "var": "isTurningRight", "state": "turnRight" }, + { "var": "isTurningLeft", "state": "turnLeft" } + ] + }, + { + "id": "strafeRight", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "isNotMoving", "state": "idle" }, + { "var": "isMovingForward", "state": "walkFwd" }, + { "var": "isMovingBackward", "state": "walkBwd" }, + { "var": "isMovingLeft", "state": "strafeLeft" }, + { "var": "isTurningRight", "state": "turnRight" }, + { "var": "isTurningLeft", "state": "turnLeft" } + ] + }, + { + "id": "strafeLeft", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "isNotMoving", "state": "idle" }, + { "var": "isMovingForward", "state": "walkFwd" }, + { "var": "isMovingBackward", "state": "walkBwd" }, + { "var": "isMovingRight", "state": "strafeRight" }, + { "var": "isTurningRight", "state": "turnRight" }, + { "var": "isTurningLeft", "state": "turnLeft" } + ] + }, + { + "id": "turnRight", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "isNotTurning", "state": "idle" }, + { "var": "isMovingForward", "state": "walkFwd" }, + { "var": "isMovingBackward", "state": "walkBwd" }, + { "var": "isMovingRight", "state": "strafeRight" }, + { "var": "isMovingLeft", "state": "strafeLeft" }, + { "var": "isTurningLeft", "state": "turnLeft" } + ] + }, + { + "id": "turnLeft", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "isNotTurning", "state": "idle" }, + { "var": "isMovingForward", "state": "walkFwd" }, + { "var": "isMovingBackward", "state": "walkBwd" }, + { "var": "isMovingRight", "state": "strafeRight" }, + { "var": "isMovingLeft", "state": "strafeLeft" }, + { "var": "isTurningRight", "state": "turnRight" } ] } ] @@ -45,7 +120,8 @@ "startFrame": 0.0, "endFrame": 35.0, "timeScale": 1.0, - "loopFlag": true + "loopFlag": true, + "timeScaleVar": "walkTimeScale" }, "children": [] }, @@ -57,7 +133,8 @@ "startFrame": 0.0, "endFrame": 37.0, "timeScale": 1.0, - "loopFlag": true + "loopFlag": true, + "timeScaleVar": "walkTimeScale" }, "children": [] },