First pass at Rig timeScaling and blending between slow, walk and run.

This commit is contained in:
Anthony J. Thibault 2015-10-20 16:37:05 -07:00
parent 11f2d29bf8
commit 5cd2786c1d
4 changed files with 65 additions and 5 deletions

View file

@ -4,7 +4,7 @@
"id": "ikOverlay",
"type": "overlay",
"data": {
"alpha": 1.0,
"alpha": 0.0,
"boneSet": "fullBody"
},
"children": [
@ -532,7 +532,8 @@
"alpha": 0.0,
"sync": true,
"timeScale": 1.0,
"timeScaleVar": "walkTimeScale"
"timeScaleVar": "walkTimeScale",
"alphaVar": "walkAlpha"
},
"children": [
{
@ -558,6 +559,18 @@
"loopFlag": true
},
"children": []
},
{
"id": "walkFwdRun",
"type": "clip",
"data": {
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/run_fwd.fbx",
"startFrame": 0.0,
"endFrame": 21.0,
"timeScale": 1.0,
"loopFlag": true
},
"children": []
}
]
},

View file

@ -408,6 +408,41 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const {
return _jointStates[jointIndex].getTransform();
}
void Rig::calcWalkForwardAlphaAndTimeScale(float speed, float* alphaOut, float* timeScaleOut) {
// filter speed using a moving average.
_averageForwardSpeed.updateAverage(speed);
speed = _averageForwardSpeed.getAverage();
const int NUM_FWD_SPEEDS = 3;
float FWD_SPEEDS[NUM_FWD_SPEEDS] = { 0.3f, 1.4f, 2.7f }; // m/s
// first calculate alpha by lerping between speeds.
float alpha = 0.0f;
if (speed <= FWD_SPEEDS[0]) {
alpha = 0.0f;
} else if (speed > FWD_SPEEDS[NUM_FWD_SPEEDS - 1]) {
alpha = (float)(NUM_FWD_SPEEDS - 1);
} else {
for (int i = 0; i < NUM_FWD_SPEEDS - 1; i++) {
if (FWD_SPEEDS[i] < speed && speed < FWD_SPEEDS[i + 1]) {
alpha = (float)i + ((speed - FWD_SPEEDS[i]) / (FWD_SPEEDS[i + 1] - FWD_SPEEDS[i]));
break;
}
}
}
// now keeping the alpha fixed compute the timeScale.
// NOTE: This makes the assumption that the velocity of a linear blend between two animations is also linear.
int prevIndex = glm::floor(alpha);
int nextIndex = glm::ceil(alpha);
float animSpeed = lerp(FWD_SPEEDS[prevIndex], FWD_SPEEDS[nextIndex], (float)glm::fract(alpha));
float timeScale = glm::clamp(0.5f, 2.0f, speed / animSpeed);
*alphaOut = alpha;
*timeScaleOut = timeScale;
}
void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) {
glm::vec3 front = worldRotation * IDENTITY_FRONT;
@ -435,10 +470,13 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
// sine wave LFO var for testing.
static float t = 0.0f;
_animVars.set("sine", static_cast<float>(0.5 * sin(t) + 0.5));
_animVars.set("sine", 2.0f * static_cast<float>(0.5 * sin(t) + 0.5));
const float ANIM_WALK_SPEED = 1.4f; // m/s
_animVars.set("walkTimeScale", glm::clamp(0.5f, 2.0f, glm::length(localVel) / ANIM_WALK_SPEED));
float walkAlpha, walkTimeScale;
calcWalkForwardAlphaAndTimeScale(glm::length(localVel), &walkAlpha, &walkTimeScale);
_animVars.set("walkTimeScale", walkTimeScale);
_animVars.set("walkAlpha", walkAlpha);
const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec
const float MOVE_EXIT_SPEED_THRESHOLD = 0.07f; // m/sec

View file

@ -42,6 +42,7 @@
#include "AnimNode.h"
#include "AnimNodeLoader.h"
#include "SimpleMovingAverage.h"
class AnimationHandle;
typedef std::shared_ptr<AnimationHandle> AnimationHandlePointer;
@ -206,6 +207,7 @@ public:
void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist);
void updateNeckJoint(int index, const HeadParameters& params);
void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade);
void calcWalkForwardAlphaAndTimeScale(float speed, float* alphaOut, float* timeScaleOut);
QVector<JointState> _jointStates;
int _rootJointIndex = -1;
@ -240,6 +242,8 @@ public:
float _desiredStateAge = 0.0f;
float _leftHandOverlayAlpha = 0.0f;
float _rightHandOverlayAlpha = 0.0f;
SimpleMovingAverage _averageForwardSpeed{ 25 };
};
#endif /* defined(__hifi__Rig__) */

View file

@ -183,6 +183,11 @@ T toNormalizedDeviceScale(const T& value, const T& size) {
#define PITCH(euler) euler.x
#define ROLL(euler) euler.z
// float - linear interpolate
inline float lerp(float x, float y, float a) {
return x * (1.0f - a) + (y * a);
}
// vec2 lerp - linear interpolate
template<typename T, glm::precision P>
glm::detail::tvec2<T, P> lerp(const glm::detail::tvec2<T, P>& x, const glm::detail::tvec2<T, P>& y, T a) {