diff options
Diffstat (limited to 'indra/llcharacter/llkeyframewalkmotion.cpp')
-rw-r--r-- | indra/llcharacter/llkeyframewalkmotion.cpp | 319 |
1 files changed, 189 insertions, 130 deletions
diff --git a/indra/llcharacter/llkeyframewalkmotion.cpp b/indra/llcharacter/llkeyframewalkmotion.cpp index d52eb89a5c..f814618fc1 100644 --- a/indra/llcharacter/llkeyframewalkmotion.cpp +++ b/indra/llcharacter/llkeyframewalkmotion.cpp @@ -2,25 +2,31 @@ * @file llkeyframewalkmotion.cpp * @brief Implementation of LLKeyframeWalkMotion class. * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-2009, Linden Research, Inc. * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ @@ -38,31 +44,32 @@ //----------------------------------------------------------------------------- // 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 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 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 +F32 SPEED_ADJUST_MAX = 2.5f; // maximum adjustment of walk animation playback speed +F32 SPEED_ADJUST_MAX_SEC = 3.f; // maximum adjustment to walk animation playback speed for a second +F32 ANIM_SPEED_MAX = 10.0f; // absolute upper limit on animation speed +F32 ANIM_SPEED_MIN = 0.0f; // absolute lower limit on animation speed +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 MAX_ROLL = 0.6f; -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() // Class Constructor //----------------------------------------------------------------------------- LLKeyframeWalkMotion::LLKeyframeWalkMotion(const LLUUID &id) -: LLKeyframeMotion(id), + : LLKeyframeMotion(id), + mCharacter(NULL), mCyclePhase(0.0f), mRealTimeLast(0.0f), mAdjTimeLast(0.0f), mDownFoot(0) -{} +{ +} //----------------------------------------------------------------------------- @@ -70,7 +77,8 @@ LLKeyframeWalkMotion::LLKeyframeWalkMotion(const LLUUID &id) // Class Destructor //----------------------------------------------------------------------------- LLKeyframeWalkMotion::~LLKeyframeWalkMotion() -{} +{ +} //----------------------------------------------------------------------------- @@ -141,12 +149,15 @@ 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), - mAdjustedSpeed(0.f), + mAvgSpeed(0.f), mRelativeDir(0.f), mAnkleOffset(0.f) { mName = "walk_adjust"; + mPelvisState = new LLJointState; } @@ -178,16 +189,15 @@ LLMotion::LLMotionInitStatus LLWalkAdjustMotion::onInitialize(LLCharacter *chara //----------------------------------------------------------------------------- BOOL LLWalkAdjustMotion::onActivate() { + mAvgCorrection = 0.f; + mSpeedAdjust = 0.f; mAnimSpeed = 0.f; - mAdjustedSpeed = 0.f; + mAvgSpeed = 0.f; mRelativeDir = 1.f; mPelvisState->setPosition(LLVector3::zero); // store ankle positions for next frame - mLastLeftFootGlobalPos = mCharacter->getPosGlobalFromAgent(mLeftAnkleJoint->getWorldPosition()); - mLastLeftFootGlobalPos.mdV[VZ] = 0.0; - - mLastRightFootGlobalPos = mCharacter->getPosGlobalFromAgent(mRightAnkleJoint->getWorldPosition()); - mLastRightFootGlobalPos.mdV[VZ] = 0.0; + mLastLeftAnklePos = mCharacter->getPosGlobalFromAgent(mLeftAnkleJoint->getWorldPosition()); + mLastRightAnklePos = mCharacter->getPosGlobalFromAgent(mRightAnkleJoint->getWorldPosition()); F32 leftAnkleOffset = (mLeftAnkleJoint->getWorldPosition() - mCharacter->getCharacterPosition()).magVec(); F32 rightAnkleOffset = (mRightAnkleJoint->getWorldPosition() - mCharacter->getCharacterPosition()).magVec(); @@ -201,121 +211,164 @@ BOOL LLWalkAdjustMotion::onActivate() //----------------------------------------------------------------------------- BOOL LLWalkAdjustMotion::onUpdate(F32 time, U8* joint_mask) { - // delta_time is guaranteed to be non zero - F32 delta_time = llclamp(time - mLastTime, TIME_EPSILON, MAX_TIME_DELTA); + LLVector3 footCorrection; + LLVector3 vel = mCharacter->getCharacterVelocity() * mCharacter->getTimeDilation(); + F32 deltaTime = llclamp(time - mLastTime, 0.f, MAX_TIME_DELTA); mLastTime = time; - // find the avatar motion vector in the XY plane - LLVector3 avatar_velocity = mCharacter->getCharacterVelocity() * mCharacter->getTimeDilation(); - avatar_velocity.mV[VZ] = 0.f; + LLQuaternion inv_rotation = ~mPelvisJoint->getWorldRotation(); - F32 speed = llclamp(avatar_velocity.magVec(), 0.f, MAX_WALK_PLAYBACK_SPEED); + // 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)); - // grab avatar->world transforms - LLQuaternion avatar_to_world_rot = mCharacter->getRootJoint()->getWorldRotation(); + // 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; + } - LLQuaternion world_to_avatar_rot(avatar_to_world_rot); - world_to_avatar_rot.conjugate(); + // calculate world-space foot drift + LLVector3 leftFootDelta; + LLVector3 leftFootWorldPosition = mLeftAnkleJoint->getWorldPosition(); + LLVector3d leftFootGlobalPosition = mCharacter->getPosGlobalFromAgent(leftFootWorldPosition); + leftFootDelta.setVec(mLastLeftAnklePos - leftFootGlobalPosition); + mLastLeftAnklePos = leftFootGlobalPosition; - LLVector3 foot_slip_vector; + LLVector3 rightFootDelta; + LLVector3 rightFootWorldPosition = mRightAnkleJoint->getWorldPosition(); + LLVector3d rightFootGlobalPosition = mCharacter->getPosGlobalFromAgent(rightFootWorldPosition); + rightFootDelta.setVec(mLastRightAnklePos - rightFootGlobalPosition); + mLastRightAnklePos = rightFootGlobalPosition; // find foot drift along velocity vector - if (speed > MIN_WALK_SPEED) - { // walking/running - - // 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; - - 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; - } + if (mAvgSpeed > 0.1) + { + // walking/running + F32 leftFootDriftAmt = leftFootDelta * vel; + F32 rightFootDriftAmt = rightFootDelta * vel; + + if (rightFootDriftAmt > leftFootDriftAmt) + { + footCorrection = rightFootDelta; + } else + { + footCorrection = leftFootDelta; + } + } + else + { + mAvgSpeed = ang_vel.magVec() * mAnkleOffset; + mRelativeDir = 1.f; + + // standing/turning + // find the lower foot + if (leftFootWorldPosition.mV[VZ] < rightFootWorldPosition.mV[VZ]) + { + // pivot on left foot + footCorrection = leftFootDelta; + } else - { // otherwise use the left foot - foot_slip_vector = leftFootDelta; + { + // pivot on right foot + footCorrection = rightFootDelta; } + } + + // 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. - - //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)); - - ////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; - - //// 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); - - //foot_slip_vector -= mPelvisOffset; - - LLVector3 avatar_movement_dir = avatar_velocity; - avatar_movement_dir.normalize(); - - // 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); - - // 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; + // 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)); + + // pelvis drift (along walk direction) + mAvgCorrection = lerp(mAvgCorrection, footCorrection.mV[VX] * mRelativeDir, LLCriticalDamp::getInterpolant(0.1f)); + + // calculate average velocity of foot slippage + F32 footSlipVelocity = (deltaTime != 0.f) ? (-mAvgCorrection / deltaTime) : 0.f; + + 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 + + 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)); + + F32 speedDelta = newSpeedAdjust - mSpeedAdjust; + speedDelta = llclamp(speedDelta, -SPEED_ADJUST_MAX_SEC * deltaTime, SPEED_ADJUST_MAX_SEC * deltaTime); + + mSpeedAdjust = mSpeedAdjust + speedDelta; } else - { // 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)); + { + mSpeedAdjust = lerp(mSpeedAdjust, 0.f, LLCriticalDamp::getInterpolant(0.2f)); } - // broadcast walk speed change + mAnimSpeed = (mAvgSpeed + mSpeedAdjust) * mRelativeDir; + if (mAnimSpeed>0) + { + mAnimSpeed = llclamp(mAnimSpeed, ANIM_SPEED_MIN, ANIM_SPEED_MAX); + } + else + { + mAnimSpeed = llclamp(mAnimSpeed, -ANIM_SPEED_MAX, -ANIM_SPEED_MIN); + } +// 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); + if (mCharacter->getMotionController().mIsSelf) + { +// F32 elapsed = mCharacter->getMotionController().getFrameTimer().getElapsedTimeF32(); +// llinfos << "PLOT elapsed: " << elapsed +// << " footSlipVelocity: " << footSlipVelocity +// << " mAvgCorrection: " << mAvgCorrection +// << " mAvgSpeed: " << mAvgSpeed +// << " mAnimSpeed: " << mAnimSpeed +// << " ANIM_SPEED_MAX: " << ANIM_SPEED_MAX +// << " ANIM_SPEED_MIN: " << ANIM_SPEED_MIN +// << llendl; + } + + // 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; // set position - // need to update *some* joint to keep this animation active mPelvisState->setPosition(mPelvisOffset); + mCharacter->setAnimationData("Pelvis Offset", &mPelvisOffset); + return TRUE; } @@ -385,8 +438,14 @@ BOOL LLFlyAdjustMotion::onUpdate(F32 time, U8* joint_mask) // roll is critically damped interpolation between current roll and angular velocity-derived target roll mRoll = lerp(mRoll, target_roll, LLCriticalDamp::getInterpolant(0.1f)); +// llinfos << mRoll << llendl; + LLQuaternion roll(mRoll, LLVector3(0.f, 0.f, 1.f)); mPelvisState->setRotation(roll); +// F32 lerp_amt = LLCriticalDamp::getInterpolant(0.2f); +// +// LLVector3 pelvis_correction = mPelvisState->getPosition() - lerp(LLVector3::zero, mPelvisState->getJoint()->getPosition() + mPelvisState->getPosition(), lerp_amt); +// mPelvisState->setPosition(pelvis_correction); return TRUE; } |