//----------------------------------------------------------------------------- // LLKeyframeMotionParam::onInitialize(LLCharacter *character) //----------------------------------------------------------------------------- LLMotion::LLMotionInitStatus LLKeyframeMotionParam::onInitialize(LLCharacter *character) { mCharacter = character; if (!loadMotions()) { return STATUS_FAILURE; } for (motion_map_t::iterator iter = mParameterizedMotions.begin(); iter != mParameterizedMotions.end(); ++iter) { motion_list_t& motionList = iter->second; for (motion_list_t::iterator iter2 = motionList.begin(); iter2 != motionList.end(); ++iter2) { const ParameterizedMotion& paramMotion = *iter2; 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; }
//----------------------------------------------------------------------------- // deactivateMotion() //----------------------------------------------------------------------------- BOOL LLMotionController::deactivateMotion(LLMotion *motion, bool remove_weight) { if( remove_weight ) { // immediately remove pose weighting instead of letting it time out LLPose *posep = motion->getPose(); posep->setWeight(0.f); } motion->deactivate(); mActiveMotions.remove(motion); return TRUE; }
//----------------------------------------------------------------------------- // 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; }
//----------------------------------------------------------------------------- // LLKeyframeStandMotion::onInitialize() //----------------------------------------------------------------------------- LLMotion::LLMotionInitStatus LLKeyframeStandMotion::onInitialize(LLCharacter *character) { // save character pointer for later use mCharacter = character; mFlipFeet = FALSE; // load keyframe data, setup pose and joint states LLMotion::LLMotionInitStatus status = LLKeyframeMotion::onInitialize(character); if ( status == STATUS_FAILURE ) { return status; } // find the necessary joint states LLPose *pose = getPose(); mPelvisState = pose->findJointState("mPelvis"); mHipLeftState = pose->findJointState("mHipLeft"); mKneeLeftState = pose->findJointState("mKneeLeft"); mAnkleLeftState = pose->findJointState("mAnkleLeft"); mHipRightState = pose->findJointState("mHipRight"); mKneeRightState = pose->findJointState("mKneeRight"); mAnkleRightState = pose->findJointState("mAnkleRight"); if ( !mPelvisState || !mHipLeftState || !mKneeLeftState || !mAnkleLeftState || !mHipRightState || !mKneeRightState || !mAnkleRightState ) { llinfos << getName() << ": Can't find necessary joint states" << llendl; return STATUS_FAILURE; } return STATUS_SUCCESS; }
void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_type) { BOOL update_result = TRUE; U8 last_joint_signature[LL_CHARACTER_MAX_JOINTS]; memset(&last_joint_signature, 0, sizeof(U8) * LL_CHARACTER_MAX_JOINTS); // iterate through active motions in chronological order for (motion_list_t::iterator iter = mActiveMotions.begin(); iter != mActiveMotions.end(); ) { motion_list_t::iterator curiter = iter++; LLMotion* motionp = *curiter; if (motionp->getBlendType() != anim_type) { continue; } BOOL update_motion = FALSE; if (motionp->getPose()->getWeight() < 1.f) { update_motion = TRUE; } else { // NUM_JOINT_SIGNATURE_STRIDES should be multiple of 4 for (S32 i = 0; i < NUM_JOINT_SIGNATURE_STRIDES; i++) { U32 *current_signature = (U32*)&(mJointSignature[0][i * 4]); U32 test_signature = *(U32*)&(motionp->mJointSignature[0][i * 4]); if ((*current_signature | test_signature) > (*current_signature)) { *current_signature |= test_signature; update_motion = TRUE; } *((U32*)&last_joint_signature[i * 4]) = *(U32*)&(mJointSignature[1][i * 4]); current_signature = (U32*)&(mJointSignature[1][i * 4]); test_signature = *(U32*)&(motionp->mJointSignature[1][i * 4]); if ((*current_signature | test_signature) > (*current_signature)) { *current_signature |= test_signature; update_motion = TRUE; } } } if (!update_motion) { updateIdleMotion(motionp); continue; } LLPose *posep = motionp->getPose(); // only filter by LOD after running every animation at least once (to prime the avatar state) if (mHasRunOnce && motionp->getMinPixelArea() > mCharacter->getPixelArea()) { motionp->fadeOut(); //should we notify the simulator that this motion should be stopped (check even if skipped by LOD logic) if (mAnimTime > motionp->mSendStopTimestamp) { // notify character of timed stop event on first iteration past sendstoptimestamp // this will only be called when an animation stops itself (runs out of time) if (mLastTime <= motionp->mSendStopTimestamp) { mCharacter->requestStopMotion( motionp ); stopMotionInstance(motionp, FALSE); } } if (motionp->getFadeWeight() < 0.01f) { if (motionp->isStopped() && mAnimTime > motionp->getStopTime() + motionp->getEaseOutDuration()) { posep->setWeight(0.f); deactivateMotionInstance(motionp); } continue; } } else { motionp->fadeIn(); } //********************** // MOTION INACTIVE //********************** if (motionp->isStopped() && mAnimTime > motionp->getStopTime() + motionp->getEaseOutDuration()) { // this motion has gone on too long, deactivate it // did we have a chance to stop it? if (mLastTime <= motionp->getStopTime()) { // if not, let's stop it this time through and deactivate it the next posep->setWeight(motionp->getFadeWeight()); motionp->onUpdate(motionp->getStopTime() - motionp->mActivationTimestamp, last_joint_signature); } else { posep->setWeight(0.f); deactivateMotionInstance(motionp); continue; } } //********************** // MOTION EASE OUT //********************** else if (motionp->isStopped() && mAnimTime > motionp->getStopTime()) { // is this the first iteration in the ease out phase? if (mLastTime <= motionp->getStopTime()) { // store residual weight for this motion motionp->mResidualWeight = motionp->getPose()->getWeight(); } if (motionp->getEaseOutDuration() == 0.f) { posep->setWeight(0.f); } else { posep->setWeight(motionp->getFadeWeight() * motionp->mResidualWeight * cubic_step(1.f - ((mAnimTime - motionp->getStopTime()) / motionp->getEaseOutDuration()))); } // perform motion update update_result = motionp->onUpdate(mAnimTime - motionp->mActivationTimestamp, last_joint_signature); } //********************** // MOTION ACTIVE //********************** else if (mAnimTime > motionp->mActivationTimestamp + motionp->getEaseInDuration()) { posep->setWeight(motionp->getFadeWeight()); //should we notify the simulator that this motion should be stopped? if (mAnimTime > motionp->mSendStopTimestamp) { // notify character of timed stop event on first iteration past sendstoptimestamp // this will only be called when an animation stops itself (runs out of time) if (mLastTime <= motionp->mSendStopTimestamp) { mCharacter->requestStopMotion( motionp ); stopMotionInstance(motionp, FALSE); } } // perform motion update { LLFastTimer t(FTM_MOTION_ON_UPDATE); update_result = motionp->onUpdate(mAnimTime - motionp->mActivationTimestamp, last_joint_signature); } } //********************** // MOTION EASE IN //********************** else if (mAnimTime >= motionp->mActivationTimestamp) { if (mLastTime < motionp->mActivationTimestamp) { motionp->mResidualWeight = motionp->getPose()->getWeight(); } if (motionp->getEaseInDuration() == 0.f) { posep->setWeight(motionp->getFadeWeight()); } else { // perform motion update posep->setWeight(motionp->getFadeWeight() * motionp->mResidualWeight + (1.f - motionp->mResidualWeight) * cubic_step((mAnimTime - motionp->mActivationTimestamp) / motionp->getEaseInDuration())); } // perform motion update update_result = motionp->onUpdate(mAnimTime - motionp->mActivationTimestamp, last_joint_signature); } else { posep->setWeight(0.f); update_result = motionp->onUpdate(0.f, last_joint_signature); } // allow motions to deactivate themselves if (!update_result) { if (!motionp->isStopped() || motionp->getStopTime() > mAnimTime) { // animation has stopped itself due to internal logic // propagate this to the network // as not all viewers are guaranteed to have access to the same logic mCharacter->requestStopMotion( motionp ); stopMotionInstance(motionp, FALSE); } } // even if onupdate returns FALSE, add this motion in to the blend one last time mPoseBlender.addMotion(motionp); } }
//----------------------------------------------------------------------------- // LLKeyframeMotionParam::onUpdate() //----------------------------------------------------------------------------- BOOL LLKeyframeMotionParam::onUpdate(F32 time, U8* joint_mask) { F32 weightFactor = 1.f / (F32)mParameterizedMotions.size(); // zero out all pose weights for (motion_map_t::iterator iter = mParameterizedMotions.begin(); iter != mParameterizedMotions.end(); ++iter) { motion_list_t& motionList = iter->second; for (motion_list_t::iterator iter2 = motionList.begin(); iter2 != motionList.end(); ++iter2) { const ParameterizedMotion& paramMotion = *iter2; // llinfos << "Weight for pose " << paramMotion.mMotion->getName() << " is " << paramMotion.mMotion->getPose()->getWeight() << llendl; paramMotion.mMotion->getPose()->setWeight(0.f); } } for (motion_map_t::iterator iter = mParameterizedMotions.begin(); iter != mParameterizedMotions.end(); ++iter) { const std::string& paramName = iter->first; F32* paramValue = (F32 *)mCharacter->getAnimationData(paramName); if (NULL == paramValue) // unexpected, but... { llwarns << "paramValue == NULL" << llendl; continue; } // DANGER! Do not modify mParameterizedMotions while using these pointers! const ParameterizedMotion* firstMotion = NULL; const ParameterizedMotion* secondMotion = NULL; motion_list_t& motionList = iter->second; for (motion_list_t::iterator iter2 = motionList.begin(); iter2 != motionList.end(); ++iter2) { const ParameterizedMotion& paramMotion = *iter2; 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); // 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; }