V2 walk/run animations

This commit is contained in:
Shyotl
2011-03-08 23:22:46 -06:00
parent 4c3a73658c
commit 60f3104eb6
5 changed files with 193 additions and 157 deletions

View File

@@ -44,16 +44,18 @@
//-----------------------------------------------------------------------------
// Macros
//-----------------------------------------------------------------------------
const F32 MAX_WALK_PLAYBACK_SPEED = 8.f; // max m/s for which we adjust walk cycle speed
const F32 MAX_WALK_PLAYBACK_SPEED = 8.f; // max m/s for which we adjust walk cycle speed
const F32 MIN_WALK_SPEED = 0.1f; // minimum speed at which we use velocity for down foot detection
const F32 MAX_TIME_DELTA = 2.f; //max two seconds a frame for calculating interpolation
const F32 SPEED_ADJUST_MAX = 2.5f; // maximum adjustment of walk animation playback speed
const F32 SPEED_ADJUST_MAX_SEC = 3.f; // maximum adjustment to walk animation playback speed for a second
const F32 DRIFT_COMP_MAX_TOTAL = 0.07f;//0.55f; // maximum drift compensation overall, in any direction
const F32 DRIFT_COMP_MAX_SPEED = 4.f; // speed at which drift compensation total maxes out
const F32 MIN_WALK_SPEED = 0.1f; // minimum speed at which we use velocity for down foot detection
const F32 TIME_EPSILON = 0.001f; // minumum frame time
const F32 MAX_TIME_DELTA = 2.f; // max two seconds a frame for calculating interpolation
F32 SPEED_ADJUST_MAX_SEC = 2.f; // maximum adjustment to walk animation playback speed for a second
F32 ANIM_SPEED_MAX = 1.5f; // absolute upper limit on animation speed
const F32 DRIFT_COMP_MAX_TOTAL = 0.1f; // maximum drift compensation overall, in any direction
const F32 DRIFT_COMP_MAX_SPEED = 4.f; // speed at which drift compensation total maxes out
const F32 MAX_ROLL = 0.6f;
const F32 SPEED_FINAL_SCALING = 0.5f; // final scaling for walk animation
const F32 PELVIS_COMPENSATION_WIEGHT = 0.7f; // proportion of foot drift that is compensated by moving the avatar directly
const F32 SPEED_ADJUST_TIME_CONSTANT = 0.1f; // time constant for speed adjustment interpolation
//-----------------------------------------------------------------------------
// LLKeyframeWalkMotion()
@@ -145,10 +147,8 @@ BOOL LLKeyframeWalkMotion::onUpdate(F32 time, U8* joint_mask)
LLWalkAdjustMotion::LLWalkAdjustMotion(const LLUUID &id) :
LLMotion(id),
mLastTime(0.f),
mAvgCorrection(0.f),
mSpeedAdjust(0.f),
mAnimSpeed(0.f),
mAvgSpeed(0.f),
mAdjustedSpeed(0.f),
mRelativeDir(0.f),
mAnkleOffset(0.f)
{
@@ -185,15 +185,16 @@ LLMotion::LLMotionInitStatus LLWalkAdjustMotion::onInitialize(LLCharacter *chara
//-----------------------------------------------------------------------------
BOOL LLWalkAdjustMotion::onActivate()
{
mAvgCorrection = 0.f;
mSpeedAdjust = 0.f;
mAnimSpeed = 0.f;
mAvgSpeed = 0.f;
mAdjustedSpeed = 0.f;
mRelativeDir = 1.f;
mPelvisState->setPosition(LLVector3::zero);
// store ankle positions for next frame
mLastLeftAnklePos = mCharacter->getPosGlobalFromAgent(mLeftAnkleJoint->getWorldPosition());
mLastRightAnklePos = mCharacter->getPosGlobalFromAgent(mRightAnkleJoint->getWorldPosition());
mLastLeftFootGlobalPos = mCharacter->getPosGlobalFromAgent(mLeftAnkleJoint->getWorldPosition());
mLastLeftFootGlobalPos.mdV[VZ] = 0.0;
mLastRightFootGlobalPos = mCharacter->getPosGlobalFromAgent(mRightAnkleJoint->getWorldPosition());
mLastRightFootGlobalPos.mdV[VZ] = 0.0;
F32 leftAnkleOffset = (mLeftAnkleJoint->getWorldPosition() - mCharacter->getCharacterPosition()).magVec();
F32 rightAnkleOffset = (mRightAnkleJoint->getWorldPosition() - mCharacter->getCharacterPosition()).magVec();
@@ -207,144 +208,121 @@ BOOL LLWalkAdjustMotion::onActivate()
//-----------------------------------------------------------------------------
BOOL LLWalkAdjustMotion::onUpdate(F32 time, U8* joint_mask)
{
LLVector3 footCorrection;
LLVector3 vel = mCharacter->getCharacterVelocity() * mCharacter->getTimeDilation();
F32 deltaTime = llclamp(time - mLastTime, 0.f, MAX_TIME_DELTA);
// delta_time is guaranteed to be non zero
F32 delta_time = llclamp(time - mLastTime, TIME_EPSILON, MAX_TIME_DELTA);
mLastTime = time;
LLQuaternion inv_rotation = ~mPelvisJoint->getWorldRotation();
// find the avatar motion vector in the XY plane
LLVector3 avatar_velocity = mCharacter->getCharacterVelocity() * mCharacter->getTimeDilation();
avatar_velocity.mV[VZ] = 0.f;
// get speed and normalize velocity vector
LLVector3 ang_vel = mCharacter->getCharacterAngularVelocity() * mCharacter->getTimeDilation();
F32 speed = llmin(vel.normVec(), MAX_WALK_PLAYBACK_SPEED);
mAvgSpeed = lerp(mAvgSpeed, speed, LLCriticalDamp::getInterpolant(0.2f));
F32 speed = llclamp(avatar_velocity.magVec(), 0.f, MAX_WALK_PLAYBACK_SPEED);
// calculate facing vector in pelvis-local space
// (either straight forward or back, depending on velocity)
LLVector3 localVel = vel * inv_rotation;
if (localVel.mV[VX] > 0.f)
{
mRelativeDir = 1.f;
}
else if (localVel.mV[VX] < 0.f)
{
mRelativeDir = -1.f;
}
// grab avatar->world transforms
LLQuaternion avatar_to_world_rot = mCharacter->getRootJoint()->getWorldRotation();
// calculate world-space foot drift
LLVector3 leftFootDelta;
LLVector3 leftFootWorldPosition = mLeftAnkleJoint->getWorldPosition();
LLVector3d leftFootGlobalPosition = mCharacter->getPosGlobalFromAgent(leftFootWorldPosition);
leftFootDelta.setVec(mLastLeftAnklePos - leftFootGlobalPosition);
mLastLeftAnklePos = leftFootGlobalPosition;
LLQuaternion world_to_avatar_rot(avatar_to_world_rot);
world_to_avatar_rot.conjugate();
LLVector3 rightFootDelta;
LLVector3 rightFootWorldPosition = mRightAnkleJoint->getWorldPosition();
LLVector3d rightFootGlobalPosition = mCharacter->getPosGlobalFromAgent(rightFootWorldPosition);
rightFootDelta.setVec(mLastRightAnklePos - rightFootGlobalPosition);
mLastRightAnklePos = rightFootGlobalPosition;
LLVector3 foot_slip_vector;
// find foot drift along velocity vector
if (mAvgSpeed > 0.1)
{
// walking/running
F32 leftFootDriftAmt = leftFootDelta * vel;
F32 rightFootDriftAmt = rightFootDelta * vel;
if (speed > MIN_WALK_SPEED)
{ // walking/running
if (rightFootDriftAmt > leftFootDriftAmt)
{
footCorrection = rightFootDelta;
} else
{
footCorrection = leftFootDelta;
}
}
else
{
mAvgSpeed = ang_vel.magVec() * mAnkleOffset;
mRelativeDir = 1.f;
// calculate world-space foot drift
// use global coordinates to seamlessly handle region crossings
LLVector3d leftFootGlobalPosition = mCharacter->getPosGlobalFromAgent(mLeftAnkleJoint->getWorldPosition());
leftFootGlobalPosition.mdV[VZ] = 0.0;
LLVector3 leftFootDelta(leftFootGlobalPosition - mLastLeftFootGlobalPos);
mLastLeftFootGlobalPos = leftFootGlobalPosition;
// standing/turning
// find the lower foot
if (leftFootWorldPosition.mV[VZ] < rightFootWorldPosition.mV[VZ])
{
// pivot on left foot
footCorrection = leftFootDelta;
}
LLVector3d rightFootGlobalPosition = mCharacter->getPosGlobalFromAgent(mRightAnkleJoint->getWorldPosition());
rightFootGlobalPosition.mdV[VZ] = 0.0;
LLVector3 rightFootDelta(rightFootGlobalPosition - mLastRightFootGlobalPos);
mLastRightFootGlobalPos = rightFootGlobalPosition;
// get foot drift along avatar direction of motion
F32 left_foot_slip_amt = leftFootDelta * avatar_velocity;
F32 right_foot_slip_amt = rightFootDelta * avatar_velocity;
// if right foot is pushing back faster than left foot...
if (right_foot_slip_amt < left_foot_slip_amt)
{ //...use it to calculate optimal animation speed
foot_slip_vector = rightFootDelta;
}
else
{
// pivot on right foot
footCorrection = rightFootDelta;
{ // otherwise use the left foot
foot_slip_vector = leftFootDelta;
}
}
// rotate into avatar coordinates
footCorrection = footCorrection * inv_rotation;
// calculate ideal pelvis offset so that foot is glued to ground and damp towards it
// this will soak up transient slippage
//
// FIXME: this interacts poorly with speed adjustment
// mPelvisOffset compensates for foot drift by moving the avatar pelvis in the opposite
// direction of the drift, up to a certain limited distance
// but this will cause the animation playback rate calculation below to
// kick in too slowly and sometimes start playing the animation in reverse.
// calculate ideal pelvis offset so that foot is glued to ground and damp towards it
// the amount of foot slippage this frame + the offset applied last frame
mPelvisOffset = mPelvisState->getPosition() + lerp(LLVector3::zero, footCorrection, LLCriticalDamp::getInterpolant(0.2f));
//mPelvisOffset -= PELVIS_COMPENSATION_WIEGHT * (foot_slip_vector * world_to_avatar_rot);//lerp(LLVector3::zero, -1.f * (foot_slip_vector * world_to_avatar_rot), LLCriticalDamp::getInterpolant(0.1f));
// pelvis drift (along walk direction)
mAvgCorrection = lerp(mAvgCorrection, footCorrection.mV[VX] * mRelativeDir, LLCriticalDamp::getInterpolant(0.1f));
////F32 drift_comp_max = DRIFT_COMP_MAX_TOTAL * (llclamp(speed, 0.f, DRIFT_COMP_MAX_SPEED) / DRIFT_COMP_MAX_SPEED);
//F32 drift_comp_max = DRIFT_COMP_MAX_TOTAL;
// calculate average velocity of foot slippage
F32 footSlipVelocity = (deltaTime != 0.f) ? (-mAvgCorrection / deltaTime) : 0.f;
//// clamp pelvis offset to a 90 degree arc behind the nominal position
//// NB: this is an ADDITIVE amount that is accumulated every frame, so clamping it alone won't do the trick
//// must clamp with absolute position of pelvis in mind
//LLVector3 currentPelvisPos = mPelvisState->getJoint()->getPosition();
//mPelvisOffset.mV[VX] = llclamp( mPelvisOffset.mV[VX], -drift_comp_max, drift_comp_max );
//mPelvisOffset.mV[VY] = llclamp( mPelvisOffset.mV[VY], -drift_comp_max, drift_comp_max );
//mPelvisOffset.mV[VZ] = 0.f;
//
//mLastRightFootGlobalPos += LLVector3d(mPelvisOffset * avatar_to_world_rot);
//mLastLeftFootGlobalPos += LLVector3d(mPelvisOffset * avatar_to_world_rot);
F32 newSpeedAdjust = 0.f;
// modulate speed by dot products of facing and velocity
// so that if we are moving sideways, we slow down the animation
// and if we're moving backward, we walk backward
//foot_slip_vector -= mPelvisOffset;
F32 directional_factor = localVel.mV[VX] * mRelativeDir;
if (speed > 0.1f)
{
// calculate ratio of desired foot velocity to detected foot velocity
newSpeedAdjust = llclamp(footSlipVelocity - mAvgSpeed * (1.f - directional_factor),
-SPEED_ADJUST_MAX, SPEED_ADJUST_MAX);
newSpeedAdjust = lerp(mSpeedAdjust, newSpeedAdjust, LLCriticalDamp::getInterpolant(0.2f));
LLVector3 avatar_movement_dir = avatar_velocity;
avatar_movement_dir.normalize();
F32 speedDelta = newSpeedAdjust - mSpeedAdjust;
speedDelta = llclamp(speedDelta, -SPEED_ADJUST_MAX_SEC * deltaTime, SPEED_ADJUST_MAX_SEC * deltaTime);
// planted foot speed is avatar velocity - foot slip amount along avatar movement direction
F32 foot_speed = speed - ((foot_slip_vector * avatar_movement_dir) / delta_time);
mSpeedAdjust = mSpeedAdjust + speedDelta;
// multiply animation playback rate so that foot speed matches avatar speed
F32 min_speed_multiplier = clamp_rescale(speed, 0.f, 1.f, 0.f, 0.1f);
F32 desired_speed_multiplier = llclamp(speed / foot_speed, min_speed_multiplier, ANIM_SPEED_MAX);
// blend towards new speed adjustment value
F32 new_speed_adjust = lerp(mAdjustedSpeed, desired_speed_multiplier, LLCriticalDamp::getInterpolant(SPEED_ADJUST_TIME_CONSTANT));
// limit that rate at which the speed adjustment changes
F32 speedDelta = llclamp(new_speed_adjust - mAdjustedSpeed, -SPEED_ADJUST_MAX_SEC * delta_time, SPEED_ADJUST_MAX_SEC * delta_time);
mAdjustedSpeed += speedDelta;
// modulate speed by dot products of facing and velocity
// so that if we are moving sideways, we slow down the animation
// and if we're moving backward, we walk backward
// do this at the end to be more responsive to direction changes instead of in the above speed calculations
F32 directional_factor = (avatar_movement_dir * world_to_avatar_rot).mV[VX];
mAnimSpeed = mAdjustedSpeed * directional_factor;
}
else
{
mSpeedAdjust = lerp(mSpeedAdjust, 0.f, LLCriticalDamp::getInterpolant(0.2f));
{ // standing/turning
// damp out speed adjustment to 0
mAnimSpeed = lerp(mAnimSpeed, 1.f, LLCriticalDamp::getInterpolant(0.2f));
//mPelvisOffset = lerp(mPelvisOffset, LLVector3::zero, LLCriticalDamp::getInterpolant(0.2f));
}
mAnimSpeed = (mAvgSpeed + mSpeedAdjust) * mRelativeDir;
mAnimSpeed = mAnimSpeed * SPEED_FINAL_SCALING;
// char debug_text[64];
// sprintf(debug_text, "Foot slip vel: %.2f", footSlipVelocity);
// mCharacter->addDebugText(debug_text);
// sprintf(debug_text, "Speed: %.2f", mAvgSpeed);
// mCharacter->addDebugText(debug_text);
// sprintf(debug_text, "Speed Adjust: %.2f", mSpeedAdjust);
// mCharacter->addDebugText(debug_text);
// sprintf(debug_text, "Animation Playback Speed: %.2f", mAnimSpeed);
// mCharacter->addDebugText(debug_text);
mCharacter->setAnimationData("Walk Speed", &mAnimSpeed);
// clamp pelvis offset to a 90 degree arc behind the nominal position
F32 drift_comp_max = llclamp(speed, 0.f, DRIFT_COMP_MAX_SPEED) / DRIFT_COMP_MAX_SPEED;
drift_comp_max *= DRIFT_COMP_MAX_TOTAL;
LLVector3 currentPelvisPos = mPelvisState->getJoint()->getPosition();
// NB: this is an ADDITIVE amount that is accumulated every frame, so clamping it alone won't do the trick
// must clamp with absolute position of pelvis in mind
mPelvisOffset.mV[VX] = llclamp( mPelvisOffset.mV[VX], -drift_comp_max - currentPelvisPos.mV[VX], drift_comp_max - currentPelvisPos.mV[VX] );
mPelvisOffset.mV[VY] = llclamp( mPelvisOffset.mV[VY], -drift_comp_max - currentPelvisPos.mV[VY], drift_comp_max - currentPelvisPos.mV[VY]);
mPelvisOffset.mV[VZ] = 0.f;
// broadcast walk speed change
mCharacter->setAnimationData("Walk Speed", &mAnimSpeed);
// set position
// need to update *some* joint to keep this animation active
mPelvisState->setPosition(mPelvisOffset);
mCharacter->setAnimationData("Pelvis Offset", &mPelvisOffset);
return TRUE;
}