Merge branch 'master' of git://github.com/AlericInglewood/SingularityViewer

This commit is contained in:
Latif Khalifa
2014-04-04 20:08:19 +02:00
45 changed files with 2227 additions and 1022 deletions

View File

@@ -392,6 +392,40 @@ void InstanceTracker<T>::dump(void)
} // namespace debug
template<class T>
class AIDebugInstanceCounter
{
public:
static int sInstanceCount;
protected:
static void print_count(char const* name, int count, bool destruction);
AIDebugInstanceCounter()
{
print_count(typeid(T).name(), ++sInstanceCount, false);
}
AIDebugInstanceCounter(AIDebugInstanceCounter const&)
{
print_count(typeid(T).name(), ++sInstanceCount, false);
}
~AIDebugInstanceCounter()
{
print_count(typeid(T).name(), --sInstanceCount, true);
}
};
//static
template<class T>
int AIDebugInstanceCounter<T>::sInstanceCount;
//static
template<class T>
void AIDebugInstanceCounter<T>::print_count(char const* name, int count, bool destruction)
{
Dout(dc::notice, (destruction ? "Destructed " : "Constructing ") << name << ", now " << count << " instance" << ((count == 1) ? "." : "s."));
}
//! Debugging macro.
//
// Print "Entering " << \a data to channel \a cntrl and increment

View File

@@ -29,7 +29,6 @@ set(llcharacter_SOURCE_FILES
lljointsolverrp3.cpp
llkeyframefallmotion.cpp
llkeyframemotion.cpp
llkeyframemotionparam.cpp
llkeyframestandmotion.cpp
llkeyframewalkmotion.cpp
llmotion.cpp
@@ -57,7 +56,6 @@ set(llcharacter_HEADER_FILES
lljointstate.h
llkeyframefallmotion.h
llkeyframemotion.h
llkeyframemotionparam.h
llkeyframestandmotion.h
llkeyframewalkmotion.h
llmotion.h

View File

@@ -136,7 +136,6 @@ LLMotion* LLCharacter::findMotion( const LLUUID &id )
//-----------------------------------------------------------------------------
// createMotion()
// NOTE: Always assign the result to a LLPointer!
//-----------------------------------------------------------------------------
LLMotion* LLCharacter::createMotion( const LLUUID &id )
{
@@ -195,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)
@@ -529,3 +543,17 @@ LLAnimPauseRequest LLCharacter::requestPause()
return mPauseRequest;
}
void LLCharacter::requestPause(std::vector<LLAnimPauseRequest>& avatar_pause_handles)
{
mMotionController.pauseAllMotions();
avatar_pause_handles.push_back(mPauseRequest);
}
void LLCharacter::pauseAllSyncedCharacters(std::vector<LLAnimPauseRequest>& avatar_pause_handles)
{
// Pause this avatar.
requestPause(avatar_pause_handles);
// Also pause all avatars with synchronized motions.
mMotionController.pauseAllSyncedCharacters(avatar_pause_handles);
}

View File

@@ -146,6 +146,7 @@ public:
// is this motion active?
BOOL isMotionActive( const LLUUID& id );
bool isMotionActive(U32 bit) const { return mMotionController.isactive(bit); }
// Event handler for motion deactivation.
// Called when a motion has completely stopped and has been deactivated.
@@ -158,6 +159,8 @@ public:
void updateMotions(e_update_t update_type);
LLAnimPauseRequest requestPause();
void requestPause(std::vector<LLAnimPauseRequest>& avatar_pause_handles);
void pauseAllSyncedCharacters(std::vector<LLAnimPauseRequest>& avatar_pause_handles);
BOOL areAnimationsPaused() const { return mMotionController.isPaused(); }
void setAnimTimeFactor(F32 factor) { mMotionController.setTimeFactor(factor); }
void setTimeStep(F32 time_step) { mMotionController.setTimeStep(time_step); }

View File

@@ -49,7 +49,7 @@ S32 LLEditingMotion::sHandPosePriority = 3;
// LLEditingMotion()
// Class Constructor
//-----------------------------------------------------------------------------
LLEditingMotion::LLEditingMotion( const LLUUID &id) : LLMotion(id)
LLEditingMotion::LLEditingMotion(LLUUID const& id, LLMotionController* controller) : AIMaskedMotion(id, controller, ANIM_AGENT_EDITING)
{
mCharacter = NULL;
@@ -155,7 +155,7 @@ BOOL LLEditingMotion::onActivate()
mShoulderJoint.setRotation( mShoulderState->getJoint()->getRotation() );
mElbowJoint.setRotation( mElbowState->getJoint()->getRotation() );
return TRUE;
return AIMaskedMotion::onActivate();
}
//-----------------------------------------------------------------------------
@@ -256,12 +256,4 @@ BOOL LLEditingMotion::onUpdate(F32 time, U8* joint_mask)
return result;
}
//-----------------------------------------------------------------------------
// LLEditingMotion::onDeactivate()
//-----------------------------------------------------------------------------
void LLEditingMotion::onDeactivate()
{
}
// End

View File

@@ -49,11 +49,11 @@
// class LLEditingMotion
//-----------------------------------------------------------------------------
class LLEditingMotion :
public LLMotion
public AIMaskedMotion
{
public:
// Constructor
LLEditingMotion(const LLUUID &id);
LLEditingMotion(LLUUID const& id, LLMotionController* controller);
// Destructor
virtual ~LLEditingMotion();
@@ -65,7 +65,7 @@ public:
// static constructor
// all subclasses must implement such a function and register it
static LLMotion *create(const LLUUID &id) { return new LLEditingMotion(id); }
static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLEditingMotion(id, controller); }
public:
//-------------------------------------------------------------------------
@@ -107,9 +107,6 @@ public:
// must return FALSE when the motion is completed.
virtual BOOL onUpdate(F32 time, U8* joint_mask);
// called when a motion is deactivated
virtual void onDeactivate();
public:
//-------------------------------------------------------------------------
// joint states to be animated

View File

@@ -61,7 +61,7 @@ const F32 HAND_MORPH_BLEND_TIME = 0.2f;
// LLHandMotion()
// Class Constructor
//-----------------------------------------------------------------------------
LLHandMotion::LLHandMotion(const LLUUID &id) : LLMotion(id)
LLHandMotion::LLHandMotion(LLUUID const& id, LLMotionController* controller) : AIMaskedMotion(id, controller, ANIM_AGENT_HAND_MOTION)
{
mCharacter = NULL;
mLastTime = 0.f;
@@ -112,7 +112,7 @@ BOOL LLHandMotion::onActivate()
mCharacter->setVisualParamWeight(gHandPoseNames[mCurrentPose], 1.f);
mCharacter->updateVisualParams();
}
return TRUE;
return AIMaskedMotion::onActivate();
}
@@ -235,14 +235,6 @@ BOOL LLHandMotion::onUpdate(F32 time, U8* joint_mask)
return TRUE;
}
//-----------------------------------------------------------------------------
// LLHandMotion::onDeactivate()
//-----------------------------------------------------------------------------
void LLHandMotion::onDeactivate()
{
}
//-----------------------------------------------------------------------------
// LLHandMotion::getHandPoseName()
//-----------------------------------------------------------------------------

View File

@@ -45,7 +45,7 @@
// class LLHandMotion
//-----------------------------------------------------------------------------
class LLHandMotion :
public LLMotion
public AIMaskedMotion
{
public:
typedef enum e_hand_pose
@@ -68,7 +68,7 @@ public:
} eHandPose;
// Constructor
LLHandMotion(const LLUUID &id);
LLHandMotion(LLUUID const& id, LLMotionController* controller);
// Destructor
virtual ~LLHandMotion();
@@ -80,7 +80,7 @@ public:
// static constructor
// all subclasses must implement such a function and register it
static LLMotion *create(const LLUUID &id) { return new LLHandMotion(id); }
static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLHandMotion(id, controller); }
public:
//-------------------------------------------------------------------------
@@ -122,9 +122,6 @@ public:
// must return FALSE when the motion is completed.
virtual BOOL onUpdate(F32 time, U8* joint_mask);
// called when a motion is deactivated
virtual void onDeactivate();
virtual BOOL canDeprecate() { return FALSE; }
static std::string getHandPoseName(eHandPose pose);

View File

@@ -76,8 +76,8 @@ const F32 EYE_BLINK_TIME_DELTA = 0.005f; // time between one eye starting a blin
// LLHeadRotMotion()
// Class Constructor
//-----------------------------------------------------------------------------
LLHeadRotMotion::LLHeadRotMotion(const LLUUID &id) :
LLMotion(id),
LLHeadRotMotion::LLHeadRotMotion(LLUUID const& id, LLMotionController* controller) :
AIMaskedMotion(id, controller, ANIM_AGENT_HEAD_ROT),
mCharacter(NULL),
mTorsoJoint(NULL),
mHeadJoint(NULL)
@@ -104,7 +104,10 @@ LLHeadRotMotion::~LLHeadRotMotion()
LLMotion::LLMotionInitStatus LLHeadRotMotion::onInitialize(LLCharacter *character)
{
if (!character)
{
llwarns << "character is NULL." << llendl;
return STATUS_FAILURE;
}
mCharacter = character;
mPelvisJoint = character->getJoint("mPelvis");
@@ -169,16 +172,6 @@ LLMotion::LLMotionInitStatus LLHeadRotMotion::onInitialize(LLCharacter *characte
return STATUS_SUCCESS;
}
//-----------------------------------------------------------------------------
// LLHeadRotMotion::onActivate()
//-----------------------------------------------------------------------------
BOOL LLHeadRotMotion::onActivate()
{
return TRUE;
}
//-----------------------------------------------------------------------------
// LLHeadRotMotion::onUpdate()
//-----------------------------------------------------------------------------
@@ -263,19 +256,11 @@ BOOL LLHeadRotMotion::onUpdate(F32 time, U8* joint_mask)
}
//-----------------------------------------------------------------------------
// LLHeadRotMotion::onDeactivate()
//-----------------------------------------------------------------------------
void LLHeadRotMotion::onDeactivate()
{
}
//-----------------------------------------------------------------------------
// LLEyeMotion()
// Class Constructor
//-----------------------------------------------------------------------------
LLEyeMotion::LLEyeMotion(const LLUUID &id) : LLMotion(id)
LLEyeMotion::LLEyeMotion(LLUUID const& id, LLMotionController* controller) : AIMaskedMotion(id, controller, ANIM_AGENT_EYE)
{
mCharacter = NULL;
mEyeJitterTime = 0.f;
@@ -343,16 +328,6 @@ LLMotion::LLMotionInitStatus LLEyeMotion::onInitialize(LLCharacter *character)
return STATUS_SUCCESS;
}
//-----------------------------------------------------------------------------
// LLEyeMotion::onActivate()
//-----------------------------------------------------------------------------
BOOL LLEyeMotion::onActivate()
{
return TRUE;
}
//-----------------------------------------------------------------------------
// LLEyeMotion::onUpdate()
//-----------------------------------------------------------------------------
@@ -533,6 +508,8 @@ void LLEyeMotion::onDeactivate()
{
joint->setRotation(LLQuaternion::DEFAULT);
}
AIMaskedMotion::onDeactivate();
}
// End

View File

@@ -46,11 +46,11 @@
// class LLHeadRotMotion
//-----------------------------------------------------------------------------
class LLHeadRotMotion :
public LLMotion
public AIMaskedMotion
{
public:
// Constructor
LLHeadRotMotion(const LLUUID &id);
LLHeadRotMotion(LLUUID const& id, LLMotionController* controller);
// Destructor
virtual ~LLHeadRotMotion();
@@ -62,7 +62,7 @@ public:
// static constructor
// all subclasses must implement such a function and register it
static LLMotion *create(const LLUUID &id) { return new LLHeadRotMotion(id); }
static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLHeadRotMotion(id, controller); }
public:
//-------------------------------------------------------------------------
@@ -94,19 +94,11 @@ public:
// must return true to indicate success and be available for activation
virtual LLMotionInitStatus onInitialize(LLCharacter *character);
// called when a motion is activated
// must return TRUE to indicate success, or else
// it will be deactivated
virtual BOOL onActivate();
// called per time step
// must return TRUE while it is active, and
// must return FALSE when the motion is completed.
virtual BOOL onUpdate(F32 time, U8* joint_mask);
// called when a motion is deactivated
virtual void onDeactivate();
public:
//-------------------------------------------------------------------------
// joint states to be animated
@@ -129,11 +121,11 @@ public:
// class LLEyeMotion
//-----------------------------------------------------------------------------
class LLEyeMotion :
public LLMotion
public AIMaskedMotion
{
public:
// Constructor
LLEyeMotion(const LLUUID &id);
LLEyeMotion(LLUUID const& id, LLMotionController* controller);
// Destructor
virtual ~LLEyeMotion();
@@ -145,7 +137,7 @@ public:
// static constructor
// all subclasses must implement such a function and register it
static LLMotion *create( const LLUUID &id) { return new LLEyeMotion(id); }
static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLEyeMotion(id, controller); }
public:
//-------------------------------------------------------------------------
@@ -177,11 +169,6 @@ public:
// must return true to indicate success and be available for activation
virtual LLMotionInitStatus onInitialize(LLCharacter *character);
// called when a motion is activated
// must return TRUE to indicate success, or else
// it will be deactivated
virtual BOOL onActivate();
// called per time step
// must return TRUE while it is active, and
// must return FALSE when the motion is completed.

View File

@@ -41,7 +41,7 @@
#include "lldarray.h"
const S32 LL_CHARACTER_MAX_JOINTS_PER_MESH = 15;
const U32 LL_CHARACTER_MAX_JOINTS = 32; // must be divisible by 4!
const U32 LL_CHARACTER_MAX_JOINTS = 32; // must be divisible by 16!
const U32 LL_HAND_JOINT_NUM = 31;
const U32 LL_FACE_JOINT_NUM = 30;
const S32 LL_CHARACTER_MAX_PRIORITY = 7;

View File

