/** * @file llkeyframemotionparam.cpp * @brief Implementation of LLKeyframeMotion class. * * $LicenseInfo:firstyear=2001&license=viewergpl$ * * Copyright (c) 2001-2007, 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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 "llkeyframemotionparam.h" #include "llcharacter.h" #include "llmath.h" #include "m3math.h" #include "lldir.h" #include "llanimationstates.h" //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // LLKeyframeMotionParam class //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // sortFunc() //----------------------------------------------------------------------------- BOOL LLKeyframeMotionParam::sortFunc(ParameterizedMotion *new_motion, ParameterizedMotion *tested_motion) { return (new_motion->second < tested_motion->second); } //----------------------------------------------------------------------------- // LLKeyframeMotionParam() // Class Constructor //----------------------------------------------------------------------------- LLKeyframeMotionParam::LLKeyframeMotionParam( const LLUUID &id) : LLMotion(id) { mDefaultKeyframeMotion = NULL; mCharacter = NULL; mEaseInDuration = 0.f; mEaseOutDuration = 0.f; mDuration = 0.f; mPriority = LLJoint::LOW_PRIORITY; } //----------------------------------------------------------------------------- // ~LLKeyframeMotionParam() // Class Destructor //----------------------------------------------------------------------------- LLKeyframeMotionParam::~LLKeyframeMotionParam() { for (U32 i = 0; i < mParameterizedMotions.length(); i++) { LLLinkedList< ParameterizedMotion > *motionList = *mParameterizedMotions.getValueAt(i); for (ParameterizedMotion* paramMotion = motionList->getFirstData(); paramMotion; paramMotion = motionList->getNextData()) { delete paramMotion->first; } delete motionList; } mParameterizedMotions.removeAll(); } //----------------------------------------------------------------------------- // LLKeyframeMotionParam::onInitialize(LLCharacter *character) //----------------------------------------------------------------------------- LLMotion::LLMotionInitStatus LLKeyframeMotionParam::onInitialize(LLCharacter *character) { mCharacter = character; if (!loadMotions()) { return STATUS_FAILURE; } for (U32 i = 0; i < mParameterizedMotions.length(); i++) { LLLinkedList< ParameterizedMotion > *motionList = *mParameterizedMotions.getValueAt(i); for (ParameterizedMotion* paramMotion = motionList->getFirstData(); paramMotion; paramMotion = motionList->getNextData()) { paramMotion->first->onInitialize(character); if (paramMotion->first->getDuration() > mEaseInDuration) { mEaseInDuration = paramMotion->first->getEaseInDuration(); } if (paramMotion->first->getEaseOutDuration() > mEaseOutDuration) { mEaseOutDuration = paramMotion->first->getEaseOutDuration(); } if (paramMotion->first->getDuration() > mDuration) { mDuration = paramMotion->first->getDuration(); } if (paramMotion->first->getPriority() > mPriority) { mPriority = paramMotion->first->getPriority(); } LLPose *pose = paramMotion->first->getPose(); mPoseBlender.addMotion(paramMotion->first); for (LLJointState *jsp = pose->getFirstJointState(); jsp; jsp = pose->getNextJointState()) { LLPose *blendedPose = mPoseBlender.getBlendedPose(); blendedPose->addJointState(jsp); } } } return STATUS_SUCCESS; } //----------------------------------------------------------------------------- // LLKeyframeMotionParam::onActivate() //----------------------------------------------------------------------------- BOOL LLKeyframeMotionParam::onActivate() { for (U32 i = 0; i < mParameterizedMotions.length(); i++) { LLLinkedList< ParameterizedMotion > *motionList = *mParameterizedMotions.getValueAt(i); for (ParameterizedMotion* paramMotion = motionList->getFirstData(); paramMotion; paramMotion = motionList->getNextData()) { paramMotion->first->activate(); } } return TRUE; } //----------------------------------------------------------------------------- // LLKeyframeMotionParam::onUpdate() //----------------------------------------------------------------------------- BOOL LLKeyframeMotionParam::onUpdate(F32 time, U8* joint_mask) { F32 weightFactor = 1.f / (F32)mParameterizedMotions.length(); U32 i; // zero out all pose weights for (i = 0; i < mParameterizedMotions.length(); i++) { LLLinkedList< ParameterizedMotion > *motionList = *mParameterizedMotions.getValueAt(i); for (ParameterizedMotion* paramMotion = motionList->getFirstData(); paramMotion; paramMotion = motionList->getNextData()) { // llinfos << "Weight for pose " << paramMotion->first->getName() << " is " << paramMotion->first->getPose()->getWeight() << llendl; paramMotion->first->getPose()->setWeight(0.f); } } for (i = 0; i < mParameterizedMotions.length(); i++) { LLLinkedList< ParameterizedMotion > *motionList = *mParameterizedMotions.getValueAt(i); std::string *paramName = mParameterizedMotions.getIndexAt(i); F32* paramValue = (F32 *)mCharacter->getAnimationData(*paramName); ParameterizedMotion* firstMotion = NULL; ParameterizedMotion* secondMotion = NULL; if (NULL == paramValue) // unexpected, but... { llwarns << "paramValue == NULL" << llendl; continue; } for (ParameterizedMotion* paramMotion = motionList->getFirstData(); paramMotion; paramMotion = motionList->getNextData()) { paramMotion->first->onUpdate(time, joint_mask); F32 distToParam = paramMotion->second - *paramValue; if ( distToParam <= 0.f) { // keep track of the motion closest to the parameter value firstMotion = paramMotion; } else { // we've passed the parameter value // so store the first motion we find as the second one we want to blend... if (firstMotion && !secondMotion ) { secondMotion = paramMotion; } //...or, if we've seen no other motion so far, make sure we blend to this only else if (!firstMotion) { firstMotion = paramMotion; secondMotion = paramMotion; } } } LLPose *firstPose; LLPose *secondPose; if (firstMotion) firstPose = firstMotion->first->getPose(); else firstPose = NULL; if (secondMotion) secondPose = secondMotion->first->getPose(); else secondPose = NULL; // now modify weight of the subanim (only if we are blending between two motions) if (firstMotion && secondMotion) { if (firstMotion == secondMotion) { firstPose->setWeight(weightFactor); } else if (firstMotion->second == secondMotion->second) { firstPose->setWeight(0.5f * weightFactor); secondPose->setWeight(0.5f * weightFactor); } else { F32 first_weight = 1.f - ((llclamp(*paramValue - firstMotion->second, 0.f, (secondMotion->second - firstMotion->second))) / (secondMotion->second - firstMotion->second)); first_weight = llclamp(first_weight, 0.f, 1.f); F32 second_weight = 1.f - first_weight; firstPose->setWeight(first_weight * weightFactor); secondPose->setWeight(second_weight * weightFactor); // llinfos << "Parameter " << *paramName << ": " << *paramValue << llendl; // llinfos << "Weights " << firstPose->getWeight() << " " << secondPose->getWeight() << llendl; } } else if (firstMotion && !secondMotion) { firstPose->setWeight(weightFactor); } } // blend poses mPoseBlender.blendAndApply(); llinfos << "Param Motion weight " << mPoseBlender.getBlendedPose()->getWeight() << llendl; return TRUE; } //----------------------------------------------------------------------------- // LLKeyframeMotionParam::onDeactivate() //----------------------------------------------------------------------------- void LLKeyframeMotionParam::onDeactivate() { for (U32 i = 0; i < mParameterizedMotions.length(); i++) { LLLinkedList< ParameterizedMotion > *motionList = *mParameterizedMotions.getValueAt(i); for (ParameterizedMotion* paramMotion = motionList->getFirstData(); paramMotion; paramMotion = motionList->getNextData()) { paramMotion->first->onDeactivate(); } } } //----------------------------------------------------------------------------- // LLKeyframeMotionParam::addKeyframeMotion() //----------------------------------------------------------------------------- BOOL LLKeyframeMotionParam::addKeyframeMotion(char *name, const LLUUID &id, char *param, F32 value) { LLMotion *newMotion = mCharacter->createMotion( id ); if (!newMotion) { return FALSE; } newMotion->setName(name); // make sure a list of motions exists for this parameter LLLinkedList< ParameterizedMotion > *motionList; if (mParameterizedMotions.getValue(param)) { motionList = *mParameterizedMotions.getValue(param); } else { motionList = new LLLinkedList< ParameterizedMotion >; motionList->setInsertBefore(sortFunc); mParameterizedMotions.addToHead(param, motionList); } // now add motion to this list ParameterizedMotion *parameterizedMotion = new ParameterizedMotion(newMotion, value); motionList->addDataSorted(parameterizedMotion); return TRUE; } //----------------------------------------------------------------------------- // LLKeyframeMotionParam::setDefaultKeyframeMotion() //----------------------------------------------------------------------------- void LLKeyframeMotionParam::setDefaultKeyframeMotion(char *name) { for (U32 i = 0; i < mParameterizedMotions.length(); i++) { LLLinkedList< ParameterizedMotion > *motionList = *mParameterizedMotions.getValueAt(i); for (ParameterizedMotion* paramMotion = motionList->getFirstData(); paramMotion; paramMotion = motionList->getNextData()) { if (paramMotion->first->getName() == name) { mDefaultKeyframeMotion = paramMotion->first; } } } } //----------------------------------------------------------------------------- // loadMotions() //----------------------------------------------------------------------------- BOOL LLKeyframeMotionParam::loadMotions() { //------------------------------------------------------------------------- // Load named file by concatenating the character prefix with the motion name. // Load data into a buffer to be parsed. //------------------------------------------------------------------------- char path[LL_MAX_PATH]; /* Flawfinder: ignore */ snprintf( path,sizeof(path), "%s_%s.llp", gDirUtilp->getExpandedFilename(LL_PATH_MOTIONS,mCharacter->getAnimationPrefix()).c_str(), getName().c_str() ); //------------------------------------------------------------------------- // open the file //------------------------------------------------------------------------- S32 fileSize = 0; apr_file_t* fp = ll_apr_file_open(path, LL_APR_R, &fileSize); if (!fp || fileSize == 0) { llinfos << "ERROR: can't open: " << path << llendl; return FALSE; } // allocate a text buffer char *text = new char[ fileSize+1 ]; if ( !text ) { llinfos << "ERROR: can't allocated keyframe text buffer." << llendl; apr_file_close(fp); return FALSE; } //------------------------------------------------------------------------- // load data from file into buffer //------------------------------------------------------------------------- bool error = false; char *p = text; while ( 1 ) { if (apr_file_eof(fp) == APR_EOF) { break; } if (apr_file_gets(p, 1024, fp) != APR_SUCCESS) { error = true; break; } while ( *(++p) ) ; } //------------------------------------------------------------------------- // close the file //------------------------------------------------------------------------- apr_file_close( fp ); //------------------------------------------------------------------------- // check for error //------------------------------------------------------------------------- llassert( p <= (text+fileSize) ); if ( error ) { llinfos << "ERROR: error while reading from " << path << llendl; delete [] text; return FALSE; } llinfos << "Loading parametric keyframe data for: " << getName() << llendl; //------------------------------------------------------------------------- // parse the text and build keyframe data structures //------------------------------------------------------------------------- p = text; S32 num; char strA[80]; /* Flawfinder: ignore */ char strB[80]; /* Flawfinder: ignore */ F32 floatA = 0.0f; //------------------------------------------------------------------------- // get priority //------------------------------------------------------------------------- BOOL isFirstMotion = TRUE; num = sscanf(p, "%79s %79s %f", strA, strB, &floatA); /* Flawfinder: ignore */ while(1) { if (num == 0 || num == EOF) break; if ((num != 3)) { llinfos << "WARNING: can't read parametric motion" << llendl; delete [] text; return FALSE; } addKeyframeMotion(strA, gAnimLibrary.stringToAnimState(strA), strB, floatA); if (isFirstMotion) { isFirstMotion = FALSE; setDefaultKeyframeMotion(strA); } p = strstr(p, "\n"); if (!p) { break; } p++; num = sscanf(p, "%79s %79s %f", strA, strB, &floatA); /* Flawfinder: ignore */ } delete [] text; return TRUE; } // End