//----------------------------------------------------------------------------- // updateLoadingMotions() //----------------------------------------------------------------------------- void LLMotionController::updateLoadingMotions() { // query pending motions for completion for (motion_set_t::iterator iter = mLoadingMotions.begin(); iter != mLoadingMotions.end(); ) { motion_set_t::iterator curiter = iter++; LLMotion* motionp = *curiter; if( !motionp) { continue; // maybe shouldn't happen but i've seen it -MG } LLMotion::LLMotionInitStatus status = motionp->onInitialize(mCharacter); if (status == LLMotion::STATUS_SUCCESS) { mLoadingMotions.erase(curiter); // add motion to our loaded motion list mLoadedMotions.insert(motionp); // this motion should be playing if (!motionp->isStopped()) { //<singu> F32 start_time = mAnimTime; if (!mDisableSyncing) { motionp->aisync_loaded(); start_time = motionp->syncActivationTime(start_time); } ++mDisableSyncing; //</singu> activateMotionInstance(motionp, start_time); //<singu> --mDisableSyncing; //</singu> } } else if (status == LLMotion::STATUS_FAILURE) { llinfos << "Motion " << motionp->getID() << " init failed." << llendl; sRegistry.markBad(motionp->getID()); mLoadingMotions.erase(curiter); // Singu note: a motion in mLoadingMotions will not be in mActiveMotions // and therefore not be in mDeprecatedMotions. So, we don't have to // check for it's existence there. llassert(mDeprecatedMotions.find(motionp) == mDeprecatedMotions.end()); mAllMotions.erase(motionp->getID()); //<singu> // Make sure we're not registered anymore. motionp->unregister_client(); //</singu> delete motionp; } } }
//----------------------------------------------------------------------------- // flushAllMotions() //----------------------------------------------------------------------------- void LLMotionController::flushAllMotions() { std::vector<std::pair<LLUUID,F32> > active_motions; active_motions.reserve(mActiveMotions.size()); for (motion_list_t::iterator iter = mActiveMotions.begin(); iter != mActiveMotions.end(); ) { motion_list_t::iterator curiter = iter++; LLMotion* motionp = *curiter; F32 dtime = mAnimTime - motionp->mActivationTimestamp; active_motions.push_back(std::make_pair(motionp->getID(),dtime)); motionp->deactivate(); // don't call deactivateMotionInstance() because we are going to reactivate it } mActiveMotions.clear(); // delete all motion instances deleteAllMotions(); // kill current hand pose that was previously called out by // keyframe motion mCharacter->removeAnimationData("Hand Pose"); // restart motions for (std::vector<std::pair<LLUUID,F32> >::iterator iter = active_motions.begin(); iter != active_motions.end(); ++iter) { startMotion(iter->first, iter->second); } }
//----------------------------------------------------------------------------- // updateLoadingMotions() //----------------------------------------------------------------------------- void LLMotionController::updateLoadingMotions() { // query pending motions for completion for (motion_set_t::iterator iter = mLoadingMotions.begin(); iter != mLoadingMotions.end(); ) { motion_set_t::iterator curiter = iter++; LLMotion* motionp = *curiter; if( !motionp) { continue; // maybe shouldn't happen but i've seen it -MG } LLMotion::LLMotionInitStatus status = motionp->onInitialize(mCharacter); if (status == LLMotion::STATUS_SUCCESS) { mLoadingMotions.erase(curiter); // add motion to our loaded motion list mLoadedMotions.insert(motionp); // this motion should be playing if (!motionp->isStopped()) { activateMotionInstance(motionp, mAnimTime); } } else if (status == LLMotion::STATUS_FAILURE) { llinfos << "Motion " << motionp->getID() << " init failed." << llendl; sRegistry.markBad(motionp->getID()); mLoadingMotions.erase(curiter); motion_set_t::iterator found_it = mDeprecatedMotions.find(motionp); if (found_it != mDeprecatedMotions.end()) { mDeprecatedMotions.erase(found_it); } mAllMotions.erase(motionp->getID()); delete motionp; } } }
//----------------------------------------------------------------------------- // dumpMotions() //----------------------------------------------------------------------------- void LLMotionController::dumpMotions() { llinfos << "=====================================" << llendl; for (motion_map_t::iterator iter = mAllMotions.begin(); iter != mAllMotions.end(); iter++) { LLUUID id = iter->first; std::string state_string; LLMotion *motion = iter->second; if (mLoadingMotions.find(motion) != mLoadingMotions.end()) state_string += std::string("l"); if (mLoadedMotions.find(motion) != mLoadedMotions.end()) state_string += std::string("L"); if (std::find(mActiveMotions.begin(), mActiveMotions.end(), motion)!=mActiveMotions.end()) state_string += std::string("A"); llassert(mDeprecatedMotions.find(motion) == mDeprecatedMotions.end()); // singu: it's impossible that a motion is in mAllMotions and mDeprecatedMotions at the same time. llinfos << gAnimLibrary.animationName(id) << " " << state_string << llendl; } //<singu> // Also dump the deprecated motions. for (motion_set_t::iterator iter = mDeprecatedMotions.begin(); iter != mDeprecatedMotions.end(); ++iter) { std::string state_string; LLMotion* motion = *iter; LLUUID id = motion->getID(); llassert(mLoadingMotions.find(motion) == mLoadingMotions.end()); if (mLoadedMotions.find(motion) != mLoadedMotions.end()) state_string += std::string("L"); if (std::find(mActiveMotions.begin(), mActiveMotions.end(), motion)!=mActiveMotions.end()) state_string += std::string("A"); state_string += "D"; llinfos << gAnimLibrary.animationName(id) << " " << state_string << llendl; } //</singu> }
//----------------------------------------------------------------------------- // updateMotion() //----------------------------------------------------------------------------- void LLMotionController::updateMotion() { BOOL use_quantum = (mTimeStep != 0.f); // Update timing info for this time step. if (!mPaused) { F32 update_time = mTimeOffset + (mTimer.getElapsedTimeF32() * mTimeFactor); if (use_quantum) { F32 time_interval = fmodf(update_time, mTimeStep); // always animate *ahead* of actual time S32 quantum_count = llmax(0, llfloor((update_time - time_interval) / mTimeStep)) + 1; if (quantum_count == mTimeStepCount) { // we're still in same time quantum as before, so just interpolate and exit if (!mPaused) { F32 interp = time_interval / mTimeStep; mPoseBlender.interpolate(interp - mLastInterp); mLastInterp = interp; } return; } // is calculating a new keyframe pose, make sure the last one gets applied mPoseBlender.interpolate(1.f); mPoseBlender.clearBlenders(); mTimeStepCount = quantum_count; mLastTime = mTime; mTime = (F32)quantum_count * mTimeStep; mLastInterp = 0.f; } else { mLastTime = mTime; mTime = update_time; } } // query pending motions for completion for (motion_set_t::iterator iter = mLoadingMotions.begin(); iter != mLoadingMotions.end(); ) { motion_set_t::iterator curiter = iter++; LLMotion* motionp = *curiter; if( !motionp) { continue; // maybe shouldn't happen but i've seen it -MG } LLMotion::LLMotionInitStatus status = motionp->onInitialize(mCharacter); if (status == LLMotion::STATUS_SUCCESS) { mLoadingMotions.erase(curiter); // add motion to our loaded motion list addLoadedMotion(motionp); // this motion should be playing if (!motionp->isStopped()) { activateMotion(motionp, mTime); } } else if (status == LLMotion::STATUS_FAILURE) { llinfos << "Motion " << motionp->getID() << " init failed." << llendl; sRegistry.markBad(motionp->getID()); mLoadingMotions.erase(curiter); mAllMotions.erase(motionp->getID()); delete motionp; } } resetJointSignatures(); if (!mPaused) { // update additive motions updateAdditiveMotions(); resetJointSignatures(); // update all regular motions updateRegularMotions(); if (use_quantum) { mPoseBlender.blendAndCache(TRUE); } else { mPoseBlender.blendAndApply(); } } mHasRunOnce = TRUE; // llinfos << "Motion controller time " << motionTimer.getElapsedTimeF32() << llendl; }
//----------------------------------------------------------------------------- // updateMotionsByType() //----------------------------------------------------------------------------- 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) { if (motionp->isStopped() && mTime > motionp->getStopTime() + motionp->getEaseOutDuration()) { deactivateMotion(motionp, false); } else if (motionp->isStopped() && mTime > 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(); } } else if (mTime > 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 ); stopMotionLocally(motionp->getID(), FALSE); } } else if (mTime >= motionp->mActivationTimestamp) { if (mLastTime < motionp->mActivationTimestamp) { motionp->mResidualWeight = motionp->getPose()->getWeight(); } } 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 (mTime > 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 ); stopMotionLocally(motionp->getID(), FALSE); } } if (motionp->getFadeWeight() < 0.01f) { if (motionp->isStopped() && mTime > motionp->getStopTime() + motionp->getEaseOutDuration()) { deactivateMotion(motionp, true); } continue; } } else { motionp->fadeIn(); } //********************** // MOTION INACTIVE //********************** if (motionp->isStopped() && mTime > 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 { deactivateMotion(motionp, true); continue; } } //********************** // MOTION EASE OUT //********************** else if (motionp->isStopped() && mTime > 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 - ((mTime - motionp->getStopTime()) / motionp->getEaseOutDuration()))); } // perform motion update update_result = motionp->onUpdate(mTime - motionp->mActivationTimestamp, last_joint_signature); } //********************** // MOTION ACTIVE //********************** else if (mTime > motionp->mActivationTimestamp + motionp->getEaseInDuration()) { posep->setWeight(motionp->getFadeWeight()); //should we notify the simulator that this motion should be stopped? if (mTime > 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 ); stopMotionLocally(motionp->getID(), FALSE); } } // perform motion update update_result = motionp->onUpdate(mTime - motionp->mActivationTimestamp, last_joint_signature); } //********************** // MOTION EASE IN //********************** else if (mTime >= 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((mTime - motionp->mActivationTimestamp) / motionp->getEaseInDuration())); } // perform motion update update_result = motionp->onUpdate(mTime - 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() > mTime) { // 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 ); stopMotionLocally(motionp->getID(), FALSE); } } // even if onupdate returns FALSE, add this motion in to the blend one last time mPoseBlender.addMotion(motionp); } }