@@ -49,7 +49,7 @@
// LLKeyframeFallMotion()
// Class Constructor
//-----------------------------------------------------------------------------
LLKeyframeFallMotion::LLKeyframeFallMotion(const LLUUID &id) : LLKeyframeMotion(id)
LLKeyframeFallMotion::LLKeyframeFallMotion(LLUUID const& id, LLMotionController* controller) : LLKeyframeMotion(id, controller)
{
mVelocityZ = 0.f;
mCharacter = NULL;

View File

@@ -47,7 +47,7 @@ class LLKeyframeFallMotion :
{
public:
// Constructor
LLKeyframeFallMotion(const LLUUID &id);
LLKeyframeFallMotion(LLUUID const& id, LLMotionController* controller);
// Destructor
virtual ~LLKeyframeFallMotion();
@@ -59,7 +59,7 @@ public:
// static constructor
// all subclasses must implement such a function and register it
static LLMotion *create(const LLUUID &id) { return new LLKeyframeFallMotion(id); }
static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLKeyframeFallMotion(id, controller); }
public:
//-------------------------------------------------------------------------

View File

@@ -84,38 +84,55 @@ LLKeyframeMotion::JointMotionList::~JointMotionList()
for_each(mJointMotionArray.begin(), mJointMotionArray.end(), DeletePointer());
}
U32 LLKeyframeMotion::JointMotionList::dumpDiagInfo()
//Singu: add parameter 'silent'.
U32 LLKeyframeMotion::JointMotionList::dumpDiagInfo(bool silent) const
{
S32 total_size = sizeof(JointMotionList);
for (U32 i = 0; i < getNumJointMotions(); i++)
{
LLKeyframeMotion::JointMotion* joint_motion_p = mJointMotionArray[i];
LLKeyframeMotion::JointMotion const* joint_motion_p = mJointMotionArray[i];
llinfos << "\tJoint " << joint_motion_p->mJointName << llendl;
if (!silent)
{
llinfos << "\tJoint " << joint_motion_p->mJointName << llendl;
}
if (joint_motion_p->mUsage & LLJointState::SCALE)
{
llinfos << "\t" << joint_motion_p->mScaleCurve.mNumKeys << " scale keys at "
<< joint_motion_p->mScaleCurve.mNumKeys * sizeof(ScaleKey) << " bytes" << llendl;
if (!silent)
{
llinfos << "\t" << joint_motion_p->mScaleCurve.mNumKeys << " scale keys at "
<< joint_motion_p->mScaleCurve.mNumKeys * sizeof(ScaleKey) << " bytes" << llendl;
}
total_size += joint_motion_p->mScaleCurve.mNumKeys * sizeof(ScaleKey);
}
if (joint_motion_p->mUsage & LLJointState::ROT)
{
llinfos << "\t" << joint_motion_p->mRotationCurve.mNumKeys << " rotation keys at "
<< joint_motion_p->mRotationCurve.mNumKeys * sizeof(RotationKey) << " bytes" << llendl;
if (!silent)
{
llinfos << "\t" << joint_motion_p->mRotationCurve.mNumKeys << " rotation keys at "
<< joint_motion_p->mRotationCurve.mNumKeys * sizeof(RotationKey) << " bytes" << llendl;
}
total_size += joint_motion_p->mRotationCurve.mNumKeys * sizeof(RotationKey);
}
if (joint_motion_p->mUsage & LLJointState::POS)
{
llinfos << "\t" << joint_motion_p->mPositionCurve.mNumKeys << " position keys at "
<< joint_motion_p->mPositionCurve.mNumKeys * sizeof(PositionKey) << " bytes" << llendl;
if (!silent)
{
llinfos << "\t" << joint_motion_p->mPositionCurve.mNumKeys << " position keys at "
<< joint_motion_p->mPositionCurve.mNumKeys * sizeof(PositionKey) << " bytes" << llendl;
}
total_size += joint_motion_p->mPositionCurve.mNumKeys * sizeof(PositionKey);
}
}
llinfos << "Size: " << total_size << " bytes" << llendl;
//Singu: Also add memory used by the constraints.
S32 constraints_size = mConstraints.size() * sizeof(constraint_list_t::value_type);
total_size += constraints_size;
if (!silent)
{
llinfos << "\t" << mConstraints.size() << " constraints at " << constraints_size << " bytes" << llendl;
llinfos << "Size: " << total_size << " bytes" << llendl;
}
return total_size;
}
@@ -427,9 +444,8 @@ void LLKeyframeMotion::JointMotion::update(LLJointState* joint_state, F32 time,
// LLKeyframeMotion()
// Class Constructor
//-----------------------------------------------------------------------------
LLKeyframeMotion::LLKeyframeMotion(const LLUUID &id)
: LLMotion(id),
mJointMotionList(NULL),
LLKeyframeMotion::LLKeyframeMotion(const LLUUID &id, LLMotionController* controller)
: LLMotion(id, controller),
mPelvisp(NULL),
mLastSkeletonSerialNum(0),
mLastUpdateTime(0.f),
@@ -452,9 +468,9 @@ LLKeyframeMotion::~LLKeyframeMotion()
//-----------------------------------------------------------------------------
// create()
//-----------------------------------------------------------------------------
LLMotion *LLKeyframeMotion::create(const LLUUID &id)
LLMotion* LLKeyframeMotion::create(LLUUID const& id, LLMotionController* controller)
{
return new LLKeyframeMotion(id);
return new LLKeyframeMotion(id, controller);
}
//-----------------------------------------------------------------------------
@@ -517,6 +533,7 @@ LLMotion::LLMotionInitStatus LLKeyframeMotion::onInitialize(LLCharacter *charact
case ASSET_FETCHED:
return STATUS_HOLD;
case ASSET_FETCH_FAILED:
llwarns << "Trying to initialize a motion that failed to be fetched." << llendl;
return STATUS_FAILURE;
case ASSET_LOADED:
return STATUS_SUCCESS;
@@ -526,7 +543,7 @@ LLMotion::LLMotionInitStatus LLKeyframeMotion::onInitialize(LLCharacter *charact
break;
}
LLKeyframeMotion::JointMotionList* joint_motion_list = LLKeyframeDataCache::getKeyframeData(getID());
LLKeyframeMotion::JointMotionListPtr joint_motion_list = LLKeyframeDataCache::getKeyframeData(getID());
if(joint_motion_list)
{
@@ -801,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);
@@ -819,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());
}
@@ -1227,13 +1283,42 @@ void LLKeyframeMotion::applyConstraint(JointConstraint* constraint, F32 time, U8
}
}
// Helper class.
template<typename T>
struct AIAutoDestruct
{
T* mPtr;
AIAutoDestruct() : mPtr(NULL) { }
~AIAutoDestruct() { delete mPtr; }
void add(T* ptr) { mPtr = ptr; }
};
//-----------------------------------------------------------------------------
// deserialize()
//-----------------------------------------------------------------------------
BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp)
{
BOOL old_version = FALSE;
mJointMotionList = new LLKeyframeMotion::JointMotionList;
//<singu>
// First add a new LLKeyframeMotion::JointMotionList to the cache, then assign a pointer
// to that to mJointMotionList. In LLs code the cache is never deleted again. Now it is
// is deleted when the last mJointMotionList pointer is destructed.
//
// It is possible that we get here for an already added animation, because animations can
// be requested multiple times (we get here from LLKeyframeMotion::onLoadComplete) when
// the animation was still downloading from a previous request for another LLMotionController
// object (avatar). In that case we just overwrite the old data while decoding it again.
mJointMotionList = LLKeyframeDataCache::getKeyframeData(getID());
bool singu_new_joint_motion_list = !mJointMotionList;
if (singu_new_joint_motion_list)
{
// Create a new JointMotionList.
mJointMotionList = LLKeyframeDataCache::createKeyframeData(getID());
}
//</singu>
//-------------------------------------------------------------------------
// get base priority
@@ -1396,8 +1481,10 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp)
return FALSE;
}
mJointMotionList->mJointMotionArray.clear();
mJointMotionList->mJointMotionArray.reserve(num_motions);
if (singu_new_joint_motion_list)
{
mJointMotionList->mJointMotionArray.reserve(num_motions);
}
mJointStates.clear();
mJointStates.reserve(num_motions);
@@ -1407,8 +1494,19 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp)
for(U32 i=0; i<num_motions; ++i)
{
AIAutoDestruct<JointMotion> watcher;
JointMotion* joint_motion = new JointMotion;
mJointMotionList->mJointMotionArray.push_back(joint_motion);
if (singu_new_joint_motion_list)
{
// Pass ownership to mJointMotionList.
mJointMotionList->mJointMotionArray.push_back(joint_motion);
}
else
{
// Just delete this at the end.
watcher.add(joint_motion);
}
std::string joint_name;
if (!dp.unpackString(joint_name, "joint_name"))
@@ -1836,7 +1934,15 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp)
return FALSE;
}
mJointMotionList->mConstraints.push_front(constraintp);
AIAutoDestruct<JointConstraintSharedData> watcher;
if (singu_new_joint_motion_list)
{
mJointMotionList->mConstraints.push_front(constraintp);
}
else
{
watcher.add(constraintp);
}
constraintp->mJointStateIndices = new S32[constraintp->mChainLength + 1]; // note: mChainLength is size-limited - comes from a byte
@@ -1876,15 +1982,12 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp)
if (constraintp->mJointStateIndices[i] < 0 )
{
llwarns << "No joint index for constraint " << i << llendl;
delete constraintp;
return FALSE;
}
}
}
}
// *FIX: support cleanup of old keyframe data
LLKeyframeDataCache::addKeyframeData(getID(), mJointMotionList);
mAssetStatus = ASSET_LOADED;
setupPose();
@@ -2226,16 +2329,24 @@ void LLKeyframeMotion::onLoadComplete(LLVFS *vfs,
//--------------------------------------------------------------------
// LLKeyframeDataCache::dumpDiagInfo()
//--------------------------------------------------------------------
void LLKeyframeDataCache::dumpDiagInfo()
// <singu>
// quiet = 0 : print everything in detail.
// 1 : print the UUIDs of all animations in the cache and the total memory usage.
// 2 : only print the total memory usage.
// </singu>
void LLKeyframeDataCache::dumpDiagInfo(int quiet)
{
// keep track of totals
U32 total_size = 0;
char buf[1024]; /* Flawfinder: ignore */
llinfos << "-----------------------------------------------------" << llendl;
llinfos << " Global Motion Table (DEBUG only)" << llendl;
llinfos << "-----------------------------------------------------" << llendl;
if (quiet < 2)
{
llinfos << "-----------------------------------------------------" << llendl;
llinfos << " Global Motion Table" << llendl;
llinfos << "-----------------------------------------------------" << llendl;
}
// print each loaded mesh, and it's memory usage
for (keyframe_data_map_t::iterator map_it = sKeyframeDataMap.begin();
@@ -2243,30 +2354,46 @@ void LLKeyframeDataCache::dumpDiagInfo()
{
U32 joint_motion_kb;
LLKeyframeMotion::JointMotionList *motion_list_p = map_it->second;
LLKeyframeMotion::JointMotionList const* motion_list_p = map_it->get();
llinfos << "Motion: " << map_it->first << llendl;
if (quiet < 2)
{
llinfos << "Motion: " << map_it->key() << llendl;
}
joint_motion_kb = motion_list_p->dumpDiagInfo();
total_size += joint_motion_kb;
if (motion_list_p)
{
joint_motion_kb = motion_list_p->dumpDiagInfo(quiet);
total_size += joint_motion_kb;
}
}
llinfos << "-----------------------------------------------------" << llendl;
if (quiet < 2)
{
llinfos << "-----------------------------------------------------" << llendl;
}
llinfos << "Motions\tTotal Size" << llendl;
snprintf(buf, sizeof(buf), "%d\t\t%d bytes", (S32)sKeyframeDataMap.size(), total_size ); /* Flawfinder: ignore */
llinfos << buf << llendl;
llinfos << "-----------------------------------------------------" << llendl;
if (quiet < 2)
{
llinfos << "-----------------------------------------------------" << llendl;
}
}
//--------------------------------------------------------------------
// LLKeyframeDataCache::addKeyframeData()
// LLKeyframeDataCache::createKeyframeData()
//--------------------------------------------------------------------
void LLKeyframeDataCache::addKeyframeData(const LLUUID& id, LLKeyframeMotion::JointMotionList* joint_motion_listp)
//<singu> This function replaces LLKeyframeDataCache::addKeyframeData and was rewritten to fix a memory leak (aka, the usage of AICachedPointer).
LLKeyframeMotion::JointMotionListPtr LLKeyframeDataCache::createKeyframeData(LLUUID const& id)
{
sKeyframeDataMap[id] = joint_motion_listp;
std::pair<keyframe_data_map_t::iterator, bool> result =
sKeyframeDataMap.insert(AICachedPointer<LLUUID, LLKeyframeMotion::JointMotionList>(id, new LLKeyframeMotion::JointMotionList, &sKeyframeDataMap));
llassert(result.second); // id may not already exist in the cache.
return &*result.first; // Construct and return a JointMotionListPt from a pointer to the actually inserted AICachedPointer.
}
//</singu>
//--------------------------------------------------------------------
// LLKeyframeDataCache::removeKeyframeData()
@@ -2276,7 +2403,6 @@ void LLKeyframeDataCache::removeKeyframeData(const LLUUID& id)
keyframe_data_map_t::iterator found_data = sKeyframeDataMap.find(id);
if (found_data != sKeyframeDataMap.end())
{
delete found_data->second;
sKeyframeDataMap.erase(found_data);
}
}
@@ -2284,14 +2410,14 @@ void LLKeyframeDataCache::removeKeyframeData(const LLUUID& id)
//--------------------------------------------------------------------
// LLKeyframeDataCache::getKeyframeData()
//--------------------------------------------------------------------
LLKeyframeMotion::JointMotionList* LLKeyframeDataCache::getKeyframeData(const LLUUID& id)
LLKeyframeMotion::JointMotionListPtr LLKeyframeDataCache::getKeyframeData(const LLUUID& id)
{
keyframe_data_map_t::iterator found_data = sKeyframeDataMap.find(id);
if (found_data == sKeyframeDataMap.end())
{
return NULL;
}
return found_data->second;
return &*found_data; // Construct and return a JointMotionListPt from a pointer to the found AICachedPointer.
}
//--------------------------------------------------------------------
@@ -2307,7 +2433,6 @@ LLKeyframeDataCache::~LLKeyframeDataCache()
//-----------------------------------------------------------------------------
void LLKeyframeDataCache::clear()
{
for_each(sKeyframeDataMap.begin(), sKeyframeDataMap.end(), DeletePairedPointer());
sKeyframeDataMap.clear();
}

View File

@@ -5,6 +5,7 @@
* $LicenseInfo:firstyear=2001&license=viewergpl$
*
* Copyright (c) 2001-2009, Linden Research, Inc.
* AICachedPointer and AICachedPointPtr copyright (c) 2013, Aleric Inglewood.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
@@ -48,10 +49,12 @@
#include "v3dmath.h"
#include "v3math.h"
#include "llbvhconsts.h"
#include <boost/intrusive_ptr.hpp>
class LLKeyframeDataCache;
class LLVFS;
class LLDataPacker;
class LLMotionController;
#define MIN_REQUIRED_PIXEL_AREA_KEYFRAME (40.f)
#define MAX_CHAIN_LENGTH (4)
@@ -59,6 +62,112 @@ class LLDataPacker;
const S32 KEYFRAME_MOTION_VERSION = 1;
const S32 KEYFRAME_MOTION_SUBVERSION = 0;
//-----------------------------------------------------------------------------
// <singu>
template<typename KEY, typename T>
class AICachedPointer;
template<typename KEY, typename T>
void intrusive_ptr_add_ref(AICachedPointer<KEY, T> const* p);
template<typename KEY, typename T>
void intrusive_ptr_release(AICachedPointer<KEY, T> const* p);
template<typename KEY, typename T>
class AICachedPointer
{
public:
typedef std::set<AICachedPointer<KEY, T> > container_type;
private:
KEY mKey; // The unique key.
LLPointer<T> mData; // The actual data pointer.
container_type* mCache; // Pointer to the underlaying cache.
mutable int mRefCount; // Number of AICachedPointerPtr's pointing to this object.
public:
// Construct a NULL pointer. This is needed when adding a new entry to a std::set, it is always first default constructed.
AICachedPointer(void) : mCache(NULL) { }
// Copy constructor. This is needed to replace the std::set inserted instance with its actual value.
AICachedPointer(AICachedPointer const& cptr) : mKey(cptr.mKey), mData(cptr.mData), mCache(cptr.mCache), mRefCount(0) { }
// Construct a AICachedPointer that points to 'ptr' with key 'key'.
AICachedPointer(KEY const& key, T* ptr, container_type* cache) : mKey(key), mData(ptr), mCache(cache), mRefCount(-1) { }
// Construct a temporary NULL pointer that can be used in a search for a key.
AICachedPointer(KEY const& key) : mKey(key), mCache(NULL) { }
// Accessors for key and data.
KEY const& key(void) const { return mKey; }
T const* get(void) const { return mData.get(); }
T* get(void) { return mData.get(); }
// Order only by key.
friend bool operator<(AICachedPointer const& cp1, AICachedPointer const& cp2) { return cp1.mKey < cp2.mKey; }
private:
friend void intrusive_ptr_add_ref<>(AICachedPointer<KEY, T> const* p);
friend void intrusive_ptr_release<>(AICachedPointer<KEY, T> const* p);
private:
AICachedPointer& operator=(AICachedPointer const&);
};
template<typename KEY, typename T>
void intrusive_ptr_add_ref(AICachedPointer<KEY, T> const* p)
{
llassert(p->mCache);
if (p->mCache)
{
p->mRefCount++;
}
}
template<typename KEY, typename T>
void intrusive_ptr_release(AICachedPointer<KEY, T> const* p)
{
llassert(p->mCache);
if (p->mCache)
{
if (--p->mRefCount == 0)
{
p->mCache->erase(p->mKey);
}
}
}
template<typename KEY, typename T>
class AICachedPointerPtr
{
private:
boost::intrusive_ptr<AICachedPointer<KEY, T> const> mPtr;
static int sCnt;
public:
AICachedPointerPtr(void) { ++sCnt; }
AICachedPointerPtr(AICachedPointerPtr const& cpp) : mPtr(cpp.mPtr) { ++sCnt; }
AICachedPointerPtr(AICachedPointer<KEY, T> const* cp) : mPtr(cp) { ++sCnt; }
~AICachedPointerPtr() { --sCnt; }
typedef boost::intrusive_ptr<AICachedPointer<KEY, T> const> const AICachedPointerPtr<KEY, T>::* const bool_type;
operator bool_type() const { return mPtr ? &AICachedPointerPtr<KEY, T>::mPtr : NULL; }
T const* operator->() const { return mPtr->get(); }
T* operator->() { return const_cast<AICachedPointer<KEY, T>&>(*mPtr).get(); }
T const& operator*() const { return *mPtr->get(); }
T& operator*() { return *const_cast<AICachedPointer<KEY, T>&>(*mPtr).get(); }
AICachedPointerPtr& operator=(AICachedPointerPtr const& cpp) { mPtr = cpp.mPtr; return *this; }
};
template<typename KEY, typename T>
int AICachedPointerPtr<KEY, T>::sCnt;
// </singu>
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// class LLKeyframeMotion
//-----------------------------------------------------------------------------
@@ -68,7 +177,7 @@ class LLKeyframeMotion :
friend class LLKeyframeDataCache;
public:
// Constructor
LLKeyframeMotion(const LLUUID &id);
LLKeyframeMotion(const LLUUID &id, LLMotionController* controller);
// Destructor
virtual ~LLKeyframeMotion();
@@ -85,7 +194,7 @@ public:
// static constructor
// all subclasses must implement such a function and register it
static LLMotion *create(const LLUUID& id);
static LLMotion* create(LLUUID const& id, LLMotionController* controller);
public:
//-------------------------------------------------------------------------
@@ -158,7 +267,7 @@ public:
U32 getFileSize();
BOOL serialize(LLDataPacker& dp) const;
BOOL deserialize(LLDataPacker& dp);
BOOL isLoaded() { return mJointMotionList != NULL; }
BOOL isLoaded() { return !!mJointMotionList; }
// setters for modifying a keyframe animation
@@ -393,7 +502,7 @@ public:
//-------------------------------------------------------------------------
// JointMotionList
//-------------------------------------------------------------------------
class JointMotionList
class JointMotionList : public LLRefCount
{
public:
std::vector<JointMotion*> mJointMotionArray;
@@ -416,19 +525,22 @@ public:
public:
JointMotionList();
~JointMotionList();
U32 dumpDiagInfo();
U32 dumpDiagInfo(bool silent = false) const;
JointMotion* getJointMotion(U32 index) const { llassert(index < mJointMotionArray.size()); return mJointMotionArray[index]; }
U32 getNumJointMotions() const { return mJointMotionArray.size(); }
};
// Singu: Type of a pointer to the cached pointer (in LLKeyframeDataCache::sKeyframeDataMap) to a JointMotionList object.
typedef AICachedPointerPtr<LLUUID, JointMotionList> JointMotionListPtr;
protected:
static LLVFS* sVFS;
//-------------------------------------------------------------------------
// Member Data
//-------------------------------------------------------------------------
JointMotionList* mJointMotionList;
JointMotionListPtr mJointMotionList; // singu: automatically clean up cache entry when destructed.
std::vector<LLPointer<LLJointState> > mJointStates;
LLJoint* mPelvisp;
LLCharacter* mCharacter;
@@ -442,21 +554,24 @@ protected:
class LLKeyframeDataCache
{
public:
// *FIX: implement this as an actual singleton member of LLKeyframeMotion
private:
friend class LLKeyframeMotion;
LLKeyframeDataCache(){};
~LLKeyframeDataCache();
typedef std::map<LLUUID, class LLKeyframeMotion::JointMotionList*> keyframe_data_map_t;
public:
typedef AICachedPointer<LLUUID, LLKeyframeMotion::JointMotionList>::container_type keyframe_data_map_t; // singu: add automatic cache cleanup.
static keyframe_data_map_t sKeyframeDataMap;
static void addKeyframeData(const LLUUID& id, LLKeyframeMotion::JointMotionList*);
static LLKeyframeMotion::JointMotionList* getKeyframeData(const LLUUID& id);
//<singu>
static LLKeyframeMotion::JointMotionListPtr createKeyframeData(LLUUID const& id); // id may not exist.
static LLKeyframeMotion::JointMotionListPtr getKeyframeData(LLUUID const& id); // id may or may not exists. Returns a NULL pointer when it doesn't exist.
//</singu>
static void removeKeyframeData(const LLUUID& id);
//print out diagnostic info
static void dumpDiagInfo();
static void dumpDiagInfo(int quiet = 0); // singu: added param 'quiet'.
static void clear();
};

View File

@@ -1,456 +0,0 @@
/**
* @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::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;
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::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;
}
//-----------------------------------------------------------------------------
// LLKeyframeMotionParam::onActivate()
//-----------------------------------------------------------------------------
BOOL LLKeyframeMotionParam::onActivate()
{
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;
paramMotion.mMotion->activate(mActivationTimestamp);
}
}
return TRUE;
}
//-----------------------------------------------------------------------------
// 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 = &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->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;
}
//-----------------------------------------------------------------------------
// LLKeyframeMotionParam::onDeactivate()
//-----------------------------------------------------------------------------
void LLKeyframeMotionParam::onDeactivate()
{
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;
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::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;
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(path, LL_APR_R, &fileSize);
apr_file_t* fp = infile.getFileHandle() ;
if (!fp || fileSize == 0)
{
llinfos << "ERROR: can't open: " << path << llendl;
return FALSE;
}
// allocate a text buffer
try
{
std::vector<char> 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 )
{
llinfos << "ERROR: error while reading from " << path << llendl;
return FALSE;
}
llinfos << "Loading parametric keyframe data for: " << getName() << llendl;
//-------------------------------------------------------------------------
// 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))
{
llinfos << "WARNING: can't read parametric motion" << llendl;
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;
}
catch(std::bad_alloc)
{
llinfos << "ERROR: Unable to allocate keyframe text buffer." << llendl;
return FALSE;
}
}
// End

View File

@@ -1,176 +0,0 @@
/**
* @file llkeyframemotionparam.h
* @brief Implementation of LLKeframeMotionParam class.
*
* $LicenseInfo:firstyear=2002&license=viewergpl$
*
* Copyright (c) 2002-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#ifndef LL_LLKEYFRAMEMOTIONPARAM_H
#define LL_LLKEYFRAMEMOTIONPARAM_H
//-----------------------------------------------------------------------------
// Header files
//-----------------------------------------------------------------------------
#include <string>
#include "llmotion.h"
#include "lljointstate.h"
#include "v3math.h"
#include "llquaternion.h"
#include "linked_lists.h"
#include "llkeyframemotion.h"
//-----------------------------------------------------------------------------
// class LLKeyframeMotionParam
//-----------------------------------------------------------------------------
class LLKeyframeMotionParam :
public LLMotion
{
public:
// Constructor
LLKeyframeMotionParam(const LLUUID &id);
// Destructor
virtual ~LLKeyframeMotionParam();
public:
//-------------------------------------------------------------------------
// functions to support MotionController and MotionRegistry
//-------------------------------------------------------------------------
// static constructor
// all subclasses must implement such a function and register it
static LLMotion *create(const LLUUID &id) { return new LLKeyframeMotionParam(id); }
public:
//-------------------------------------------------------------------------
// animation callbacks to be implemented by subclasses
//-------------------------------------------------------------------------
// motions must specify whether or not they loop
virtual BOOL getLoop() {
return TRUE;
}
// motions must report their total duration
virtual F32 getDuration() {
return mDuration;
}
// motions must report their "ease in" duration
virtual F32 getEaseInDuration() {
return mEaseInDuration;
}
// motions must report their "ease out" duration.
virtual F32 getEaseOutDuration() {
return mEaseOutDuration;
}
// motions must report their priority
virtual LLJoint::JointPriority getPriority() {
return mPriority;
}
virtual LLMotionBlendType getBlendType() { return NORMAL_BLEND; }
// called to determine when a motion should be activated/deactivated based on avatar pixel coverage
virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_KEYFRAME; }
// run-time (post constructor) initialization,
// called after parameters have been set
// must return true to indicate success and be available for activation
virtual LLMotionInitStatus onInitialize(LLCharacter *character);
// called when a motion is activated
// must return TRUE to indicate success, or else
// it will be deactivated
virtual BOOL onActivate();
// called per time step
// must return TRUE while it is active, and
// must return FALSE when the motion is completed.
virtual BOOL onUpdate(F32 time, U8* joint_mask);
// called when a motion is deactivated
virtual void onDeactivate();
virtual LLPose* getPose() { return mPoseBlender.getBlendedPose();}
protected:
//-------------------------------------------------------------------------
// new functions defined by this subclass
//-------------------------------------------------------------------------
struct ParameterizedMotion
{
ParameterizedMotion(LLMotion* motion, F32 param) : mMotion(motion), mParam(param) {}
LLMotion* mMotion;
F32 mParam;
};
// add a motion and associated parameter triplet
BOOL addKeyframeMotion(char *name, const LLUUID &id, char *param, F32 value);
// set default motion for LOD and retrieving blend constants
void setDefaultKeyframeMotion(char *);
BOOL loadMotions();
protected:
//-------------------------------------------------------------------------
// Member Data
//-------------------------------------------------------------------------
struct compare_motions
{
bool operator() (const ParameterizedMotion& a, const ParameterizedMotion& b) const
{
if (a.mParam != b.mParam)
return (a.mParam < b.mParam);
else
return a.mMotion < b.mMotion;
}
};
typedef std::set < ParameterizedMotion, compare_motions > motion_list_t;
typedef std::map <std::string, motion_list_t > motion_map_t;
motion_map_t mParameterizedMotions;
LLMotion* mDefaultKeyframeMotion;
LLCharacter* mCharacter;
LLPoseBlender mPoseBlender;
F32 mEaseInDuration;
F32 mEaseOutDuration;
F32 mDuration;
LLJoint::JointPriority mPriority;
LLUUID mTransactionID;
};
#endif // LL_LLKEYFRAMEMOTIONPARAM_H

View File

@@ -50,7 +50,7 @@ const F32 POSITION_THRESHOLD = 0.1f;
// LLKeyframeStandMotion()
// Class Constructor
//-----------------------------------------------------------------------------
LLKeyframeStandMotion::LLKeyframeStandMotion(const LLUUID &id) : LLKeyframeMotion(id)
LLKeyframeStandMotion::LLKeyframeStandMotion(LLUUID const& id, LLMotionController* controller) : LLKeyframeMotion(id, controller)
{
mFlipFeet = FALSE;
mCharacter = NULL;

View File

@@ -48,7 +48,7 @@ class LLKeyframeStandMotion :
{
public:
// Constructor
LLKeyframeStandMotion(const LLUUID &id);
LLKeyframeStandMotion(LLUUID const& id, LLMotionController* controller);
// Destructor
virtual ~LLKeyframeStandMotion();
@@ -60,7 +60,7 @@ public:
// static constructor
// all subclasses must implement such a function and register it
static LLMotion *create(const LLUUID &id) { return new LLKeyframeStandMotion(id); }
static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLKeyframeStandMotion(id, controller); }
public:
//-------------------------------------------------------------------------

View File

@@ -55,8 +55,8 @@ const F32 SPEED_ADJUST_TIME_CONSTANT = 0.1f; // time constant for speed adjustm
// LLKeyframeWalkMotion()
// Class Constructor
//-----------------------------------------------------------------------------
LLKeyframeWalkMotion::LLKeyframeWalkMotion(const LLUUID &id)
: LLKeyframeMotion(id),
LLKeyframeWalkMotion::LLKeyframeWalkMotion(LLUUID const& id, LLMotionController* controller)
: LLKeyframeMotion(id, controller),
mCharacter(NULL),
mCyclePhase(0.0f),
mRealTimeLast(0.0f),
@@ -138,8 +138,8 @@ BOOL LLKeyframeWalkMotion::onUpdate(F32 time, U8* joint_mask)
// LLWalkAdjustMotion()
// Class Constructor
//-----------------------------------------------------------------------------
LLWalkAdjustMotion::LLWalkAdjustMotion(const LLUUID &id) :
LLMotion(id),
LLWalkAdjustMotion::LLWalkAdjustMotion(LLUUID const& id, LLMotionController* controller) :
AIMaskedMotion(id, controller, ANIM_AGENT_WALK_ADJUST),
mLastTime(0.f),
mAnimSpeed(0.f),
mAdjustedSpeed(0.f),
@@ -193,7 +193,7 @@ BOOL LLWalkAdjustMotion::onActivate()
F32 rightAnkleOffset = (mRightAnkleJoint->getWorldPosition() - mCharacter->getCharacterPosition()).magVec();
mAnkleOffset = llmax(leftAnkleOffset, rightAnkleOffset);
return TRUE;
return AIMaskedMotion::onActivate();
}
//-----------------------------------------------------------------------------
@@ -325,13 +325,14 @@ BOOL LLWalkAdjustMotion::onUpdate(F32 time, U8* joint_mask)
void LLWalkAdjustMotion::onDeactivate()
{
mCharacter->removeAnimationData("Walk Speed");
AIMaskedMotion::onDeactivate();
}
//-----------------------------------------------------------------------------
// LLFlyAdjustMotion::LLFlyAdjustMotion()
//-----------------------------------------------------------------------------
LLFlyAdjustMotion::LLFlyAdjustMotion(const LLUUID &id)
: LLMotion(id),
LLFlyAdjustMotion::LLFlyAdjustMotion(LLUUID const& id, LLMotionController* controller)
: AIMaskedMotion(id, controller, ANIM_AGENT_FLY_ADJUST),
mRoll(0.f)
{
mName = "fly_adjust";
@@ -368,7 +369,7 @@ BOOL LLFlyAdjustMotion::onActivate()
mPelvisState->setPosition(LLVector3::zero);
mPelvisState->setRotation(LLQuaternion::DEFAULT);
mRoll = 0.f;
return TRUE;
return AIMaskedMotion::onActivate();
}
//-----------------------------------------------------------------------------

View File

@@ -52,7 +52,7 @@ class LLKeyframeWalkMotion :
friend class LLWalkAdjustMotion;
public:
// Constructor
LLKeyframeWalkMotion(const LLUUID &id);
LLKeyframeWalkMotion(LLUUID const& id, LLMotionController* controller);
// Destructor
virtual ~LLKeyframeWalkMotion();
@@ -64,7 +64,7 @@ public:
// static constructor
// all subclasses must implement such a function and register it
static LLMotion *create(const LLUUID &id) { return new LLKeyframeWalkMotion(id); }
static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLKeyframeWalkMotion(id, controller); }
public:
//-------------------------------------------------------------------------
@@ -86,11 +86,11 @@ public:
S32 mDownFoot;
};
class LLWalkAdjustMotion : public LLMotion
class LLWalkAdjustMotion : public AIMaskedMotion
{
public:
// Constructor
LLWalkAdjustMotion(const LLUUID &id);
LLWalkAdjustMotion(LLUUID const& id, LLMotionController* controller);
public:
//-------------------------------------------------------------------------
@@ -99,7 +99,7 @@ public:
// static constructor
// all subclasses must implement such a function and register it
static LLMotion *create(const LLUUID &id) { return new LLWalkAdjustMotion(id); }
static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLWalkAdjustMotion(id, controller); }
public:
//-------------------------------------------------------------------------
@@ -136,11 +136,11 @@ public:
F32 mAnkleOffset;
};
class LLFlyAdjustMotion : public LLMotion
class LLFlyAdjustMotion : public AIMaskedMotion
{
public:
// Constructor
LLFlyAdjustMotion(const LLUUID &id);
LLFlyAdjustMotion(LLUUID const& id, LLMotionController* controller);
public:
//-------------------------------------------------------------------------
@@ -149,7 +149,7 @@ public:
// static constructor
// all subclasses must implement such a function and register it
static LLMotion *create(const LLUUID &id) { return new LLFlyAdjustMotion(id); }
static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLFlyAdjustMotion(id, controller); }
public:
//-------------------------------------------------------------------------
@@ -157,7 +157,6 @@ public:
//-------------------------------------------------------------------------
virtual LLMotionInitStatus onInitialize(LLCharacter *character);
virtual BOOL onActivate();
virtual void onDeactivate() {};
virtual BOOL onUpdate(F32 time, U8* joint_mask);
virtual LLJoint::JointPriority getPriority(){return LLJoint::HIGHER_PRIORITY;}
virtual BOOL getLoop() { return TRUE; }

View File

@@ -37,6 +37,125 @@
#include "llmotion.h"
#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)
{
#ifdef SHOW_ASSERT
mReadyEvents = 0;
#endif
}
//</singu>
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
@@ -48,10 +167,11 @@
// LLMotion()
// Class Constructor
//-----------------------------------------------------------------------------
LLMotion::LLMotion( const LLUUID &id ) :
LLMotion::LLMotion(LLUUID const& id, LLMotionController* controller) :
mStopped(TRUE),
mActive(FALSE),
mID(id),
mController(controller),
mActivationTimestamp(0.f),
mStopTimestamp(0.f),
mSendStopTimestamp(F32_MAX),
@@ -147,6 +267,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();
}
@@ -159,6 +292,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);
@@ -174,4 +315,19 @@ BOOL LLMotion::canDeprecate()
return TRUE;
}
//-----------------------------------------------------------------------------
// AIMaskedMotion
//-----------------------------------------------------------------------------
BOOL AIMaskedMotion::onActivate()
{
mController->activated(mMaskBit);
return TRUE;
}
void AIMaskedMotion::onDeactivate()
{
mController->deactivated(mMaskBit);
}
// End

View File

@@ -38,16 +38,85 @@
//-----------------------------------------------------------------------------
#include <string>
#include "aisyncclient.h"
#include "llerror.h"
#include "llpose.h"
#include "lluuid.h"
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;
@@ -66,7 +135,7 @@ public:
};
// Constructor
LLMotion(const LLUUID &id);
LLMotion(LLUUID const& id, LLMotionController* controller);
// Destructor
virtual ~LLMotion();
@@ -114,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
@@ -181,6 +265,9 @@ protected:
//-------------------------------------------------------------------------
std::string mName; // instance name assigned by motion controller
LLUUID mID;
//<singu>
LLMotionController* mController;
//</singu>
F32 mActivationTimestamp; // time when motion was activated
F32 mStopTimestamp; // time when motion was told to stop
@@ -199,9 +286,9 @@ protected:
class LLTestMotion : public LLMotion
{
public:
LLTestMotion(const LLUUID &id) : LLMotion(id){}
LLTestMotion(LLUUID const& id, LLMotionController* controller) : LLMotion(id, controller){}
~LLTestMotion() {}
static LLMotion *create(const LLUUID& id) { return new LLTestMotion(id); }
static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLTestMotion(id, controller); }
BOOL getLoop() { return FALSE; }
F32 getDuration() { return 0.0f; }
F32 getEaseInDuration() { return 0.0f; }
@@ -223,9 +310,9 @@ public:
class LLNullMotion : public LLMotion
{
public:
LLNullMotion(const LLUUID &id) : LLMotion(id) {}
LLNullMotion(LLUUID const& id, LLMotionController* controller) : LLMotion(id, controller) {}
~LLNullMotion() {}
static LLMotion *create(const LLUUID &id) { return new LLNullMotion(id); }
static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLNullMotion(id, controller); }
// motions must specify whether or not they loop
/*virtual*/ BOOL getLoop() { return TRUE; }
@@ -266,5 +353,41 @@ public:
// called when a motion is deactivated
/*virtual*/ void onDeactivate() {}
};
//-----------------------------------------------------------------------------
// AIMaskedMotion
//-----------------------------------------------------------------------------
// These motions have a bit assigned in LLMotionController::mActiveMask
// that is set and uset upon activation/deactivation.
// This must be in the same order as ANIM_AGENT_BODY_NOISE_ID through ANIM_AGENT_WALK_ADJUST_ID in llvoavatar.cpp.
U32 const ANIM_AGENT_BODY_NOISE = 0x001;
U32 const ANIM_AGENT_BREATHE_ROT = 0x002;
U32 const ANIM_AGENT_PHYSICS_MOTION = 0x004;
U32 const ANIM_AGENT_EDITING = 0x008;
U32 const ANIM_AGENT_EYE = 0x010;
U32 const ANIM_AGENT_FLY_ADJUST = 0x020;
U32 const ANIM_AGENT_HAND_MOTION = 0x040;
U32 const ANIM_AGENT_HEAD_ROT = 0x080;
U32 const ANIM_AGENT_PELVIS_FIX = 0x100;
U32 const ANIM_AGENT_TARGET = 0x200;
U32 const ANIM_AGENT_WALK_ADJUST = 0x400;
class AIMaskedMotion : public LLMotion
{
private:
U32 mMaskBit;
public:
AIMaskedMotion(LLUUID const& id, LLMotionController* controller, U32 mask_bit) : LLMotion(id, controller), mMaskBit(mask_bit) { }
/*virtual*/ BOOL onActivate();
/*virtual*/ void onDeactivate();
U32 getMaskBit(void) const { return mMaskBit; }
};
#endif // LL_LLMOTION_H

