/** * @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) { 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(); ) { 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(); }