/** * @file llkeyframemotionparam.cpp * @brief Implementation of LLKeyframeMotion class. * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/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 //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // 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 (motion_map_t::value_type& motion_pair : mParameterizedMotions) { motion_list_t& motionList = motion_pair.second; for (const ParameterizedMotion& paramMotion : motionList) { delete paramMotion.mMotion; } motionList.clear(); } mParameterizedMotions.clear(); } //----------------------------------------------------------------------------- // LLKeyframeMotionParam::onInitialize(LLCharacter *character) //----------------------------------------------------------------------------- LLMotion::LLMotionInitStatus LLKeyframeMotionParam::onInitialize(LLCharacter *character) { mCharacter = character; if (!loadMotions()) { return STATUS_FAILURE; } for (motion_map_t::value_type& motion_pair : mParameterizedMotions) { motion_list_t& motionList = motion_pair.second; for (const ParameterizedMotion& paramMotion : motionList) { LLMotion* motion = paramMotion.mMotion; motion->onInitialize(character); if (motion->getDuration() > mEaseInDuration) { mEaseInDuration = motion->getEaseInDuration(); } if (motion->getEaseOutDuration() > mEaseOutDuration) { mEaseOutDuration = motion->getEaseOutDuration(); } if (motion->getDuration() > mDuration) { mDuration = motion->getDuration(); } if (motion->getPriority() > mPriority) { mPriority = motion->getPriority(); } LLPose *pose = motion->getPose(); mPoseBlender.addMotion(motion); 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 (motion_map_t::value_type& motion_pair : mParameterizedMotions) { motion_list_t& motionList = motion_pair.second; for (const ParameterizedMotion& paramMotion : motionList) { paramMotion.mMotion->activate(mActivationTimestamp); } } return true; } //----------------------------------------------------------------------------- // LLKeyframeMotionParam::onUpdate() //----------------------------------------------------------------------------- bool LLKeyframeMotionParam::onUpdate(F32 time, U8* joint_mask) { LL_PROFILE_ZONE_SCOPED; F32 weightFactor = 1.f / (F32)mParameterizedMotions.size(); // zero out all pose weights for (motion_map_t::value_type& motion_pair : mParameterizedMotions) { motion_list_t& motionList = motion_pair.second; for (const ParameterizedMotion& paramMotion : motionList) { // LL_INFOS() << "Weight for pose " << paramMotion.mMotion->getName() << " is " << paramMotion.mMotion->getPose()->getWeight() << LL_ENDL; paramMotion.mMotion->getPose()->setWeight(0.f); } } for (motion_map_t::value_type& motion_pair : mParameterizedMotions) { const std::string& paramName = motion_pair.first; F32* paramValue = (F32 *)mCharacter->getAnimationData(paramName); if (NULL == paramValue) // unexpected, but... { LL_WARNS() << "paramValue == NULL" << LL_ENDL; continue; } // DANGER! Do not modify mParameterizedMotions while using these pointers! const ParameterizedMotion* firstMotion = NULL; const ParameterizedMotion* secondMotion = NULL; motion_list_t& motionList = motion_pair.second; for (const ParameterizedMotion& paramMotion : motionList) { paramMotion.mMotion->onUpdate(time, joint_mask); F32 distToParam = paramMotion.mParam - *paramValue; if ( distToParam <= 0.f) { // keep track of the motion closest to the parameter value firstMotion = ¶mMotion; } 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 = ¶mMotion; } //...or, if we've seen no other motion so far, make sure we blend to this only else if (!firstMotion) { firstMotion = ¶mMotion; secondMotion = ¶mMotion; } } } LLPose *firstPose; LLPose *secondPose; if (firstMotion) firstPose = firstMotion->mMotion->getPose(); else firstPose = NULL; if (secondMotion) secondPose = secondMotion->mMotion->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->mParam == secondMotion->mParam) { firstPose->setWeight(0.5f * weightFactor); secondPose->setWeight(0.5f * weightFactor); } else { F32 first_weight = 1.f - ((llclamp(*paramValue - firstMotion->mParam, 0.f, (secondMotion->mParam - firstMotion->mParam))) / (secondMotion->mParam - firstMotion->mParam)); 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); // LL_INFOS() << "Parameter " << *paramName << ": " << *paramValue << LL_ENDL; // LL_INFOS() << "Weights " << firstPose->getWeight() << " " << secondPose->getWeight() << LL_ENDL; } } else if (firstMotion && !secondMotion) { firstPose->setWeight(weightFactor); } } // blend poses mPoseBlender.blendAndApply(); LL_INFOS() << "Param Motion weight " << mPoseBlender.getBlendedPose()->getWeight() << LL_ENDL; return true; } //----------------------------------------------------------------------------- // LLKeyframeMotionParam::onDeactivate() //----------------------------------------------------------------------------- void LLKeyframeMotionParam::onDeactivate() { for (motion_map_t::value_type& motion_pair : mParameterizedMotions) { motion_list_t& motionList = motion_pair.second; for (const ParameterizedMotion& paramMotion : motionList) { paramMotion.mMotion->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); // now add motion to this list mParameterizedMotions[param].insert(ParameterizedMotion(newMotion, value)); return true; } //----------------------------------------------------------------------------- // LLKeyframeMotionParam::setDefaultKeyframeMotion() //----------------------------------------------------------------------------- void LLKeyframeMotionParam::setDefaultKeyframeMotion(char *name) { for (motion_map_t::value_type& motion_pair : mParameterizedMotions) { motion_list_t& motionList = motion_pair.second; for (const ParameterizedMotion& paramMotion : motionList) { if (paramMotion.mMotion->getName() == name) { mDefaultKeyframeMotion = paramMotion.mMotion; } } } } //----------------------------------------------------------------------------- // loadMotions() //----------------------------------------------------------------------------- bool LLKeyframeMotionParam::loadMotions() { //------------------------------------------------------------------------- // Load named file by concatenating the character prefix with the motion name. // Load data into a buffer to be parsed. //------------------------------------------------------------------------- //std::string path = gDirUtilp->getExpandedFilename(LL_PATH_MOTIONS,mCharacter->getAnimationPrefix()) // + "_" + getName() + ".llp"; //RN: deprecated unused reference to "motion" directory std::string path; //------------------------------------------------------------------------- // open the file //------------------------------------------------------------------------- S32 fileSize = 0; LLAPRFile infile ; infile.open(path, LL_APR_R, NULL, &fileSize); apr_file_t* fp = infile.getFileHandle() ; if (!fp || fileSize == 0) { LL_INFOS() << "ERROR: can't open: " << path << LL_ENDL; return false; } // allocate a text buffer std::vector text(fileSize+1); //------------------------------------------------------------------------- // load data from file into buffer //------------------------------------------------------------------------- bool error = false; char *p = &text[0]; 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 //------------------------------------------------------------------------- infile.close(); //------------------------------------------------------------------------- // check for error //------------------------------------------------------------------------- llassert( p <= (&text[0] + fileSize) ); if ( error ) { LL_INFOS() << "ERROR: error while reading from " << path << LL_ENDL; return false; } LL_INFOS() << "Loading parametric keyframe data for: " << getName() << LL_ENDL; //------------------------------------------------------------------------- // parse the text and build keyframe data structures //------------------------------------------------------------------------- p = &text[0]; 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)) { LL_INFOS() << "WARNING: can't read parametric motion" << LL_ENDL; return false; } addKeyframeMotion(strA, gAnimLibrary.stringToAnimState(std::string(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 */ } return true; } // End