View File

@@ -97,7 +97,7 @@ void LLMotionRegistry::markBad( const LLUUID& id )
//-----------------------------------------------------------------------------
// createMotion()
//-----------------------------------------------------------------------------
LLMotion *LLMotionRegistry::createMotion( const LLUUID &id )
LLMotion* LLMotionRegistry::createMotion(LLUUID const& id, LLMotionController* controller)
{
LLMotionConstructor constructor = get_if_there(mMotionTable, id, LLMotionConstructor(NULL));
LLMotion* motion = NULL;
@@ -105,11 +105,11 @@ LLMotion *LLMotionRegistry::createMotion( const LLUUID &id )
if ( constructor == NULL )
{
// *FIX: need to replace with a better default scheme. RN
motion = LLKeyframeMotion::create(id);
motion = LLKeyframeMotion::create(id, controller);
}
else
{
motion = constructor(id);
motion = constructor(id, controller);
}
return motion;
@@ -126,18 +126,22 @@ LLMotion *LLMotionRegistry::createMotion( const LLUUID &id )
// Class Constructor
//-----------------------------------------------------------------------------
LLMotionController::LLMotionController()
: mTimeFactor(sCurrentTimeFactor),
: mIsSelf(FALSE),
mTimeFactor(sCurrentTimeFactor),
mCharacter(NULL),
mAnimTime(0.f),
mActiveMask(0),
mDisableSyncing(0),
mHidden(false),
mHaveVisibleSyncedMotions(false),
mPrevTimerElapsed(0.f),
mAnimTime(0.f),
mLastTime(0.0f),
mHasRunOnce(FALSE),
mPaused(FALSE),
mPauseTime(0.f),
mTimeStep(0.f),
mTimeStepCount(0),
mLastInterp(0.f),
mIsSelf(FALSE)
mLastInterp(0.f)
{
}
@@ -168,7 +172,15 @@ void LLMotionController::deleteAllMotions()
mLoadingMotions.clear();
mLoadedMotions.clear();
mActiveMotions.clear();
//<singu>
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();
}
@@ -178,26 +190,19 @@ void LLMotionController::deleteAllMotions()
//-----------------------------------------------------------------------------
void LLMotionController::purgeExcessMotions()
{
if (mLoadedMotions.size() > MAX_MOTION_INSTANCES)
//<singu>
// The old code attempted to remove non-active motions from mDeprecatedMotions,
// but that is nonsense: there are no motions in mDeprecatedMotions that are not active.
if (mLoadedMotions.size() <= MAX_MOTION_INSTANCES)
{
// clean up deprecated motions
for (motion_set_t::iterator deprecated_motion_it = mDeprecatedMotions.begin();
deprecated_motion_it != mDeprecatedMotions.end(); )
{
motion_set_t::iterator cur_iter = deprecated_motion_it++;
LLMotion* cur_motionp = *cur_iter;
if (!isMotionActive(cur_motionp))
{
// Motion is deprecated so we know it's not cannonical,
// we can safely remove the instance
removeMotionInstance(cur_motionp); // modifies mDeprecatedMotions
mDeprecatedMotions.erase(cur_iter);
}
}
// Speed up, no need to create motions_to_kill.
return;
}
//</singu>
std::set<LLUUID> motions_to_kill;
if (mLoadedMotions.size() > MAX_MOTION_INSTANCES)
if (1) // Singu: leave indentation alone...
{
// too many motions active this frame, kill all blenders
mPoseBlender.clearBlenders();
@@ -308,24 +313,44 @@ BOOL LLMotionController::registerMotion( const LLUUID& id, LLMotionConstructor c
void LLMotionController::removeMotion( const LLUUID& id)
{
LLMotion* motionp = findMotion(id);
mAllMotions.erase(id);
removeMotionInstance(motionp);
//<singu>
// If a motion is erased from mAllMotions, it must be deleted.
if (motionp)
{
mAllMotions.erase(id);
removeMotionInstance(motionp);
delete motionp;
}
//</singu>
}
// removes instance of a motion from all runtime structures, but does
// not erase entry by ID, as this could be a duplicate instance
// use removeMotion(id) to remove all references to a given motion by id.
// use removeMotion(id) to remove a reference to a given motion by id
// (that will not remove (active) deprecated motions).
void LLMotionController::removeMotionInstance(LLMotion* motionp)
{
if (motionp)
{
llassert(findMotion(motionp->getID()) != motionp);
if (motionp->isActive())
motionp->deactivate();
mLoadingMotions.erase(motionp);
mLoadedMotions.erase(motionp);
mActiveMotions.remove(motionp);
delete motionp;
//<singu>
// Deactivation moved here. Only delete motionp when it is being removed from mDeprecatedMotions.
if (motionp->isActive())
{
motionp->deactivate();
// If a motion is deactivated, it must be removed from mDeprecatedMotions if there.
motion_set_t::iterator found_it = mDeprecatedMotions.find(motionp);
if (found_it != mDeprecatedMotions.end())
{
mDeprecatedMotions.erase(found_it);
// If a motion is erased from mDeprecatedMotions, it must be deleted.
delete motionp;
}
}
//</singu>
}
}
@@ -341,7 +366,7 @@ LLMotion* LLMotionController::createMotion( const LLUUID &id )
if (!motion)
{
// look up constructor and create it
motion = sRegistry.createMotion(id);
motion = sRegistry.createMotion(id, this);
if (!motion)
{
return NULL;
@@ -393,6 +418,7 @@ BOOL LLMotionController::startMotion(const LLUUID &id, F32 start_offset)
if (motion
&& !mPaused
&& motion->canDeprecate()
&& motion->isActive() // singu: do not deprecate motions that are not active.
&& motion->getFadeWeight() > 0.01f // not LOD-ed out
&& (motion->isBlending() || motion->getStopTime() != 0.f))
{
@@ -418,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;
}
@@ -774,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)
@@ -782,12 +832,15 @@ void LLMotionController::updateLoadingMotions()
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);
}
// 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;
}
}
@@ -821,8 +874,15 @@ void LLMotionController::updateMotions(bool force_update)
{
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;
//<singu>
// This old code is nonsense.
//S32 quantum_count = llmax(0, llround((update_time - time_interval) / mTimeStep)) + 1;
// (update_time - time_interval) / mTimeStep is an integer! We need llround to get rid of floating point errors, not llfloor.
// Moreover, just rounding off to the nearest integer with llround(update_time / mTimeStep) makes a lot more sense:
// it is the best we can do to get as close to what we should draw as possible.
// However, mAnimTime may only be incremented; therefore make sure of that with the llmax.
S32 quantum_count = llmax(llround(update_time / mTimeStep), llceil(mAnimTime / mTimeStep));
//</singu>
if (quantum_count == mTimeStepCount)
{
// we're still in same time quantum as before, so just interpolate and exit
@@ -848,7 +908,8 @@ void LLMotionController::updateMotions(bool force_update)
}
else
{
mAnimTime = update_time;
// Singu note: mAnimTime may never be set back in time.
mAnimTime = llmax(mAnimTime, update_time);
}
}
@@ -915,6 +976,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
@@ -970,18 +1037,16 @@ BOOL LLMotionController::activateMotionInstance(LLMotion *motion, F32 time)
//-----------------------------------------------------------------------------
BOOL LLMotionController::deactivateMotionInstance(LLMotion *motion)
{
motion->deactivate();
motion_set_t::iterator found_it = mDeprecatedMotions.find(motion);
if (found_it != mDeprecatedMotions.end())
{
// deprecated motions need to be completely excised
removeMotionInstance(motion);
mDeprecatedMotions.erase(found_it);
removeMotionInstance(motion); // singu note: this deactivates motion and removes it from mDeprecatedMotions.
}
else
{
// for motions that we are keeping, simply remove from active queue
motion->deactivate(); // singu note: moved here from the top of the function.
mActiveMotions.remove(motion);
}
@@ -1049,11 +1114,26 @@ void LLMotionController::dumpMotions()
state_string += std::string("L");
if (std::find(mActiveMotions.begin(), mActiveMotions.end(), motion)!=mActiveMotions.end())
state_string += std::string("A");
if (mDeprecatedMotions.find(motion) != mDeprecatedMotions.end())
state_string += std::string("D");
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>
}
//-----------------------------------------------------------------------------
@@ -1061,11 +1141,11 @@ void LLMotionController::dumpMotions()
//-----------------------------------------------------------------------------
void LLMotionController::deactivateAllMotions()
{
for (motion_map_t::iterator iter = mAllMotions.begin();
iter != mAllMotions.end(); iter++)
// 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();)
{
LLMotion* motionp = iter->second;
deactivateMotionInstance(motionp);
deactivateMotionInstance(*iter++); // This might invalidate iter by erasing it from mActiveMotions.
}
}
@@ -1086,8 +1166,7 @@ void LLMotionController::flushAllMotions()
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();
@@ -1096,13 +1175,136 @@ 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->isActive()) // 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->isActive()) // 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;
}
}
}
}
void LLMotionController::pauseAllSyncedCharacters(std::vector<LLAnimPauseRequest>& avatar_pause_handles)
{
// 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->isActive()) // Skip motions that aren't synchronized at all or that are not active.
{
// Run over all clients of the found servers.
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;
}
controller->requestPause(avatar_pause_handles);
}
}
}
}
void LLMotionController::requestPause(std::vector<LLAnimPauseRequest>& avatar_pause_handles)
{
if (mCharacter)
{
mCharacter->requestPause(avatar_pause_handles);
}
}
//</singu>
//-----------------------------------------------------------------------------
// pause()
//-----------------------------------------------------------------------------

