phoenix-firestorm/indra/llcharacter/llkeyframemotionparam.cpp

443 lines
13 KiB
C++

/**
* @file llkeyframemotionparam.cpp
* @brief Implementation of LLKeyframeMotion class.
*
* Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
* $License$
*/
//-----------------------------------------------------------------------------
// 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)
{
mJointStates = NULL;
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;
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