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.
This commit is contained in:
Anthony J. Thibault 2015-09-02 17:28:06 -07:00
parent 99586f259c
commit 46b3a7fd23
4 changed files with 181 additions and 54 deletions

View file

@ -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());
}

View file

@ -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();

View file

@ -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<float>(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();
}
}

View file

@ -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": []
},