View File

@@ -51,11 +51,14 @@
// This is necessary because llcharacter.h includes this file.
//-----------------------------------------------------------------------------
class LLCharacter;
class LLMotionController;
class LLPauseRequestHandle;
typedef LLPointer<LLPauseRequestHandle> LLAnimPauseRequest;
//-----------------------------------------------------------------------------
// LLMotionRegistry
//-----------------------------------------------------------------------------
typedef LLMotion*(*LLMotionConstructor)(const LLUUID &id);
typedef LLMotion* (*LLMotionConstructor)(LLUUID const& id, LLMotionController*);
class LLMotionRegistry
{
@@ -72,7 +75,7 @@ public:
// creates a new instance of a named motion
// returns NULL motion is not registered
LLMotion *createMotion( const LLUUID &id );
LLMotion* createMotion(LLUUID const& id, LLMotionController* controller);
// initialization of motion failed, don't try to create this motion again
void markBad( const LLUUID& id );
@@ -115,7 +118,6 @@ public:
// unregisters a motion with the controller
// (actually just forwards call to motion registry)
// returns true if successfull
void removeMotion( const LLUUID& id );
// start motion
@@ -150,10 +152,20 @@ public:
//Flush is a liar.
void deactivateAllMotions();
//<singu>
void activated(U32 bit) { mActiveMask |= bit; }
void deactivated(U32 bit) { mActiveMask &= ~bit; }
bool isactive(U32 bit) const { return (mActiveMask & bit) != 0; }
//</singu>
// pause and continue all motions
void pauseAllMotions();
void unpauseAllMotions();
BOOL isPaused() const { return mPaused; }
//<singu>
void requestPause(std::vector<LLAnimPauseRequest>& avatar_pause_handles);
void pauseAllSyncedCharacters(std::vector<LLAnimPauseRequest>& avatar_pause_handles);
//</singu>
void setTimeStep(F32 step);
@@ -180,7 +192,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);
@@ -205,10 +220,13 @@ protected:
// Life cycle of an animation:
//
// Animations are instantiated and immediately put in the mAllMotions map for their entire lifetime.
// Singu note: that is not true, they are moved to mDeprecatedMotions (often) for the last part of their lifetime.
// If the animations depend on any asset data, the appropriate data is fetched from the data server,
// and the animation is put on the mLoadingMotions list.
// Once an animations is loaded, it will be initialized and put on the mLoadedMotions list.
// Any animation that is currently playing also sits in the mActiveMotions list.
// Singu note: animations are only put in mDeprecatedMotions if and while they are playing,
// therefore animations in mDeprecatedMotions will be (must be) active and in mActiveMotions.
typedef std::map<LLUUID, LLMotion*> motion_map_t;
motion_map_t mAllMotions;
@@ -217,7 +235,13 @@ protected:
motion_set_t mLoadedMotions;
motion_list_t mActiveMotions;
motion_set_t mDeprecatedMotions;
//<singu>
U32 mActiveMask;
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;
@@ -230,6 +254,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>
};
//-----------------------------------------------------------------------------

