Merge branch 'master' of git://github.com/AlericInglewood/SingularityViewer
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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); }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
|
||||
@@ -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 = ¶mMotion;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we've passed the parameter value
|
||||
// so store the first motion we find as the second one we want to blend...
|
||||
if (firstMotion && !secondMotion )
|
||||
{
|
||||
secondMotion = ¶mMotion;
|
||||
}
|
||||
//...or, if we've seen no other motion so far, make sure we blend to this only
|
||||
else if (!firstMotion)
|
||||
{
|
||||
firstMotion = ¶mMotion;
|
||||
secondMotion = ¶mMotion;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LLPose *firstPose;
|
||||
LLPose *secondPose;
|
||||
|
||||
if (firstMotion)
|
||||
firstPose = firstMotion->mMotion->getPose();
|
||||
else
|
||||
firstPose = NULL;
|
||||
|
||||
if (secondMotion)
|
||||
secondPose = secondMotion->mMotion->getPose();
|
||||
else
|
||||
secondPose = NULL;
|
||||
|
||||
// now modify weight of the subanim (only if we are blending between two motions)
|
||||
if (firstMotion && secondMotion)
|
||||
{
|
||||
if (firstMotion == secondMotion)
|
||||
{
|
||||
firstPose->setWeight(weightFactor);
|
||||
}
|
||||
else if (firstMotion->mParam == secondMotion->mParam)
|
||||
{
|
||||
firstPose->setWeight(0.5f * weightFactor);
|
||||
secondPose->setWeight(0.5f * weightFactor);
|
||||
}
|
||||
else
|
||||
{
|
||||
F32 first_weight = 1.f -
|
||||
((llclamp(*paramValue - firstMotion->mParam, 0.f, (secondMotion->mParam - firstMotion->mParam))) /
|
||||
(secondMotion->mParam - firstMotion->mParam));
|
||||
first_weight = llclamp(first_weight, 0.f, 1.f);
|
||||
|
||||
F32 second_weight = 1.f - first_weight;
|
||||
|
||||
firstPose->setWeight(first_weight * weightFactor);
|
||||
secondPose->setWeight(second_weight * weightFactor);
|
||||
|
||||
// llinfos << "Parameter " << *paramName << ": " << *paramValue << llendl;
|
||||
// llinfos << "Weights " << firstPose->getWeight() << " " << secondPose->getWeight() << llendl;
|
||||
}
|
||||
}
|
||||
else if (firstMotion && !secondMotion)
|
||||
{
|
||||
firstPose->setWeight(weightFactor);
|
||||
}
|
||||
}
|
||||
|
||||
// blend poses
|
||||
mPoseBlender.blendAndApply();
|
||||
|
||||
llinfos << "Param Motion weight " << mPoseBlender.getBlendedPose()->getWeight() << llendl;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
@@ -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>
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
698
indra/llcommon/aisyncclient.cpp
Normal file
698
indra/llcommon/aisyncclient.cpp
Normal 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
|
||||
303
indra/llcommon/aisyncclient.h
Normal file
303
indra/llcommon/aisyncclient.h
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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:
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
@@ -375,6 +375,10 @@ BOOL LLFloaterBvhPreview::postBuild()
|
||||
}
|
||||
}
|
||||
|
||||
if (motionp && mInWorld)
|
||||
{
|
||||
gAgentAvatarp->removeMotion(mMotionID);
|
||||
}
|
||||
//setEnabled(FALSE);
|
||||
mMotionID.setNull();
|
||||
mAnimPreview = NULL;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -793,7 +793,7 @@ private:
|
||||
LLFrameTimer mEffectsTimer;
|
||||
BOOL mForceSelection;
|
||||
|
||||
LLAnimPauseRequest mPauseRequest;
|
||||
std::vector<LLAnimPauseRequest> mPauseRequests; // Selected avatar and all synchronized avatars.
|
||||
|
||||
friend class LLObjectBackup;
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
Reference in New Issue
Block a user