diff --git a/indra/llcharacter/llkeyframemotion.cpp b/indra/llcharacter/llkeyframemotion.cpp index b9840d9381b4600d09acd2c960e740566de208c3..938cc35cb3f7b69e6c5f8fe3db6ee5770189b8ad 100644 --- a/indra/llcharacter/llkeyframemotion.cpp +++ b/indra/llcharacter/llkeyframemotion.cpp @@ -122,257 +122,6 @@ U32 LLKeyframeMotion::JointMotionList::dumpDiagInfo() return total_size; } -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -// ****Curve classes -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - - -//----------------------------------------------------------------------------- -// ScaleCurve::ScaleCurve() -//----------------------------------------------------------------------------- -LLKeyframeMotion::ScaleCurve::ScaleCurve() -{ - mInterpolationType = LLKeyframeMotion::IT_LINEAR; - mNumKeys = 0; -} - -//----------------------------------------------------------------------------- -// ScaleCurve::~ScaleCurve() -//----------------------------------------------------------------------------- -LLKeyframeMotion::ScaleCurve::~ScaleCurve() -{ - mKeys.clear(); - mNumKeys = 0; -} - -//----------------------------------------------------------------------------- -// getValue() -//----------------------------------------------------------------------------- -LLVector3 LLKeyframeMotion::ScaleCurve::getValue(F32 time, F32 duration) -{ - LLVector3 value; - - if (mKeys.empty()) - { - value.clearVec(); - return value; - } - - key_map_t::iterator right = std::lower_bound(mKeys.begin(), mKeys.end(), time, [](const key_map_t::value_type& a, const F32 b) { return a.first < b; }); - if (right == mKeys.end()) - { - // Past last key - --right; - value = right->second.mScale; - } - else if (right == mKeys.begin() || right->first == time) - { - // Before first key or exactly on a key - value = right->second.mScale; - } - else - { - // Between two keys - key_map_t::iterator left = right; --left; - F32 index_before = left->first; - F32 index_after = right->first; - ScaleKey& scale_before = left->second; - ScaleKey& scale_after = right->second; - if (right == mKeys.end()) - { - scale_after = mLoopInKey; - index_after = duration; - } - - F32 u = (time - index_before) / (index_after - index_before); - value = interp(u, scale_before, scale_after); - } - return value; -} - -//----------------------------------------------------------------------------- -// interp() -//----------------------------------------------------------------------------- -LLVector3 LLKeyframeMotion::ScaleCurve::interp(F32 u, ScaleKey& before, ScaleKey& after) -{ - switch (mInterpolationType) - { - case IT_STEP: - return before.mScale; - - default: - case IT_LINEAR: - case IT_SPLINE: - return lerp(before.mScale, after.mScale, u); - } -} - -//----------------------------------------------------------------------------- -// RotationCurve::RotationCurve() -//----------------------------------------------------------------------------- -LLKeyframeMotion::RotationCurve::RotationCurve() -{ - mInterpolationType = LLKeyframeMotion::IT_LINEAR; - mNumKeys = 0; -} - -//----------------------------------------------------------------------------- -// RotationCurve::~RotationCurve() -//----------------------------------------------------------------------------- -LLKeyframeMotion::RotationCurve::~RotationCurve() -{ - mKeys.clear(); - mNumKeys = 0; -} - -//----------------------------------------------------------------------------- -// RotationCurve::getValue() -//----------------------------------------------------------------------------- -LLQuaternion LLKeyframeMotion::RotationCurve::getValue(F32 time, F32 duration) -{ - LLQuaternion value; - - if (mKeys.empty()) - { - value = LLQuaternion::DEFAULT; - return value; - } - - key_map_t::iterator right = std::lower_bound(mKeys.begin(), mKeys.end(), time, [](const key_map_t::value_type& a, const F32 b) { return a.first < b; }); - if (right == mKeys.end()) - { - // Past last key - --right; - value = right->second.mRotation; - } - else if (right == mKeys.begin() || right->first == time) - { - // Before first key or exactly on a key - value = right->second.mRotation; - } - else - { - // Between two keys - key_map_t::iterator left = right; --left; - F32 index_before = left->first; - F32 index_after = right->first; - RotationKey& rot_before = left->second; - RotationKey& rot_after = right->second; - if (right == mKeys.end()) - { - rot_after = mLoopInKey; - index_after = duration; - } - - F32 u = (time - index_before) / (index_after - index_before); - value = interp(u, rot_before, rot_after); - } - return value; -} - -//----------------------------------------------------------------------------- -// interp() -//----------------------------------------------------------------------------- -LLQuaternion LLKeyframeMotion::RotationCurve::interp(F32 u, RotationKey& before, RotationKey& after) -{ - switch (mInterpolationType) - { - case IT_STEP: - return before.mRotation; - - default: - case IT_LINEAR: - case IT_SPLINE: - return nlerp(u, before.mRotation, after.mRotation); - } -} - - -//----------------------------------------------------------------------------- -// PositionCurve::PositionCurve() -//----------------------------------------------------------------------------- -LLKeyframeMotion::PositionCurve::PositionCurve() -{ - mInterpolationType = LLKeyframeMotion::IT_LINEAR; - mNumKeys = 0; -} - -//----------------------------------------------------------------------------- -// PositionCurve::~PositionCurve() -//----------------------------------------------------------------------------- -LLKeyframeMotion::PositionCurve::~PositionCurve() -{ - mKeys.clear(); - mNumKeys = 0; -} - -//----------------------------------------------------------------------------- -// PositionCurve::getValue() -//----------------------------------------------------------------------------- -LLVector3 LLKeyframeMotion::PositionCurve::getValue(F32 time, F32 duration) -{ - LLVector3 value; - - if (mKeys.empty()) - { - value.clearVec(); - return value; - } - - key_map_t::iterator right = std::lower_bound(mKeys.begin(), mKeys.end(), time, [](const key_map_t::value_type& a, const F32 b) { return a.first < b; }); - if (right == mKeys.end()) - { - // Past last key - --right; - value = right->second.mPosition; - } - else if (right == mKeys.begin() || right->first == time) - { - // Before first key or exactly on a key - value = right->second.mPosition; - } - else - { - // Between two keys - key_map_t::iterator left = right; --left; - F32 index_before = left->first; - F32 index_after = right->first; - PositionKey& pos_before = left->second; - PositionKey& pos_after = right->second; - if (right == mKeys.end()) - { - pos_after = mLoopInKey; - index_after = duration; - } - - F32 u = (time - index_before) / (index_after - index_before); - value = interp(u, pos_before, pos_after); - } - - llassert(value.isFinite()); - - return value; -} - -//----------------------------------------------------------------------------- -// interp() -//----------------------------------------------------------------------------- -LLVector3 LLKeyframeMotion::PositionCurve::interp(F32 u, PositionKey& before, PositionKey& after) -{ - switch (mInterpolationType) - { - case IT_STEP: - return before.mPosition; - default: - case IT_LINEAR: - case IT_SPLINE: - return lerp(before.mPosition, after.mPosition, u); - } -} - - //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // JointMotion class @@ -433,6 +182,7 @@ LLKeyframeMotion::LLKeyframeMotion(const LLUUID &id) : LLMotion(id), mJointMotionList(NULL), mPelvisp(NULL), + mCharacter(NULL), mLastSkeletonSerialNum(0), mLastUpdateTime(0.f), mLastLoopedTime(0.f), @@ -498,9 +248,7 @@ LLMotion::LLMotionInitStatus LLKeyframeMotion::onInitialize(LLCharacter *charact // request asset mAssetStatus = ASSET_FETCHED; -#if SHOW_DEBUG LL_DEBUGS("Animation") << "Requesting data fetch for: " << mID << LL_ENDL; -#endif character_id = new LLUUID(mCharacter->getID()); gAssetStorage->getAssetData(mID, LLAssetType::AT_ANIMATION, @@ -601,9 +349,7 @@ LLMotion::LLMotionInitStatus LLKeyframeMotion::onInitialize(LLCharacter *charact return STATUS_FAILURE; } -#if SHOW_DEBUG LL_DEBUGS() << "Loading keyframe data for: " << getName() << ":" << getID() << " (" << anim_file_size << " bytes)" << LL_ENDL; -#endif LLDataPackerBinaryBuffer dp(anim_data, anim_file_size); @@ -1578,7 +1324,7 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id) } LLQuaternion::Order ro = StringToOrder("ZYX"); - rot_key.mRotation = mayaQ(rot_angles.mV[VX], rot_angles.mV[VY], rot_angles.mV[VZ], ro); + rot_key.mValue = mayaQ(rot_angles.mV[VX], rot_angles.mV[VY], rot_angles.mV[VZ], ro); } else { @@ -1604,23 +1350,24 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id) rot_vec.mV[VZ] = U16_to_F32(z, -1.f, 1.f); if (!rot_vec.isFinite()) { - LL_WARNS() << "non-finite angle in rotation key (" << k << ")" << LL_ENDL; + LL_WARNS() << "non-finite angle in rotation key (" << k << ")" + << " for animation " << asset_id << LL_ENDL; return FALSE; } - rot_key.mRotation.unpackFromVector3(rot_vec); + rot_key.mValue.unpackFromVector3(rot_vec); } - if( !(rot_key.mRotation.isFinite()) ) + if( !(rot_key.mValue.isFinite()) ) { LL_WARNS() << "non-finite angle in rotation key (" << k << ")" - << " for animation " << asset_id << LL_ENDL; + << " for animation " << asset_id << LL_ENDL; return FALSE; } - + rCurve->mKeys.emplace_back(time, rot_key); } - std::sort(rCurve->mKeys.begin(), rCurve->mKeys.end(), [](const RotationCurve::key_map_t::value_type& a, const RotationCurve::key_map_t::value_type& b) { return a.first < b.first; }); + std::sort(rCurve->mKeys.begin(), rCurve->mKeys.end(), [](const auto& a, const auto& b) { return a.first < b.first; }); //--------------------------------------------------------------------- // scan position curve header @@ -1672,16 +1419,16 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id) if (old_version) { - if (!dp.unpackVector3(pos_key.mPosition, "pos")) + if (!dp.unpackVector3(pos_key.mValue, "pos")) { LL_WARNS() << "can't read pos in position key (" << k << ")" << LL_ENDL; return FALSE; } //MAINT-6162 - pos_key.mPosition.mV[VX] = llclamp( pos_key.mPosition.mV[VX], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET); - pos_key.mPosition.mV[VY] = llclamp( pos_key.mPosition.mV[VY], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET); - pos_key.mPosition.mV[VZ] = llclamp( pos_key.mPosition.mV[VZ], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET); + pos_key.mValue.mV[VX] = llclamp( pos_key.mValue.mV[VX], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET); + pos_key.mValue.mV[VY] = llclamp( pos_key.mValue.mV[VY], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET); + pos_key.mValue.mV[VZ] = llclamp( pos_key.mValue.mV[VZ], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET); } else @@ -1704,12 +1451,12 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id) return FALSE; } - pos_key.mPosition.mV[VX] = U16_to_F32(x, -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET); - pos_key.mPosition.mV[VY] = U16_to_F32(y, -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET); - pos_key.mPosition.mV[VZ] = U16_to_F32(z, -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET); + pos_key.mValue.mV[VX] = U16_to_F32(x, -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET); + pos_key.mValue.mV[VY] = U16_to_F32(y, -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET); + pos_key.mValue.mV[VZ] = U16_to_F32(z, -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET); } - if( !(pos_key.mPosition.isFinite()) ) + if( !(pos_key.mValue.isFinite()) ) { LL_WARNS() << "non-finite position in key (" << k << ")" << " for animation " << asset_id << LL_ENDL; @@ -1720,11 +1467,11 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id) if (is_pelvis) { - joint_motion_list->mPelvisBBox.addPoint(pos_key.mPosition); + joint_motion_list->mPelvisBBox.addPoint(pos_key.mValue); } } - std::sort(pCurve->mKeys.begin(), pCurve->mKeys.end(), [](const PositionCurve::key_map_t::value_type& a, const PositionCurve::key_map_t::value_type& b) { return a.first < b.first; }); + std::sort(pCurve->mKeys.begin(), pCurve->mKeys.end(), [](const auto& a, const auto& b) { return a.first < b.first; }); joint_motion->mUsage = joint_state->getUsage(); } @@ -2018,7 +1765,7 @@ BOOL LLKeyframeMotion::serialize(LLDataPacker& dp) const U16 time_short = F32_to_U16(rot_key.mTime, 0.f, mJointMotionList->mDuration); success &= dp.packU16(time_short, "time"); - LLVector3 rot_angles = rot_key.mRotation.packToVector3(); + LLVector3 rot_angles = rot_key.mValue.packToVector3(); U16 x, y, z; rot_angles.quantize16(-1.f, 1.f, -1.f, 1.f); @@ -2041,15 +1788,15 @@ BOOL LLKeyframeMotion::serialize(LLDataPacker& dp) const success &= dp.packU16(time_short, "time"); U16 x, y, z; - pos_key.mPosition.quantize16(-LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET, -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET); - x = F32_to_U16(pos_key.mPosition.mV[VX], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET); - y = F32_to_U16(pos_key.mPosition.mV[VY], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET); - z = F32_to_U16(pos_key.mPosition.mV[VZ], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET); + pos_key.mValue.quantize16(-LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET, -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET); + x = F32_to_U16(pos_key.mValue.mV[VX], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET); + y = F32_to_U16(pos_key.mValue.mV[VY], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET); + z = F32_to_U16(pos_key.mValue.mV[VZ], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET); success &= dp.packU16(x, "pos_x"); success &= dp.packU16(y, "pos_y"); success &= dp.packU16(z, "pos_z"); - LL_DEBUGS("BVH") << " pos: t " << pos_key.mTime << " pos " << pos_key.mPosition.mV[VX] <<","<< pos_key.mPosition.mV[VY] <<","<< pos_key.mPosition.mV[VZ] << LL_ENDL; + LL_DEBUGS("BVH") << " pos: t " << pos_key.mTime << " pos " << pos_key.mValue.mV[VX] <<","<< pos_key.mValue.mV[VY] <<","<< pos_key.mValue.mV[VZ] << LL_ENDL; } } @@ -2272,9 +2019,9 @@ void LLKeyframeMotion::setLoopIn(F32 in_point) rot_curve->mLoopInKey.mTime = mJointMotionList->mLoopInPoint; scale_curve->mLoopInKey.mTime = mJointMotionList->mLoopInPoint; - pos_curve->mLoopInKey.mPosition = pos_curve->getValue(mJointMotionList->mLoopInPoint, mJointMotionList->mDuration); - rot_curve->mLoopInKey.mRotation = rot_curve->getValue(mJointMotionList->mLoopInPoint, mJointMotionList->mDuration); - scale_curve->mLoopInKey.mScale = scale_curve->getValue(mJointMotionList->mLoopInPoint, mJointMotionList->mDuration); + pos_curve->mLoopInKey.mValue = pos_curve->getValue(mJointMotionList->mLoopInPoint, mJointMotionList->mDuration); + rot_curve->mLoopInKey.mValue = rot_curve->getValue(mJointMotionList->mLoopInPoint, mJointMotionList->mDuration); + scale_curve->mLoopInKey.mValue = scale_curve->getValue(mJointMotionList->mLoopInPoint, mJointMotionList->mDuration); } } } @@ -2301,9 +2048,9 @@ void LLKeyframeMotion::setLoopOut(F32 out_point) rot_curve->mLoopOutKey.mTime = mJointMotionList->mLoopOutPoint; scale_curve->mLoopOutKey.mTime = mJointMotionList->mLoopOutPoint; - pos_curve->mLoopOutKey.mPosition = pos_curve->getValue(mJointMotionList->mLoopOutPoint, mJointMotionList->mDuration); - rot_curve->mLoopOutKey.mRotation = rot_curve->getValue(mJointMotionList->mLoopOutPoint, mJointMotionList->mDuration); - scale_curve->mLoopOutKey.mScale = scale_curve->getValue(mJointMotionList->mLoopOutPoint, mJointMotionList->mDuration); + pos_curve->mLoopOutKey.mValue = pos_curve->getValue(mJointMotionList->mLoopOutPoint, mJointMotionList->mDuration); + rot_curve->mLoopOutKey.mValue = rot_curve->getValue(mJointMotionList->mLoopOutPoint, mJointMotionList->mDuration); + scale_curve->mLoopOutKey.mValue = scale_curve->getValue(mJointMotionList->mLoopOutPoint, mJointMotionList->mDuration); } } } diff --git a/indra/llcharacter/llkeyframemotion.h b/indra/llcharacter/llkeyframemotion.h index 7cc7537a19063fcbb98059c1f0258b67702e1cce..b441c75cfb83186805da8a4e4fb477c2d57e4506 100644 --- a/indra/llcharacter/llkeyframemotion.h +++ b/indra/llcharacter/llkeyframemotion.h @@ -58,6 +58,22 @@ const S32 KEYFRAME_MOTION_SUBVERSION = 0; //----------------------------------------------------------------------------- // class LLKeyframeMotion //----------------------------------------------------------------------------- + +namespace LLKeyframeMotionLerp +{ + template<typename T> + inline T lerp(F32 t, const T& before, const T& after) + { + return ::lerp(before, after, t); + } + + template<> + inline LLQuaternion lerp(F32 t, const LLQuaternion& before, const LLQuaternion& after) + { + return nlerp(t, before, after); + } +} + class LLKeyframeMotion : public LLMotion { @@ -275,101 +291,84 @@ class LLKeyframeMotion : enum InterpolationType { IT_STEP, IT_LINEAR, IT_SPLINE }; - //------------------------------------------------------------------------- - // ScaleKey - //------------------------------------------------------------------------- - class ScaleKey - { - public: - ScaleKey() { mTime = 0.0f; } - ScaleKey(F32 time, const LLVector3 &scale) { mTime = time; mScale = scale; } - - F32 mTime; - LLVector3 mScale; - }; - - //------------------------------------------------------------------------- - // RotationKey - //------------------------------------------------------------------------- - class RotationKey + template<typename T> + struct Curve { - public: - RotationKey() { mTime = 0.0f; } - RotationKey(F32 time, const LLQuaternion &rotation) { mTime = time; mRotation = rotation; } - - F32 mTime; - LLQuaternion mRotation; - }; - - //------------------------------------------------------------------------- - // PositionKey - //------------------------------------------------------------------------- - class PositionKey - { - public: - PositionKey() { mTime = 0.0f; } - PositionKey(F32 time, const LLVector3 &position) { mTime = time; mPosition = position; } - - F32 mTime; - LLVector3 mPosition; - }; - - //------------------------------------------------------------------------- - // ScaleCurve - //------------------------------------------------------------------------- - class ScaleCurve - { - public: - ScaleCurve(); - ~ScaleCurve(); - LLVector3 getValue(F32 time, F32 duration); - LLVector3 interp(F32 u, ScaleKey& before, ScaleKey& after); - - InterpolationType mInterpolationType; - S32 mNumKeys; - typedef std::vector<std::pair<F32, ScaleKey> > key_map_t; + struct Key + { + Key() = default; + Key(F32 time, const T& value) { mTime = time; mValue = value; } + F32 mTime = 0; + T mValue; + }; + + T interp(F32 u, Key& before, Key& after) + { + switch (mInterpolationType) + { + case IT_STEP: + return before.mValue; + default: + case IT_LINEAR: + case IT_SPLINE: + return LLKeyframeMotionLerp::lerp(u, before.mValue, after.mValue); + } + } + + T getValue(F32 time, F32 duration) + { + if (mKeys.empty()) + { + return T(); + } + + T value; + typename key_map_t::iterator right = std::lower_bound(mKeys.begin(), mKeys.end(), time, [](const auto& a, const auto& b) { return a.first < b; }); + if (right == mKeys.end()) + { + // Past last key + --right; + value = right->second.mValue; + } + else if (right == mKeys.begin() || right->first == time) + { + // Before first key or exactly on a key + value = right->second.mValue; + } + else + { + // Between two keys + typename key_map_t::iterator left = right; --left; + F32 index_before = left->first; + F32 index_after = right->first; + Key& pos_before = left->second; + Key& pos_after = right->second; + if (right == mKeys.end()) + { + pos_after = mLoopInKey; + index_after = duration; + } + + F32 u = (time - index_before) / (index_after - index_before); + value = interp(u, pos_before, pos_after); + } + return value; + } + + InterpolationType mInterpolationType = LLKeyframeMotion::IT_LINEAR; + S32 mNumKeys = 0; + typedef std::vector< std::pair<F32, Key> > key_map_t; key_map_t mKeys; - ScaleKey mLoopInKey; - ScaleKey mLoopOutKey; + Key mLoopInKey; + Key mLoopOutKey; }; - //------------------------------------------------------------------------- - // RotationCurve - //------------------------------------------------------------------------- - class RotationCurve - { - public: - RotationCurve(); - ~RotationCurve(); - LLQuaternion getValue(F32 time, F32 duration); - LLQuaternion interp(F32 u, RotationKey& before, RotationKey& after); - - InterpolationType mInterpolationType; - S32 mNumKeys; - typedef std::vector<std::pair<F32, RotationKey> > key_map_t; - key_map_t mKeys; - RotationKey mLoopInKey; - RotationKey mLoopOutKey; - }; - - //------------------------------------------------------------------------- - // PositionCurve - //------------------------------------------------------------------------- - class PositionCurve - { - public: - PositionCurve(); - ~PositionCurve(); - LLVector3 getValue(F32 time, F32 duration); - LLVector3 interp(F32 u, PositionKey& before, PositionKey& after); - - InterpolationType mInterpolationType; - S32 mNumKeys; - typedef std::vector< std::pair<F32, PositionKey> > key_map_t; - key_map_t mKeys; - PositionKey mLoopInKey; - PositionKey mLoopOutKey; - }; + typedef Curve<LLVector3> ScaleCurve; + typedef ScaleCurve::Key ScaleKey; + typedef Curve<LLQuaternion> RotationCurve; + typedef RotationCurve::Key RotationKey; + typedef Curve<LLVector3> PositionCurve; + typedef PositionCurve::Key PositionKey; //------------------------------------------------------------------------- // JointMotion diff --git a/indra/llcharacter/llkeyframewalkmotion.cpp b/indra/llcharacter/llkeyframewalkmotion.cpp index 95f314577de12388bca9702e13f1a20fa15c57ff..565a009212f89c3b2a5d039f7467e77d6d46ef39 100644 --- a/indra/llcharacter/llkeyframewalkmotion.cpp +++ b/indra/llcharacter/llkeyframewalkmotion.cpp @@ -270,6 +270,7 @@ BOOL LLWalkAdjustMotion::onUpdate(F32 time, U8* joint_mask) // 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); + if(foot_speed < 0.0f) foot_speed = 0.0f; // 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);