View File

@@ -52,7 +52,7 @@ const F32 TORSO_ROT_FRACTION = 0.5f;
// LLTargetingMotion()
// Class Constructor
//-----------------------------------------------------------------------------
LLTargetingMotion::LLTargetingMotion(const LLUUID &id) : LLMotion(id)
LLTargetingMotion::LLTargetingMotion(LLUUID const& id, LLMotionController* controller) : AIMaskedMotion(id, controller, ANIM_AGENT_TARGET)
{
mCharacter = NULL;
mName = "targeting";
@@ -99,14 +99,6 @@ LLMotion::LLMotionInitStatus LLTargetingMotion::onInitialize(LLCharacter *charac
return STATUS_SUCCESS;
}
//-----------------------------------------------------------------------------
// LLTargetingMotion::onActivate()
//-----------------------------------------------------------------------------
BOOL LLTargetingMotion::onActivate()
{
return TRUE;
}
//-----------------------------------------------------------------------------
// LLTargetingMotion::onUpdate()
//-----------------------------------------------------------------------------
@@ -166,12 +158,4 @@ BOOL LLTargetingMotion::onUpdate(F32 time, U8* joint_mask)
return result;
}
//-----------------------------------------------------------------------------
// LLTargetingMotion::onDeactivate()
//-----------------------------------------------------------------------------
void LLTargetingMotion::onDeactivate()
{
}
// End

View File

@@ -48,11 +48,11 @@
// class LLTargetingMotion
//-----------------------------------------------------------------------------
class LLTargetingMotion :
public LLMotion
public AIMaskedMotion
{
public:
// Constructor
LLTargetingMotion(const LLUUID &id);
LLTargetingMotion(LLUUID const& id, LLMotionController* controller);
// Destructor
virtual ~LLTargetingMotion();
@@ -64,7 +64,7 @@ public:
// static constructor
// all subclasses must implement such a function and register it
static LLMotion *create(const LLUUID &id) { return new LLTargetingMotion(id); }
static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLTargetingMotion(id, controller); }
public:
//-------------------------------------------------------------------------
@@ -96,19 +96,11 @@ public:
// must return true to indicate success and be available for activation
virtual LLMotionInitStatus onInitialize(LLCharacter *character);
// called when a motion is activated
// must return TRUE to indicate success, or else
// it will be deactivated
virtual BOOL onActivate();
// called per time step
// must return TRUE while it is active, and
// must return FALSE when the motion is completed.
virtual BOOL onUpdate(F32 time, U8* joint_mask);
// called when a motion is deactivated
virtual void onDeactivate();
public:
LLCharacter *mCharacter;

View File

