Synchronize looping animations that start at the same moment.
This commit is contained in:
@@ -194,11 +194,26 @@ void LLCharacter::updateMotions(e_update_t update_type)
|
||||
{
|
||||
if (update_type == HIDDEN_UPDATE)
|
||||
{
|
||||
//<singu>
|
||||
// Keep updating avatars that have at least one motion that is synchronized with a still running motion.
|
||||
// This call tells the other controllers that we are in principle hidden.
|
||||
// It returns false if we need to keep updating anyway.
|
||||
if (!mMotionController.hidden(true))
|
||||
{
|
||||
mMotionController.updateMotions(LLCharacter::NORMAL_UPDATE);
|
||||
return;
|
||||
}
|
||||
//</singu>
|
||||
LLFastTimer t(FTM_UPDATE_HIDDEN_ANIMATION);
|
||||
mMotionController.updateMotionsMinimal();
|
||||
}
|
||||
else
|
||||
{
|
||||
//<singu>
|
||||
// This call tells the other controllers that we are visible and that they need
|
||||
// to keep updating if they are synchronized with us, even if they are hidden.
|
||||
mMotionController.hidden(false);
|
||||
//</singu>
|
||||
LLFastTimer t(FTM_UPDATE_ANIMATION);
|
||||
// unpause if the number of outstanding pause requests has dropped to the initial one
|
||||
if (mMotionController.isPaused() && mPauseRequest->getNumRefs() == 1)
|
||||
|
||||
@@ -818,7 +818,44 @@ void LLKeyframeMotion::onDeactivate()
|
||||
//-----------------------------------------------------------------------------
|
||||
// setStopTime()
|
||||
//-----------------------------------------------------------------------------
|
||||
// time is in seconds since character creation
|
||||
//
|
||||
// Consider a looping animation of 20 frames, where the loop in point is at 3 frames
|
||||
// and the loop out point at 16 frames:
|
||||
//
|
||||
// The first 3 frames of the animation would be the "loop in" animation.
|
||||
// The last 4 frames of the animation would be the "loop out" animation.
|
||||
// Frames 4 through 15 would be the looping animation frames.
|
||||
//
|
||||
// If the animation would not be looping, all frames would just be played once sequentially:
|
||||
//
|
||||
// mActivationTimestamp -.
|
||||
// v
|
||||
// 0 3 15 16 20
|
||||
// | | \| |
|
||||
// ---------------------
|
||||
// <--> <-- mLoopInPoint (relative to mActivationTimestamp)
|
||||
// <--------------> <-- mLoopOutPoint (relative to mActivationTimestamp)
|
||||
// <----mDuration------>
|
||||
//
|
||||
// When looping the animation would repeat frames 3 to 16 (loop) a few times, for example:
|
||||
//
|
||||
// 0 3 15 3 15 3 15 3 15 16 20
|
||||
// | | loop 1 \| loop 2 \| loop 3 \| loop 4 \| |
|
||||
// ------------------------------------------------------------
|
||||
//LOOP^ ^ LOOP
|
||||
// IN | <----->| OUT
|
||||
// start_loop_time loop_fraction_time-' time
|
||||
//
|
||||
// The time at which the animation is started corresponds to frame 0 and is stored
|
||||
// in mActivationTimestamp (in seconds since character creation).
|
||||
//
|
||||
// If setStopTime() is called with a time somewhere inside loop 4,
|
||||
// then 'loop_fraction_time' is the time from the beginning of
|
||||
// loop 4 till 'time'. Thus 'time - loop_fraction_time' is the first
|
||||
// frame of loop 4, and '(time - loop_fraction_time) +
|
||||
// (mJointMotionList->mDuration - mJointMotionList->mLoopInPoint)'
|
||||
// would correspond to frame 20.
|
||||
//
|
||||
void LLKeyframeMotion::setStopTime(F32 time)
|
||||
{
|
||||
LLMotion::setStopTime(time);
|
||||
@@ -836,6 +873,8 @@ void LLKeyframeMotion::setStopTime(F32 time)
|
||||
loop_fraction_time = fmod(time - start_loop_time,
|
||||
mJointMotionList->mLoopOutPoint - mJointMotionList->mLoopInPoint);
|
||||
}
|
||||
// This sets mStopTimestamp to the time that corresponds to the end of the animation (ie, frame 20 in the above example)
|
||||
// minus the ease out duration, so that the animation eases out during the loop out and finishes exactly at the end.
|
||||
mStopTimestamp = llmax(time,
|
||||
(time - loop_fraction_time) + (mJointMotionList->mDuration - mJointMotionList->mLoopInPoint) - getEaseOutDuration());
|
||||
}
|
||||
|
||||
@@ -39,6 +39,122 @@
|
||||
#include "llcriticaldamp.h"
|
||||
#include "llmotioncontroller.h"
|
||||
|
||||
//<singu>
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
// AISyncClientMotion class
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
AISyncKey* AISyncClientMotion::createSyncKey(AISyncKey const* from_key) const
|
||||
{
|
||||
// The const cast is needed because getDuration() is non-const while it should have been.
|
||||
AISyncClientMotion* self = const_cast<AISyncClientMotion*>(this);
|
||||
// Only synchronize motions with the same duration and loop value.
|
||||
return new AISyncKeyMotion(from_key, self->getDuration(), self->getLoop());
|
||||
}
|
||||
|
||||
void AISyncClientMotion::aisync_loading(void)
|
||||
{
|
||||
// Register the motion for (possible) synchronization: this marks the time at which is should have started.
|
||||
unregister_client(); // In case it is already registered. Getting here means we are being (re)started now, we need to synchronize with other motions that start now.
|
||||
register_client();
|
||||
}
|
||||
|
||||
void AISyncClientMotion::aisync_loaded(void)
|
||||
{
|
||||
AISyncServer* server = this->server();
|
||||
if (!server)
|
||||
{
|
||||
// Already expired without being synchronized (no other motion was started at the same time).
|
||||
return;
|
||||
}
|
||||
AISyncKey const& key = server->key(); // The allocation of this is owned by server.
|
||||
// There is no need to resync if there was not another motion started at the same time and the key already expired.
|
||||
bool need_resync = !(server->never_synced() && key.expired());
|
||||
AISyncKey* new_key;
|
||||
if (need_resync)
|
||||
{
|
||||
// Create a new key using the old start time.
|
||||
new_key = createSyncKey(&key);
|
||||
}
|
||||
server->remove(this); // This resets mServer and might even delete server.
|
||||
if (need_resync)
|
||||
{
|
||||
// Add the client to another server (based on the new key). This takes ownership of the key allocation.
|
||||
AISyncServerMap::instance().register_client(this, new_key);
|
||||
}
|
||||
}
|
||||
|
||||
F32 LLMotion::getRuntime(void) const
|
||||
{
|
||||
llassert(mActive);
|
||||
return mController->getAnimTime() - mActivationTimestamp;
|
||||
}
|
||||
|
||||
F32 LLMotion::getAnimTime(void) const
|
||||
{
|
||||
return mController->getAnimTime();
|
||||
}
|
||||
|
||||
F32 LLMotion::syncActivationTime(F32 time)
|
||||
{
|
||||
AISyncServer* server = this->server();
|
||||
if (!server)
|
||||
{
|
||||
register_client();
|
||||
server = this->server();
|
||||
}
|
||||
AISyncServer::client_list_t const& clients = server->getClients();
|
||||
if (clients.size() > 1)
|
||||
{
|
||||
// Look for the client with the smallest runtime.
|
||||
AISyncClientMotion* motion_with_smallest_runtime = NULL;
|
||||
F32 runtime = 1e10;
|
||||
// Run over all motions in this to be synchronized group.
|
||||
for (AISyncServer::client_list_t::const_iterator client = clients.begin(); client != clients.end(); ++client)
|
||||
{
|
||||
if ((client->mReadyEvents & 2)) // Is this motion active? Motions that aren't loaded yet are not active.
|
||||
{
|
||||
// Currently, if event 2 is set then this is an LLMotion.
|
||||
llassert(dynamic_cast<AISyncClientMotion*>(client->mClientPtr));
|
||||
AISyncClientMotion* motion = static_cast<AISyncClientMotion*>(client->mClientPtr);
|
||||
// Deactivated motions should have been deregistered, certainly not have event 2 set.
|
||||
llassert(static_cast<LLMotion*>(motion)->isActive());
|
||||
if (motion->getRuntime() < runtime)
|
||||
{
|
||||
// This is a bit fuzzy since theoretically the runtime of all active motions in the list should be the same.
|
||||
// Just use the smallest value to get rid of some randomness. We might even synchronizing with ourselves
|
||||
// in which case 'time' would be set to a value such that mActivationTimestamp won't change.
|
||||
// In practise however, this list will contain only two clients: this, being inactive, and our partner.
|
||||
runtime = motion->getRuntime();
|
||||
motion_with_smallest_runtime = motion;
|
||||
}
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------
|
||||
// Here is where the actual synchronization takes place.
|
||||
// Current we only synchronize looped motions.
|
||||
if (getLoop())
|
||||
{
|
||||
if (motion_with_smallest_runtime)
|
||||
{
|
||||
// Pretend the motion was started in the past at the same time as the other motion(s).
|
||||
time = getAnimTime() - runtime;
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
return time;
|
||||
}
|
||||
|
||||
void AISyncClientMotion::deregistered(void)
|
||||
{
|
||||
mReadyEvents = 0;
|
||||
}
|
||||
//</singu>
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
// LLMotion class
|
||||
@@ -149,6 +265,19 @@ void LLMotion::activate(F32 time)
|
||||
{
|
||||
mActivationTimestamp = time;
|
||||
mStopped = FALSE;
|
||||
//<singu>
|
||||
if (mController && !mController->syncing_disabled()) // Avoid being registered when syncing is disabled for this motion.
|
||||
{
|
||||
if (mActive)
|
||||
{
|
||||
// If the motion is already active then we are being restarted.
|
||||
// Unregister it first (if it is registered) so that the call to ready will cause it to be registered
|
||||
// and be synchronized with other motions that are started at this moment.
|
||||
unregister_client();
|
||||
}
|
||||
ready(6, 2 | (mController->isHidden() ? 0 : 4)); // Signal that mActivationTimestamp is set/valid (2), and that this server has a visible motion (4) (or not).
|
||||
}
|
||||
//</singu>
|
||||
mActive = TRUE;
|
||||
onActivate();
|
||||
}
|
||||
@@ -161,6 +290,14 @@ void LLMotion::deactivate()
|
||||
mActive = FALSE;
|
||||
mPose.setWeight(0.f);
|
||||
|
||||
//<singu>
|
||||
if (server()) // Only when this motion is already registered.
|
||||
{
|
||||
ready(6, 0); // Signal that mActivationTimestamp is no longer valid.
|
||||
unregister_client(); // No longer running, so no longer a part of this sync group.
|
||||
}
|
||||
//</singu>
|
||||
|
||||
if (mDeactivateCallback)
|
||||
{
|
||||
(*mDeactivateCallback)(mDeactivateCallbackUserData);
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
#include <string>
|
||||
|
||||
#include "aisyncclient.h"
|
||||
#include "llerror.h"
|
||||
#include "llpose.h"
|
||||
#include "lluuid.h"
|
||||
@@ -45,10 +46,77 @@
|
||||
class LLCharacter;
|
||||
class LLMotionController;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// class AISync* stuff
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class AISyncKeyMotion : public AISyncKey
|
||||
{
|
||||
private:
|
||||
F32 mDuration;
|
||||
bool mLoop;
|
||||
|
||||
public:
|
||||
AISyncKeyMotion(AISyncKey const* from_key, F32 duration, bool loop) : AISyncKey(from_key), mDuration(duration), mLoop(loop) { }
|
||||
|
||||
// Virtual functions of AISyncKey.
|
||||
public:
|
||||
/*virtual*/ synckeytype_t getkeytype(void) const
|
||||
{
|
||||
// Return a unique identifier for this class, where the low 8 bits represent the syncgroup.
|
||||
return synckeytype_motion;
|
||||
}
|
||||
|
||||
/*virtual*/ bool equals(AISyncKey const& key) const
|
||||
{
|
||||
switch (key.getkeytype())
|
||||
{
|
||||
case synckeytype_motion:
|
||||
{
|
||||
// The other key is of the same type.
|
||||
AISyncKeyMotion const& motion_key = static_cast<AISyncKeyMotion const&>(key);
|
||||
return mLoop == motion_key.mLoop && is_approx_equal(mDuration, motion_key.mDuration);
|
||||
}
|
||||
default:
|
||||
// The keys must be in the same syncgroup.
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class AISyncClientMotion : public AISyncClient
|
||||
{
|
||||
protected:
|
||||
// Make sure that clients that are destroyed are first unregistered.
|
||||
// This is needed, for example, when loading fails or when excess motions are being purged.
|
||||
/*virtual*/ ~AISyncClientMotion() { unregister_client(); }
|
||||
|
||||
// AISyncClient events.
|
||||
/*virtual*/ AISyncKey* createSyncKey(AISyncKey const* from_key = NULL) const;
|
||||
/*virtual*/ void event1_ready(void) { }
|
||||
/*virtual*/ void event1_not_ready(void) { }
|
||||
/*virtual*/ void deregistered(void);
|
||||
|
||||
protected:
|
||||
// This is called when the server sent us a message that it wants us to play this animation, but we can't because it isn't fully downloaded yet.
|
||||
void aisync_loading(void);
|
||||
// This is called when that motion is successfully loaded and it has to be re-registered because now the duration etc is known.
|
||||
void aisync_loaded(void);
|
||||
|
||||
public:
|
||||
// Virtual functions of AISyncClientMotion.
|
||||
// These are defined by classes derived from LLMotion (which is derived from this class).
|
||||
virtual BOOL getLoop() = 0;
|
||||
virtual F32 getDuration() = 0;
|
||||
virtual F32 getAnimTime(void) const = 0;
|
||||
virtual F32 getRuntime(void) const = 0;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// class LLMotion
|
||||
//-----------------------------------------------------------------------------
|
||||
class LLMotion
|
||||
class LLMotion : public AISyncClientMotion
|
||||
{
|
||||
friend class LLMotionController;
|
||||
|
||||
@@ -115,7 +183,22 @@ protected:
|
||||
BOOL isActive() { return mActive; }
|
||||
public:
|
||||
void activate(F32 time);
|
||||
|
||||
|
||||
//<singu>
|
||||
// Returns the time that this motion has been running.
|
||||
virtual F32 getRuntime(void) const;
|
||||
|
||||
// Return the current time (in seconds since creation of the controller).
|
||||
virtual F32 getAnimTime(void) const;
|
||||
|
||||
// This is called when a motion is to be activated, but might need synchronization.
|
||||
// It adjusts the start time to match that of other motions in the same synchronization group that were already started.
|
||||
F32 syncActivationTime(F32 time);
|
||||
|
||||
// Accessor.
|
||||
LLMotionController* getController(void) const { return mController; }
|
||||
//</singu>
|
||||
|
||||
public:
|
||||
//-------------------------------------------------------------------------
|
||||
// animation callbacks to be implemented by subclasses
|
||||
|
||||
@@ -130,6 +130,9 @@ LLMotionController::LLMotionController()
|
||||
mTimeFactor(sCurrentTimeFactor),
|
||||
mCharacter(NULL),
|
||||
mActiveMask(0),
|
||||
mDisableSyncing(0),
|
||||
mHidden(false),
|
||||
mHaveVisibleSyncedMotions(false),
|
||||
mPrevTimerElapsed(0.f),
|
||||
mAnimTime(0.f),
|
||||
mLastTime(0.0f),
|
||||
@@ -173,6 +176,10 @@ void LLMotionController::deleteAllMotions()
|
||||
mActiveMask = 0;
|
||||
for_each(mDeprecatedMotions.begin(), mDeprecatedMotions.end(), DeletePointer());
|
||||
mDeprecatedMotions.clear();
|
||||
for (motion_map_t::iterator iter = mAllMotions.begin(); iter != mAllMotions.end(); ++iter)
|
||||
{
|
||||
iter->second->unregister_client();
|
||||
}
|
||||
//</singu>
|
||||
for_each(mAllMotions.begin(), mAllMotions.end(), DeletePairedPointer());
|
||||
mAllMotions.clear();
|
||||
@@ -437,7 +444,19 @@ BOOL LLMotionController::startMotion(const LLUUID &id, F32 start_offset)
|
||||
}
|
||||
|
||||
// llinfos << "Starting motion " << name << llendl;
|
||||
return activateMotionInstance(motion, mAnimTime - start_offset);
|
||||
//<singu>
|
||||
F32 start_time = mAnimTime - start_offset;
|
||||
if (!mDisableSyncing)
|
||||
{
|
||||
start_time = motion->syncActivationTime(start_time);
|
||||
}
|
||||
++mDisableSyncing;
|
||||
//</singu>
|
||||
BOOL res = activateMotionInstance(motion, start_time);
|
||||
//<singu>
|
||||
--mDisableSyncing;
|
||||
//</singu>
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@@ -793,7 +812,19 @@ void LLMotionController::updateLoadingMotions()
|
||||
// this motion should be playing
|
||||
if (!motionp->isStopped())
|
||||
{
|
||||
activateMotionInstance(motionp, mAnimTime);
|
||||
//<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)
|
||||
@@ -806,6 +837,10 @@ void LLMotionController::updateLoadingMotions()
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
@@ -933,6 +968,12 @@ BOOL LLMotionController::activateMotionInstance(LLMotion *motion, F32 time)
|
||||
|
||||
if (mLoadingMotions.find(motion) != mLoadingMotions.end())
|
||||
{
|
||||
//<singu>
|
||||
if (!syncing_disabled())
|
||||
{
|
||||
motion->aisync_loading();
|
||||
}
|
||||
//</singu>
|
||||
// we want to start this motion, but we can't yet, so flag it as started
|
||||
motion->setStopped(FALSE);
|
||||
// report pending animations as activated
|
||||
@@ -1094,9 +1135,9 @@ void LLMotionController::deactivateAllMotions()
|
||||
{
|
||||
// Singu note: this must run over mActiveMotions: other motions are not active,
|
||||
// and running over mAllMotions will miss the ones in mDeprecatedMotions.
|
||||
for (motion_list_t::iterator iter = mActiveMotions.begin(); iter != mActiveMotions.end(); ++iter)
|
||||
for (motion_list_t::iterator iter = mActiveMotions.begin(); iter != mActiveMotions.end();)
|
||||
{
|
||||
deactivateMotionInstance(*iter);
|
||||
deactivateMotionInstance(*iter++); // This might invalidate iter by erasing it from mActiveMotions.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1126,13 +1167,98 @@ void LLMotionController::flushAllMotions()
|
||||
mCharacter->removeAnimationData("Hand Pose");
|
||||
|
||||
// restart motions
|
||||
//<singu>
|
||||
// Because we called motionp->deactivate() above, instead of deactivateMotionInstance(),
|
||||
// prevent calling AISyncClientMotion::activateInstance in startMotion below.
|
||||
disable_syncing();
|
||||
//</singu>
|
||||
for (std::vector<std::pair<LLUUID,F32> >::iterator iter = active_motions.begin();
|
||||
iter != active_motions.end(); ++iter)
|
||||
{
|
||||
startMotion(iter->first, iter->second);
|
||||
}
|
||||
//<singu>
|
||||
enable_syncing();
|
||||
//</singu>
|
||||
}
|
||||
|
||||
//<singu>
|
||||
//-----------------------------------------------------------------------------
|
||||
// toggle_hidden()
|
||||
//-----------------------------------------------------------------------------
|
||||
void LLMotionController::toggle_hidden(void)
|
||||
{
|
||||
mHaveVisibleSyncedMotions = mHidden; // Default is false if we just became invisible (otherwise this value isn't used).
|
||||
mHidden = !mHidden;
|
||||
synceventset_t const visible = mHidden ? 0 : 4;
|
||||
|
||||
// Run over all motions.
|
||||
for (motion_list_t::iterator iter = mActiveMotions.begin(); iter != mActiveMotions.end(); ++iter)
|
||||
{
|
||||
LLMotion* motionp = *iter;
|
||||
AISyncServer* server = motionp->server();
|
||||
if (server && !server->never_synced() && (motionp->mReadyEvents & 2)) // Skip motions that aren't synchronized at all or that are not active.
|
||||
{
|
||||
bool visible_before = server->events_with_at_least_one_client_ready() & 4;
|
||||
server->ready(4, visible, motionp); // Mark that now we are visible or no longer visible.
|
||||
bool visible_after = server->events_with_at_least_one_client_ready() & 4;
|
||||
if (visible_after) // Are there any synchronized motions (left) that ARE visible?
|
||||
{
|
||||
mHaveVisibleSyncedMotions = true;
|
||||
}
|
||||
if (visible_before != visible_after)
|
||||
{
|
||||
// The group as a whole now might need to change whether or not it is animated.
|
||||
AISyncServer::client_list_t const& clients = server->getClients();
|
||||
for (AISyncServer::client_list_t::const_iterator client = clients.begin(); client != clients.end(); ++client)
|
||||
{
|
||||
LLMotion* motion = dynamic_cast<LLMotion*>(client->mClientPtr);
|
||||
if (!motion)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
LLMotionController* controller = motion->getController();
|
||||
if (controller == this)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (visible_after)
|
||||
{
|
||||
// Us becoming visible means that all synchronized avatars need to be animated again too.
|
||||
controller->setHaveVisibleSyncedMotions();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Us becoming hidden means that all synchronized avatars might stop animating.
|
||||
controller->refresh_hidden(); // It is extremely unlikely, but harmless, to call this twice on the same controller.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LLMotionController::refresh_hidden(void)
|
||||
{
|
||||
mHaveVisibleSyncedMotions = !mHidden;
|
||||
|
||||
// Run over all motions.
|
||||
for (motion_list_t::iterator iter = mActiveMotions.begin(); iter != mActiveMotions.end(); ++iter)
|
||||
{
|
||||
LLMotion* motionp = *iter;
|
||||
AISyncServer* server = motionp->server();
|
||||
if (server && !server->never_synced() && (motionp->mReadyEvents & 2)) // Skip motions that aren't synchronized at all or that are not active.
|
||||
{
|
||||
bool visible_after = server->events_with_at_least_one_client_ready() & 4;
|
||||
if (visible_after) // Are there any synchronized motions (left) that ARE visible?
|
||||
{
|
||||
mHaveVisibleSyncedMotions = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//</singu>
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// pause()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
@@ -150,11 +150,11 @@ public:
|
||||
//Flush is a liar.
|
||||
void deactivateAllMotions();
|
||||
|
||||
//<edit>
|
||||
//<singu>
|
||||
void activated(U32 bit) { mActiveMask |= bit; }
|
||||
void deactivated(U32 bit) { mActiveMask &= ~bit; }
|
||||
bool isactive(U32 bit) const { return (mActiveMask & bit) != 0; }
|
||||
//</edit>
|
||||
//</singu>
|
||||
|
||||
// pause and continue all motions
|
||||
void pauseAllMotions();
|
||||
@@ -186,7 +186,10 @@ protected:
|
||||
// internal operations act on motion instances directly
|
||||
// as there can be duplicate motions per id during blending overlap
|
||||
void deleteAllMotions();
|
||||
// singu: LLMotion needs access to activateMotionInstance.
|
||||
public:
|
||||
BOOL activateMotionInstance(LLMotion *motion, F32 time);
|
||||
protected:
|
||||
BOOL deactivateMotionInstance(LLMotion *motion);
|
||||
void deprecateMotionInstance(LLMotion* motion);
|
||||
BOOL stopMotionInstance(LLMotion *motion, BOOL stop_imemdiate);
|
||||
@@ -227,9 +230,12 @@ protected:
|
||||
motion_list_t mActiveMotions;
|
||||
motion_set_t mDeprecatedMotions;
|
||||
|
||||
//<edit>
|
||||
//<singu>
|
||||
U32 mActiveMask;
|
||||
//</edit>
|
||||
int mDisableSyncing; // Set while LLMotion::onActivate (and onDeactivate) are called for this controller.
|
||||
bool mHidden; // The value of the last call to hidden().
|
||||
bool mHaveVisibleSyncedMotions; // Set when we are synchronized with one or more motions of a controller that is not hidden.
|
||||
//</singu>
|
||||
LLFrameTimer mTimer;
|
||||
F32 mPrevTimerElapsed;
|
||||
F32 mAnimTime;
|
||||
@@ -242,6 +248,26 @@ protected:
|
||||
F32 mLastInterp;
|
||||
|
||||
U8 mJointSignature[2][LL_CHARACTER_MAX_JOINTS];
|
||||
|
||||
//<singu>
|
||||
public:
|
||||
// Internal administration for AISync.
|
||||
void disable_syncing(void) { mDisableSyncing += 100; }
|
||||
void enable_syncing(void) { mDisableSyncing -= 100; }
|
||||
bool syncing_disabled(void) const { return mDisableSyncing >= 100; }
|
||||
|
||||
// Accessors needed for synchronization.
|
||||
F32 getAnimTime(void) const { return mAnimTime; }
|
||||
bool isHidden(void) const { return mHidden; }
|
||||
|
||||
// Called often. Should return false if we still need to keep updating our motions even if we're not visible.
|
||||
bool hidden(bool not_visible) { if (mHidden != not_visible) toggle_hidden(); return !mHaveVisibleSyncedMotions; }
|
||||
|
||||
private:
|
||||
void toggle_hidden(void);
|
||||
void refresh_hidden(void);
|
||||
void setHaveVisibleSyncedMotions(void) { mHaveVisibleSyncedMotions = true; }
|
||||
//</singu>
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
@@ -126,20 +126,13 @@ void print_clients(AISyncServer const* server, AISyncServer::client_list_t const
|
||||
}
|
||||
#endif
|
||||
|
||||
void AISyncServerMap::register_client(AISyncClient* client)
|
||||
void AISyncServerMap::register_client(AISyncClient* client, AISyncKey* new_key)
|
||||
{
|
||||
#ifdef DEBUG_SYNCOUTPUT
|
||||
DoutEntering(dc::notice, "AISyncServerMap::register_client(" << client << ")");
|
||||
#endif
|
||||
|
||||
// client must always be a new client that has to be stored somewhere.
|
||||
llassert(client->server() == NULL);
|
||||
// Obviously the client can't be ready for anything when it isn't registered yet.
|
||||
llassert(!client->mReadyEvents);
|
||||
|
||||
// First we need its sync key.
|
||||
AISyncKey* new_key = client->createSyncKey();
|
||||
|
||||
// Find if a server with this key already exists.
|
||||
AISyncServer* server = NULL;
|
||||
for (server_list_t::iterator iter = mServers.begin(); iter != mServers.end();)
|
||||
@@ -183,7 +176,6 @@ void AISyncServerMap::register_client(AISyncClient* client)
|
||||
server = new AISyncServer(new_key);
|
||||
// Add it to mServers, before the last server that is younger then the new key.
|
||||
server_list_t::iterator where = mServers.end(); // Insert the new server before 'where',
|
||||
#if 0 // This is probably not necessary.
|
||||
server_list_t::iterator new_where = where;
|
||||
while (where != mServers.begin()) // unless there exists a server before that
|
||||
{
|
||||
@@ -194,10 +186,6 @@ void AISyncServerMap::register_client(AISyncClient* client)
|
||||
}
|
||||
where = new_where; // then insert it before that element (etc).
|
||||
}
|
||||
#elif defined(SHOW_ASSERT)
|
||||
server_list_t::iterator new_where = where;
|
||||
llassert(where == mServers.begin() || new_key->getCreationTime() >= (*--new_where)->key().getCreationTime());
|
||||
#endif
|
||||
// This method causes a single call to intrusive_ptr_add_ref and none to intrusive_ptr_release.
|
||||
server_ptr_t server_ptr = server;
|
||||
mServers.insert(where, server_ptr_t())->swap(server_ptr);
|
||||
@@ -231,10 +219,6 @@ void AISyncServer::sanity_check(void) const
|
||||
|
||||
void AISyncServer::add(AISyncClient* client)
|
||||
{
|
||||
#ifdef DEBUG_SYNCOUTPUT
|
||||
DoutEntering(dc::notice, "AISyncServer::add(" << client << "), with this = " << this);
|
||||
print_clients(this, getClients());
|
||||
#endif
|
||||
#ifdef SYNC_TESTSUITE
|
||||
sanity_check();
|
||||
#endif
|
||||
@@ -263,16 +247,10 @@ void AISyncServer::add(AISyncClient* client)
|
||||
|
||||
void AISyncServer::remove(AISyncClient* client)
|
||||
{
|
||||
#ifdef DEBUG_SYNCOUTPUT
|
||||
DoutEntering(dc::notice, "AISyncServer::remove(" << client << "), with this = " << this);
|
||||
print_clients(this, getClients());
|
||||
#endif
|
||||
#ifdef SYNC_TESTSUITE
|
||||
sanity_check();
|
||||
#endif
|
||||
|
||||
// A client may only be unregistered after it was marked not-ready for all events.
|
||||
llassert(!client->mReadyEvents);
|
||||
client_list_t::iterator client_iter = mClients.begin();
|
||||
synceventset_t remaining_ready_events = (synceventset_t)-1; // All clients are ready.
|
||||
synceventset_t remaining_pending_events = 0; // At least one client is ready (waiting for the other clients thus).
|
||||
@@ -292,15 +270,14 @@ void AISyncServer::remove(AISyncClient* client)
|
||||
}
|
||||
llassert(found_client != mClients.end());
|
||||
// This must be the same as client->mReadyEvents.
|
||||
llassert(!found_client->mReadyEvents);
|
||||
llassert(found_client->mReadyEvents == client->mReadyEvents);
|
||||
mClients.erase(found_client);
|
||||
client->mServer.reset();
|
||||
synceventset_t old_ready_events = mReadyEvents;
|
||||
mReadyEvents = remaining_ready_events;
|
||||
// Since client->mReadyEvents is zero, this should be the same.
|
||||
llassert(mPendingEvents == remaining_pending_events);
|
||||
mPendingEvents = remaining_pending_events;
|
||||
trigger(old_ready_events);
|
||||
client->mServer.reset();
|
||||
client->deregistered();
|
||||
|
||||
#ifdef SYNC_TESTSUITE
|
||||
sanity_check();
|
||||
@@ -309,10 +286,6 @@ void AISyncServer::remove(AISyncClient* client)
|
||||
|
||||
void AISyncServer::unregister_last_client(void)
|
||||
{
|
||||
#ifdef DEBUG_SYNCOUTPUT
|
||||
DoutEntering(dc::notice, "AISyncServer::unregister_last_client()");
|
||||
print_clients(this, getClients());
|
||||
#endif
|
||||
#ifdef SYNC_TESTSUITE
|
||||
sanity_check();
|
||||
#endif
|
||||
@@ -333,22 +306,6 @@ void AISyncServer::unregister_last_client(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
synceventset_t AISyncServer::events_with_all_clients_ready(void) const
|
||||
{
|
||||
#ifdef SYNC_TESTSUITE
|
||||
sanity_check();
|
||||
#endif
|
||||
return mReadyEvents;
|
||||
}
|
||||
|
||||
synceventset_t AISyncServer::events_with_at_least_one_client_ready(void) const
|
||||
{
|
||||
#ifdef SYNC_TESTSUITE
|
||||
sanity_check();
|
||||
#endif
|
||||
return mPendingEvents;
|
||||
}
|
||||
|
||||
void AISyncServer::trigger(synceventset_t old_ready_events)
|
||||
{
|
||||
// If event 1 changed, informat all clients about it.
|
||||
@@ -368,22 +325,14 @@ void AISyncServer::trigger(synceventset_t old_ready_events)
|
||||
}
|
||||
}
|
||||
|
||||
bool AISyncServer::ready(synceventset_t events, synceventset_t yesno, AISyncClient* client)
|
||||
void AISyncServer::ready(synceventset_t events, synceventset_t yesno, AISyncClient* client)
|
||||
{
|
||||
#ifdef DEBUG_SYNCOUTPUT
|
||||
DoutEntering(dc::notice, "AISyncServer::ready(" << SyncEventSet(events) << ", " << SyncEventSet(yesno) << ", " << client << ")");
|
||||
print_clients(this, getClients());
|
||||
#endif
|
||||
#ifdef SYNC_TESTSUITE
|
||||
sanity_check();
|
||||
#endif
|
||||
|
||||
synceventset_t added_events = events & yesno;
|
||||
synceventset_t removed_events = events & ~yesno;
|
||||
// May not add events that are already ready.
|
||||
llassert(!(client->mReadyEvents & added_events));
|
||||
// Cannot remove events that weren't ready.
|
||||
llassert((client->mReadyEvents & removed_events) == removed_events);
|
||||
|
||||
// Run over all clients to find the client and calculate the current state.
|
||||
synceventset_t remaining_ready_events = (synceventset_t)-1; // All clients are ready.
|
||||
|
||||
@@ -117,9 +117,16 @@ class LL_COMMON_API AISyncKey
|
||||
|
||||
public:
|
||||
// Constructor.
|
||||
AISyncKey(void) : mStartFrameCount(LLFrameTimer::getFrameCount())
|
||||
AISyncKey(AISyncKey const* from_key) : mStartFrameCount(from_key ? from_key->mStartFrameCount : LLFrameTimer::getFrameCount())
|
||||
{
|
||||
mFrameTimer.reset(sExpirationTime);
|
||||
if (from_key)
|
||||
{
|
||||
mFrameTimer.copy(from_key->mFrameTimer);
|
||||
}
|
||||
else
|
||||
{
|
||||
mFrameTimer.reset(sExpirationTime);
|
||||
}
|
||||
}
|
||||
|
||||
// Destructor.
|
||||
@@ -186,7 +193,7 @@ class LL_COMMON_API AISyncServer
|
||||
// Add a new client to this server.
|
||||
void add(AISyncClient* client);
|
||||
|
||||
// Add a new client to this server.
|
||||
// Remove a client from this server.
|
||||
void remove(AISyncClient* client);
|
||||
|
||||
// Return the key associated to this server (which is the key produced by the first client of the largest synckeytype_t that was added).
|
||||
@@ -198,20 +205,19 @@ class LL_COMMON_API AISyncServer
|
||||
bool never_synced(void) const { return !mSynchronized; }
|
||||
|
||||
// Set readiness of all events at once.
|
||||
bool ready(synceventset_t events, synceventset_t yesno, AISyncClient* client);
|
||||
void ready(synceventset_t events, synceventset_t yesno, AISyncClient* client);
|
||||
|
||||
// Unregister the (only) client because it's own its own and will never need synchronization.
|
||||
void unregister_last_client(void);
|
||||
|
||||
// Return the events that all clients for.
|
||||
synceventset_t events_with_all_clients_ready(void) const;
|
||||
synceventset_t events_with_all_clients_ready(void) const { return mReadyEvents; }
|
||||
|
||||
// Return events that at least one client is ready for.
|
||||
synceventset_t events_with_at_least_one_client_ready(void) const;
|
||||
synceventset_t events_with_at_least_one_client_ready(void) const { return mPendingEvents; }
|
||||
|
||||
#ifdef SHOW_ASSERT
|
||||
// Return a list of all registered clients.
|
||||
client_list_t const& getClients(void) const { return mClients; }
|
||||
#endif
|
||||
|
||||
private:
|
||||
// Call event1_ready() or event1_not_ready() on all clients if the least significant bit of mReadyEvents changed.
|
||||
@@ -238,7 +244,7 @@ class LL_COMMON_API AISyncServerMap : public LLSingleton<AISyncServerMap>
|
||||
public:
|
||||
// Find or create a server object that the client belongs to and store the client in it.
|
||||
// If a new server is created, it is stored in mServers.
|
||||
void register_client(AISyncClient* client);
|
||||
void register_client(AISyncClient* client, AISyncKey* new_key);
|
||||
|
||||
private:
|
||||
friend void intrusive_ptr_release(AISyncServer* server);
|
||||
@@ -258,8 +264,8 @@ class LL_COMMON_API AISyncClient
|
||||
synceventset_t mReadyEvents;
|
||||
AISyncClient(void) : mReadyEvents(0) { }
|
||||
#endif
|
||||
virtual ~AISyncClient() { }
|
||||
virtual AISyncKey* createSyncKey(void) const = 0;
|
||||
virtual ~AISyncClient() { llassert(!mServer); /* If this fails then you need to add unregister_client() to the top of the destructor of the derived class that implements deregistered(). */ }
|
||||
virtual AISyncKey* createSyncKey(AISyncKey const* from_key = NULL) const = 0;
|
||||
|
||||
virtual void event1_ready(void) = 0;
|
||||
virtual void event1_not_ready(void) = 0;
|
||||
@@ -275,21 +281,22 @@ class LL_COMMON_API AISyncClient
|
||||
AISyncServer* server(void) const { return mServer.get(); }
|
||||
|
||||
// Add this client to a server with matching sync key. Optionally the server is first created.
|
||||
void register_client(void) { AISyncServerMap::instance().register_client(this); }
|
||||
void register_client(void) { AISyncServerMap::instance().register_client(this, createSyncKey()); }
|
||||
|
||||
// Remove this client from its server.
|
||||
void unregister_client(void) { mServer->remove(this); }
|
||||
// Remove this client from its server, if any.
|
||||
void unregister_client(void) { if (mServer) mServer->remove(this); }
|
||||
|
||||
// Call 'ready' when you are ready (or not) to get a call to start().
|
||||
// Returns true if that call was made (immediately), otherwise it may happen later.
|
||||
|
||||
bool ready(synceventset_t events, synceventset_t yesno) // Set readiness of all events at once.
|
||||
// Set readiness of all events at once.
|
||||
void ready(synceventset_t events, synceventset_t yesno)
|
||||
{
|
||||
if (!mServer)
|
||||
{
|
||||
register_client();
|
||||
}
|
||||
return mServer->ready(events, yesno, this);
|
||||
mServer->ready(events, yesno, this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -47,6 +47,10 @@ public:
|
||||
// Create an LLFrameTimer and start it. After creation it is running and in the state expired (hasExpired will return true).
|
||||
LLFrameTimer(void) : mExpiry(0), mRunning(true), mPaused(false) { if (!sGlobalMutex) global_initialization(); setAge(0.0); }
|
||||
|
||||
//<singu>
|
||||
void copy(LLFrameTimer const& timer) { mStartTime = timer.mStartTime; mExpiry = timer.mExpiry; mRunning = timer.mRunning; mPaused = timer.mPaused; }
|
||||
//</singu>
|
||||
|
||||
// Atomic reads of static variables.
|
||||
|
||||
// Return the number of seconds since the start of the application.
|
||||
@@ -142,6 +146,9 @@ public:
|
||||
bool hasExpired() const { return getElapsedSeconds() >= mExpiry; }
|
||||
F32 getElapsedTimeF32() const { llassert(mRunning); return mPaused ? (F32)mStartTime : (F32)(getElapsedSeconds() - mStartTime); }
|
||||
bool getStarted() const { return mRunning; }
|
||||
//<singu>
|
||||
F64 getStartTime() const { llassert(!mPaused); return mStartTime; }
|
||||
//</singu>
|
||||
|
||||
// return the seconds since epoch when this timer will expire.
|
||||
F64 expiresAt() const;
|
||||
|
||||
@@ -5772,7 +5772,7 @@ void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data)
|
||||
}
|
||||
}
|
||||
|
||||
if (num_blocks)
|
||||
//if (num_blocks) Singu note: commented out; having blocks or not is totally irrelevant!
|
||||
{
|
||||
avatarp->processAnimationStateChanges();
|
||||
}
|
||||
|
||||
@@ -231,7 +231,9 @@ void LLVOAvatar::startMotion(U32 bit, F32 time_offset)
|
||||
{
|
||||
if (!isMotionActive(bit))
|
||||
{
|
||||
mMotionController.disable_syncing(); // Don't attempt to synchronize AIMaskedMotion.
|
||||
startMotion(mask2ID(bit), time_offset);
|
||||
mMotionController.enable_syncing();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3810,6 +3812,7 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
|
||||
|
||||
if (LLVOAvatar::sShowAnimationDebug)
|
||||
{
|
||||
addDebugText(llformat("at=%.1f", mMotionController.getAnimTime()));
|
||||
for (LLMotionController::motion_list_t::iterator iter = mMotionController.getActiveMotions().begin();
|
||||
iter != mMotionController.getActiveMotions().end(); ++iter)
|
||||
{
|
||||
@@ -3829,6 +3832,10 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
|
||||
motionp->getName().c_str(),
|
||||
(U32)motionp->getPriority());
|
||||
}
|
||||
if (motionp->server())
|
||||
{
|
||||
output += llformat(" rt=%.1f r=%d s=0x%xl", motionp->getRuntime(), motionp->mReadyEvents, motionp->server());
|
||||
}
|
||||
addDebugText(output);
|
||||
}
|
||||
}
|
||||
@@ -5499,6 +5506,7 @@ void LLVOAvatar::processAnimationStateChanges()
|
||||
}
|
||||
|
||||
// clear all current animations
|
||||
BOOL const AOEnabled = gSavedSettings.getBOOL("AOEnabled"); // Singu note: put this outside the loop.
|
||||
AnimIterator anim_it;
|
||||
for (anim_it = mPlayingAnimations.begin(); anim_it != mPlayingAnimations.end();)
|
||||
{
|
||||
@@ -5508,9 +5516,9 @@ void LLVOAvatar::processAnimationStateChanges()
|
||||
if (found_anim == mSignaledAnimations.end())
|
||||
{
|
||||
|
||||
if (isSelf())
|
||||
if (AOEnabled && isSelf())
|
||||
{
|
||||
if ((gSavedSettings.getBOOL("AOEnabled")) && LLFloaterAO::stopMotion(anim_it->first, FALSE)) // if the AO replaced this anim serverside then stop it serverside
|
||||
if (LLFloaterAO::stopMotion(anim_it->first, FALSE)) // if the AO replaced this anim serverside then stop it serverside
|
||||
{
|
||||
// return TRUE; //no local stop needed
|
||||
}
|
||||
@@ -5540,7 +5548,7 @@ void LLVOAvatar::processAnimationStateChanges()
|
||||
// </edit>
|
||||
if (processSingleAnimationStateChange(anim_it->first, TRUE))
|
||||
{
|
||||
if (isSelf() && gSavedSettings.getBOOL("AOEnabled")) // AO is only for ME
|
||||
if (AOEnabled && isSelf()) // AO is only for ME
|
||||
{
|
||||
LLFloaterAO::startMotion(anim_it->first, 0,FALSE); // AO overrides the anim if needed
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user