577 lines
16 KiB
C++
577 lines
16 KiB
C++
/**
|
|
* @file llpose.cpp
|
|
* @brief Implementation of LLPose class.
|
|
*
|
|
* $LicenseInfo:firstyear=2001&license=viewergpl$
|
|
*
|
|
* Copyright (c) 2001-2009, Linden Research, Inc.
|
|
*
|
|
* 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
|
|
*
|
|
* 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
|
|
*
|
|
* 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.
|
|
*
|
|
* 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$
|
|
*/
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Header Files
|
|
//-----------------------------------------------------------------------------
|
|
#include "linden_common.h"
|
|
|
|
#include "llpose.h"
|
|
|
|
#include "llmotion.h"
|
|
#include "llmath.h"
|
|
#include "llstl.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Static
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// LLPose
|
|
//-----------------------------------------------------------------------------
|
|
LLPose::~LLPose()
|
|
{
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// getFirstJointState()
|
|
//-----------------------------------------------------------------------------
|
|
LLJointState* LLPose::getFirstJointState()
|
|
{
|
|
mListIter = mJointMap.begin();
|
|
if (mListIter == mJointMap.end())
|
|
{
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
return mListIter->second;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// getNextJointState()
|
|
//-----------------------------------------------------------------------------
|
|
LLJointState *LLPose::getNextJointState()
|
|
{
|
|
mListIter++;
|
|
if (mListIter == mJointMap.end())
|
|
{
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
return mListIter->second;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// addJointState()
|
|
//-----------------------------------------------------------------------------
|
|
BOOL LLPose::addJointState(const LLPointer<LLJointState>& jointState)
|
|
{
|
|
if (mJointMap.find(jointState->getJoint()->getName()) == mJointMap.end())
|
|
{
|
|
mJointMap[jointState->getJoint()->getName()] = jointState;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// removeJointState()
|
|
//-----------------------------------------------------------------------------
|
|
BOOL LLPose::removeJointState(const LLPointer<LLJointState>& jointState)
|
|
{
|
|
mJointMap.erase(jointState->getJoint()->getName());
|
|
return TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// removeAllJointStates()
|
|
//-----------------------------------------------------------------------------
|
|
BOOL LLPose::removeAllJointStates()
|
|
{
|
|
mJointMap.clear();
|
|
return TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// findJointState()
|
|
//-----------------------------------------------------------------------------
|
|
LLJointState* LLPose::findJointState(LLJoint *joint)
|
|
{
|
|
joint_map_iterator iter = mJointMap.find(joint->getName());
|
|
|
|
if (iter == mJointMap.end())
|
|
{
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
return iter->second;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// findJointState()
|
|
//-----------------------------------------------------------------------------
|
|
LLJointState* LLPose::findJointState(const std::string &name)
|
|
{
|
|
joint_map_iterator iter = mJointMap.find(name);
|
|
|
|
if (iter == mJointMap.end())
|
|
{
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
return iter->second;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// setWeight()
|
|
//-----------------------------------------------------------------------------
|
|
void LLPose::setWeight(F32 weight)
|
|
{
|
|
joint_map_iterator iter;
|
|
for(iter = mJointMap.begin();
|
|
iter != mJointMap.end();
|
|
++iter)
|
|
{
|
|
// <edit>
|
|
// there was a crash here
|
|
// </edit>
|
|
iter->second->setWeight(weight);
|
|
}
|
|
mWeight = weight;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// getWeight()
|
|
//-----------------------------------------------------------------------------
|
|
F32 LLPose::getWeight() const
|
|
{
|
|
return mWeight;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// getNumJointStates()
|
|
//-----------------------------------------------------------------------------
|
|
S32 LLPose::getNumJointStates() const
|
|
{
|
|
return (S32)mJointMap.size();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// LLJointStateBlender
|
|
//-----------------------------------------------------------------------------
|
|
|
|
LLJointStateBlender::LLJointStateBlender()
|
|
{
|
|
for(S32 i = 0; i < JSB_NUM_JOINT_STATES; i++)
|
|
{
|
|
mJointStates[i] = NULL;
|
|
mPriorities[i] = S32_MIN;
|
|
mAdditiveBlends[i] = FALSE;
|
|
}
|
|
}
|
|
|
|
LLJointStateBlender::~LLJointStateBlender()
|
|
{
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// addJointState()
|
|
//-----------------------------------------------------------------------------
|
|
BOOL LLJointStateBlender::addJointState(const LLPointer<LLJointState>& joint_state, S32 priority, BOOL additive_blend)
|
|
{
|
|
llassert(joint_state);
|
|
|
|
if (!joint_state->getJoint())
|
|
// this joint state doesn't point to an actual joint, so we don't care about applying it
|
|
return FALSE;
|
|
|
|
for(S32 i = 0; i < JSB_NUM_JOINT_STATES; i++)
|
|
{
|
|
if (mJointStates[i].isNull())
|
|
{
|
|
mJointStates[i] = joint_state;
|
|
mPriorities[i] = priority;
|
|
mAdditiveBlends[i] = additive_blend;
|
|
return TRUE;
|
|
}
|
|
else if (priority > mPriorities[i])
|
|
{
|
|
// we're at a higher priority than the current joint state in this slot
|
|
// so shift everyone over
|
|
// previous joint states (newer motions) with same priority should stay in place
|
|
for (S32 j = JSB_NUM_JOINT_STATES - 1; j > i; j--)
|
|
{
|
|
mJointStates[j] = mJointStates[j - 1];
|
|
mPriorities[j] = mPriorities[j - 1];
|
|
mAdditiveBlends[j] = mAdditiveBlends[j - 1];
|
|
}
|
|
// now store ourselves in this slot
|
|
mJointStates[i] = joint_state;
|
|
mPriorities[i] = priority;
|
|
mAdditiveBlends[i] = additive_blend;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// blendJointStates()
|
|
//-----------------------------------------------------------------------------
|
|
void LLJointStateBlender::blendJointStates(BOOL apply_now)
|
|
{
|
|
// we need at least one joint to blend
|
|
// if there is one, it will be in slot zero according to insertion logic
|
|
// instead of resetting joint state to default, just leave it unchanged from last frame
|
|
if (mJointStates[0].isNull())
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLJoint* target_joint = apply_now ? mJointStates[0]->getJoint() : &mJointCache;
|
|
|
|
const S32 POS_WEIGHT = 0;
|
|
const S32 ROT_WEIGHT = 1;
|
|
const S32 SCALE_WEIGHT = 2;
|
|
|
|
F32 sum_weights[3];
|
|
U32 sum_usage = 0;
|
|
|
|
LLVector3 blended_pos = target_joint->getPosition();
|
|
LLQuaternion blended_rot = target_joint->getRotation();
|
|
LLVector3 blended_scale = target_joint->getScale();
|
|
|
|
LLVector3 added_pos;
|
|
LLQuaternion added_rot;
|
|
LLVector3 added_scale;
|
|
|
|
//S32 joint_state_index;
|
|
|
|
sum_weights[POS_WEIGHT] = 0.f;
|
|
sum_weights[ROT_WEIGHT] = 0.f;
|
|
sum_weights[SCALE_WEIGHT] = 0.f;
|
|
|
|
for(S32 joint_state_index = 0;
|
|
joint_state_index < JSB_NUM_JOINT_STATES && mJointStates[joint_state_index].notNull();
|
|
joint_state_index++)
|
|
{
|
|
LLJointState* jsp = mJointStates[joint_state_index];
|
|
U32 current_usage = jsp->getUsage();
|
|
F32 current_weight = jsp->getWeight();
|
|
|
|
if (current_weight == 0.f)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (mAdditiveBlends[joint_state_index])
|
|
{
|
|
if(current_usage & LLJointState::POS)
|
|
{
|
|
F32 new_weight_sum = llmin(1.f, current_weight + sum_weights[POS_WEIGHT]);
|
|
|
|
// add in pos for this jointstate modulated by weight
|
|
added_pos += jsp->getPosition() * (new_weight_sum - sum_weights[POS_WEIGHT]);
|
|
}
|
|
|
|
if(current_usage & LLJointState::SCALE)
|
|
{
|
|
F32 new_weight_sum = llmin(1.f, current_weight + sum_weights[SCALE_WEIGHT]);
|
|
|
|
// add in scale for this jointstate modulated by weight
|
|
added_scale += jsp->getScale() * (new_weight_sum - sum_weights[SCALE_WEIGHT]);
|
|
}
|
|
|
|
if (current_usage & LLJointState::ROT)
|
|
{
|
|
F32 new_weight_sum = llmin(1.f, current_weight + sum_weights[ROT_WEIGHT]);
|
|
|
|
// add in rotation for this jointstate modulated by weight
|
|
added_rot = nlerp((new_weight_sum - sum_weights[ROT_WEIGHT]), added_rot, jsp->getRotation()) * added_rot;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// blend two jointstates together
|
|
|
|
// blend position
|
|
if(current_usage & LLJointState::POS)
|
|
{
|
|
if(sum_usage & LLJointState::POS)
|
|
{
|
|
F32 new_weight_sum = llmin(1.f, current_weight + sum_weights[POS_WEIGHT]);
|
|
|
|
// blend positions from both
|
|
blended_pos = lerp(jsp->getPosition(), blended_pos, sum_weights[POS_WEIGHT] / new_weight_sum);
|
|
sum_weights[POS_WEIGHT] = new_weight_sum;
|
|
}
|
|
else
|
|
{
|
|
// copy position from current
|
|
blended_pos = jsp->getPosition();
|
|
sum_weights[POS_WEIGHT] = current_weight;
|
|
}
|
|
}
|
|
|
|
// now do scale
|
|
if(current_usage & LLJointState::SCALE)
|
|
{
|
|
if(sum_usage & LLJointState::SCALE)
|
|
{
|
|
F32 new_weight_sum = llmin(1.f, current_weight + sum_weights[SCALE_WEIGHT]);
|
|
|
|
// blend scales from both
|
|
blended_scale = lerp(jsp->getScale(), blended_scale, sum_weights[SCALE_WEIGHT] / new_weight_sum);
|
|
sum_weights[SCALE_WEIGHT] = new_weight_sum;
|
|
}
|
|
else
|
|
{
|
|
// copy scale from current
|
|
blended_scale = jsp->getScale();
|
|
sum_weights[SCALE_WEIGHT] = current_weight;
|
|
}
|
|
}
|
|
|
|
// rotation
|
|
if (current_usage & LLJointState::ROT)
|
|
{
|
|
if(sum_usage & LLJointState::ROT)
|
|
{
|
|
F32 new_weight_sum = llmin(1.f, current_weight + sum_weights[ROT_WEIGHT]);
|
|
|
|
// blend rotations from both
|
|
blended_rot = nlerp(sum_weights[ROT_WEIGHT] / new_weight_sum, jsp->getRotation(), blended_rot);
|
|
sum_weights[ROT_WEIGHT] = new_weight_sum;
|
|
}
|
|
else
|
|
{
|
|
// copy rotation from current
|
|
blended_rot = jsp->getRotation();
|
|
sum_weights[ROT_WEIGHT] = current_weight;
|
|
}
|
|
}
|
|
|
|
// update resulting usage mask
|
|
sum_usage = sum_usage | current_usage;
|
|
}
|
|
}
|
|
|
|
if (!added_scale.isFinite())
|
|
{
|
|
added_scale.clearVec();
|
|
}
|
|
|
|
if (!blended_scale.isFinite())
|
|
{
|
|
blended_scale.setVec(1,1,1);
|
|
}
|
|
|
|
// apply transforms
|
|
target_joint->setPosition(blended_pos + added_pos);
|
|
target_joint->setScale(blended_scale + added_scale);
|
|
target_joint->setRotation(added_rot * blended_rot);
|
|
|
|
if (apply_now)
|
|
{
|
|
// now clear joint states
|
|
for(S32 i = 0; i < JSB_NUM_JOINT_STATES; i++)
|
|
{
|
|
mJointStates[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// interpolate()
|
|
//-----------------------------------------------------------------------------
|
|
void LLJointStateBlender::interpolate(F32 u)
|
|
{
|
|
// only interpolate if we have a joint state
|
|
if (!mJointStates[0])
|
|
{
|
|
return;
|
|
}
|
|
LLJoint* target_joint = mJointStates[0]->getJoint();
|
|
|
|
if (!target_joint)
|
|
{
|
|
return;
|
|
}
|
|
|
|
target_joint->setPosition(lerp(target_joint->getPosition(), mJointCache.getPosition(), u));
|
|
target_joint->setScale(lerp(target_joint->getScale(), mJointCache.getScale(), u));
|
|
target_joint->setRotation(nlerp(u, target_joint->getRotation(), mJointCache.getRotation()));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// clear()
|
|
//-----------------------------------------------------------------------------
|
|
void LLJointStateBlender::clear()
|
|
{
|
|
// now clear joint states
|
|
for(S32 i = 0; i < JSB_NUM_JOINT_STATES; i++)
|
|
{
|
|
mJointStates[i] = NULL;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// resetCachedJoint()
|
|
//-----------------------------------------------------------------------------
|
|
void LLJointStateBlender::resetCachedJoint()
|
|
{
|
|
if (!mJointStates[0])
|
|
{
|
|
return;
|
|
}
|
|
LLJoint* source_joint = mJointStates[0]->getJoint();
|
|
mJointCache.setPosition(source_joint->getPosition());
|
|
mJointCache.setScale(source_joint->getScale());
|
|
mJointCache.setRotation(source_joint->getRotation());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// LLPoseBlender
|
|
//-----------------------------------------------------------------------------
|
|
|
|
LLPoseBlender::LLPoseBlender()
|
|
: mNextPoseSlot(0)
|
|
{
|
|
}
|
|
|
|
LLPoseBlender::~LLPoseBlender()
|
|
{
|
|
for_each(mJointStateBlenderPool.begin(), mJointStateBlenderPool.end(), DeletePairedPointer());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// addMotion()
|
|
//-----------------------------------------------------------------------------
|
|
BOOL LLPoseBlender::addMotion(LLMotion* motion)
|
|
{
|
|
LLPose* pose = motion->getPose();
|
|
|
|
for(LLJointState* jsp = pose->getFirstJointState(); jsp; jsp = pose->getNextJointState())
|
|
{
|
|
LLJoint *jointp = jsp->getJoint();
|
|
LLJointStateBlender* joint_blender;
|
|
if (mJointStateBlenderPool.find(jointp) == mJointStateBlenderPool.end())
|
|
{
|
|
// this is the first time we are animating this joint
|
|
// so create new jointblender and add it to our pool
|
|
joint_blender = new LLJointStateBlender();
|
|
mJointStateBlenderPool[jointp] = joint_blender;
|
|
}
|
|
else
|
|
{
|
|
joint_blender = mJointStateBlenderPool[jointp];
|
|
}
|
|
|
|
if (jsp->getPriority() == LLJoint::USE_MOTION_PRIORITY)
|
|
{
|
|
joint_blender->addJointState(jsp, motion->getPriority(), motion->getBlendType() == LLMotion::ADDITIVE_BLEND);
|
|
}
|
|
else
|
|
{
|
|
joint_blender->addJointState(jsp, jsp->getPriority(), motion->getBlendType() == LLMotion::ADDITIVE_BLEND);
|
|
}
|
|
|
|
// add it to our list of active blenders
|
|
if (std::find(mActiveBlenders.begin(), mActiveBlenders.end(), joint_blender) == mActiveBlenders.end())
|
|
{
|
|
mActiveBlenders.push_front(joint_blender);
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// blendAndApply()
|
|
//-----------------------------------------------------------------------------
|
|
void LLPoseBlender::blendAndApply()
|
|
{
|
|
for (blender_list_t::iterator iter = mActiveBlenders.begin();
|
|
iter != mActiveBlenders.end(); ++iter)
|
|
{
|
|
LLJointStateBlender* jsbp = *iter;
|
|
jsbp->blendJointStates();
|
|
}
|
|
|
|
// we're done now so there are no more active blenders for this frame
|
|
mActiveBlenders.clear();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// blendAndCache()
|
|
//-----------------------------------------------------------------------------
|
|
void LLPoseBlender::blendAndCache(BOOL reset_cached_joints)
|
|
{
|
|
for (blender_list_t::iterator iter = mActiveBlenders.begin();
|
|
iter != mActiveBlenders.end(); ++iter)
|
|
{
|
|
LLJointStateBlender* jsbp = *iter;
|
|
if (reset_cached_joints)
|
|
{
|
|
jsbp->resetCachedJoint();
|
|
}
|
|
jsbp->blendJointStates(FALSE);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// interpolate()
|
|
//-----------------------------------------------------------------------------
|
|
void LLPoseBlender::interpolate(F32 u)
|
|
{
|
|
for (blender_list_t::iterator iter = mActiveBlenders.begin();
|
|
iter != mActiveBlenders.end(); ++iter)
|
|
{
|
|
LLJointStateBlender* jsbp = *iter;
|
|
jsbp->interpolate(u);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// clearBlenders()
|
|
//-----------------------------------------------------------------------------
|
|
void LLPoseBlender::clearBlenders()
|
|
{
|
|
for (blender_list_t::iterator iter = mActiveBlenders.begin();
|
|
iter != mActiveBlenders.end(); ++iter)
|
|
{
|
|
LLJointStateBlender* jsbp = *iter;
|
|
jsbp->clear();
|
|
}
|
|
|
|
mActiveBlenders.clear();
|
|
}
|
|
|