@@ -20,6 +20,7 @@ set(llcommon_SOURCE_FILES
aialert.cpp
aifile.cpp
aiframetimer.cpp
aisyncclient.cpp
aithreadid.cpp
imageids.cpp
indra_constants.cpp
@@ -112,6 +113,7 @@ set(llcommon_HEADER_FILES
aifile.h
aiframetimer.h
airecursive.h
aisyncclient.h
aithreadid.h
aithreadsafe.h
bitpack.h

View File

@@ -0,0 +1,698 @@
/**
* @file aisyncclient.cpp
*
* Copyright (c) 2013, Aleric Inglewood.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution.
*
* CHANGELOG
* and additional copyright holders.
*
* 13/12/2013
* - Initial version, written by Aleric Inglewood @ SL
*/
/*
* AISyncClient : the base class of a client object (LLMotion) that needs to be informed
* of the state of other such objects and/or to poll a server object about the state of
* other such objects, in order to be and stay synchronized with those other objects.
* In the case of an LLMotion (animation), all clients would be started and stopped at
* the same time, as long as they are part of the same synchronization group (AISyncServer).
*
* AISyncKey: object that determines what synchronization group a client belongs to.
* When a new client is created, a new AISyncKey is created too, using information from
* the client, the current frame number and the current frame time. If two AISyncKey
* compare equal, using operator==(AISyncKey const& key1, AISyncKey const& key2),
* then the clients that created them need to be synchronized.
*
* AISyncServer: object that represents a group of clients that need to be synchronized:
* it's a wrapper around a std::list<AISyncClientData> with pointers to all the clients
* that need to be synchronized. It also stores the AISyncKey of the first (oldest) client
* that was added. Clients with keys that compare equal to that will be added.
* If a client is added with a synckeytype_t that is larger then it always replaces
* the existing key however.
*
* AISyncServerMap: A wrapper around std::list<boost::intrusive_ptr<AISyncServer> >
* that stores pointers to all currently existing AISyncServer objects. New entries
* are added at the end, so the oldest key is at the front.
*
* AISyncServerMap list + - - - - - - + - - - - - - + ... The AISyncServerMap is
* | | | a list with refcounted
* V V V pointers to AISyncServers.
* AISyncClient +--> AISyncServer
* | <--- list key ---> AISyncKey Each AISyncServer is a
* DerivedClient . list of normal pointers
* . AISyncClients and one
* <--- . pointer to a AISyncKey.
* Each AISyncClient is the
* base class of a DerivedClient
* and pointers back to the
* server with a refcounted
* pointer.
*
* A new client is passed to the AISyncServerMap to be stored in a new or existing AISyncServer
* object, using the key that the client produces. A boost::intrusive_ptr<AISyncServer> member
* of the client is set to point to this server.
*
* The lifetime of the server objects is determined by the intrusive_ptr objects that
* point to it: all the clients (which have an externally determined lifetime) and one
* pointer in the AISyncServerMap. However, regularly a check is done on all servers in
* the list to find expired servers: objects with keys older than two frames and older
* than 0.1 seconds; if such a server is found and it has zero or one client, then the
* client is unregistered and the pointer (and thus the server) removed from the
* AISyncServerMap. If it has two or more clients then the entry is kept until both
* clients are removed, which therefore can only be detected in intrusive_ptr_release
* which only has access to the server object. The server then is removed from the list
* by searching through it for the pointer to the server.
*/
#include "sys.h"
#include "aisyncclient.h"
#include <cmath>
#include <algorithm>
#include "debug.h"
bool operator==(AISyncKey const& key1, AISyncKey const& key2)
{
// Test if these keys match based on time.
if (std::abs((S32)(key1.mStartFrameCount - key2.mStartFrameCount)) > 1 &&
std::abs(key1.mFrameTimer.getStartTime() - key2.mFrameTimer.getStartTime()) >= AISyncKey::sExpirationTime)
{
return false;
}
// Time matches, let the derived classes determine if they also match otherwise.
return key1.equals(key2);
}
#ifdef CWDEBUG
struct SyncEventSet {
synceventset_t mBits;
SyncEventSet(synceventset_t bits) : mBits(bits) { }
};
std::ostream& operator<<(std::ostream& os, SyncEventSet const& ses)
{
for (int b = sizeof(ses.mBits) * 8 - 1; b >= 0; --b)
{
int m = 1 << b;
os << ((ses.mBits & m) ? '1' : '0');
}
return os;
}
void print_clients(AISyncServer const* server, AISyncServer::client_list_t const& client_list)
{
Dout(dc::notice, "Clients of server " << server << ": ");
for (AISyncServer::client_list_t::const_iterator iter = client_list.begin(); iter != client_list.end(); ++ iter)
{
llassert(iter->mClientPtr->mReadyEvents == iter->mReadyEvents);
Dout(dc::notice, "-> " << iter->mClientPtr << " : " << SyncEventSet(iter->mReadyEvents));
}
}
#endif
void AISyncServerMap::register_client(AISyncClient* client, AISyncKey* new_key)
{
// 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);
// Find if a server with this key already exists.
AISyncServer* server = NULL;
for (server_list_t::iterator iter = mServers.begin(); iter != mServers.end();)
{
boost::intrusive_ptr<AISyncServer>& server_ptr = *iter++; // Immediately increment iter because the call to unregister_last_client will erase it.
AISyncKey const& server_key(server_ptr->key());
if (server_key.is_older_than(*new_key)) // This means that the server key is expired: a new key will never match.
{
if (server_ptr->never_synced()) // This means that it contains one or zero clients and will never contain more.
{
// Get rid of this server.
server_ptr->unregister_last_client(); // This will cause the server to be deleted, and erased from mServers.
}
continue;
}
if (*new_key == server_key)
{
server = server_ptr.get();
// mServers stores new servers in strict order of the creation time of the keys,
// so once we find a server with a key that is equal, none of the remaining servers
// will have expired if they were never synced and we're done with the loop.
// Servers that synced might have been added later, but we don't unregister
// clients from those anyway because their sync partner might still show up.
break;
}
}
if (server)
{
// A server already exists.
// Keep the oldest key, unless this new key has a synckeytype_t that is larger!
if (new_key->getkeytype() > server->key().getkeytype())
{
server->swapkey(new_key);
}
delete new_key;
}
else
{
// Create a new server for this client. Transfers the ownership of the key allocation to the server.
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',
server_list_t::iterator new_where = where;
while (where != mServers.begin()) // unless there exists a server before that
{
--new_where;
if (new_key->getCreationTime() > (*new_where)->key().getCreationTime()) // and the new key is not younger then that,
{
break;
}
where = new_where; // then insert it before that element (etc).
}
// 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);
}
// Add the client to the server.
server->add(client);
}
#ifdef SYNC_TESTSUITE
void AISyncServer::sanity_check(void) const
{
synceventset_t ready_events = (synceventset_t)-1; // All clients are ready.
client_list_t::const_iterator client_iter = mClients.begin();
while (client_iter != mClients.end())
{
ready_events &= client_iter->mReadyEvents;
++client_iter;
}
synceventset_t pending_events = 0; // At least one client is ready.
client_iter = mClients.begin();
while (client_iter != mClients.end())
{
pending_events |= client_iter->mReadyEvents;
++client_iter;
}
llassert(ready_events == mReadyEvents);
llassert(pending_events == mPendingEvents);
}
#endif
void AISyncServer::add(AISyncClient* client)
{
#ifdef SYNC_TESTSUITE
sanity_check();
#endif
// The client can't already be ready when it isn't even added to a server yet.
llassert(!client->mReadyEvents);
synceventset_t old_ready_events = mReadyEvents;
// A new client is not ready for anything.
mReadyEvents = 0;
// Set mSynchronized if after adding client we'll have more than 1 client (that prevents the
// server from being deleted unless all clients are actually destructed or explicitly unregistered).
if (!mSynchronized && mClients.size() > 0)
{
mSynchronized = true;
}
// Trigger the existing clients to be not-ready anymore (if we were before).
trigger(old_ready_events);
// Only then add the new client (so that it didn't get not-ready trigger).
mClients.push_back(client);
client->mServer = this;
#ifdef SYNC_TESTSUITE
sanity_check();
#endif
}
void AISyncServer::remove(AISyncClient* client)
{
#ifdef SYNC_TESTSUITE
sanity_check();
#endif
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).
client_list_t::iterator found_client = mClients.end();
while (client_iter != mClients.end())
{
if (client_iter->mClientPtr == client)
{
found_client = client_iter;
}
else
{
remaining_ready_events &= client_iter->mReadyEvents;
remaining_pending_events |= client_iter->mReadyEvents;
}
++client_iter;
}
llassert(found_client != mClients.end());
// This must be the same as client->mReadyEvents.
llassert(found_client->mReadyEvents == client->mReadyEvents);
mClients.erase(found_client);
synceventset_t old_ready_events = mReadyEvents;
mReadyEvents = remaining_ready_events;
mPendingEvents = remaining_pending_events;
trigger(old_ready_events);
client->mServer.reset();
client->deregistered();
#ifdef SYNC_TESTSUITE
sanity_check();
#endif
}
void AISyncServer::unregister_last_client(void)
{
#ifdef SYNC_TESTSUITE
sanity_check();
#endif
// This function may only be called for servers with exactly one client that was never (potentially) synchronized.
llassert(!mSynchronized && mClients.size() == 1);
AISyncClient* client = mClients.begin()->mClientPtr;
mClients.clear();
client->mServer.reset();
llassert(mReadyEvents == client->mReadyEvents);
llassert(mPendingEvents == mReadyEvents);
// No need to update mReadyEvents/mPendingEvents because the server is going to be deleted,
// but inform the client that is no longer has a server.
client->deregistered();
#ifdef SYNC_TESTSUITE
sanity_check();
#endif
}
void AISyncServer::trigger(synceventset_t old_ready_events)
{
// If event 1 changed, informat all clients about it.
if (((old_ready_events ^ mReadyEvents) & 1))
{
for (client_list_t::iterator client_iter = mClients.begin(); client_iter != mClients.end(); ++client_iter)
{
if ((mReadyEvents & 1))
{
client_iter->mClientPtr->event1_ready();
}
else
{
client_iter->mClientPtr->event1_not_ready();
}
}
}
}
void AISyncServer::ready(synceventset_t events, synceventset_t yesno, AISyncClient* client)
{
#ifdef SYNC_TESTSUITE
sanity_check();
#endif
synceventset_t added_events = events & yesno;
synceventset_t removed_events = events & ~yesno;
// 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.
synceventset_t remaining_pending_events = 0; // At least one client is ready (waiting for the other clients thus).
client_list_t::iterator found_client = mClients.end();
for (client_list_t::iterator client_iter = mClients.begin(); client_iter != mClients.end(); ++client_iter)
{
if (client_iter->mClientPtr == client)
{
found_client = client_iter;
}
else
{
remaining_ready_events &= client_iter->mReadyEvents;
remaining_pending_events |= client_iter->mReadyEvents;
}
}
llassert(mReadyEvents == (remaining_ready_events & found_client->mReadyEvents));
llassert(mPendingEvents == (remaining_pending_events | found_client->mReadyEvents));
found_client->mReadyEvents &= ~removed_events;
found_client->mReadyEvents |= added_events;
#ifdef SHOW_ASSERT
client->mReadyEvents = found_client->mReadyEvents;
#endif
synceventset_t old_ready_events = mReadyEvents;
mReadyEvents = remaining_ready_events & found_client->mReadyEvents;
mPendingEvents = remaining_pending_events | found_client->mReadyEvents;
trigger(old_ready_events);
#ifdef SYNC_TESTSUITE
sanity_check();
#endif
}
void intrusive_ptr_add_ref(AISyncServer* server)
{
server->mRefCount++;
}
void intrusive_ptr_release(AISyncServer* server)
{
llassert(server->mRefCount > 0);
server->mRefCount--;
if (server->mRefCount == 0)
{
delete server;
}
else if (server->mRefCount == 1)
{
// If the the last pointer to this server is in the the AISyncServerMap, then delete that too.
AISyncServerMap::instance().remove_server(server);
}
}
void AISyncServerMap::remove_server(AISyncServer* server)
{
for (server_list_t::iterator iter = mServers.begin(); iter != mServers.end(); ++iter)
{
if (server == iter->get())
{
mServers.erase(iter); // This causes server to be deleted too.
return;
}
}
// The server must be found: this function is only called from intrusive_ptr_release for servers
// with just a single server_ptr_t left, which must be the one in mServers (otherwise it wasn't
// even registered properly!)
llassert(false);
}
//=============================================================================
// SYNC_TESTSUITE
//=============================================================================
#ifdef SYNC_TESTSUITE
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <boost/io/ios_state.hpp>
//static
U32 LLFrameTimer::sFrameCount;
double innerloop_count = 0;
double LLFrameTimer::getCurrentTime()
{
return innerloop_count * 0.001;
}
template<synckeytype_t synckeytype>
class TestsuiteKey : public AISyncKey
{
private:
int mIndex;
public:
TestsuiteKey(int index) : mIndex(index) { }
int getIndex(void) const { return mIndex; }
// 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;
}
/*virtual*/ bool equals(AISyncKey const& key) const
{
synckeytype_t const theotherkey = (synckeytype_t)(synckeytype ^ 0x100);
switch (key.getkeytype())
{
case synckeytype:
{
// The other key is of the same type.
TestsuiteKey<synckeytype> const& test_key = static_cast<TestsuiteKey<synckeytype> const&>(key);
return (mIndex & 1) == (test_key.mIndex & 1);
}
case theotherkey:
{
TestsuiteKey<theotherkey> const& test_key = static_cast<TestsuiteKey<theotherkey> const&>(key);
return (mIndex & 2) == (test_key.getIndex() & 2);
}
default:
// The keys must be in the same syncgroup.
break;
}
return false;
}
};
template<synckeytype_t synckeytype>
class TestsuiteClient : public AISyncClient
{
// AISyncClient events.
protected:
/*virtual*/ AISyncKey* createSyncKey(void) const
{
return new TestsuiteKey<synckeytype>(mIndex);
}
private:
int mIndex;
bool mRequestedRegistered;
synceventset_t mRequestedReady;
bool mActualReady1;
public:
TestsuiteClient() : mIndex(-1), mRequestedRegistered(false), mRequestedReady(0), mActualReady1(false) { }
~TestsuiteClient() { if (is_registered()) this->ready(mRequestedReady, (synceventset_t)0); }
void setIndex(int index) { mIndex = index; }
protected:
/*virtual*/ void event1_ready(void)
{
#ifdef DEBUG_SYNCOUTPUT
Dout(dc::notice, "Calling TestsuiteClient<" << synckeytype << ">::event1_ready() (mIndex = " << mIndex << ") of client " << this);
#endif
llassert(!mActualReady1);
mActualReady1 = true;
}
/*virtual*/ void event1_not_ready(void)
{
#ifdef DEBUG_SYNCOUTPUT
Dout(dc::notice, "Calling TestsuiteClient<" << synckeytype << ">::event1_not_ready() (mIndex = " << mIndex << ") of client " << this);
#endif
llassert(mActualReady1);
mActualReady1 = false;
}
// This is called when the server expired and we're the only client on it.
/*virtual*/ void deregistered(void)
{
#ifdef DEBUG_SYNCOUTPUT
DoutEntering(dc::notice, "TestsuiteClient<" << synckeytype << ">::deregistered(), with this = " << this);
#endif
mRequestedRegistered = false;
mRequestedReady = 0;
mActualReady1 = false;
this->mReadyEvents = 0;
}
private:
bool is_registered(void) const { return this->server(); }
public:
void change_state(unsigned long r);
bool getRequestedRegistered(void) const { return mRequestedRegistered; }
synceventset_t getRequestedReady(void) const { return mRequestedReady; }
};
TestsuiteClient<synckeytype_test1a>* client1ap;
TestsuiteClient<synckeytype_test1b>* client1bp;
TestsuiteClient<synckeytype_test2a>* client2ap;
TestsuiteClient<synckeytype_test2b>* client2bp;
int const number_of_clients_per_syncgroup = 8;
template<synckeytype_t synckeytype>
void TestsuiteClient<synckeytype>::change_state(unsigned long r)
{
bool change_registered = r & 1;
r >>= 1;
synceventset_t toggle_events = r & 15;
r >>= 4;
if (change_registered)
{
if (mRequestedRegistered && !mRequestedReady)
{
mRequestedRegistered = false;
this->unregister_client();
}
}
else if (toggle_events)
{
mRequestedReady ^= toggle_events;
mRequestedRegistered = true;
this->ready(toggle_events, mRequestedReady & toggle_events);
}
llassert(mRequestedRegistered == is_registered());
AISyncServer* server = this->server();
if (mRequestedRegistered)
{
synceventset_t all_ready = synceventset_t(-1);
synceventset_t any_ready = 0;
int nr = 0;
for (int cl = 0; cl < number_of_clients_per_syncgroup; ++cl)
{
switch ((synckeytype & 0xff))
{
case syncgroup_test1:
{
if (client1ap[cl].server() == server)
{
if (client1ap[cl].getRequestedRegistered())
{
++nr;
all_ready &= client1ap[cl].getRequestedReady();
any_ready |= client1ap[cl].getRequestedReady();
}
}
if (client1bp[cl].server() == server)
{
if (client1bp[cl].getRequestedRegistered())
{
++nr;
all_ready &= client1bp[cl].getRequestedReady();
any_ready |= client1bp[cl].getRequestedReady();
}
}
break;
}
case syncgroup_test2:
{
if (client2ap[cl].server() == server)
{
if (client2ap[cl].getRequestedRegistered())
{
++nr;
all_ready &= client2ap[cl].getRequestedReady();
any_ready |= client2ap[cl].getRequestedReady();
}
}
if (client2bp[cl].server() == server)
{
if (client2bp[cl].getRequestedRegistered())
{
++nr;
all_ready &= client2bp[cl].getRequestedReady();
any_ready |= client2bp[cl].getRequestedReady();
}
}
break;
}
}
}
llassert(nr == server->getClients().size());
llassert(!!(all_ready & 1) == mActualReady1);
llassert(this->server()->events_with_all_clients_ready() == all_ready);
llassert(this->server()->events_with_at_least_one_client_ready() == any_ready);
llassert(nr == 0 || (any_ready & all_ready) == all_ready);
}
llassert(mRequestedReady == this->mReadyEvents);
}
int main()
{
Debug(libcw_do.on());
Debug(dc::notice.on());
Debug(libcw_do.set_ostream(&std::cout));
Debug(list_channels_on(libcw_do));
unsigned short seed16v[3] = { 0x1234, 0xfedc, 0x7091 };
for (int k = 0;; ++k)
{
std::cout << "Loop: " << k << "; SEED: " << std::hex << seed16v[0] << ", " << seed16v[1] << ", " << seed16v[2] << std::dec << std::endl;
++LLFrameTimer::sFrameCount;
seed48(seed16v);
seed16v[0] = lrand48() & 0xffff;
seed16v[1] = lrand48() & 0xffff;
seed16v[2] = lrand48() & 0xffff;
TestsuiteClient<synckeytype_test1a> client1a[number_of_clients_per_syncgroup];
TestsuiteClient<synckeytype_test1b> client1b[number_of_clients_per_syncgroup];
TestsuiteClient<synckeytype_test2a> client2a[number_of_clients_per_syncgroup];
TestsuiteClient<synckeytype_test2b> client2b[number_of_clients_per_syncgroup];
client1ap = client1a;
client1bp = client1b;
client2ap = client2a;
client2bp = client2b;
for (int i = 0; i < number_of_clients_per_syncgroup; ++i)
{
client1a[i].setIndex(i);
client1b[i].setIndex(i);
client2a[i].setIndex(i);
client2b[i].setIndex(i);
}
for (int j = 0; j < 1000000; ++j)
{
innerloop_count += 1;
#ifdef DEBUG_SYNCOUTPUT
Dout(dc::notice, "Innerloop: " << j);
#endif
unsigned long r = lrand48();
synckeytype_t keytype = (r & 1) ? ((r & 2) ? synckeytype_test1a : synckeytype_test1b) : ((r & 2) ? synckeytype_test2a : synckeytype_test2b);
r >>= 2;
int cl = (r & 255) % number_of_clients_per_syncgroup;
r >>= 8;
switch (keytype)
{
case synckeytype_test1a:
client1a[cl].change_state(r);
break;
case synckeytype_test1b:
client1b[cl].change_state(r);
break;
case synckeytype_test2a:
client2a[cl].change_state(r);
break;
case synckeytype_test2b:
client2b[cl].change_state(r);
break;
}
}
}
}
#endif

View File

@@ -0,0 +1,303 @@
/**
* @file aisyncclient.h
* @brief Declaration of AISyncClient.
*
* Copyright (c) 2013, Aleric Inglewood.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution.
*
* CHANGELOG
* and additional copyright holders.
*
* 12/12/2013
* Initial version, written by Aleric Inglewood @ SL
*/
#ifndef AI_SYNC_CLIENT_H
#define AI_SYNC_CLIENT_H
#ifdef SYNC_TESTSUITE
/*
* To compile the testsuite, run:
*
* cd indra/llcommon
* g++ -O3 -DCWDEBUG -DSYNC_TESTSUITE -I. -I../cwdebug aisyncclient.cpp -lcwd
*/
#include <stdint.h>
#include <cassert>
typedef uint32_t U32;
typedef int32_t S32;
typedef uint64_t U64;
typedef float F32;
typedef double F64;
#define LL_COMMON_API
#define SHOW_ASSERT
#define ASSERT_ONLY_COMMA(...) , __VA_ARGS__
#define llassert assert
struct LLFrameTimer
{
double mStartTime;
double mExpiry;
static double getCurrentTime(void);
static U32 sFrameCount;
static U32 getFrameCount() { return sFrameCount; }
F64 getStartTime() const { return mStartTime; }
void reset(double expiration) { mStartTime = getCurrentTime(); mExpiry = mStartTime + expiration; }
bool hasExpired(void) const { return getCurrentTime() > mExpiry; }
};
template<typename T>
struct LLSingleton
{
static T sInstance;
static T& instance(void) { return sInstance; }
};
template<typename T>
T LLSingleton<T>::sInstance;
#else // !SYNC_TESTSUITE
#include "llsingleton.h"
#include "llframetimer.h"
#endif
#include <list>
#include <boost/intrusive_ptr.hpp>
//---------------------------------------------------------------------------------------------------------------------
// Keys with a different syncgroup are never equal (so they are never synchronized).
enum syncgroups
{
#ifdef SYNC_TESTSUITE
syncgroup_test1,
syncgroup_test2,
#else
syncgroup_motions, // Syncgroup used for animations.
#endif
syncgroup_size
};
// Each key type must return a unique identifier that exists of its syncgroup (the least significant 8 bit) plus a few bit to make it unique (bit 9 and higher).
enum synckeytype_t
{
#ifdef SYNC_TESTSUITE
synckeytype_test1a = 0x000 + syncgroup_test1,
synckeytype_test1b = 0x100 + syncgroup_test1,
synckeytype_test2a = 0x000 + syncgroup_test2,
synckeytype_test2b = 0x100 + syncgroup_test2,
#else
synckeytype_motion = syncgroup_motions // There is currently only one key type in the syncgroup_motions group: AISyncKeyMotion.
#endif
};
typedef U32 synceventset_t; // A mask where each bit represents a ready state.
class LL_COMMON_API AISyncKey
{
private:
LLFrameTimer mFrameTimer; // This timer is started at the moment the sync key is created.
U32 mStartFrameCount; // The frame count at which the timer was started.
static F32 const sExpirationTime = 0.1; // In seconds.
public:
// Constructor.
AISyncKey(AISyncKey const* from_key) : mStartFrameCount(from_key ? from_key->mStartFrameCount : LLFrameTimer::getFrameCount())
{
if (from_key)
{
mFrameTimer.copy(from_key->mFrameTimer);
}
else
{
mFrameTimer.reset(sExpirationTime);
}
}
// Destructor.
virtual ~AISyncKey() { }
// Return true if this key expired.
bool expired(void) const
{
// The key has expired when sExpirationTime seconds have elapsed AND at least two frames have passed.
return mFrameTimer.getFrameCount() > mStartFrameCount + 1 && mFrameTimer.hasExpired();
}
// Returns true if this object and key would not compare equal based on time because this object is too old.
bool is_older_than(AISyncKey const& key) const
{
return key.mStartFrameCount > mStartFrameCount + 1 && key.mFrameTimer.getStartTime() > mFrameTimer.getStartTime() + sExpirationTime;
}
// Return the creation time of this key (in number of seconds since application start).
F64 getCreationTime(void) const { return mFrameTimer.getStartTime(); }
// Returns true if the two keys match, meaning that they should be synchronized.
friend bool operator==(AISyncKey const& key1, AISyncKey const& key2);
// Returns an ID that uniquely identifies the derived type.
// Currently the only derived type is AISyncKeyMotion with ID synckeytype_motion.
virtual synckeytype_t getkeytype(void) const = 0;
// Returns true if the data in the derived objects match, meaning that they should be synchronized.
virtual bool equals(AISyncKey const& key) const = 0;
};
// Forward declaration.
class AISyncClient;
class AISyncServer;
LL_COMMON_API extern void intrusive_ptr_add_ref(AISyncServer* server);
LL_COMMON_API extern void intrusive_ptr_release(AISyncServer* server);
struct LL_COMMON_API AISyncClientData
{
AISyncClient* mClientPtr;
synceventset_t mReadyEvents;
AISyncClientData(AISyncClient* client) : mClientPtr(client), mReadyEvents(0) { }
};
class LL_COMMON_API AISyncServer
{
public:
typedef std::list<AISyncClientData> client_list_t;
private:
int mRefCount; // Number of boost::intrusive_ptr objects pointing to this object.
AISyncKey* mKey; // The key of the first client that was added.
client_list_t mClients; // A list with pointers to all registered clients.
bool mSynchronized; // Set when a server gets more than one client, and not reset anymore after that.
synceventset_t mReadyEvents; // 0xFFFFFFFF bitwise-AND-ed with all clients.
synceventset_t mPendingEvents; // The bitwise-OR of all clients.
public:
AISyncServer(AISyncKey* key) : mRefCount(0), mKey(key), mSynchronized(false), mReadyEvents((synceventset_t)-1), mPendingEvents(0) { }
~AISyncServer() { delete mKey; }
// Add a new client to this server.
void add(AISyncClient* client);
// 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).
AISyncKey const& key(void) const { return *mKey; }
// Replace they key with another key (of larger synckeytype_t).
void swapkey(AISyncKey*& key_ptr) { AISyncKey* tmp = key_ptr; key_ptr = mKey; mKey = tmp; }
// Returns true if this server never had more than one client.
bool never_synced(void) const { return !mSynchronized; }
// Set readiness of all events at once.
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 { return mReadyEvents; }
// Return events that at least one client is ready for.
synceventset_t events_with_at_least_one_client_ready(void) const { return mPendingEvents; }
// Return a list of all registered clients.
client_list_t const& getClients(void) const { return mClients; }
private:
// Call event1_ready() or event1_not_ready() on all clients if the least significant bit of mReadyEvents changed.
void trigger(synceventset_t old_ready_events);
#ifdef SYNC_TESTSUITE
void sanity_check(void) const;
#endif
private:
friend void intrusive_ptr_add_ref(AISyncServer* server);
friend void intrusive_ptr_release(AISyncServer* server);
};
class LL_COMMON_API AISyncServerMap : public LLSingleton<AISyncServerMap>
{
public:
typedef boost::intrusive_ptr<AISyncServer> server_ptr_t; // The type of a (stored) pointer to the server objects.
typedef std::list<server_ptr_t> server_list_t; // The type of the list with pointers to the server objects.
private:
server_list_t mServers; // A list with pointers to all server objects.
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, AISyncKey* new_key);
private:
friend void intrusive_ptr_release(AISyncServer* server);
// Remove a server from the map, only called by intrusive_ptr_release when there is one pointer left;
// therefore, the server should not have any clients.
void remove_server(AISyncServer* server);
};
class LL_COMMON_API AISyncClient
{
private:
friend class AISyncServer;
boost::intrusive_ptr<AISyncServer> mServer; // The server this client was registered with, or NULL when unregistered.
public:
#ifdef SHOW_ASSERT
synceventset_t mReadyEvents;
AISyncClient(void) : mReadyEvents(0) { }
#endif
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;
// Only client. Client was forcefully deregistered from expired server because it was the only client.
virtual void deregistered(void)
{
#ifdef SHOW_ASSERT
mReadyEvents = 0;
#endif
}
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, createSyncKey()); }
// 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.
// Set readiness of all events at once.
void ready(synceventset_t events, synceventset_t yesno)
{
if (!mServer)
{
register_client();
}
mServer->ready(events, yesno, this);
}
};
#endif

View File

@@ -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;

View File

@@ -48,7 +48,7 @@
// LLEmote()
// Class Constructor
//-----------------------------------------------------------------------------
LLEmote::LLEmote(const LLUUID &id) : LLMotion(id)
LLEmote::LLEmote(LLUUID const& id, LLMotionController* controller) : LLMotion(id, controller)
{
mCharacter = NULL;

View File

@@ -55,7 +55,7 @@ class LLEmote :
{
public:
// Constructor
LLEmote(const LLUUID &id);
LLEmote(LLUUID const& id, LLMotionController* controller);
// Destructor
virtual ~LLEmote();
@@ -67,7 +67,7 @@ public:
// static constructor
// all subclasses must implement such a function and register it
static LLMotion *create(const LLUUID &id) { return new LLEmote(id); }
static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLEmote(id, controller); }
public:
//-------------------------------------------------------------------------

View File

@@ -375,6 +375,10 @@ BOOL LLFloaterBvhPreview::postBuild()
}
}
if (motionp && mInWorld)
{
gAgentAvatarp->removeMotion(mMotionID);
}
//setEnabled(FALSE);
mMotionID.setNull();
mAnimPreview = NULL;

View File

@@ -611,8 +611,9 @@ void LLHUDEffectLookAt::update()
{
if (calcTargetPosition())
{
LLMotion* head_motion = ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->findMotion(ANIM_AGENT_HEAD_ROT);
if (!head_motion || head_motion->isStopped())
//LLMotion* head_motion = ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->findMotion(ANIM_AGENT_HEAD_ROT);
//if (!head_motion || head_motion->isStopped())
// singu: startMotion does basically the same as the above two lines... it starts it, unless it was already started.
{
((LLVOAvatar*)(LLViewerObject*)mSourceObject)->startMotion(ANIM_AGENT_HEAD_ROT);
}

View File

@@ -80,14 +80,33 @@ void LLHUDManager::updateEffects()
void LLHUDManager::sendEffects()
{
static LLCachedControl<bool> disable_lookat_effect(gSavedSettings, "PrivateLookAt", false);
static LLCachedControl<bool> disable_pointat_effect(gSavedSettings, "DisablePointAtAndBeam", false);
static LLCachedControl<bool> broadcast_viewer_effects(gSavedSettings, "BroadcastViewerEffects", true);
if(!gSavedSettings.getBOOL("BroadcastViewerEffects"))
return;
S32 i;
for (i = 0; i < mHUDEffects.count(); i++)
{
LLHUDEffect *hep = mHUDEffects[i];
if (hep->mType == LLHUDObject::LL_HUD_EFFECT_LOOKAT)
{
if (disable_lookat_effect)
{
continue;
}
}
else if (hep->mType == LLHUDObject::LL_HUD_EFFECT_POINTAT ||
hep->mType == LLHUDObject::LL_HUD_EFFECT_BEAM)
{
if (disable_pointat_effect)
{
continue;
}
}
else if (!broadcast_viewer_effects)
{
continue;
}
if (hep->isDead())
{
llwarns << "Trying to send dead effect!" << llendl;

View File

@@ -314,8 +314,8 @@ void LLPhysicsMotion::getString(std::ostringstream &oss)
}
}
LLPhysicsMotionController::LLPhysicsMotionController(const LLUUID &id) :
LLMotion(id),
LLPhysicsMotionController::LLPhysicsMotionController(LLUUID const& id, LLMotionController* controller) :
AIMaskedMotion(id, controller, ANIM_AGENT_PHYSICS_MOTION),
mCharacter(NULL),
mIsDefault(true)
{
@@ -332,15 +332,6 @@ LLPhysicsMotionController::~LLPhysicsMotionController()
}
}
BOOL LLPhysicsMotionController::onActivate()
{
return TRUE;
}
void LLPhysicsMotionController::onDeactivate()
{
}
LLMotion::LLMotionInitStatus LLPhysicsMotionController::onInitialize(LLCharacter *character)
{
mCharacter = character;
@@ -889,4 +880,4 @@ void LLPhysicsMotion::reset()
mCharacter->setVisualParamWeight((*iter).mParam,(*iter).mParam->getDefaultWeight());
}
}
}
}

View File

@@ -42,14 +42,14 @@ class LLPhysicsMotion;
// class LLPhysicsMotion
//-----------------------------------------------------------------------------
class LLPhysicsMotionController :
public LLMotion
public AIMaskedMotion
{
public:
std::string getString();
// Constructor
LLPhysicsMotionController(const LLUUID &id);
LLPhysicsMotionController(LLUUID const& id, LLMotionController* controller);
// Destructor
virtual ~LLPhysicsMotionController();
@@ -61,7 +61,7 @@ public:
// static constructor
// all subclasses must implement such a function and register it
static LLMotion *create(const LLUUID &id) { return new LLPhysicsMotionController(id); }
static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLPhysicsMotionController(id, controller); }
public:
//-------------------------------------------------------------------------
@@ -93,19 +93,11 @@ public:
// must return true to indicate success and be available for activation
virtual LLMotionInitStatus onInitialize(LLCharacter *character);
// called when a motion is activated
// must return TRUE to indicate success, or else
// it will be deactivated
virtual BOOL onActivate();
// called per time step
// must return TRUE while it is active, and
// must return FALSE when the motion is completed.
virtual BOOL onUpdate(F32 time, U8* joint_mask);
// called when a motion is deactivated
virtual void onDeactivate();
LLCharacter* getCharacter() { return mCharacter; }
protected:

View File

@@ -6506,7 +6506,7 @@ void LLSelectMgr::updateSelectionCenter()
mSelectionCenterGlobal.clearVec();
mShowSelection = FALSE;
mSelectionBBox = LLBBox();
mPauseRequest = NULL;
mPauseRequests.clear();
resetAgentHUDZoom();
}
@@ -6516,27 +6516,18 @@ void LLSelectMgr::updateSelectionCenter()
if (mSelectedObjects->mSelectType == SELECT_TYPE_ATTACHMENT && isAgentAvatarValid())
{
// Singu Note: Chalice Yao's pause agent on attachment selection
if (object->permYouOwner())
// Freeze avatars with a selected attachment, and all avatars with synchronized motions, if any.
LLVOAvatar* avatar = object->getAvatar();
// It is possible that 'avatar' is NULL despite this being an attachment because of some race condition.
// In that case just don't freeze the avatar.
if (avatar)
{
mPauseRequest = gAgentAvatarp->requestPause();
}
else if (LLViewerObject* objectp = mSelectedObjects->getPrimaryObject())
{
while (objectp && !objectp->isAvatar())
{
objectp = (LLViewerObject*)objectp->getParent();
}
if (objectp)
{
mPauseRequest = objectp->asAvatar()->requestPause();
}
avatar->pauseAllSyncedCharacters(mPauseRequests);
}
}
else
{
mPauseRequest = NULL;
mPauseRequests.clear();
}
if (mSelectedObjects->mSelectType != SELECT_TYPE_HUD && isAgentAvatarValid())

View File

@@ -793,7 +793,7 @@ private:
LLFrameTimer mEffectsTimer;
BOOL mForceSelection;
LLAnimPauseRequest mPauseRequest;
std::vector<LLAnimPauseRequest> mPauseRequests; // Selected avatar and all synchronized avatars.
friend class LLObjectBackup;
};

View File

@@ -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();
}

View File

@@ -145,18 +145,106 @@ using namespace LLAvatarAppearanceDefines;
//-----------------------------------------------------------------------------
// Global constants
//-----------------------------------------------------------------------------
const LLUUID ANIM_AGENT_BODY_NOISE = LLUUID("9aa8b0a6-0c6f-9518-c7c3-4f41f2c001ad"); //"body_noise"
const LLUUID ANIM_AGENT_BREATHE_ROT = LLUUID("4c5a103e-b830-2f1c-16bc-224aa0ad5bc8"); //"breathe_rot"
const LLUUID ANIM_AGENT_EDITING = LLUUID("2a8eba1d-a7f8-5596-d44a-b4977bf8c8bb"); //"editing"
const LLUUID ANIM_AGENT_EYE = LLUUID("5c780ea8-1cd1-c463-a128-48c023f6fbea"); //"eye"
const LLUUID ANIM_AGENT_FLY_ADJUST = LLUUID("db95561f-f1b0-9f9a-7224-b12f71af126e"); //"fly_adjust"
const LLUUID ANIM_AGENT_HAND_MOTION = LLUUID("ce986325-0ba7-6e6e-cc24-b17c4b795578"); //"hand_motion"
const LLUUID ANIM_AGENT_HEAD_ROT = LLUUID("e6e8d1dd-e643-fff7-b238-c6b4b056a68d"); //"head_rot"
const LLUUID ANIM_AGENT_PELVIS_FIX = LLUUID("0c5dd2a2-514d-8893-d44d-05beffad208b"); //"pelvis_fix"
const LLUUID ANIM_AGENT_TARGET = LLUUID("0e4896cb-fba4-926c-f355-8720189d5b55"); //"target"
const LLUUID ANIM_AGENT_WALK_ADJUST = LLUUID("829bc85b-02fc-ec41-be2e-74cc6dd7215d"); //"walk_adjust"
const LLUUID ANIM_AGENT_PHYSICS_MOTION = LLUUID("7360e029-3cb8-ebc4-863e-212df440d987"); //"physics_motion"
const LLUUID ANIM_AGENT_BODY_NOISE_ID = LLUUID("9aa8b0a6-0c6f-9518-c7c3-4f41f2c001ad"); //"body_noise"
const LLUUID ANIM_AGENT_BREATHE_ROT_ID = LLUUID("4c5a103e-b830-2f1c-16bc-224aa0ad5bc8"); //"breathe_rot"
const LLUUID ANIM_AGENT_PHYSICS_MOTION_ID = LLUUID("7360e029-3cb8-ebc4-863e-212df440d987"); //"physics_motion"
const LLUUID ANIM_AGENT_EDITING_ID = LLUUID("2a8eba1d-a7f8-5596-d44a-b4977bf8c8bb"); //"editing"
const LLUUID ANIM_AGENT_EYE_ID = LLUUID("5c780ea8-1cd1-c463-a128-48c023f6fbea"); //"eye"
const LLUUID ANIM_AGENT_FLY_ADJUST_ID = LLUUID("db95561f-f1b0-9f9a-7224-b12f71af126e"); //"fly_adjust"
const LLUUID ANIM_AGENT_HAND_MOTION_ID = LLUUID("ce986325-0ba7-6e6e-cc24-b17c4b795578"); //"hand_motion"
const LLUUID ANIM_AGENT_HEAD_ROT_ID = LLUUID("e6e8d1dd-e643-fff7-b238-c6b4b056a68d"); //"head_rot"
const LLUUID ANIM_AGENT_PELVIS_FIX_ID = LLUUID("0c5dd2a2-514d-8893-d44d-05beffad208b"); //"pelvis_fix"
const LLUUID ANIM_AGENT_TARGET_ID = LLUUID("0e4896cb-fba4-926c-f355-8720189d5b55"); //"target"
const LLUUID ANIM_AGENT_WALK_ADJUST_ID = LLUUID("829bc85b-02fc-ec41-be2e-74cc6dd7215d"); //"walk_adjust"
//<singu>
// This must be in the same order as ANIM_AGENT_BODY_NOISE through ANIM_AGENT_WALK_ADJUST (see llmotion.h)!
static LLUUID const* lookup[] = {
&ANIM_AGENT_BODY_NOISE_ID,
&ANIM_AGENT_BREATHE_ROT_ID,
&ANIM_AGENT_PHYSICS_MOTION_ID,
&ANIM_AGENT_EDITING_ID,
&ANIM_AGENT_EYE_ID,
&ANIM_AGENT_FLY_ADJUST_ID,
&ANIM_AGENT_HAND_MOTION_ID,
&ANIM_AGENT_HEAD_ROT_ID,
&ANIM_AGENT_PELVIS_FIX_ID,
&ANIM_AGENT_TARGET_ID,
&ANIM_AGENT_WALK_ADJUST_ID
};
LLUUID const& mask2ID(U32 bit)
{
int const lookupsize = sizeof(lookup) / sizeof(LLUUID const*);
int i = lookupsize - 1;
U32 mask = 1 << i;
for(;;)
{
if (bit == mask)
{
return *lookup[i];
}
--i;
mask >>= 1;
llassert_always(i >= 0);
}
}
#ifdef CWDEBUG
static char const* strlookup[] = {
"ANIM_AGENT_BODY_NOISE",
"ANIM_AGENT_BREATHE_ROT",
"ANIM_AGENT_PHYSICS_MOTION",
"ANIM_AGENT_EDITING",
"ANIM_AGENT_EYE",
"ANIM_AGENT_FLY_ADJUST",
"ANIM_AGENT_HAND_MOTION",
"ANIM_AGENT_HEAD_ROT",
"ANIM_AGENT_PELVIS_FIX",
"ANIM_AGENT_TARGET",
"ANIM_AGENT_WALK_ADJUST"
};
char const* mask2str(U32 bit)
{
int const lookupsize = sizeof(lookup) / sizeof(LLUUID const*);
int i = lookupsize - 1;
U32 mask = 1 << i;
do
{
if (bit == mask)
{
return strlookup[i];
}
--i;
mask >>= 1;
}
while(i >= 0);
return "<unknown>";
}
#endif
// stopMotion(ANIM_AGENT_WALK_ADJUST) is called every frame, and for every avatar on the radar.
// That can be like 1000 times per second, so... speed that up a bit and lets not lookup the same LLUUID 1000 times
// per second in a std::map. Added the rest of the animations while I was at it.
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();
}
}
void LLVOAvatar::stopMotion(U32 bit, BOOL stop_immediate)
{
if (isMotionActive(bit))
{
stopMotion(mask2ID(bit), stop_immediate);
}
}
//</singu>
//-----------------------------------------------------------------------------
// Constants
@@ -257,12 +345,12 @@ struct LLTextureMaskData
// class LLBodyNoiseMotion
//-----------------------------------------------------------------------------
class LLBodyNoiseMotion :
public LLMotion
public AIMaskedMotion
{
public:
// Constructor
LLBodyNoiseMotion(const LLUUID &id)
: LLMotion(id)
LLBodyNoiseMotion(LLUUID const& id, LLMotionController* controller)
: AIMaskedMotion(id, controller, ANIM_AGENT_BODY_NOISE)
{
mName = "body_noise";
mTorsoState = new LLJointState;
@@ -277,7 +365,7 @@ public:
//-------------------------------------------------------------------------
// static constructor
// all subclasses must implement such a function and register it
static LLMotion *create(const LLUUID &id) { return new LLBodyNoiseMotion(id); }
static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLBodyNoiseMotion(id, controller); }
public:
//-------------------------------------------------------------------------
@@ -320,11 +408,6 @@ public:
return STATUS_SUCCESS;
}
// called when a motion is activated
// must return TRUE to indicate success, or else
// it will be deactivated
virtual BOOL onActivate() { return TRUE; }
// called per time step
// must return TRUE while it is active, and
// must return FALSE when the motion is completed.
@@ -348,9 +431,6 @@ public:
return TRUE;
}
// called when a motion is deactivated
virtual void onDeactivate() {}
private:
//-------------------------------------------------------------------------
// joint states to be animated
@@ -362,12 +442,12 @@ private:
// class LLBreatheMotionRot
//-----------------------------------------------------------------------------
class LLBreatheMotionRot :
public LLMotion
public AIMaskedMotion
{
public:
// Constructor
LLBreatheMotionRot(const LLUUID &id) :
LLMotion(id),
LLBreatheMotionRot(LLUUID const& id, LLMotionController* controller) :
AIMaskedMotion(id, controller, ANIM_AGENT_BREATHE_ROT),
mBreatheRate(1.f),
mCharacter(NULL)
{
@@ -384,7 +464,7 @@ public:
//-------------------------------------------------------------------------
// static constructor
// all subclasses must implement such a function and register it
static LLMotion *create(const LLUUID &id) { return new LLBreatheMotionRot(id); }
static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLBreatheMotionRot(id, controller); }
public:
//-------------------------------------------------------------------------
@@ -437,11 +517,6 @@ public:
}
}
// called when a motion is activated
// must return TRUE to indicate success, or else
// it will be deactivated
virtual BOOL onActivate() { return TRUE; }
// called per time step
// must return TRUE while it is active, and
// must return FALSE when the motion is completed.
@@ -456,9 +531,6 @@ public:
return TRUE;
}
// called when a motion is deactivated
virtual void onDeactivate() {}
private:
//-------------------------------------------------------------------------
// joint states to be animated
@@ -472,12 +544,12 @@ private:
// class LLPelvisFixMotion
//-----------------------------------------------------------------------------
class LLPelvisFixMotion :
public LLMotion
public AIMaskedMotion
{
public:
// Constructor
LLPelvisFixMotion(const LLUUID &id)
: LLMotion(id), mCharacter(NULL)
LLPelvisFixMotion(LLUUID const& id, LLMotionController* controller)
: AIMaskedMotion(id, controller, ANIM_AGENT_PELVIS_FIX), mCharacter(NULL)
{
mName = "pelvis_fix";
@@ -493,7 +565,7 @@ public:
//-------------------------------------------------------------------------
// static constructor
// all subclasses must implement such a function and register it
static LLMotion *create(const LLUUID& id) { return new LLPelvisFixMotion(id); }
static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLPelvisFixMotion(id, controller); }
public:
//-------------------------------------------------------------------------
@@ -538,11 +610,6 @@ public:
return STATUS_SUCCESS;
}
// called when a motion is activated
// must return TRUE to indicate success, or else
// it will be deactivated
virtual BOOL onActivate() { return TRUE; }
// called per time step
// must return TRUE while it is active, and
// must return FALSE when the motion is completed.
@@ -553,9 +620,6 @@ public:
return TRUE;
}
// called when a motion is deactivated
virtual void onDeactivate() {}
private:
//-------------------------------------------------------------------------
// joint states to be animated
@@ -1438,17 +1502,17 @@ void LLVOAvatar::deleteCachedImages(bool clearAll)
//------------------------------------------------------------------------
void LLVOAvatar::initClass()
{
gAnimLibrary.animStateSetString(ANIM_AGENT_BODY_NOISE,"body_noise");
gAnimLibrary.animStateSetString(ANIM_AGENT_BREATHE_ROT,"breathe_rot");
gAnimLibrary.animStateSetString(ANIM_AGENT_PHYSICS_MOTION,"physics_motion");
gAnimLibrary.animStateSetString(ANIM_AGENT_EDITING,"editing");
gAnimLibrary.animStateSetString(ANIM_AGENT_EYE,"eye");
gAnimLibrary.animStateSetString(ANIM_AGENT_FLY_ADJUST,"fly_adjust");
gAnimLibrary.animStateSetString(ANIM_AGENT_HAND_MOTION,"hand_motion");
gAnimLibrary.animStateSetString(ANIM_AGENT_HEAD_ROT,"head_rot");
gAnimLibrary.animStateSetString(ANIM_AGENT_PELVIS_FIX,"pelvis_fix");
gAnimLibrary.animStateSetString(ANIM_AGENT_TARGET,"target");
gAnimLibrary.animStateSetString(ANIM_AGENT_WALK_ADJUST,"walk_adjust");
gAnimLibrary.animStateSetString(ANIM_AGENT_BODY_NOISE_ID,"body_noise");
gAnimLibrary.animStateSetString(ANIM_AGENT_BREATHE_ROT_ID,"breathe_rot");
gAnimLibrary.animStateSetString(ANIM_AGENT_PHYSICS_MOTION_ID,"physics_motion");
gAnimLibrary.animStateSetString(ANIM_AGENT_EDITING_ID,"editing");
gAnimLibrary.animStateSetString(ANIM_AGENT_EYE_ID,"eye");
gAnimLibrary.animStateSetString(ANIM_AGENT_FLY_ADJUST_ID,"fly_adjust");
gAnimLibrary.animStateSetString(ANIM_AGENT_HAND_MOTION_ID,"hand_motion");
gAnimLibrary.animStateSetString(ANIM_AGENT_HEAD_ROT_ID,"head_rot");
gAnimLibrary.animStateSetString(ANIM_AGENT_PELVIS_FIX_ID,"pelvis_fix");
gAnimLibrary.animStateSetString(ANIM_AGENT_TARGET_ID,"target");
gAnimLibrary.animStateSetString(ANIM_AGENT_WALK_ADJUST_ID,"walk_adjust");
SHClientTagMgr::instance(); //Instantiate. Parse. Will fetch a new tag file if AscentUpdateTagsOnLoad is true.
}
@@ -1506,19 +1570,19 @@ void LLVOAvatar::initInstance(void)
registerMotion( ANIM_AGENT_WALK_NEW, LLKeyframeWalkMotion::create ); //v2
// motions without a start/stop bit
registerMotion( ANIM_AGENT_BODY_NOISE, LLBodyNoiseMotion::create );
registerMotion( ANIM_AGENT_BREATHE_ROT, LLBreatheMotionRot::create );
registerMotion( ANIM_AGENT_PHYSICS_MOTION, LLPhysicsMotionController::create );
registerMotion( ANIM_AGENT_EDITING, LLEditingMotion::create );
registerMotion( ANIM_AGENT_EYE, LLEyeMotion::create );
registerMotion( ANIM_AGENT_FLY_ADJUST, LLFlyAdjustMotion::create );
registerMotion( ANIM_AGENT_HAND_MOTION, LLHandMotion::create );
registerMotion( ANIM_AGENT_HEAD_ROT, LLHeadRotMotion::create );
registerMotion( ANIM_AGENT_PELVIS_FIX, LLPelvisFixMotion::create );
registerMotion( ANIM_AGENT_SIT_FEMALE, LLKeyframeMotion::create );
registerMotion( ANIM_AGENT_TARGET, LLTargetingMotion::create );
registerMotion( ANIM_AGENT_WALK_ADJUST, LLWalkAdjustMotion::create );
registerMotion( ANIM_AGENT_BODY_NOISE_ID, LLBodyNoiseMotion::create );
registerMotion( ANIM_AGENT_BREATHE_ROT_ID, LLBreatheMotionRot::create );
registerMotion( ANIM_AGENT_PHYSICS_MOTION_ID, LLPhysicsMotionController::create );
registerMotion( ANIM_AGENT_EDITING_ID, LLEditingMotion::create );
registerMotion( ANIM_AGENT_EYE_ID, LLEyeMotion::create );
registerMotion( ANIM_AGENT_FLY_ADJUST_ID, LLFlyAdjustMotion::create );
registerMotion( ANIM_AGENT_HAND_MOTION_ID, LLHandMotion::create );
registerMotion( ANIM_AGENT_HEAD_ROT_ID, LLHeadRotMotion::create );
registerMotion( ANIM_AGENT_PELVIS_FIX_ID, LLPelvisFixMotion::create );
registerMotion( ANIM_AGENT_TARGET_ID, LLTargetingMotion::create );
registerMotion( ANIM_AGENT_WALK_ADJUST_ID, LLWalkAdjustMotion::create );
registerMotion( ANIM_AGENT_SIT_FEMALE, LLKeyframeMotion::create );
}
LLAvatarAppearance::initInstance();
@@ -3748,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)
{
@@ -3767,6 +3832,14 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
motionp->getName().c_str(),
(U32)motionp->getPriority());
}
if (motionp->server())
{
#ifdef SHOW_ASSERT
output += llformat(" rt=%.1f r=%d s=0x%xl", motionp->getRuntime(), motionp->mReadyEvents, motionp->server());
#else
output += llformat(" rt=%.1f s=0x%xl", motionp->getRuntime(), motionp->server());
#endif
}
addDebugText(output);
}
}
@@ -3884,7 +3957,7 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
getOffObject();
//<edit>
//Singu note: this appears to be a safety catch:
// when getParent() is NULL and we're note playing ANIM_AGENT_SIT_GROUND_CONSTRAINED then we aren't sitting!
// when getParent() is NULL and we're not playing ANIM_AGENT_SIT_GROUND_CONSTRAINED then we aren't sitting!
// The previous call existed in an attempt to fix this inconsistent state by standing up from an object.
// However, since getParent() is NULL that function would crash!
// Since we never got crash reports regarding to this, that apparently never happened, except, I discovered
@@ -5437,6 +5510,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();)
{
@@ -5446,9 +5520,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
}
@@ -5478,7 +5552,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
}

View File

@@ -54,6 +54,9 @@
#include "llavatarname.h"
//<singu>
#if 0
// Hide these: should be using the bit masks everywhere.
extern const LLUUID ANIM_AGENT_BODY_NOISE;
extern const LLUUID ANIM_AGENT_BREATHE_ROT;
extern const LLUUID ANIM_AGENT_PHYSICS_MOTION;
@@ -65,6 +68,8 @@ extern const LLUUID ANIM_AGENT_HEAD_ROT;
extern const LLUUID ANIM_AGENT_PELVIS_FIX;
extern const LLUUID ANIM_AGENT_TARGET;
extern const LLUUID ANIM_AGENT_WALK_ADJUST;
#endif
//</singu>
class LLAPRFile;
class LLViewerWearable;
@@ -230,6 +235,10 @@ public:
/*virtual*/ LLUUID remapMotionID(const LLUUID& id);
/*virtual*/ BOOL startMotion(const LLUUID& id, F32 time_offset = 0.f);
/*virtual*/ BOOL stopMotion(const LLUUID& id, BOOL stop_immediate = FALSE);
//<singu>
void startMotion(U32 bit, F32 start_offset = 0.f);
void stopMotion(U32 bit, BOOL stop_immediate = FALSE);
//</singu>
virtual void stopMotionFromSource(const LLUUID& source_id);
virtual void requestStopMotion(LLMotion* motion);
LLMotion* findMotion(const LLUUID& id) const;

View File

@@ -897,9 +897,9 @@ void LLVOAvatarSelf::updateRegion(LLViewerRegion *regionp)
//virtual
void LLVOAvatarSelf::idleUpdateTractorBeam()
{
if(gSavedSettings.getBOOL("DisablePointAtAndBeam"))
// <edit>
static LLCachedControl<bool> disable_pointat_effect("DisablePointAtAndBeam");
if (disable_pointat_effect)
{
return;
}

View File

@@ -83,9 +83,9 @@
</panel>
<panel border="true" left="1" bottom="-408" height="408" width="500" label="Security" name="Security">
<check_box bottom_delta="-25" control_name="BroadcastViewerEffects" follows="top" initial_value="true" label="Broadcast Viewer Effects (Does not include beams)" left="10" name="broadcast_viewer_effects" width="400"/>
<check_box bottom_delta="-25" control_name="BroadcastViewerEffects" follows="top" initial_value="true" label="Broadcast Viewer Effects (does not affect pointat and lookat)" left="10" name="broadcast_viewer_effects" width="400"/>
<check_box bottom_delta="-20" control_name="DisablePointAtAndBeam" follows="top" initial_value="true" label="Disable Point At And Beam" tool_tip="Don't point at or show your edit beam when selecting an object." name="disable_point_at_and_beams_check"/>
<check_box bottom_delta="-20" control_name="PrivateLookAt" follows="top" initial_value="false" label="Do not Look At objects (defeats ShowLookAt)" tool_tip="Turns off broadcasting headturns and lookat beacons for yourself." name="private_look_at_check"/>
<check_box bottom_delta="-20" control_name="PrivateLookAt" follows="top" initial_value="false" label="Do not Look At objects and/or avatars" tool_tip="Disables headturns and lookat beacons: causes your avatar to look straight ahead like a zombie." name="private_look_at_check"/>
<check_box bottom_delta="-20" control_name="AscentShowLookAt" follows="top" initial_value="false" label="Show others' LookAt beacons" tool_tip="Shows you where others are looking." name="show_look_at_check"/>
<check_box bottom_delta="-20" control_name="SGDetachBridge" follows="top" initial_value="false" label="Auto detach LSL Bridge" tool_tip="Automatically detach LSL Bridge of Phoenix or Firestorm viewer." name="detach_bridge"/>
<check_box bottom_delta="-20" control_name="QuietSnapshotsToDisk" follows="top" initial_value="false" label="Quiet Snapshots to Disk" tool_tip="Doesn't make a camera sound nor alert everyone when you take a snapshot to your computer." name="quiet_snapshots_check"/>