summaryrefslogtreecommitdiff
path: root/indra/newview/llvoavatar.cpp
diff options
context:
space:
mode:
authorJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
committerJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
commit420b91db29485df39fd6e724e782c449158811cb (patch)
treeb471a94563af914d3ed3edd3e856d21cb1b69945 /indra/newview/llvoavatar.cpp
Print done when done.
Diffstat (limited to 'indra/newview/llvoavatar.cpp')
-rw-r--r--indra/newview/llvoavatar.cpp9426
1 files changed, 9426 insertions, 0 deletions
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
new file mode 100644
index 0000000000..acec80c95a
--- /dev/null
+++ b/indra/newview/llvoavatar.cpp
@@ -0,0 +1,9426 @@
+/**
+ * @file llvoavatar.cpp
+ * @brief Implementation of LLVOAvatar class which is a derivation fo LLViewerObject
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <algorithm>
+#include <vector>
+#include "llstl.h"
+
+#include "llvoavatar.h"
+
+#include "audioengine.h"
+#include "imageids.h"
+#include "indra_constants.h"
+#include "llchat.h"
+#include "llfontgl.h"
+#include "llprimitive.h"
+#include "lltextureentry.h"
+#include "message.h"
+#include "noise.h"
+#include "sound_ids.h"
+#include "lltimer.h"
+#include "timing.h"
+
+#include "llagent.h" // Get state values from here
+#include "llagparray.h"
+#include "llviewercontrol.h"
+#include "llcriticaldamp.h"
+#include "lldir.h"
+#include "lldrawable.h"
+#include "lldrawpoolavatar.h"
+#include "lldrawpoolalpha.h"
+#include "lldrawpoolmedia.h"
+#include "lldrawpoolbump.h"
+#include "lldriverparam.h"
+#include "lleditingmotion.h"
+#include "llemote.h"
+#include "llface.h"
+#include "llfasttimer.h"
+#include "llfirstuse.h"
+#include "llfloatercustomize.h"
+#include "llfloatertools.h"
+#include "llgldbg.h"
+#include "llhandmotion.h"
+#include "llheadrotmotion.h"
+#include "llhudeffectbeam.h"
+#include "llhudeffectlookat.h"
+#include "llhudeffecttrail.h"
+#include "llhudmanager.h"
+#include "llhudtext.h"
+#include "llinventorymodel.h"
+#include "llinventoryview.h"
+#include "llkeyframefallmotion.h"
+#include "llkeyframemotion.h"
+#include "llkeyframemotionparam.h"
+#include "llkeyframestandmotion.h"
+#include "llkeyframewalkmotion.h"
+#include "llmenugl.h"
+#include "llmutelist.h"
+#include "llnetmap.h"
+#include "llnotify.h"
+#include "llquantize.h"
+#include "llregionhandle.h"
+#include "llresmgr.h"
+#include "llselectmgr.h"
+#include "llsky.h"
+#include "llsprite.h"
+#include "llstatusbar.h"
+#include "lltargetingmotion.h"
+#include "lltexlayer.h"
+#include "lltoolbar.h"
+#include "lltoolgrab.h" // for needsRenderBeam
+#include "lltoolmgr.h" // for needsRenderBeam
+#include "lltoolmorph.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerinventory.h"
+#include "llviewermenu.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llviewerwindow.h"
+#include "llvosky.h"
+#include "llvovolume.h"
+#include "llwearable.h"
+#include "llwearablelist.h"
+#include "llworld.h"
+#include "pipeline.h"
+#include "viewer.h"
+#include "lscript_byteformat.h"
+
+//#include "vtune/vtuneapi.h"
+
+// Direct imports, evil
+extern LLSky gSky;
+extern void set_avatar_character(void* charNameArg);
+extern BOOL gRenderForSelect;
+
+LLXmlTree LLVOAvatar::sXMLTree;
+LLXmlTree LLVOAvatar::sSkeletonXMLTree;
+LLVOAvatarSkeletonInfo* LLVOAvatar::sSkeletonInfo = NULL;
+LLVOAvatarInfo* LLVOAvatar::sAvatarInfo = NULL;
+
+BOOL gDebugAvatarRotation = FALSE;
+
+//extern BOOL gVelocityInterpolate;
+
+//-----------------------------------------------------------------------------
+// Constants
+//-----------------------------------------------------------------------------
+const F32 MIN_PIXEL_AREA_FOR_COMPOSITE = 200.f;
+
+F32 SHADOW_OFFSET_AMT = 0.03f;
+
+#define DELTA_TIME_MIN 0.01f // we clamp measured deltaTime to this
+#define DELTA_TIME_MAX 0.2f // range to insure stability of computations.
+
+const F32 PELVIS_LAG_FLYING = 0.22f;// pelvis follow half life while flying
+
+const F32 PELVIS_LAG_WALKING = 0.4f; // ...while walking
+
+const F32 PELVIS_LAG_MOUSELOOK = 0.15f;
+const F32 MOUSELOOK_PELVIS_FOLLOW_FACTOR = 0.5f;
+
+//Ventrella
+const F32 PELVIS_LAG_WHEN_FOLLOW_CAM_IS_ON = 0.0001f; // not zero! - something gets divided by this!
+//end Ventrella
+
+#define PELVIS_ROT_THRESHOLD_SLOW 60.0f // amount of deviation allowed between
+#define PELVIS_ROT_THRESHOLD_FAST 2.0f // the pelvis and the view direction
+ // when moving fast & slow
+
+const F32 MIN_SPEED_PELVIS_FOLLOW = 0.1f;
+
+#define TORSO_NOISE_AMOUNT 1.f // Amount of deviation from up-axis, in degrees
+#define TORSO_NOISE_SPEED 0.2f // Time scale factor on torso noise.
+
+const F32 BREATHE_ROT_MOTION_STRENGTH = 0.05f;
+
+const F32 BREATHE_SCALE_MOTION_STRENGTH = 0.005f;
+
+#define PELVIS_NOISE_FACTOR 0.5f // amount of random noise
+
+#define AUDIO_STEP_PRI 0xC0000000
+#define AUDIO_STEP_LO_SPEED 0.01f // as average speed goes from lo to hi,
+#define AUDIO_STEP_HI_SPEED 3.0f // from lo to hi
+#define AUDIO_STEP_LO_GAIN 0.15f // the resulting gain will ramp linearly
+#define AUDIO_STEP_HI_GAIN 0.15f
+
+const F32 DAMPED_MOTION_TIME_SCALE = 0.15f;
+
+const F32 LOOKAT_CAMERA_DIST_SQUARED = 25.f;
+
+#define AVATAR_HEADER "Linden Avatar 1.0"
+#define AVATAR_SECTION "[avatar]"
+
+#define AVATAR_DEFAULT_CHAR "avatar"
+
+const F32 MIN_SHADOW_HEIGHT = 0.f;
+const F32 MAX_SHADOW_HEIGHT = 0.3f;
+
+#define MIN_REQUIRED_PIXEL_AREA_BODY_NOISE (10000.f)
+#define MIN_REQUIRED_PIXEL_AREA_BREATHE (10000.f)
+#define MIN_REQUIRED_PIXEL_AREA_PELVIS_FIX (40.f)
+
+const S32 LOCTEX_IMAGE_SIZE_SELF = 512;
+const S32 LOCTEX_IMAGE_AREA_SELF = LOCTEX_IMAGE_SIZE_SELF * LOCTEX_IMAGE_SIZE_SELF;
+const S32 LOCTEX_IMAGE_SIZE_OTHER = LOCTEX_IMAGE_SIZE_SELF / 4; // The size of local textures for other (!mIsSelf) avatars
+const S32 LOCTEX_IMAGE_AREA_OTHER = LOCTEX_IMAGE_SIZE_OTHER * LOCTEX_IMAGE_SIZE_OTHER;
+
+const F32 HEAD_MOVEMENT_AVG_TIME = 0.9f;
+
+const S32 MORPH_MASK_REQUESTED_DISCARD = 0;
+const S32 MIN_PIXEL_AREA_BUMP = 500;
+
+// Discard level at which to switch to baked textures
+// Should probably be 4 or 3, but didn't want to change it while change other logic - SJB
+const S32 SWITCH_TO_BAKED_DISCARD = 5;
+
+const F32 FOOT_COLLIDE_FUDGE = 0.04f;
+
+const F32 HOVER_EFFECT_MAX_SPEED = 3.f;
+const F32 HOVER_EFFECT_STRENGTH = 0.f;
+F32 UNDERWATER_EFFECT_STRENGTH = 0.1f;
+const F32 UNDERWATER_FREQUENCY_DAMP = 0.33f;
+const F32 APPEARANCE_MORPH_TIME = 0.65f;
+const F32 CAMERA_SHAKE_ACCEL_THRESHOLD_SQUARED = 5.f * 5.f;
+const F32 TIME_BEFORE_MESH_CLEANUP = 5.f; // seconds
+const S32 AVATAR_AGP_RELEASE_THRESHOLD = 10; // number of avatar instances before releasing AGP memory
+const F32 FOOT_GROUND_COLLISION_TOLERANCE = 0.25f;
+const F32 AVATAR_LOD_TWEAK_RANGE = 0.7f;
+const S32 MAX_LOD_CHANGES_PER_FRAME = 2;
+const S32 MAX_BUBBLE_CHAT_LENGTH = 1023;
+const S32 MAX_BUBBLE_CHAT_UTTERANCES = 12;
+const F32 CHAT_FADE_TIME = 8.0;
+const F32 BUBBLE_CHAT_TIME = CHAT_FADE_TIME * 3.f;
+const S32 MAX_BUBBLES = 7;
+
+
+const bool USING_VENTRELLA_AVATAR_MOTION_TEST = false;
+
+
+S32 LLVOAvatar::sMaxVisible = 50;
+
+LLVOAvatar::ETextureIndex LLVOAvatar::sBakedTextureIndices[BAKED_TEXTURE_COUNT] =
+{
+ LLVOAvatar::TEX_HEAD_BAKED,
+ LLVOAvatar::TEX_UPPER_BAKED,
+ LLVOAvatar::TEX_LOWER_BAKED,
+ LLVOAvatar::TEX_EYES_BAKED,
+ LLVOAvatar::TEX_SKIRT_BAKED
+};
+
+//-----------------------------------------------------------------------------
+// Utility functions
+//-----------------------------------------------------------------------------
+
+static F32 calc_bouncy_animation(F32 x)
+{
+ return -(cosf(x * F_PI * 2.5f - F_PI_BY_TWO))*(0.4f + x * -0.1f) + x * 1.3f;
+}
+
+//-----------------------------------------------------------------------------
+// Static Data
+//-----------------------------------------------------------------------------
+S32 LLVOAvatar::sMaxOtherAvatarsToComposite = 1; // Only this many avatars (other than yourself) can be composited at a time. Set in initClass().
+LLMap< LLGLenum, LLGLuint*> LLVOAvatar::sScratchTexNames;
+LLMap< LLGLenum, F32*> LLVOAvatar::sScratchTexLastBindTime;
+S32 LLVOAvatar::sScratchTexBytes = 0;
+S32 LLVOAvatar::sNumVisibleAvatars = 0;
+S32 LLVOAvatar::sNumLODChangesThisFrame = 0;
+
+LLUUID LLVOAvatar::sStepSoundOnLand = LLUUID("e8af4a28-aa83-4310-a7c4-c047e15ea0df");
+LLUUID LLVOAvatar::sStepSounds[LL_MCODE_END] =
+{
+ LLUUID(SND_STONE_RUBBER),
+ LLUUID(SND_METAL_RUBBER),
+ LLUUID(SND_GLASS_RUBBER),
+ LLUUID(SND_WOOD_RUBBER),
+ LLUUID(SND_FLESH_RUBBER),
+ LLUUID(SND_RUBBER_PLASTIC),
+ LLUUID(SND_RUBBER_RUBBER)
+};
+
+S32 LLVOAvatar::sRenderName = RENDER_NAME_ALWAYS;
+S32 LLVOAvatar::sNumVisibleChatBubbles = 0;
+BOOL LLVOAvatar::sDebugInvisible = FALSE;
+BOOL LLVOAvatar::sShowAttachmentPoints = FALSE;
+BOOL LLVOAvatar::sShowAnimationDebug = FALSE;
+BOOL LLVOAvatar::sShowFootPlane = FALSE;
+BOOL LLVOAvatar::sShowCollisionVolumes = FALSE;
+BOOL LLVOAvatar::sVisibleInFirstPerson = FALSE;
+BOOL LLVOAvatar::sAvatarLoadTest = FALSE;
+F32 LLVOAvatar::sLODFactor = 1.f;
+BOOL LLVOAvatar::sJointDebug = FALSE;
+
+S32 LLVOAvatar::sCurJoint = 0;
+S32 LLVOAvatar::sCurVolume = 0;
+
+struct LLAvatarTexData
+{
+ LLAvatarTexData( const LLUUID& id, LLVOAvatar::ELocTexIndex index )
+ : mAvatarID(id), mIndex(index) {}
+ LLUUID mAvatarID;
+ LLVOAvatar::ELocTexIndex mIndex;
+};
+
+struct LLTextureMaskData
+{
+ LLTextureMaskData( const LLUUID& id )
+ : mAvatarID(id), mLastDiscardLevel(S32_MAX) {}
+ LLUUID mAvatarID;
+ S32 mLastDiscardLevel;
+};
+
+
+//-----------------------------------------------------------------------------
+// class LLBodyNoiseMotion
+//-----------------------------------------------------------------------------
+class LLBodyNoiseMotion :
+ public LLMotion
+{
+public:
+ // Constructor
+ LLBodyNoiseMotion(const LLUUID &id) : LLMotion(id) {mName = "body_noise";}
+
+ // Destructor
+ virtual ~LLBodyNoiseMotion() { }
+
+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 LLBodyNoiseMotion(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 0.0; }
+
+ // motions must report their "ease in" duration
+ virtual F32 getEaseInDuration() { return 0.0; }
+
+ // motions must report their "ease out" duration.
+ virtual F32 getEaseOutDuration() { return 0.0; }
+
+ // motions must report their priority
+ virtual LLJoint::JointPriority getPriority() { return LLJoint::HIGH_PRIORITY; }
+
+ virtual LLMotionBlendType getBlendType() { return ADDITIVE_BLEND; }
+
+ // called to determine when a motion should be activated/deactivated based on avatar pixel coverage
+ virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_BODY_NOISE; }
+
+ // 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)
+ {
+ if( !mTorsoState.setJoint( character->getJoint("mTorso") ))
+ {
+ return STATUS_FAILURE;
+ }
+
+ mTorsoState.setUsage(LLJointState::ROT);
+
+ addJointState( &mTorsoState );
+ 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.
+ virtual BOOL onUpdate(F32 time, U8* joint_mask)
+ {
+ F32 nx[2];
+ nx[0]=time*TORSO_NOISE_SPEED;
+ nx[1]=0.0f;
+ F32 ny[2];
+ ny[0]=0.0f;
+ ny[1]=time*TORSO_NOISE_SPEED;
+ F32 noiseX = noise2(nx);
+ F32 noiseY = noise2(ny);
+
+ F32 rx = TORSO_NOISE_AMOUNT * DEG_TO_RAD * noiseX / 0.42f;
+ F32 ry = TORSO_NOISE_AMOUNT * DEG_TO_RAD * noiseY / 0.42f;
+ LLQuaternion tQn;
+ tQn.setQuat( rx, ry, 0.0f );
+ mTorsoState.setRotation( tQn );
+
+ return TRUE;
+ }
+
+ // called when a motion is deactivated
+ virtual void onDeactivate() {}
+
+public:
+ //-------------------------------------------------------------------------
+ // joint states to be animated
+ //-------------------------------------------------------------------------
+ LLJointState mTorsoState;
+};
+
+//-----------------------------------------------------------------------------
+// class LLBreatheMotionRot
+//-----------------------------------------------------------------------------
+class LLBreatheMotionRot :
+ public LLMotion
+{
+public:
+ // Constructor
+ LLBreatheMotionRot(const LLUUID &id) :
+ LLMotion(id),
+ mBreatheRate(1.f),
+ mCharacter(NULL)
+ {
+ mName = "breathe_rot";
+ }
+
+ // Destructor
+ virtual ~LLBreatheMotionRot() { }
+
+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 LLBreatheMotionRot(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 0.0; }
+
+ // motions must report their "ease in" duration
+ virtual F32 getEaseInDuration() { return 0.0; }
+
+ // motions must report their "ease out" duration.
+ virtual F32 getEaseOutDuration() { return 0.0; }
+
+ // motions must report their priority
+ virtual LLJoint::JointPriority getPriority() { return LLJoint::MEDIUM_PRIORITY; }
+
+ 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_BREATHE; }
+
+ // 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)
+ {
+ //Ventrella
+ // I'm replacing the code below because I need to change
+ // the logic in order to add other body parts
+ /*
+ mCharacter = character;
+
+ if (!mChestState.setJoint( character->getJoint("mChest")))
+ {
+ return STATUS_FAILURE;
+ }
+
+ mChestState.setUsage(LLJointState::ROT);
+
+ addJointState( &mChestState );
+ return STATUS_SUCCESS;
+ */
+
+
+ bool success = true;
+
+ if ( !mChestState.setJoint( character->getJoint( "mChest" ) ) ) { success = false; }
+
+ if ( USING_VENTRELLA_AVATAR_MOTION_TEST )
+ {
+ if ( !mNeckState.setJoint ( character->getJoint( "mNeck" )) ) { success = false; }
+
+ if ( !mCollarLeftState.setJoint ( character->getJoint( "mCollarLeft" )) ) { success = false; }
+ if ( !mShoulderLeftState.setJoint ( character->getJoint( "mShoulderLeft" )) ) { success = false; }
+ if ( !mElbowLeftState.setJoint ( character->getJoint( "mElbowLeft" )) ) { success = false; }
+ if ( !mWristLeftState.setJoint ( character->getJoint( "mWristLeft" )) ) { success = false; }
+
+ if ( !mCollarRightState.setJoint ( character->getJoint( "mCollarRight" )) ) { success = false; }
+ if ( !mShoulderRightState.setJoint ( character->getJoint( "mShoulderRight" )) ) { success = false; }
+ if ( !mElbowRightState.setJoint ( character->getJoint( "mElbowRight" )) ) { success = false; }
+ if ( !mWristRightState.setJoint ( character->getJoint( "mWristRight" )) ) { success = false; }
+
+ if ( !mHipLeftState.setJoint ( character->getJoint( "mHipLeft" )) ) { success = false; }
+ if ( !mKneeLeftState.setJoint ( character->getJoint( "mKneeLeft" )) ) { success = false; }
+ if ( !mAnkleLeftState.setJoint ( character->getJoint( "mAnkleLeft" )) ) { success = false; }
+
+ if ( !mHipRightState.setJoint ( character->getJoint( "mHipRight" )) ) { success = false; }
+ if ( !mKneeRightState.setJoint ( character->getJoint( "mKneeRight" )) ) { success = false; }
+ if ( !mAnkleRightState.setJoint ( character->getJoint( "mAnkleRight" )) ) { success = false; }
+ }
+
+ if ( success )
+ {
+ mChestState.setUsage(LLJointState::ROT);
+ addJointState( &mChestState );
+
+ if ( USING_VENTRELLA_AVATAR_MOTION_TEST )
+ {
+ //-------------------------------------------
+ // neck
+ //-------------------------------------------
+ mNeckState.setUsage(LLJointState::ROT);
+ addJointState( &mNeckState );
+
+ //-------------------------------------------
+ // left arm
+ //-------------------------------------------
+ mCollarLeftState.setUsage(LLJointState::ROT);
+ addJointState( &mCollarLeftState );
+
+ mShoulderLeftState.setUsage(LLJointState::ROT);
+ addJointState( &mShoulderLeftState );
+
+ mElbowLeftState.setUsage(LLJointState::ROT);
+ addJointState( &mElbowLeftState );
+
+ mWristLeftState.setUsage(LLJointState::ROT);
+ addJointState( &mWristLeftState );
+
+
+ //-------------------------------------------
+ // right arm
+ //-------------------------------------------
+ mCollarRightState.setUsage(LLJointState::ROT);
+ addJointState( &mCollarRightState );
+
+ mShoulderRightState.setUsage(LLJointState::ROT);
+ addJointState( &mShoulderRightState );
+
+ mElbowRightState.setUsage(LLJointState::ROT);
+ addJointState( &mElbowRightState );
+
+ mWristRightState.setUsage(LLJointState::ROT);
+ addJointState( &mWristRightState );
+
+ //-------------------------------------------
+ // left leg
+ //-------------------------------------------
+ mHipLeftState.setUsage(LLJointState::ROT);
+ addJointState( &mHipLeftState );
+
+ mKneeLeftState.setUsage(LLJointState::ROT);
+ addJointState( &mKneeLeftState );
+
+ mAnkleLeftState.setUsage(LLJointState::ROT);
+ addJointState( &mAnkleLeftState );
+
+
+ //-------------------------------------------
+ // right leg
+ //-------------------------------------------
+ mHipRightState.setUsage(LLJointState::ROT);
+ addJointState( &mHipRightState );
+
+ mKneeRightState.setUsage(LLJointState::ROT);
+ addJointState( &mKneeRightState );
+
+ mAnkleRightState.setUsage(LLJointState::ROT);
+ addJointState( &mAnkleRightState );
+ }
+ }
+
+ if ( success )
+ {
+ return STATUS_SUCCESS;
+ }
+ else
+ {
+ return STATUS_FAILURE;
+ }
+ //end Ventrella
+ }
+
+ // 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.
+ virtual BOOL onUpdate(F32 time, U8* joint_mask)
+ {
+ mBreatheRate = 1.f;
+
+ F32 breathe_amt = (sinf(mBreatheRate * time) * BREATHE_ROT_MOTION_STRENGTH);
+
+ mChestState.setRotation(LLQuaternion(breathe_amt, LLVector3(0.f, 1.f, 0.f)));
+
+ //Ventrella
+ if ( USING_VENTRELLA_AVATAR_MOTION_TEST )
+ {
+ F32 wave = ( sinf ( time * 2.0f ) * 0.5f );
+
+ mChestState.setRotation ( LLQuaternion( wave, LLVector3( -1.0f, 0.0f, 0.0f ) ) );
+
+ mCollarLeftState.setRotation ( LLQuaternion( wave, LLVector3( 1.0f, 0.0f, 0.0f ) ) );
+ mShoulderLeftState.setRotation ( LLQuaternion( wave, LLVector3( 1.0f, 0.0f, 0.0f ) ) );
+ mElbowLeftState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 0.0f, 1.0f ) ) );
+ mWristLeftState.setRotation ( LLQuaternion( wave, LLVector3( 1.0f, 0.0f, 0.0f ) ) );
+
+ mCollarRightState.setRotation ( LLQuaternion( wave, LLVector3( -1.0f, 0.0f, 0.0f ) ) );
+ mShoulderRightState.setRotation ( LLQuaternion( wave, LLVector3( 1.0f, 0.0f, 0.0f ) ) );
+ mElbowRightState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 0.0f, 1.0f ) ) );
+ mWristRightState.setRotation ( LLQuaternion( wave, LLVector3( 1.0f, 0.0f, 0.0f ) ) );
+
+ mHipLeftState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 1.0f, 0.0f ) ) );
+ mKneeLeftState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, -1.0f, 0.0f ) ) );
+ mAnkleLeftState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 1.0f, 0.0f ) ) );
+
+ mHipRightState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 1.0f, 0.0f ) ) );
+ mKneeRightState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, -1.0f, 0.0f ) ) );
+ mAnkleRightState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 1.0f, 0.0f ) ) );
+ }
+ //end Ventrella
+
+ return TRUE;
+ }
+
+
+ // called when a motion is deactivated
+ virtual void onDeactivate() {}
+
+public:
+ //-------------------------------------------------------------------------
+ // joint states to be animated
+ //-------------------------------------------------------------------------
+ LLJointState mChestState;
+
+ //Ventrella
+ LLJointState mNeckState;
+ LLJointState mCollarLeftState;
+ LLJointState mShoulderLeftState;
+ LLJointState mElbowLeftState;
+ LLJointState mWristLeftState;
+ LLJointState mCollarRightState;
+ LLJointState mShoulderRightState;
+ LLJointState mElbowRightState;
+ LLJointState mWristRightState;
+ LLJointState mHipLeftState;
+ LLJointState mKneeLeftState;
+ LLJointState mAnkleLeftState;
+ LLJointState mHipRightState;
+ LLJointState mKneeRightState;
+ LLJointState mAnkleRightState;
+ //end Ventrella
+
+ F32 mBreatheRate;
+ LLCharacter* mCharacter;
+};
+
+//-----------------------------------------------------------------------------
+// class LLPelvisFixMotion
+//-----------------------------------------------------------------------------
+class LLPelvisFixMotion :
+ public LLMotion
+{
+public:
+ // Constructor
+ LLPelvisFixMotion(const LLUUID &id) : LLMotion(id), mCharacter(NULL) {mName = "pelvis_fix";}
+
+ // Destructor
+ virtual ~LLPelvisFixMotion() { }
+
+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 LLPelvisFixMotion(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 0.0; }
+
+ // motions must report their "ease in" duration
+ virtual F32 getEaseInDuration() { return 0.5f; }
+
+ // motions must report their "ease out" duration.
+ virtual F32 getEaseOutDuration() { return 0.5f; }
+
+ // motions must report their priority
+ virtual LLJoint::JointPriority getPriority() { return LLJoint::LOW_PRIORITY; }
+
+ 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_PELVIS_FIX; }
+
+ // 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)
+ {
+ mCharacter = character;
+
+ if (!mPelvisState.setJoint( character->getJoint("mPelvis")))
+ {
+ return STATUS_FAILURE;
+ }
+
+ mPelvisState.setUsage(LLJointState::POS);
+
+ addJointState( &mPelvisState );
+ 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.
+ virtual BOOL onUpdate(F32 time, U8* joint_mask)
+ {
+ mPelvisState.setPosition(LLVector3::zero);
+
+ return TRUE;
+ }
+
+ // called when a motion is deactivated
+ virtual void onDeactivate() {}
+
+public:
+ //-------------------------------------------------------------------------
+ // joint states to be animated
+ //-------------------------------------------------------------------------
+ LLJointState mPelvisState;
+ LLCharacter* mCharacter;
+};
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar()
+//-----------------------------------------------------------------------------
+LLVOAvatar::LLVOAvatar(
+ const LLUUID& id,
+ const LLPCode pcode,
+ LLViewerRegion* regionp)
+ :
+ LLViewerObject(id, pcode, regionp),
+ mHUDTargetZoom(1.f),
+ mHUDCurZoom(1.f),
+ mLastHeadBakedID( IMG_DEFAULT_AVATAR ),
+ mLastUpperBodyBakedID( IMG_DEFAULT_AVATAR ),
+ mLastLowerBodyBakedID( IMG_DEFAULT_AVATAR ),
+ mLastEyesBakedID( IMG_DEFAULT_AVATAR ),
+ mLastSkirtBakedID( IMG_DEFAULT_AVATAR ),
+ mIsDummy(FALSE),
+ mSpecialRenderMode(0),
+ mPelvisToFoot(0.f),
+ mLastSkeletonSerialNum( 0 ),
+ mTurning(FALSE),
+ mHeadOffset(),
+ mIsSitting(FALSE),
+ mTimeVisible(),
+ mTyping(FALSE),
+ mMeshValid(FALSE),
+ mVisible(FALSE),
+ mWindFreq(0.f),
+ mRipplePhase( 0.f ),
+ mBelowWater(FALSE),
+ mAppearanceAnimSetByUser(FALSE),
+ mLastAppearanceBlendTime(0.f),
+ mAppearanceAnimating(FALSE),
+ mHeadLayerSet( NULL ),
+ mUpperBodyLayerSet( NULL ),
+ mLowerBodyLayerSet( NULL ),
+ mEyesLayerSet( NULL ),
+ mSkirtLayerSet( NULL ),
+ mRenderPriority(1.0f),
+ mNumAGPVertices(0),
+ mNameString(),
+ mTitle(),
+ mNameAway(FALSE),
+ mNameBusy(FALSE),
+ mNameMute(FALSE),
+ mNameAppearance(FALSE),
+ mLastRegionHandle(0),
+ mRegionCrossingCount(0),
+ mFirstTEMessageReceived( FALSE ),
+ mFirstAppearanceMessageReceived( FALSE ),
+ mHeadBakedLoaded(FALSE),
+ mHeadMaskDiscard(-1),
+ mUpperBakedLoaded(FALSE),
+ mUpperMaskDiscard(-1),
+ mLowerBakedLoaded(FALSE),
+ mLowerMaskDiscard(-1),
+ mEyesBakedLoaded(FALSE),
+ mSkirtBakedLoaded(FALSE),
+ mHeadMaskTexName(0),
+ mUpperMaskTexName(0),
+ mLowerMaskTexName(0),
+ mCulled( FALSE ),
+ mTexSkinColor( NULL ),
+ mTexHairColor( NULL ),
+ mTexEyeColor( NULL )
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ //VTResume(); // VTune
+
+ lldebugs << "LLVOAvatar Constructor (0x" << this << ") id:" << mID << llendl;
+
+ mPelvisp = NULL;
+
+ for( S32 i=0; i<LOCTEX_NUM_ENTRIES; i++ )
+ {
+ mLocalTextureBaked[i] = FALSE;
+ mLocalTextureDiscard[i] = MAX_DISCARD_LEVEL+1;
+ }
+
+ mDirtyMesh = TRUE; // Dirty geometry, need to regenerate.
+ mShadow0Facep = NULL;
+ mShadow1Facep = NULL;
+ mHeadp = NULL;
+
+ mIsBuilt = FALSE;
+
+ mNumJoints = 0;
+ mSkeleton = NULL;
+ mScreenp = NULL;
+
+ mNumCollisionVolumes = 0;
+ mCollisionVolumes = NULL;
+
+ // set up animation variables
+ mSpeed = 0.f;
+ setAnimationData("Speed", &mSpeed);
+
+ strcpy(mAvatarDefinition, AVATAR_DEFAULT_CHAR);
+
+ if (id == gAgentID)
+ {
+ mIsSelf = TRUE;
+ gAgent.setAvatarObject(this);
+ lldebugs << "Marking avatar as self " << id << llendl;
+ }
+ else
+ {
+ mIsSelf = FALSE;
+ }
+
+ setNumTEs(TEX_NUM_ENTRIES);
+
+ mbCanSelect = TRUE;
+
+ mSignaledAnimations.clear();
+ mPlayingAnimations.clear();
+
+ mWasOnGroundLeft = FALSE;
+ mWasOnGroundRight = FALSE;
+
+ mTimeLast = 0.0f;
+ mSpeedAccum = 0.0f;
+
+ mRippleTimeLast = 0.f;
+
+ mShadowImageID = LLUUID( gViewerArt.getString("foot_shadow.tga"));
+ mShadowImagep = gImageList.getImage(mShadowImageID);
+ mShadowImagep->bind();
+ mShadowImagep->setClamp(TRUE, TRUE);
+
+ mInAir = FALSE;
+
+ mStepOnLand = TRUE;
+ mStepMaterial = 0;
+
+ //-------------------------------------------------------------------------
+ // initialize joint, mesh and shape members
+ //-------------------------------------------------------------------------
+ mRoot.setName( "mRoot" );
+
+ // skinned mesh objects
+ mHairLOD.setName("mHairLOD");
+ mHairMesh0.setName("mHairMesh0");
+ mHairMesh0.setMeshID(MESH_ID_HAIR);
+ mHairMesh1.setName("mHairMesh1");
+ mHairMesh2.setName("mHairMesh2");
+ mHairMesh3.setName("mHairMesh3");
+ mHairMesh4.setName("mHairMesh4");
+ mHairMesh5.setName("mHairMesh5");
+
+ mHairMesh0.setIsTransparent(TRUE);
+ mHairMesh1.setIsTransparent(TRUE);
+ mHairMesh2.setIsTransparent(TRUE);
+ mHairMesh3.setIsTransparent(TRUE);
+ mHairMesh4.setIsTransparent(TRUE);
+ mHairMesh5.setIsTransparent(TRUE);
+
+ mHeadLOD.setName("mHeadLOD");
+ mHeadMesh0.setName("mHeadMesh0");
+ mHeadMesh0.setMeshID(MESH_ID_HEAD);
+ mHeadMesh1.setName("mHeadMesh1");
+ mHeadMesh2.setName("mHeadMesh2");
+ mHeadMesh3.setName("mHeadMesh3");
+ mHeadMesh4.setName("mHeadMesh4");
+
+ mEyeLashLOD.setName("mEyeLashLOD");
+ mEyeLashMesh0.setName("mEyeLashMesh0");
+ mEyeLashMesh0.setMeshID(MESH_ID_HEAD);
+ mEyeLashMesh0.setIsTransparent(TRUE);
+
+ mUpperBodyLOD.setName("mUpperBodyLOD");
+ mUpperBodyMesh0.setName("mUpperBodyMesh0");
+ mUpperBodyMesh0.setMeshID(MESH_ID_UPPER_BODY);
+ mUpperBodyMesh1.setName("mUpperBodyMesh1");
+ mUpperBodyMesh2.setName("mUpperBodyMesh2");
+ mUpperBodyMesh3.setName("mUpperBodyMesh3");
+ mUpperBodyMesh4.setName("mUpperBodyMesh4");
+
+ mLowerBodyLOD.setName("mLowerBodyLOD");
+ mLowerBodyMesh0.setName("mLowerBodyMesh0");
+ mLowerBodyMesh0.setMeshID(MESH_ID_LOWER_BODY);
+ mLowerBodyMesh1.setName("mLowerBodyMesh1");
+ mLowerBodyMesh2.setName("mLowerBodyMesh2");
+ mLowerBodyMesh3.setName("mLowerBodyMesh3");
+ mLowerBodyMesh4.setName("mLowerBodyMesh4");
+
+ mEyeBallLeftLOD.setName("mEyeBallLeftLOD");
+ mEyeBallLeftMesh0.setName("mEyeBallLeftMesh0");
+ mEyeBallLeftMesh1.setName("mEyeBallLeftMesh1");
+
+ mEyeBallRightLOD.setName("mEyeBallRightLOD");
+ mEyeBallRightMesh0.setName("mEyeBallRightMesh0");
+ mEyeBallRightMesh1.setName("mEyeBallRightMesh1");
+
+ mSkirtLOD.setName("mSkirtLOD");
+ mSkirtMesh0.setName("mSkirtMesh0");
+ mSkirtMesh0.setMeshID(MESH_ID_SKIRT);
+ mSkirtMesh1.setName("mSkirtMesh1");
+ mSkirtMesh2.setName("mSkirtMesh2");
+ mSkirtMesh3.setName("mSkirtMesh3");
+ mSkirtMesh4.setName("mSkirtMesh4");
+
+ mSkirtMesh0.setIsTransparent(TRUE);
+ mSkirtMesh1.setIsTransparent(TRUE);
+ mSkirtMesh2.setIsTransparent(TRUE);
+ mSkirtMesh3.setIsTransparent(TRUE);
+ mSkirtMesh4.setIsTransparent(TRUE);
+
+ // set the pick names for the avatar
+ mHeadMesh0.setPickName( LLViewerJoint::PN_0 );
+ mHeadMesh1.setPickName( LLViewerJoint::PN_0 );
+ mHeadMesh2.setPickName( LLViewerJoint::PN_0 );
+ mHeadMesh3.setPickName( LLViewerJoint::PN_0 );
+ mHeadMesh4.setPickName( LLViewerJoint::PN_0 );
+ mEyeLashMesh0.setPickName( LLViewerJoint::PN_0 );
+
+ mUpperBodyMesh0.setPickName( LLViewerJoint::PN_1 );
+ mUpperBodyMesh1.setPickName( LLViewerJoint::PN_1 );
+ mUpperBodyMesh2.setPickName( LLViewerJoint::PN_1 );
+ mUpperBodyMesh3.setPickName( LLViewerJoint::PN_1 );
+ mUpperBodyMesh4.setPickName( LLViewerJoint::PN_1 );
+
+ mLowerBodyMesh0.setPickName( LLViewerJoint::PN_2 );
+ mLowerBodyMesh1.setPickName( LLViewerJoint::PN_2 );
+ mLowerBodyMesh2.setPickName( LLViewerJoint::PN_2 );
+ mLowerBodyMesh3.setPickName( LLViewerJoint::PN_2 );
+ mLowerBodyMesh4.setPickName( LLViewerJoint::PN_2 );
+
+ mEyeBallLeftMesh0.setPickName( LLViewerJoint::PN_3 );
+ mEyeBallLeftMesh1.setPickName( LLViewerJoint::PN_3 );
+ mEyeBallRightMesh0.setPickName( LLViewerJoint::PN_3 );
+ mEyeBallRightMesh1.setPickName( LLViewerJoint::PN_3 );
+
+ mHairMesh0.setPickName( LLViewerJoint::PN_4);
+ mHairMesh1.setPickName( LLViewerJoint::PN_4);
+ mHairMesh2.setPickName( LLViewerJoint::PN_4);
+ mHairMesh3.setPickName( LLViewerJoint::PN_4);
+ mHairMesh4.setPickName( LLViewerJoint::PN_4);
+ mHairMesh5.setPickName( LLViewerJoint::PN_4);
+
+ mSkirtMesh0.setPickName( LLViewerJoint::PN_5 );
+ mSkirtMesh1.setPickName( LLViewerJoint::PN_5 );
+ mSkirtMesh2.setPickName( LLViewerJoint::PN_5 );
+ mSkirtMesh3.setPickName( LLViewerJoint::PN_5 );
+ mSkirtMesh4.setPickName( LLViewerJoint::PN_5 );
+
+ // material settings
+
+ mEyeBallLeftMesh0.setSpecular( LLColor4( 1.0f, 1.0f, 1.0f, 1.0f ), 1.f );
+ mEyeBallLeftMesh1.setSpecular( LLColor4( 1.0f, 1.0f, 1.0f, 1.0f ), 1.f );
+ mEyeBallRightMesh0.setSpecular( LLColor4( 1.0f, 1.0f, 1.0f, 1.0f ), 1.f );
+ mEyeBallRightMesh1.setSpecular( LLColor4( 1.0f, 1.0f, 1.0f, 1.0f ), 1.f );
+
+ //-------------------------------------------------------------------------
+ // register motions
+ //-------------------------------------------------------------------------
+ if (LLCharacter::sInstances.getLength() == 1)
+ {
+ LLKeyframeMotion::setVFS(gStaticVFS);
+ addMotion( ANIM_AGENT_BUSY, LLNullMotion::create );
+ addMotion( ANIM_AGENT_CROUCH, LLKeyframeStandMotion::create );
+ addMotion( ANIM_AGENT_CROUCHWALK, LLKeyframeWalkMotion::create );
+ addMotion( ANIM_AGENT_EXPRESS_AFRAID, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_ANGER, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_BORED, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_CRY, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_DISDAIN, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_EMBARRASSED, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_FROWN, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_KISS, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_LAUGH, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_OPEN_MOUTH, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_REPULSED, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_SAD, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_SHRUG, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_SMILE, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_SURPRISE, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_TONGUE_OUT, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_TOOTHSMILE, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_WINK, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_WORRY, LLEmote::create );
+ addMotion( ANIM_AGENT_RUN, LLKeyframeWalkMotion::create );
+ addMotion( ANIM_AGENT_STAND, LLKeyframeStandMotion::create );
+ addMotion( ANIM_AGENT_STAND_1, LLKeyframeStandMotion::create );
+ addMotion( ANIM_AGENT_STAND_2, LLKeyframeStandMotion::create );
+ addMotion( ANIM_AGENT_STAND_3, LLKeyframeStandMotion::create );
+ addMotion( ANIM_AGENT_STAND_4, LLKeyframeStandMotion::create );
+ addMotion( ANIM_AGENT_STANDUP, LLKeyframeFallMotion::create );
+ addMotion( ANIM_AGENT_TURNLEFT, LLKeyframeWalkMotion::create );
+ addMotion( ANIM_AGENT_TURNRIGHT, LLKeyframeWalkMotion::create );
+ addMotion( ANIM_AGENT_WALK, LLKeyframeWalkMotion::create );
+
+ // motions without a start/stop bit
+ addMotion( ANIM_AGENT_BODY_NOISE, LLBodyNoiseMotion::create );
+ addMotion( ANIM_AGENT_BREATHE_ROT, LLBreatheMotionRot::create );
+ addMotion( ANIM_AGENT_EDITING, LLEditingMotion::create );
+ addMotion( ANIM_AGENT_EYE, LLEyeMotion::create );
+ addMotion( ANIM_AGENT_FEMALE_WALK, LLKeyframeWalkMotion::create );
+ addMotion( ANIM_AGENT_FLY_ADJUST, LLFlyAdjustMotion::create );
+ addMotion( ANIM_AGENT_HAND_MOTION, LLHandMotion::create );
+ addMotion( ANIM_AGENT_HEAD_ROT, LLHeadRotMotion::create );
+ addMotion( ANIM_AGENT_PELVIS_FIX, LLPelvisFixMotion::create );
+ addMotion( ANIM_AGENT_SIT_FEMALE, LLKeyframeMotion::create );
+ addMotion( ANIM_AGENT_TARGET, LLTargetingMotion::create );
+ addMotion( ANIM_AGENT_WALK_ADJUST, LLWalkAdjustMotion::create );
+ }
+
+ if (gNoRender)
+ {
+ return;
+ }
+ buildCharacter();
+
+ // preload specific motions here
+ createMotion( ANIM_AGENT_CUSTOMIZE);
+ createMotion( ANIM_AGENT_CUSTOMIZE_DONE);
+
+ //VTPause(); // VTune
+}
+
+
+//------------------------------------------------------------------------
+// LLVOAvatar::~LLVOAvatar()
+//------------------------------------------------------------------------
+LLVOAvatar::~LLVOAvatar()
+{
+ lldebugs << "LLVOAvatar Destructor (0x" << this << ") id:" << mID << llendl;
+
+ if (mIsSelf)
+ {
+ gAgent.setAvatarObject(NULL);
+ }
+
+ mRoot.removeAllChildren();
+
+ delete [] mSkeleton;
+ mSkeleton = NULL;
+
+ delete mScreenp;
+ mScreenp = NULL;
+
+ delete [] mCollisionVolumes;
+ mCollisionVolumes = NULL;
+
+
+ mNumJoints = 0;
+
+ delete mHeadLayerSet;
+ mHeadLayerSet = NULL;
+
+ delete mUpperBodyLayerSet;
+ mUpperBodyLayerSet = NULL;
+
+ delete mLowerBodyLayerSet;
+ mLowerBodyLayerSet = NULL;
+
+ delete mEyesLayerSet;
+ mEyesLayerSet = NULL;
+
+ delete mSkirtLayerSet;
+ mSkirtLayerSet = NULL;
+
+ mAttachmentPoints.deleteAllData();
+
+ delete mTexSkinColor;
+ mTexSkinColor = NULL;
+ delete mTexHairColor;
+ mTexHairColor = NULL;
+ delete mTexEyeColor;
+ mTexEyeColor = NULL;
+
+ std::for_each(mMeshes.begin(), mMeshes.end(), DeletePairedPointer());
+
+ mDead = TRUE;
+
+ // Clean up class data
+ LLVOAvatar::cullAvatarsByPixelArea();
+
+ mAnimationSources.clear();
+
+ lldebugs << "LLVOAvatar Destructor end" << llendl;
+}
+
+void LLVOAvatar::markDead()
+{
+ if (mNameText)
+ {
+ mNameText->markDead();
+ mNameText = NULL;
+ sNumVisibleChatBubbles--;
+ }
+
+ mBeam = NULL;
+ LLViewerObject::markDead();
+}
+
+
+BOOL LLVOAvatar::isFullyBaked()
+{
+ if (mIsDummy) return TRUE;
+
+ BOOL head_baked = ( getTEImage( TEX_HEAD_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL upper_baked = ( getTEImage( TEX_UPPER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL lower_baked = ( getTEImage( TEX_LOWER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL eyes_baked = ( getTEImage( TEX_EYES_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL skirt_baked = ( getTEImage( TEX_SKIRT_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+
+ if (isWearingWearableType(WT_SKIRT))
+ {
+ return head_baked && upper_baked && lower_baked && eyes_baked && skirt_baked;
+ }
+ else
+ {
+ return head_baked && upper_baked && lower_baked && eyes_baked;
+ }
+}
+
+void LLVOAvatar::deleteLayerSetCaches()
+{
+ if( mHeadLayerSet ) mHeadLayerSet->deleteCaches();
+ if( mUpperBodyLayerSet ) mUpperBodyLayerSet->deleteCaches();
+ if( mLowerBodyLayerSet ) mLowerBodyLayerSet->deleteCaches();
+ if( mEyesLayerSet ) mEyesLayerSet->deleteCaches();
+ if( mSkirtLayerSet ) mSkirtLayerSet->deleteCaches();
+}
+
+// static
+BOOL LLVOAvatar::areAllNearbyInstancesBaked()
+{
+ for( LLVOAvatar* inst = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
+ inst;
+ inst = (LLVOAvatar*)LLCharacter::sInstances.getNextData() )
+ {
+ if( inst->isDead() )
+ {
+ continue;
+ }
+ else
+ if( inst->getPixelArea() < MIN_PIXEL_AREA_FOR_COMPOSITE )
+ {
+ return TRUE; // Assumes sInstances is sorted by pixel area.
+ }
+ else
+ if( !inst->isFullyBaked() )
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+// static
+void LLVOAvatar::dumpScratchTextureByteCount()
+{
+ llinfos << "Scratch Texture GL: " << (sScratchTexBytes/1024) << "KB" << llendl;
+}
+
+// static
+void LLVOAvatar::dumpBakedStatus()
+{
+ LLVector3d camera_pos_global = gAgent.getCameraPositionGlobal();
+
+ for( LLVOAvatar* inst = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
+ inst;
+ inst = (LLVOAvatar*)LLCharacter::sInstances.getNextData() )
+ {
+ llinfos << "Avatar ";
+
+ LLNameValue* firstname = inst->getNVPair("FirstName");
+ LLNameValue* lastname = inst->getNVPair("LastName");
+
+ if( firstname )
+ {
+ llcont << firstname->getString();
+ }
+ if( lastname )
+ {
+ llcont << " " << lastname->getString();
+ }
+
+ llcont << " " << inst->mID;
+
+ if( inst->isDead() )
+ {
+ llcont << " DEAD ("<< inst->getNumRefs() << " refs)";
+ }
+
+ if( inst->mIsSelf )
+ {
+ llcont << " (self)";
+ }
+
+
+ F64 dist_to_camera = (inst->getPositionGlobal() - camera_pos_global).magVec();
+ llcont << " " << dist_to_camera << "m ";
+
+ llcont << " " << inst->mPixelArea << " pixels";
+
+ if( inst->isVisible() )
+ {
+ llcont << " (visible)";
+ }
+ else
+ {
+ llcont << " (not visible)";
+ }
+
+ if( inst->isFullyBaked() )
+ {
+ llcont << " Baked";
+ }
+ else
+ {
+ llcont << " Unbaked (";
+ if( inst->getTEImage( TEX_HEAD_BAKED )->getID() == IMG_DEFAULT_AVATAR )
+ {
+ llcont << " head";
+ }
+
+ if( inst->getTEImage( TEX_UPPER_BAKED )->getID() == IMG_DEFAULT_AVATAR )
+ {
+ llcont << " upper";
+ }
+
+ if( inst->getTEImage( TEX_LOWER_BAKED )->getID() == IMG_DEFAULT_AVATAR )
+ {
+ llcont << " lower";
+ }
+
+ if( inst->getTEImage( TEX_EYES_BAKED )->getID() == IMG_DEFAULT_AVATAR )
+ {
+ llcont << " eyes";
+ }
+
+ if (inst->isWearingWearableType(WT_SKIRT))
+ {
+ if( inst->getTEImage( TEX_SKIRT_BAKED )->getID() == IMG_DEFAULT_AVATAR )
+ {
+ llcont << " skirt";
+ }
+ }
+
+ llcont << " ) " << inst->getUnbakedPixelAreaRank() << "/" << LLVOAvatar::sMaxOtherAvatarsToComposite;
+ if( inst->isCulled() )
+ {
+ llcont << " culled";
+ }
+ }
+ llcont << llendl;
+/*
+ if( inst->isDead() )
+ {
+ llinfos << "DEAD LIST " << llendl;
+
+
+ for( S32 i = 0; i < inst->mOwners.count(); i++ )
+ {
+ llinfos << i << llendl;
+ LLPointer<LLViewerObject>* owner = (LLPointer<LLViewerObject>*)(inst->mOwners[i]);
+ LLPointer<LLViewerObject>* cur;
+ if( !owner->mName.isEmpty() )
+ {
+ llinfos << " " << owner->mName << llendl;
+ }
+
+ LLViewerObject* key_vo;
+ for( key_vo = gObjectList.mActiveObjects.getFirstKey(); key_vo; key_vo = gObjectList.mActiveObjects.getNextKey() )
+ {
+ cur = &(gObjectList.mActiveObjects.getCurrentDataWithoutIncrement());
+ if( cur == owner )
+ {
+ llinfos << " gObjectList.mActiveObjects" << llendl;
+ }
+ }
+
+ for( key_vo = gObjectList.mAvatarObjects.getFirstKey(); key_vo; key_vo = gObjectList.mAvatarObjects.getNextKey() )
+ {
+ cur = &(gObjectList.mAvatarObjects.getCurrentDataWithoutIncrement());
+ if( cur == owner )
+ {
+ llinfos << " gObjectList.mAvatarObjects" << llendl;
+ }
+ }
+
+ LLUUID id;
+ for( id = gObjectList.mDeadObjects.getFirstKey(); id; id = gObjectList.mDeadObjects.getNextKey() )
+ {
+ cur = &(gObjectList.mDeadObjects.getCurrentDataWithoutIncrement());
+ if( cur == owner )
+ {
+ llinfos << " gObjectList.mDeadObjects" << llendl;
+ }
+ }
+
+
+ for( id = gObjectList.mUUIDObjectMap.getFirstKey(); id; id = gObjectList.mUUIDObjectMap.getNextKey() )
+ {
+ cur = &(gObjectList.mUUIDObjectMap.getCurrentDataWithoutIncrement());
+ if( cur == owner )
+ {
+ llinfos << " gObjectList.mUUIDObjectMap" << llendl;
+ }
+ }
+
+ S32 j;
+ S32 k;
+ for( j = 0; j < 16; j++ )
+ {
+ for( k = 0; k < 10; k++ )
+ {
+ cur = &(gObjectList.mCloseObjects[j][k]);
+ if( cur == owner )
+ {
+ llinfos << " gObjectList.mCloseObjects" << llendl;
+ }
+ }
+ }
+
+ for( j = 0; j < gObjectList.mObjects.count(); j++ )
+ {
+ cur = &(gObjectList.mObjects[j]);
+ if( cur == owner )
+ {
+ llinfos << " gObjectList.mObjects" << llendl;
+ }
+ }
+
+ for( j = 0; j < gObjectList.mMapObjects.count(); j++ )
+ {
+ cur = &(gObjectList.mMapObjects[j]);
+ if( cur == owner )
+ {
+ llinfos << " gObjectList.mMapObjects" << llendl;
+ }
+ }
+ }
+ }
+ */
+ }
+}
+
+//static
+void LLVOAvatar::cleanupVertexPrograms()
+{
+}
+
+//static
+void LLVOAvatar::initVertexPrograms()
+{
+}
+
+//static
+void LLVOAvatar::restoreGL()
+{
+ LLVOAvatar* inst;
+ for( inst = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
+ inst;
+ inst = (LLVOAvatar*)LLCharacter::sInstances.getNextData() )
+ {
+ inst->setCompositeUpdatesEnabled( TRUE );
+ inst->invalidateComposite( inst->mHeadLayerSet, FALSE );
+ inst->invalidateComposite( inst->mLowerBodyLayerSet, FALSE );
+ inst->invalidateComposite( inst->mUpperBodyLayerSet, FALSE );
+ inst->invalidateComposite( inst->mEyesLayerSet, FALSE );
+ inst->invalidateComposite( inst->mSkirtLayerSet, FALSE );
+ inst->updateMeshTextures();
+ }
+}
+
+//static
+void LLVOAvatar::destroyGL()
+{
+ deleteCachedImages();
+ cleanupVertexPrograms();
+}
+
+// static
+void LLVOAvatar::deleteCachedImages()
+{
+ if (LLTexLayerSet::sHasCaches)
+ {
+ lldebugs << "Deleting layer set caches" << llendl;
+ for( LLVOAvatar* inst = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
+ inst;
+ inst = (LLVOAvatar*)LLCharacter::sInstances.getNextData() )
+ {
+ inst->deleteLayerSetCaches();
+ }
+ LLTexLayerSet::sHasCaches = FALSE;
+ }
+
+ for( GLuint* namep = (GLuint*)sScratchTexNames.getFirstData();
+ namep;
+ namep = (GLuint*)sScratchTexNames.getNextData() )
+ {
+ glDeleteTextures(1, namep );
+ stop_glerror();
+ }
+
+ if( sScratchTexBytes )
+ {
+ lldebugs << "Clearing Scratch Textures " << (sScratchTexBytes/1024) << "KB" << llendl;
+
+ sScratchTexNames.deleteAllData();
+ LLVOAvatar::sScratchTexLastBindTime.deleteAllData();
+ LLImageGL::sGlobalTextureMemory -= sScratchTexBytes;
+ sScratchTexBytes = 0;
+ }
+
+ gTexStaticImageList.deleteCachedImages();
+}
+
+
+//------------------------------------------------------------------------
+// static
+// LLVOAvatar::initClass()
+//------------------------------------------------------------------------
+void LLVOAvatar::initClass()
+{
+ LLVOAvatar::sMaxOtherAvatarsToComposite = gSavedSettings.getS32("AvatarCompositeLimit");
+
+ char xmlFile[MAX_PATH];
+
+ sprintf(xmlFile, "%s_lad.xml", gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,AVATAR_DEFAULT_CHAR).c_str());
+ BOOL success = sXMLTree.parseFile( xmlFile, FALSE );
+ if (!success)
+ {
+ llerrs << "Problem reading avatar configuration file:" << xmlFile << llendl;
+ }
+
+ // now sanity check xml file
+ LLXmlTreeNode* root = sXMLTree.getRoot();
+ if (!root)
+ {
+ llerrs << "No root node found in avatar configuration file: " << xmlFile << llendl;
+ }
+
+ //-------------------------------------------------------------------------
+ // <linden_avatar version="1.0"> (root)
+ //-------------------------------------------------------------------------
+ if( !root->hasName( "linden_avatar" ) )
+ {
+ llerrs << "Invalid avatar file header: " << xmlFile << llendl;
+ }
+
+ LLString version;
+ static LLStdStringHandle version_string = LLXmlTree::addAttributeString("version");
+ if( !root->getFastAttributeString( version_string, version ) || (version != "1.0") )
+ {
+ llerrs << "Invalid avatar file version: " << version << " in file: " << xmlFile << llendl;
+ }
+
+ S32 wearable_def_version = 1;
+ static LLStdStringHandle wearable_definition_version_string = LLXmlTree::addAttributeString("wearable_definition_version");
+ root->getFastAttributeS32( wearable_definition_version_string, wearable_def_version );
+ LLWearable::setCurrentDefinitionVersion( wearable_def_version );
+
+ LLString mesh_file_name;
+
+ LLXmlTreeNode* skeleton_node = root->getChildByName( "skeleton" );
+ if (!skeleton_node)
+ {
+ llerrs << "No skeleton in avatar configuration file: " << xmlFile << llendl;
+ }
+
+ LLString skeleton_file_name;
+ static LLStdStringHandle file_name_string = LLXmlTree::addAttributeString("file_name");
+ if (!skeleton_node->getFastAttributeString(file_name_string, skeleton_file_name))
+ {
+ llerrs << "No file name in skeleton node in avatar config file: " << xmlFile << llendl;
+ }
+
+ std::string skeleton_path;
+ skeleton_path = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,skeleton_file_name);
+ if (!parseSkeletonFile(skeleton_path))
+ {
+ llerrs << "Error parsing skeleton file: " << skeleton_path << llendl;
+ }
+
+ // Process XML data
+
+ // avatar_skeleton.xml
+ llassert(!sSkeletonInfo);
+ sSkeletonInfo = new LLVOAvatarSkeletonInfo;
+ if (!sSkeletonInfo->parseXml(sSkeletonXMLTree.getRoot()))
+ {
+ llerrs << "Error parsing skeleton XML file" << llendl;
+ }
+ // parse avatar_lad.xml
+ llassert(!sAvatarInfo);
+ sAvatarInfo = new LLVOAvatarInfo;
+ if (!sAvatarInfo->parseXmlSkeletonNode(root))
+ {
+ llerrs << "Error parsing skeleton node in avatar XML file" << llendl;
+ }
+ if (!sAvatarInfo->parseXmlMeshNodes(root))
+ {
+ llerrs << "Error parsing skeleton node in avatar XML file" << llendl;
+ }
+ if (!sAvatarInfo->parseXmlColorNodes(root))
+ {
+ llerrs << "Error parsing skeleton node in avatar XML file" << llendl;
+ }
+ if (!sAvatarInfo->parseXmlLayerNodes(root))
+ {
+ llerrs << "Error parsing skeleton node in avatar XML file" << llendl;
+ }
+ if (!sAvatarInfo->parseXmlDriverNodes(root))
+ {
+ llerrs << "Error parsing skeleton node in avatar XML file" << llendl;
+ }
+}
+
+
+void LLVOAvatar::cleanupClass()
+{
+ delete sAvatarInfo;
+ sAvatarInfo = NULL;
+ delete sSkeletonInfo;
+ sSkeletonInfo = NULL;
+ sSkeletonXMLTree.cleanup();
+ sXMLTree.cleanup();
+}
+
+
+//-----------------------------------------------------------------------------
+// parseSkeletonFile()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::parseSkeletonFile(const LLString& filename)
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ //-------------------------------------------------------------------------
+ // parse the file
+ //-------------------------------------------------------------------------
+ BOOL success = sSkeletonXMLTree.parseFile( filename, FALSE );
+
+ if (!success)
+ {
+ llerrs << "Can't parse skeleton file: " << filename << llendl;
+ return FALSE;
+ }
+
+ // now sanity check xml file
+ LLXmlTreeNode* root = sSkeletonXMLTree.getRoot();
+ if (!root)
+ {
+ llerrs << "No root node found in avatar skeleton file: " << filename << llendl;
+ }
+
+ if( !root->hasName( "linden_skeleton" ) )
+ {
+ llerrs << "Invalid avatar skeleton file header: " << filename << llendl;
+ }
+
+ LLString version;
+ static LLStdStringHandle version_string = LLXmlTree::addAttributeString("version");
+ if( !root->getFastAttributeString( version_string, version ) || (version != "1.0") )
+ {
+ llerrs << "Invalid avatar skeleton file version: " << version << " in file: " << filename << llendl;
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// setupBone()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::setupBone(LLVOAvatarBoneInfo* info, LLViewerJoint* parent)
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ LLViewerJoint* joint = NULL;
+
+ if (info->mIsJoint)
+ {
+ joint = (LLViewerJoint*)getCharacterJoint(sCurJoint);
+ if (!joint)
+ {
+ llwarns << "Too many bones" << llendl;
+ return FALSE;
+ }
+ joint->setName( info->mName );
+ }
+ else // collision volume
+ {
+ if (sCurVolume >= (S32)mNumCollisionVolumes)
+ {
+ llwarns << "Too many bones" << llendl;
+ return FALSE;
+ }
+ joint = (LLViewerJoint*)(&mCollisionVolumes[sCurVolume]);
+
+ joint->setName( info->mName );
+ }
+
+ // add to parent
+ if (parent)
+ {
+ parent->addChild( joint );
+ }
+
+ joint->setPosition(info->mPos);
+
+ joint->setRotation(mayaQ(info->mRot.mV[VX], info->mRot.mV[VY],
+ info->mRot.mV[VZ], LLQuaternion::XYZ));
+
+ joint->setScale(info->mScale);
+
+
+ if (info->mIsJoint)
+ {
+ joint->setSkinOffset( info->mPivot );
+ sCurJoint++;
+ }
+ else // collision volume
+ {
+ sCurVolume++;
+ }
+
+ // setup children
+ LLVOAvatarBoneInfo::child_list_t::iterator iter;
+ for (iter = info->mChildList.begin(); iter != info->mChildList.end(); iter++)
+ {
+ LLVOAvatarBoneInfo *child_info = *iter;
+ if (!setupBone(child_info, joint))
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// buildSkeleton()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::buildSkeleton(LLVOAvatarSkeletonInfo *info)
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ //-------------------------------------------------------------------------
+ // allocate joints
+ //-------------------------------------------------------------------------
+ if (!allocateCharacterJoints(info->mNumBones))
+ {
+ llerrs << "Can't allocate " << info->mNumBones << " joints" << llendl;
+ return FALSE;
+ }
+
+ //-------------------------------------------------------------------------
+ // allocate volumes
+ //-------------------------------------------------------------------------
+ if (info->mNumCollisionVolumes)
+ {
+ if (!allocateCollisionVolumes(info->mNumCollisionVolumes))
+ {
+ llerrs << "Can't allocate " << info->mNumCollisionVolumes << " collision volumes" << llendl;
+ return FALSE;
+ }
+ }
+
+ sCurJoint = 0;
+ sCurVolume = 0;
+
+ LLVOAvatarSkeletonInfo::bone_info_list_t::iterator iter;
+ for (iter = info->mBoneInfoList.begin(); iter != info->mBoneInfoList.end(); iter++)
+ {
+ LLVOAvatarBoneInfo *info = *iter;
+ if (!setupBone(info, NULL))
+ {
+ llerrs << "Error parsing bone in skeleton file" << llendl;
+ return FALSE;
+ }
+ }
+
+ // add special-purpose "screen" joint
+ mScreenp = new LLViewerJoint("mScreen", NULL);
+ // for now, put screen at origin, as it is only used during special
+ // HUD rendering mode
+ mScreenp->setWorldPosition(LLVector3::zero);
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::buildCharacter()
+// Deferred initialization and rebuild of the avatar.
+//-----------------------------------------------------------------------------
+extern BOOL gPrintMessagesThisFrame;
+void LLVOAvatar::buildCharacter()
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ //-------------------------------------------------------------------------
+ // remove all references to our existing skeleton
+ // so we can rebuild it
+ //-------------------------------------------------------------------------
+ flushAllMotions();
+
+ //-------------------------------------------------------------------------
+ // remove all of mRoot's children
+ //-------------------------------------------------------------------------
+ mRoot.removeAllChildren();
+ mIsBuilt = FALSE;
+
+ //-------------------------------------------------------------------------
+ // clear mesh data
+ //-------------------------------------------------------------------------
+ mHairMesh0.setMesh(NULL);
+ mHairMesh1.setMesh(NULL);
+ mHairMesh2.setMesh(NULL);
+ mHairMesh3.setMesh(NULL);
+ mHairMesh4.setMesh(NULL);
+ mHairMesh5.setMesh(NULL);
+
+ mHeadMesh0.setMesh(NULL);
+ mHeadMesh1.setMesh(NULL);
+ mHeadMesh2.setMesh(NULL);
+ mHeadMesh3.setMesh(NULL);
+ mHeadMesh4.setMesh(NULL);
+
+ mEyeLashMesh0.setMesh(NULL);
+
+ mUpperBodyMesh0.setMesh(NULL);
+ mUpperBodyMesh1.setMesh(NULL);
+ mUpperBodyMesh2.setMesh(NULL);
+ mUpperBodyMesh3.setMesh(NULL);
+ mUpperBodyMesh4.setMesh(NULL);
+
+ mLowerBodyMesh0.setMesh(NULL);
+ mLowerBodyMesh1.setMesh(NULL);
+ mLowerBodyMesh2.setMesh(NULL);
+ mLowerBodyMesh3.setMesh(NULL);
+ mLowerBodyMesh4.setMesh(NULL);
+
+ mEyeBallLeftMesh0.setMesh(NULL);
+ mEyeBallLeftMesh1.setMesh(NULL);
+ mEyeBallRightMesh0.setMesh(NULL);
+ mEyeBallRightMesh1.setMesh(NULL);
+
+ mSkirtMesh0.setMesh(NULL);
+ mSkirtMesh1.setMesh(NULL);
+ mSkirtMesh2.setMesh(NULL);
+ mSkirtMesh3.setMesh(NULL);
+ mSkirtMesh4.setMesh(NULL);
+
+ //-------------------------------------------------------------------------
+ // (re)load our skeleton and meshes
+ //-------------------------------------------------------------------------
+ LLTimer timer;
+
+ BOOL status = loadAvatar();
+ stop_glerror();
+
+ if (gNoRender)
+ {
+ // Still want to load the avatar skeleton so visual parameters work.
+ return;
+ }
+
+ gPrintMessagesThisFrame = TRUE;
+ lldebugs << "Avatar load took " << timer.getElapsedTimeF32() << " seconds." << llendl;
+
+ if ( ! status )
+ {
+ if ( mIsSelf )
+ {
+ llerrs << "Unable to load user's avatar" << llendl;
+ //set_avatar_character( &LLString(AVATAR_DEFAULT_CHAR));
+ }
+ else
+ {
+ llwarns << "Unable to load other's avatar" << llendl;
+ }
+ return;
+ }
+
+ //-------------------------------------------------------------------------
+ // initialize "well known" joint pointers
+ //-------------------------------------------------------------------------
+ mPelvisp = (LLViewerJoint*)mRoot.findJoint("mPelvis");
+ mTorsop = (LLViewerJoint*)mRoot.findJoint("mTorso");
+ mChestp = (LLViewerJoint*)mRoot.findJoint("mChest");
+ mNeckp = (LLViewerJoint*)mRoot.findJoint("mNeck");
+ mHeadp = (LLViewerJoint*)mRoot.findJoint("mHead");
+ mSkullp = (LLViewerJoint*)mRoot.findJoint("mSkull");
+ mHipLeftp = (LLViewerJoint*)mRoot.findJoint("mHipLeft");
+ mHipRightp = (LLViewerJoint*)mRoot.findJoint("mHipRight");
+ mKneeLeftp = (LLViewerJoint*)mRoot.findJoint("mKneeLeft");
+ mKneeRightp = (LLViewerJoint*)mRoot.findJoint("mKneeRight");
+ mAnkleLeftp = (LLViewerJoint*)mRoot.findJoint("mAnkleLeft");
+ mAnkleRightp = (LLViewerJoint*)mRoot.findJoint("mAnkleRight");
+ mFootLeftp = (LLViewerJoint*)mRoot.findJoint("mFootLeft");
+ mFootRightp = (LLViewerJoint*)mRoot.findJoint("mFootRight");
+ mWristLeftp = (LLViewerJoint*)mRoot.findJoint("mWristLeft");
+ mWristRightp = (LLViewerJoint*)mRoot.findJoint("mWristRight");
+ mEyeLeftp = (LLViewerJoint*)mRoot.findJoint("mEyeLeft");
+ mEyeRightp = (LLViewerJoint*)mRoot.findJoint("mEyeRight");
+
+ //-------------------------------------------------------------------------
+ // Make sure "well known" pointers exist
+ //-------------------------------------------------------------------------
+ if (!(mPelvisp &&
+ mTorsop &&
+ mChestp &&
+ mNeckp &&
+ mHeadp &&
+ mSkullp &&
+ mHipLeftp &&
+ mHipRightp &&
+ mKneeLeftp &&
+ mKneeRightp &&
+ mAnkleLeftp &&
+ mAnkleRightp &&
+ mFootLeftp &&
+ mFootRightp &&
+ mWristLeftp &&
+ mWristRightp &&
+ mEyeLeftp &&
+ mEyeRightp))
+ {
+ llerrs << "Failed to create avatar." << llendl;
+ }
+
+ //-------------------------------------------------------------------------
+ // initialize the pelvis
+ //-------------------------------------------------------------------------
+ mPelvisp->setPosition( LLVector3(0.0f, 0.0f, 0.0f) );
+
+ //-------------------------------------------------------------------------
+ // set head offset from pelvis
+ //-------------------------------------------------------------------------
+ updateHeadOffset();
+
+ //-------------------------------------------------------------------------
+ // start default motions
+ //-------------------------------------------------------------------------
+ startMotion( ANIM_AGENT_HEAD_ROT );
+ startMotion( ANIM_AGENT_EYE );
+ startMotion( ANIM_AGENT_BODY_NOISE );
+ startMotion( ANIM_AGENT_BREATHE_ROT );
+ startMotion( ANIM_AGENT_HAND_MOTION );
+ startMotion( ANIM_AGENT_PELVIS_FIX );
+
+ //-------------------------------------------------------------------------
+ // restart any currently active motions
+ //-------------------------------------------------------------------------
+ processAnimationStateChanges();
+
+ mIsBuilt = TRUE;
+ stop_glerror();
+
+ //-------------------------------------------------------------------------
+ // build the attach and detach menus
+ //-------------------------------------------------------------------------
+ if (mIsSelf)
+ {
+ gAttachBodyPartPieMenus[0] = NULL;
+ gAttachBodyPartPieMenus[1] = new LLPieMenu("Right Arm >");
+ gAttachBodyPartPieMenus[2] = new LLPieMenu("Head >");
+ gAttachBodyPartPieMenus[3] = new LLPieMenu("Left Arm >");
+ gAttachBodyPartPieMenus[4] = NULL;
+ gAttachBodyPartPieMenus[5] = new LLPieMenu("Left Leg >");
+ gAttachBodyPartPieMenus[6] = new LLPieMenu("Torso >");
+ gAttachBodyPartPieMenus[7] = new LLPieMenu("Right Leg >");
+
+ gDetachBodyPartPieMenus[0] = NULL;
+ gDetachBodyPartPieMenus[1] = new LLPieMenu("Right Arm >");
+ gDetachBodyPartPieMenus[2] = new LLPieMenu("Head >");
+ gDetachBodyPartPieMenus[3] = new LLPieMenu("Left Arm >");
+ gDetachBodyPartPieMenus[4] = NULL;
+ gDetachBodyPartPieMenus[5] = new LLPieMenu("Left Leg >");
+ gDetachBodyPartPieMenus[6] = new LLPieMenu("Torso >");
+ gDetachBodyPartPieMenus[7] = new LLPieMenu("Right Leg >");
+
+ for (S32 i = 0; i < 8; i++)
+ {
+ if (gAttachBodyPartPieMenus[i])
+ {
+ gAttachPieMenu->appendMenu( gAttachBodyPartPieMenus[i] );
+ }
+ else
+ {
+ BOOL attachment_found = FALSE;
+ for (LLViewerJointAttachment* attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment->getGroup() == i)
+ {
+ LLMenuItemCallGL* item;
+ item = new LLMenuItemCallGL(attachment->getName(),
+ &handle_attach_to_avatar,
+ object_selected_and_point_valid,
+ attachment);
+ gAttachPieMenu->append(item);
+
+ attachment_found = TRUE;
+ break;
+
+ }
+ }
+
+ if (!attachment_found)
+ {
+ gAttachPieMenu->appendSeparator();
+ }
+ }
+
+ if (gDetachBodyPartPieMenus[i])
+ {
+ gDetachPieMenu->appendMenu( gDetachBodyPartPieMenus[i] );
+ }
+ else
+ {
+ BOOL attachment_found = FALSE;
+ for (LLViewerJointAttachment* attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment->getGroup() == i)
+ {
+ gDetachPieMenu->append(new LLMenuItemCallGL(attachment->getName(),
+ &handle_detach_from_avatar, object_attached, attachment));
+
+ attachment_found = TRUE;
+ break;
+ }
+ }
+
+ if (!attachment_found)
+ {
+ gDetachPieMenu->appendSeparator();
+ }
+ }
+ }
+
+ // add screen attachments
+ for (LLViewerJointAttachment* attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment->getGroup() == 8)
+ {
+ LLMenuItemCallGL* item;
+ item = new LLMenuItemCallGL(attachment->getName(),
+ &handle_attach_to_avatar,
+ object_selected_and_point_valid,
+ attachment);
+ gAttachScreenPieMenu->append(item);
+ gDetachScreenPieMenu->append(new LLMenuItemCallGL(attachment->getName(),
+ &handle_detach_from_avatar, object_attached, attachment));
+ }
+ }
+
+ for (S32 pass = 0; pass < 2; pass++)
+ {
+ for (LLViewerJointAttachment* attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment->getIsHUDAttachment() != (pass == 1))
+ {
+ continue;
+ }
+ gAttachSubMenu->append(new LLMenuItemCallGL(attachment->getName(),
+ &handle_attach_to_avatar, object_selected_and_point_valid, &attach_label, attachment));
+ gDetachSubMenu->append(new LLMenuItemCallGL(attachment->getName(),
+ &handle_detach_from_avatar, object_attached, &detach_label, attachment));
+
+ }
+ if (pass == 0)
+ {
+ // put separator between non-hud and hud attachments
+ gAttachSubMenu->appendSeparator();
+ gDetachSubMenu->appendSeparator();
+ }
+ }
+
+ for (S32 group = 0; group < 8; group++)
+ {
+ // skip over groups that don't have sub menus
+ if (!gAttachBodyPartPieMenus[group] || !gDetachBodyPartPieMenus[group])
+ {
+ continue;
+ }
+
+ std::multimap<S32, LLViewerJointAttachment*> attachment_pie_menu_map;
+
+ // gather up all attachment points assigned to this group, and throw into map sorted by pie slice number
+ for (LLViewerJointAttachment* attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if(attachment->getGroup() == group)
+ {
+ // use multimap to provide a partial order off of the pie slice key
+ attachment_pie_menu_map.insert(std::pair<S32, LLViewerJointAttachment*>(attachment->getPieSlice(), attachment));
+ }
+ }
+
+ // add in requested order to pie menu, inserting separators as necessary
+ std::multimap<S32, LLViewerJointAttachment*>::iterator attach_it;
+ S32 cur_pie_slice = 0;
+ for (attach_it = attachment_pie_menu_map.begin(); attach_it != attachment_pie_menu_map.end(); ++attach_it)
+ {
+ S32 requested_pie_slice = attach_it->first;
+ while (cur_pie_slice < requested_pie_slice)
+ {
+ gAttachBodyPartPieMenus[group]->appendSeparator();
+ gDetachBodyPartPieMenus[group]->appendSeparator();
+ cur_pie_slice++;
+ }
+
+ LLViewerJointAttachment* attachment = attach_it->second;
+
+ gAttachBodyPartPieMenus[group]->append(new LLMenuItemCallGL(attachment->getName(),
+ &handle_attach_to_avatar, object_selected_and_point_valid, attachment));
+ gDetachBodyPartPieMenus[group]->append(new LLMenuItemCallGL(attachment->getName(),
+ &handle_detach_from_avatar, object_attached, attachment));
+
+ cur_pie_slice++;
+ }
+ }
+ }
+
+ mMeshValid = TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// releaseMeshData()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::releaseMeshData()
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ if (sInstances.getLength() < AVATAR_AGP_RELEASE_THRESHOLD || mIsDummy)
+ {
+ return;
+ }
+
+ //llinfos << "Releasing" << llendl;
+
+ // cleanup mesh data
+ mHairLOD.setValid(FALSE, TRUE);
+ mHeadLOD.setValid(FALSE, TRUE);
+ mEyeLashLOD.setValid(FALSE, TRUE);
+ mUpperBodyLOD.setValid(FALSE, TRUE);
+ mLowerBodyLOD.setValid(FALSE, TRUE);
+ mEyeBallLeftLOD.setValid(FALSE, TRUE);
+ mEyeBallRightLOD.setValid(FALSE, TRUE);
+ mSkirtLOD.setValid(FALSE, TRUE);
+
+ //cleanup AGP data
+ if (mDrawable.notNull())
+ {
+ LLFace* facep = mDrawable->getFace(0);
+ facep->setSize(0, 0);
+ mNumAGPVertices = 0;
+
+ // You need to reset the vertex data in order to guarantee
+ // that the data is deallocated, AND you start at the 0 index
+ // when you restore the face. We're assuming ONLY ONE FACE per
+ // avatar pool, this logic is broken if that isn't the case!
+ facep->getPool()->resetVertexData(0);
+ }
+
+ for (LLViewerJointAttachment *attachmentPoint = mAttachmentPoints.getFirstData();
+ attachmentPoint;
+ attachmentPoint = mAttachmentPoints.getNextData())
+ {
+ if (!attachmentPoint->getIsHUDAttachment())
+ {
+ attachmentPoint->setAttachmentVisibility(FALSE);
+ }
+ }
+ mMeshValid = FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// restoreMeshData()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::restoreMeshData()
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ //llinfos << "Restoring" << llendl;
+ mMeshValid = TRUE;
+ updateJointLODs();
+
+ if (mIsSelf)
+ {
+ updateAttachmentVisibility(gAgent.getCameraMode());
+ }
+ else
+ {
+ for (LLViewerJointAttachment *attachmentPoint = mAttachmentPoints.getFirstData();
+ attachmentPoint;
+ attachmentPoint = mAttachmentPoints.getNextData())
+ {
+ if (!attachmentPoint->getIsHUDAttachment())
+ {
+ attachmentPoint->setAttachmentVisibility(TRUE);
+ }
+ }
+ }
+
+ // force mesh update as LOD might not have changed to trigger this
+ updateMeshData();
+}
+
+//-----------------------------------------------------------------------------
+// updateMeshData()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::updateMeshData()
+{
+ if (mDrawable.notNull())
+ {
+ LLFace* facep = mDrawable->getFace(0);
+
+ U32 num_vertices = 0;
+
+ // this order is determined by number of LODS
+ // if a mesh earlier in this list changed LODs while a later mesh doesn't,
+ // the later mesh's index offset will be inaccurate
+ mEyeBallLeftLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
+ mEyeBallRightLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
+ mEyeLashLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
+ mHeadLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
+ mLowerBodyLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
+ mSkirtLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
+ mUpperBodyLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
+ mHairLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
+
+ if (num_vertices != mNumAGPVertices)
+ {
+ // resize immediately
+// llinfos << "Resizing avatar AGP buffer!" << llendl;
+ facep->getPool()->resetVertexData(num_vertices);
+ facep->setSize(0,0);
+ facep->setSize(num_vertices, 0);
+ mNumAGPVertices = num_vertices;
+ }
+
+ // This is a hack! Avatars have their own pool, so we are detecting
+ // the case of more than one avatar in the pool (thus > 0 instead of >= 0)
+ if (facep->getGeomIndex() > 0)
+ {
+ llerrs << "non-zero geom index: " << facep->getGeomIndex() << " in LLVOAvatar::restoreMeshData" << llendl;
+ }
+
+ mEyeBallLeftLOD.updateFaceData(facep, mAdjustedPixelArea);
+ mEyeBallRightLOD.updateFaceData(facep, mAdjustedPixelArea);
+ mEyeLashLOD.updateFaceData(facep, mAdjustedPixelArea);
+ mHeadLOD.updateFaceData(facep, mAdjustedPixelArea);
+ mLowerBodyLOD.updateFaceData(facep, mAdjustedPixelArea);
+ mSkirtLOD.updateFaceData(facep, mAdjustedPixelArea);
+ mUpperBodyLOD.updateFaceData(facep, mAdjustedPixelArea);
+ mHairLOD.updateFaceData(facep, mAdjustedPixelArea, TRUE);
+ }
+}
+
+//------------------------------------------------------------------------
+
+//------------------------------------------------------------------------
+// The viewer can only suggest a good size for the agent,
+// the simulator will keep it inside a reasonable range.
+void LLVOAvatar::computeBodySize()
+{
+ LLVector3 pelvis_scale = mPelvisp->getScale();
+
+ // some of the joints have not been cached
+ LLVector3 skull = mSkullp->getPosition();
+ LLVector3 skull_scale = mSkullp->getScale();
+
+ LLVector3 neck = mNeckp->getPosition();
+ LLVector3 neck_scale = mNeckp->getScale();
+
+ LLVector3 chest = mChestp->getPosition();
+ LLVector3 chest_scale = mChestp->getScale();
+
+ // the rest of the joints have been cached
+ LLVector3 head = mHeadp->getPosition();
+ LLVector3 head_scale = mHeadp->getScale();
+
+ LLVector3 torso = mTorsop->getPosition();
+ LLVector3 torso_scale = mTorsop->getScale();
+
+ LLVector3 hip = mHipLeftp->getPosition();
+ LLVector3 hip_scale = mHipLeftp->getScale();
+
+ LLVector3 knee = mKneeLeftp->getPosition();
+ LLVector3 knee_scale = mKneeLeftp->getScale();
+
+ LLVector3 ankle = mAnkleLeftp->getPosition();
+ LLVector3 ankle_scale = mAnkleLeftp->getScale();
+
+ LLVector3 foot = mFootLeftp->getPosition();
+
+ mPelvisToFoot = hip.mV[VZ] * pelvis_scale.mV[VZ] -
+ knee.mV[VZ] * hip_scale.mV[VZ] -
+ ankle.mV[VZ] * knee_scale.mV[VZ] -
+ foot.mV[VZ] * ankle_scale.mV[VZ];
+
+ mBodySize.mV[VZ] = mPelvisToFoot +
+ // the sqrt(2) correction below is an approximate
+ // correction to get to the top of the head
+ F_SQRT2 * (skull.mV[VZ] * head_scale.mV[VZ]) +
+ head.mV[VZ] * neck_scale.mV[VZ] +
+ neck.mV[VZ] * chest_scale.mV[VZ] +
+ chest.mV[VZ] * torso_scale.mV[VZ] +
+ torso.mV[VZ] * pelvis_scale.mV[VZ];
+
+ // TODO -- measure the real depth and width
+ mBodySize.mV[VX] = DEFAULT_AGENT_DEPTH;
+ mBodySize.mV[VY] = DEFAULT_AGENT_WIDTH;
+
+/* debug spam
+ std::cout << "skull = " << skull << std::endl; // adebug
+ std::cout << "head = " << head << std::endl; // adebug
+ std::cout << "head_scale = " << head_scale << std::endl; // adebug
+ std::cout << "neck = " << neck << std::endl; // adebug
+ std::cout << "neck_scale = " << neck_scale << std::endl; // adebug
+ std::cout << "chest = " << chest << std::endl; // adebug
+ std::cout << "chest_scale = " << chest_scale << std::endl; // adebug
+ std::cout << "torso = " << torso << std::endl; // adebug
+ std::cout << "torso_scale = " << torso_scale << std::endl; // adebug
+ std::cout << std::endl; // adebug
+
+ std::cout << "pelvis_scale = " << pelvis_scale << std::endl;// adebug
+ std::cout << std::endl; // adebug
+
+ std::cout << "hip = " << hip << std::endl; // adebug
+ std::cout << "hip_scale = " << hip_scale << std::endl; // adebug
+ std::cout << "ankle = " << ankle << std::endl; // adebug
+ std::cout << "ankle_scale = " << ankle_scale << std::endl; // adebug
+ std::cout << "foot = " << foot << std::endl; // adebug
+ std::cout << "mBodySize = " << mBodySize << std::endl; // adebug
+ std::cout << "mPelvisToFoot = " << mPelvisToFoot << std::endl; // adebug
+ std::cout << std::endl; // adebug
+*/
+}
+
+//------------------------------------------------------------------------
+// LLVOAvatar::processUpdateMessage()
+//------------------------------------------------------------------------
+U32 LLVOAvatar::processUpdateMessage(LLMessageSystem *mesgsys,
+ void **user_data,
+ U32 block_num, const EObjectUpdateType update_type,
+ LLDataPacker *dp)
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ LLVector3 old_vel = getVelocity();
+ // Do base class updates...
+ U32 retval = LLViewerObject::processUpdateMessage(mesgsys, user_data, block_num, update_type, dp);
+
+ //llinfos << getRotation() << llendl;
+ //llinfos << getPosition() << llendl;
+ if (update_type == OUT_FULL )
+ {
+ if( !mIsSelf || !mFirstTEMessageReceived )
+ {
+// dumpAvatarTEs( "PRE processUpdateMessage()" );
+ unpackTEMessage(mesgsys, _PREHASH_ObjectData, block_num);
+// dumpAvatarTEs( "POST processUpdateMessage()" );
+
+ if( !mFirstTEMessageReceived )
+ {
+ onFirstTEMessageReceived();
+ }
+
+ // Disable updates to composites. We'll decide whether we need to do
+ // any updates after we find out whether this update message has any
+ // "baked" (pre-composited) textures.
+ setCompositeUpdatesEnabled( FALSE );
+ updateMeshTextures();
+ setCompositeUpdatesEnabled( TRUE );
+ }
+ }
+
+ return retval;
+}
+
+// virtual
+S32 LLVOAvatar::setTETexture(const U8 te, const LLUUID& uuid)
+{
+ // The core setTETexture() method requests images, so we need
+ // to redirect certain avatar texture requests to different sims.
+ if (isTextureIndexBaked(te))
+ {
+ LLHost target_host = getObjectHost();
+ return setTETextureCore(te, uuid, target_host);
+ }
+ else
+ {
+ return setTETextureCore(te, uuid, LLHost::invalid);
+ }
+}
+
+
+// setTEImage
+
+//------------------------------------------------------------------------
+// idleUpdate()
+//------------------------------------------------------------------------
+BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+ LLFastTimer t(LLFastTimer::FTM_AVATAR_UPDATE);
+
+ if (isDead())
+ {
+ llinfos << "Warning! Idle on dead avatar" << llendl;
+ return TRUE;
+ }
+
+ if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_AVATAR)))
+ {
+ return TRUE;
+ }
+
+ // force immediate pixel area update on avatars using last frames data (before drawable or camera updates)
+ setPixelAreaAndAngle(gAgent);
+
+ // Update the LOD of the joints
+ static const F32 UPDATE_TIME = .5f;
+ if (mUpdateLODTimer.hasExpired())
+ {
+ mUpdateLODTimer.setTimerExpirySec(UPDATE_TIME * (.75f + frand(0.5f)));
+ updateJointLODs();
+ }
+
+ // force asynchronous drawable update
+ if(mDrawable.notNull() && !gNoRender)
+ {
+ LLFastTimer t(LLFastTimer::FTM_JOINT_UPDATE);
+
+ if (mIsSitting && getParent())
+ {
+ LLViewerObject *root_object = (LLViewerObject*)getRoot();
+ LLDrawable* drawablep = root_object->mDrawable;
+ // if this object hasn't already been updated by another avatar...
+ if (drawablep) // && !drawablep->isState(LLDrawable::EARLY_MOVE))
+ {
+ if (root_object->isSelected())
+ {
+ gPipeline.updateMoveNormalAsync(drawablep);
+ }
+ else
+ {
+ gPipeline.updateMoveDampedAsync(drawablep);
+ }
+ }
+ }
+ else
+ {
+ gPipeline.updateMoveDampedAsync(mDrawable);
+ }
+ }
+
+ //--------------------------------------------------------------------
+ // set alpha flag depending on state
+ //--------------------------------------------------------------------
+
+ if (mIsSelf)
+ {
+ LLViewerObject::idleUpdate(agent, world, time);
+
+ // trigger fidget anims
+ if (isAnyAnimationSignaled(AGENT_STAND_ANIMS, NUM_AGENT_STAND_ANIMS))
+ {
+ agent.fidget();
+ }
+ }
+ else
+ {
+ // Should override the idleUpdate stuff and leave out the angular update part.
+ LLQuaternion rotation = getRotation();
+ LLViewerObject::idleUpdate(agent, world, time);
+ setRotation(rotation);
+ }
+
+ // attach objects that were waiting for a drawable
+ lazyAttach();
+
+ // animate the character
+ // store off last frame's root position to be consistent with camera position
+ LLVector3 root_pos_last = mRoot.getWorldPosition();
+
+ updateCharacter(agent);
+
+ if (gNoRender)
+ {
+ return TRUE;
+ }
+
+ // HACK!!!!
+ // this is necessary for the floating name text above your head
+ if (mDrawable.notNull())
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+
+ // update attachments positions
+ {
+ LLFastTimer t(LLFastTimer::FTM_ATTACHMENT_UPDATE);
+ for(LLViewerJointAttachment *attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ LLViewerObject *attached_object = attachment->getObject(0);
+ if (attached_object && !attached_object->isDead() && attachment->getValid())
+ {
+ // if selecting any attachments, update all of them as non-damped
+ if (gSelectMgr->getObjectCount() && gSelectMgr->selectionIsAttachment())
+ {
+ gPipeline.updateMoveNormalAsync(attached_object->mDrawable);
+ }
+ else
+ {
+ gPipeline.updateMoveDampedAsync(attached_object->mDrawable);
+ }
+ attached_object->updateText();
+ }
+ }
+ }
+
+ //force a move if sitting on an active object
+ if (getParent() && ((LLViewerObject*) getParent())->mDrawable->isActive())
+ {
+ gPipeline.markMoved(mDrawable, TRUE);
+ }
+
+ // update morphing params
+ if (mAppearanceAnimating)
+ {
+ ESex avatar_sex = getSex();
+ F32 appearance_anim_time = mAppearanceMorphTimer.getElapsedTimeF32();
+ if (appearance_anim_time >= APPEARANCE_MORPH_TIME)
+ {
+ mAppearanceAnimating = FALSE;
+ for (LLVisualParam *param = getFirstVisualParam();
+ param;
+ param = getNextVisualParam())
+ {
+ if (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE)
+ {
+ param->stopAnimating(mAppearanceAnimSetByUser);
+ }
+ }
+ updateVisualParams();
+ if (mIsSelf)
+ {
+ gAgent.sendAgentSetAppearance();
+ }
+ }
+ else
+ {
+ F32 blend_frac = calc_bouncy_animation(appearance_anim_time / APPEARANCE_MORPH_TIME);
+ F32 last_blend_frac = calc_bouncy_animation(mLastAppearanceBlendTime / APPEARANCE_MORPH_TIME);
+ F32 morph_amt;
+ if (last_blend_frac == 1.f)
+ {
+ morph_amt = 1.f;
+ }
+ else
+ {
+ morph_amt = (blend_frac - last_blend_frac) / (1.f - last_blend_frac);
+ }
+
+ LLVisualParam *param;
+
+ // animate only top level params
+ for (param = getFirstVisualParam();
+ param;
+ param = getNextVisualParam())
+ {
+ if (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE)
+ {
+ param->animate(morph_amt, mAppearanceAnimSetByUser);
+ }
+ }
+
+ // apply all params
+ for (param = getFirstVisualParam();
+ param;
+ param = getNextVisualParam())
+ {
+ param->apply(avatar_sex);
+ }
+
+ mLastAppearanceBlendTime = appearance_anim_time;
+ }
+ dirtyMesh();
+ }
+
+ // update wind effect
+ if ((gPipeline.getVertexShaderLevel(LLPipeline::SHADER_AVATAR) >= LLDrawPoolAvatar::SHADER_LEVEL_CLOTH))
+ {
+ F32 hover_strength = 0.f;
+ F32 time_delta = mRippleTimer.getElapsedTimeF32() - mRippleTimeLast;
+ mRippleTimeLast = mRippleTimer.getElapsedTimeF32();
+ LLVector3 velocity = getVelocity();
+ F32 speed = velocity.magVec();
+ //RN: velocity varies too much frame to frame for this to work
+ mRippleAccel.clearVec();//lerp(mRippleAccel, (velocity - mLastVel) * time_delta, LLCriticalDamp::getInterpolant(0.02f));
+ mLastVel = velocity;
+ LLVector4 wind;
+ wind.setVec(getRegion()->mWind.getVelocityNoisy(getPositionAgent(), 4.f) - velocity);
+
+ if (mInAir)
+ {
+ hover_strength = HOVER_EFFECT_STRENGTH * llmax(0.f, HOVER_EFFECT_MAX_SPEED - speed);
+ }
+
+ if (mBelowWater)
+ {
+ // TODO: make cloth flow more gracefully when underwater
+ hover_strength += UNDERWATER_EFFECT_STRENGTH;
+ }
+
+ wind.mV[VZ] += hover_strength;
+ wind.normVec();
+
+ wind.mV[VW] = llmin(0.025f + (speed * 0.015f) + hover_strength, 0.5f);
+ F32 interp;
+ if (wind.mV[VW] > mWindVec.mV[VW])
+ {
+ interp = LLCriticalDamp::getInterpolant(0.2f);
+ }
+ else
+ {
+ interp = LLCriticalDamp::getInterpolant(0.4f);
+ }
+ mWindVec = lerp(mWindVec, wind, interp);
+
+ F32 wind_freq = hover_strength + llclamp(8.f + (speed * 0.7f) + (noise1(mRipplePhase) * 4.f), 8.f, 25.f);
+ mWindFreq = lerp(mWindFreq, wind_freq, interp);
+
+ if (mBelowWater)
+ {
+ mWindFreq *= UNDERWATER_FREQUENCY_DAMP;
+ }
+
+ mRipplePhase += (time_delta * mWindFreq);
+ if (mRipplePhase > F_TWO_PI)
+ {
+ mRipplePhase = fmodf(mRipplePhase, F_TWO_PI);
+ }
+ }
+
+ // update chat bubble
+ //--------------------------------------------------------------------
+ // draw text label over characters head
+ //--------------------------------------------------------------------
+ if (mChatTimer.getElapsedTimeF32() > BUBBLE_CHAT_TIME)
+ {
+ mChats.clear();
+ }
+
+ const F32 time_visible = mTimeVisible.getElapsedTimeF32();
+ const F32 NAME_SHOW_TIME = gSavedSettings.getF32("RenderNameShowTime"); // seconds
+ const F32 FADE_DURATION = gSavedSettings.getF32("RenderNameFadeDuration"); // seconds
+ BOOL visible_chat = gSavedSettings.getBOOL("UseChatBubbles") && (mChats.size() || mTyping);
+ BOOL render_name = visible_chat ||
+ (mVisible &&
+ (sRenderName == RENDER_NAME_ALWAYS) ||
+ (sRenderName == RENDER_NAME_FADE && time_visible < NAME_SHOW_TIME));
+ // If it's your own avatar, don't draw in mouselook, and don't
+ // draw if we're specifically hiding our own name.
+ if (mIsSelf)
+ {
+ render_name = render_name
+ && !gAgent.cameraMouselook()
+ && (visible_chat || !gSavedSettings.getBOOL("RenderNameHideSelf"));
+ }
+
+ if ( render_name )
+ {
+ BOOL new_name = FALSE;
+ if (visible_chat != mVisibleChat)
+ {
+ mVisibleChat = visible_chat;
+ new_name = TRUE;
+ }
+
+ // First Calculate Alpha
+ // If alpha > 0, create mNameText if necessary, otherwise delete it
+ {
+ F32 alpha = 0.f;
+ if (mAppAngle > 5.f)
+ {
+ const F32 START_FADE_TIME = NAME_SHOW_TIME - FADE_DURATION;
+ if (!visible_chat && sRenderName == RENDER_NAME_FADE && time_visible > START_FADE_TIME)
+ {
+ alpha = 1.f - (time_visible - START_FADE_TIME) / FADE_DURATION;
+ }
+ else
+ {
+ // ...not fading, full alpha
+ alpha = 1.f;
+ }
+ }
+ else if (mAppAngle > 2.f)
+ {
+ // far away is faded out also
+ alpha = (mAppAngle-2.f)/3.f;
+ }
+
+ if (alpha > 0.f)
+ {
+ if (!mNameText)
+ {
+ mNameText = (LLHUDText *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_TEXT);
+ mNameText->setMass(10.f);
+ mNameText->setSourceObject(this);
+ mNameText->setVertAlignment(LLHUDText::ALIGN_VERT_TOP);
+ mNameText->setVisibleOffScreen(TRUE);
+ mNameText->setMaxLines(11);
+ mNameText->setFadeDistance(CHAT_NORMAL_RADIUS, 5.f);
+ mNameText->setUseBubble(TRUE);
+ sNumVisibleChatBubbles++;
+ new_name = TRUE;
+ }
+
+ LLColor4 avatar_name_color = gColors.getColor( "AvatarNameColor" );
+ avatar_name_color.setAlpha(alpha);
+ mNameText->setColor(avatar_name_color);
+
+ LLQuaternion root_rot = mRoot.getWorldRotation();
+ mNameText->setUsePixelSize(TRUE);
+ LLVector3 pixel_right_vec;
+ LLVector3 pixel_up_vec;
+ gCamera->getPixelVectors(root_pos_last, pixel_up_vec, pixel_right_vec);
+ LLVector3 camera_to_av = root_pos_last - gCamera->getOrigin();
+ camera_to_av.normVec();
+ LLVector3 local_camera_at = camera_to_av * ~root_rot;
+ LLVector3 local_camera_up = camera_to_av % gCamera->getLeftAxis();
+ local_camera_up.normVec();
+ local_camera_up = local_camera_up * ~root_rot;
+
+ local_camera_up.scaleVec(mBodySize * 0.5f);
+ local_camera_at.scaleVec(mBodySize * 0.5f);
+
+ LLVector3 name_position = mRoot.getWorldPosition() +
+ (local_camera_up * root_rot) -
+ (projected_vec(local_camera_at * root_rot, camera_to_av));
+ name_position += pixel_up_vec * 15.f;
+ mNameText->setPositionAgent(name_position);
+ }
+ else if (mNameText)
+ {
+ mNameText->markDead();
+ mNameText = NULL;
+ sNumVisibleChatBubbles--;
+ }
+ }
+
+ LLNameValue *title = getNVPair("Title");
+ LLNameValue* firstname = getNVPair("FirstName");
+ LLNameValue* lastname = getNVPair("LastName");
+
+ if (mNameText.notNull() && firstname && lastname)
+ {
+ BOOL is_away = mSignaledAnimations.find(ANIM_AGENT_AWAY) != mSignaledAnimations.end();
+ BOOL is_busy = mSignaledAnimations.find(ANIM_AGENT_BUSY) != mSignaledAnimations.end();
+ BOOL is_appearance = mSignaledAnimations.find(ANIM_AGENT_CUSTOMIZE) != mSignaledAnimations.end();
+ BOOL is_muted;
+ if (mIsSelf)
+ {
+ is_muted = FALSE;
+ }
+ else
+ {
+ is_muted = gMuteListp->isMuted(getID());
+ }
+
+ if (mNameString.empty() ||
+ new_name ||
+ (!title && !mTitle.empty()) ||
+ (title && mTitle != title->getString()) ||
+ (is_away != mNameAway || is_busy != mNameBusy || is_muted != mNameMute)
+ || is_appearance != mNameAppearance)
+ {
+ char line[MAX_STRING];
+ if (title && title->getString() && title->getString()[0] != '\0')
+ {
+ strcpy(line, title->getString() );
+ strcat(line, "\n");
+ strcat(line, firstname->getString() );
+ }
+ else
+ {
+ strcpy(line, firstname->getString() );
+ }
+
+ strcat(line, " ");
+ strcat(line, lastname->getString());
+ BOOL need_comma = FALSE;
+
+ if (is_away || is_muted || is_busy)
+ {
+ strcat(line, " (");
+ if (is_away)
+ {
+ strcat(line, "Away");
+ need_comma = TRUE;
+ }
+ if (is_busy)
+ {
+ if (need_comma)
+ {
+ strcat(line, ", ");
+ }
+ strcat(line, "Busy");
+ need_comma = TRUE;
+ }
+ if (is_muted)
+ {
+ if (need_comma)
+ {
+ strcat(line, ", ");
+ }
+ strcat(line, "Muted");
+ need_comma = TRUE;
+ }
+ strcat(line,")");
+ }
+ if (is_appearance)
+ {
+ strcat(line, "\n(Editing Appearance)");
+ }
+ mNameAway = is_away;
+ mNameBusy = is_busy;
+ mNameMute = is_muted;
+ mNameAppearance = is_appearance;
+ mTitle = title ? title->getString() : "";
+ mNameString = utf8str_to_wstring(line);
+ new_name = TRUE;
+ }
+
+ if (visible_chat)
+ {
+ mNameText->setDropShadow(TRUE);
+ mNameText->setFont(LLFontGL::sSansSerif);
+ mNameText->setTextAlignment(LLHUDText::ALIGN_TEXT_LEFT);
+ mNameText->setFadeDistance(CHAT_NORMAL_RADIUS * 2.f, 5.f);
+ if (new_name)
+ {
+ mNameText->setLabel(mNameString);
+ }
+
+ char line[MAX_STRING];
+ line[0] = '\0';
+ std::deque<LLChat>::iterator chat_iter = mChats.begin();
+ mNameText->clearString();
+
+ LLColor4 new_chat = gColors.getColor( "AvatarNameColor" );
+ LLColor4 normal_chat = lerp(new_chat, LLColor4(0.8f, 0.8f, 0.8f, 1.f), 0.7f);
+ LLColor4 old_chat = lerp(normal_chat, LLColor4(0.6f, 0.6f, 0.6f, 1.f), 0.7f);
+ if (mTyping && mChats.size() >= MAX_BUBBLE_CHAT_UTTERANCES)
+ {
+ ++chat_iter;
+ }
+
+ for(; chat_iter != mChats.end(); ++chat_iter)
+ {
+ F32 chat_fade_amt = llclamp((F32)((LLFrameTimer::getElapsedSeconds() - chat_iter->mTime) / CHAT_FADE_TIME), 0.f, 4.f);
+ LLFontGL::StyleFlags style;
+ switch(chat_iter->mChatType)
+ {
+ case CHAT_TYPE_WHISPER:
+ style = LLFontGL::ITALIC;
+ break;
+ case CHAT_TYPE_SHOUT:
+ style = LLFontGL::BOLD;
+ break;
+ default:
+ style = LLFontGL::NORMAL;
+ break;
+ }
+ if (chat_fade_amt < 1.f)
+ {
+ F32 u = clamp_rescale(chat_fade_amt, 0.9f, 1.f, 0.f, 1.f);
+ mNameText->addLine(utf8str_to_wstring(chat_iter->mText), lerp(new_chat, normal_chat, u), style);
+ }
+ else if (chat_fade_amt < 2.f)
+ {
+ F32 u = clamp_rescale(chat_fade_amt, 1.9f, 2.f, 0.f, 1.f);
+ mNameText->addLine(utf8str_to_wstring(chat_iter->mText), lerp(normal_chat, old_chat, u), style);
+ }
+ else if (chat_fade_amt < 3.f)
+ {
+ //FIXME: only remove lines down to minimum number
+ mNameText->addLine(utf8str_to_wstring(chat_iter->mText), old_chat, style);
+ }
+ }
+ mNameText->setVisibleOffScreen(TRUE);
+
+ if (mTyping)
+ {
+ S32 dot_count = (llfloor(mTypingTimer.getElapsedTimeF32() * 3.f) + 2) % 3 + 1;
+ switch(dot_count)
+ {
+ case 1:
+ mNameText->addLine(".", new_chat);
+ break;
+ case 2:
+ mNameText->addLine("..", new_chat);
+ break;
+ case 3:
+ mNameText->addLine("...", new_chat);
+ break;
+ }
+
+ }
+ }
+ else
+ {
+ if (gSavedSettings.getBOOL("SmallAvatarNames"))
+ {
+ mNameText->setFont(LLFontGL::sSansSerif);
+ }
+ else
+ {
+ mNameText->setFont(LLFontGL::sSansSerifBig);
+ }
+ mNameText->setTextAlignment(LLHUDText::ALIGN_TEXT_CENTER);
+ mNameText->setFadeDistance(CHAT_NORMAL_RADIUS, 5.f);
+ mNameText->setVisibleOffScreen(FALSE);
+ if (new_name)
+ {
+ mNameText->setLabel("");
+ mNameText->setString(mNameString);
+ }
+ }
+ }
+ }
+ else if (mNameText)
+ {
+ mNameText->markDead();
+ mNameText = NULL;
+ sNumVisibleChatBubbles--;
+ }
+
+ //--------------------------------------------------------------------
+ // draw tractor beam when editing objects
+ //--------------------------------------------------------------------
+ if (!mIsSelf)
+ {
+ return TRUE;
+ }
+
+ // This is only done for yourself (maybe it should be in the agent?)
+ if (!needsRenderBeam() || !mIsBuilt)
+ {
+ mBeam = NULL;
+ }
+ else if (!mBeam || mBeam->isDead())
+ {
+ // VEFFECT: Tractor Beam
+ mBeam = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM);
+ mBeam->setColor(LLColor4U(gAgent.getEffectColor()));
+ mBeam->setSourceObject(this);
+ mBeamTimer.reset();
+ }
+
+ if (!mBeam.isNull())
+ {
+ if (gAgent.mPointAt.notNull())
+ {
+ // get point from pointat effect
+ mBeam->setPositionGlobal(gAgent.mPointAt->getPointAtPosGlobal());
+ mBeam->triggerLocal();
+ }
+ else if (gSelectMgr->getFirstRootObject() &&
+ gSelectMgr->getSelectType() != SELECT_TYPE_HUD)
+ {
+ LLViewerObject* objectp = gSelectMgr->getFirstRootObject();
+ mBeam->setTargetObject(objectp);
+ }
+ else
+ {
+ mBeam->setTargetObject(NULL);
+ LLTool *tool = gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) );
+ if (tool->isEditing())
+ {
+ if (tool->getEditingObject())
+ {
+ mBeam->setTargetObject(tool->getEditingObject());
+ }
+ else
+ {
+ mBeam->setPositionGlobal(tool->getEditingPointGlobal());
+ }
+ }
+ else
+ {
+ mBeam->setPositionGlobal(gLastHitNonFloraPosGlobal + gLastHitNonFloraObjectOffset);
+ }
+
+ }
+ if (mBeamTimer.getElapsedTimeF32() > 0.25f)
+ {
+ mBeam->setColor(LLColor4U(gAgent.getEffectColor()));
+ mBeam->setNeedsSendToSim(TRUE);
+ mBeamTimer.reset();
+ }
+ }
+
+ F32 avatar_height = (F32)(getPositionGlobal().mdV[VZ]);
+
+ F32 water_height;
+ water_height = getRegion()->getWaterHeight();
+
+ mBelowWater = avatar_height < water_height;
+
+ return TRUE;
+}
+
+void LLVOAvatar::slamPosition()
+{
+ gAgent.setPositionAgent(getPositionAgent());
+ mRoot.setWorldPosition(getPositionAgent()); // teleport
+ setChanged(TRANSLATED);
+ if (mDrawable.notNull())
+ {
+ gPipeline.updateMoveNormalAsync(mDrawable);
+ }
+ mRoot.updateWorldMatrixChildren();
+}
+
+//------------------------------------------------------------------------
+// updateCharacter()
+// called on both your avatar and other avatars
+//------------------------------------------------------------------------
+void LLVOAvatar::updateCharacter(LLAgent &agent)
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ // clear debug text
+ mDebugText.clear();
+
+ if (LLVOAvatar::sShowAnimationDebug)
+ {
+ LLString playing_anims;
+ for (LLMotion* motionp = mMotionController.getFirstActiveMotion();
+ motionp;
+ motionp = mMotionController.getNextActiveMotion())
+ {
+ if (motionp->getMinPixelArea() < getPixelArea())
+ {
+ char output[256];
+ if (motionp->getName().empty())
+ {
+ sprintf(output, "%s - %d", motionp->getID().getString().c_str(), (U32)motionp->getPriority());
+ }
+ else
+ {
+ sprintf(output, "%s - %d", motionp->getName().c_str(), (U32)motionp->getPriority());
+ }
+ addDebugText(output);
+ }
+ }
+ }
+
+ if (LLVOAvatar::sJointDebug)
+ {
+ llinfos << getNVPair("FirstName")->getString() << ": joint touches: " << LLJoint::sNumTouches << " updates: " << LLJoint::sNumUpdates << llendl;
+
+ LLJoint::sNumUpdates = 0;
+ LLJoint::sNumTouches = 0;
+ }
+// llinfos << mPixelArea << llendl;
+ if (gNoRender)
+ {
+ // Hack if we're running drones...
+ if (mIsSelf)
+ {
+ gAgent.setPositionAgent(getPositionAgent());
+ }
+ return;
+ }
+
+
+ LLVector3d root_pos_global;
+
+ if (!mIsBuilt)
+ {
+ return;
+ }
+
+ // change animation time quanta based on avatar render load
+ if (!mIsSelf)
+ {
+ F32 time_quantum = clamp_rescale((F32)sInstances.getLength(), 10.f, 35.f, 0.f, 0.25f);
+ F32 pixel_area_scale = clamp_rescale(mPixelArea, 100, 5000, 1.f, 0.f);
+ F32 time_step = time_quantum * pixel_area_scale;
+ if (time_step != 0.f)
+ {
+ // disable walk motion servo controller as it doesn't work with motion timesteps
+ stopMotion(ANIM_AGENT_WALK_ADJUST);
+ removeAnimationData("Walk Speed");
+ }
+ mMotionController.setTimeStep(time_step);
+// llinfos << "Setting timestep to " << time_quantum * pixel_area_scale << llendl;
+ }
+
+ if (getParent() && !mIsSitting)
+ {
+ sitOnObject((LLViewerObject*)getParent());
+ }
+ else if (!getParent() && mIsSitting && !isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED))
+ {
+ getOffObject();
+ }
+
+ // For fading out the names above heads, only let the timer
+ // run if we're visible.
+ if (mDrawable.notNull() && !mDrawable->isVisible())
+ {
+ mTimeVisible.reset();
+ }
+
+ // update screen joint size
+ mScreenp->setScale(LLVector3(1.f, gCamera->getAspect(), 1.f));
+ mScreenp->updateWorldMatrixChildren();
+
+ //--------------------------------------------------------------------
+ // create local variables in world coords for region position values
+ //--------------------------------------------------------------------
+ F32 speed;
+ LLVector3 normal;
+
+ LLVector3 xyVel = getVelocity();
+ xyVel.mV[VZ] = 0.0f;
+ speed = xyVel.magVec();
+
+ if (!(mIsSitting && getParent()))
+ {
+ //--------------------------------------------------------------------
+ // get timing info
+ // handle initial condition case
+ //--------------------------------------------------------------------
+ F32 animation_time = mAnimTimer.getElapsedTimeF32();
+ if (mTimeLast == 0.0f)
+ {
+ mTimeLast = animation_time;
+
+ // put the pelvis at slaved position/mRotation
+ mRoot.setWorldPosition( getPositionAgent() ); // first frame
+ mRoot.setWorldRotation( getRotation() );
+ }
+
+ //--------------------------------------------------------------------
+ // dont' let dT get larger than 1/5th of a second
+ //--------------------------------------------------------------------
+ F32 deltaTime = animation_time - mTimeLast;
+
+ deltaTime = llclamp( deltaTime, DELTA_TIME_MIN, DELTA_TIME_MAX );
+ mTimeLast = animation_time;
+
+ mSpeedAccum = (mSpeedAccum * 0.95f) + (speed * 0.05f);
+
+ //--------------------------------------------------------------------
+ // compute the position of the avatar's root
+ //--------------------------------------------------------------------
+ LLVector3d root_pos;
+ LLVector3d ground_under_pelvis;
+
+ if (mIsSelf)
+ {
+ gAgent.setPositionAgent(getRenderPosition());
+ }
+
+ root_pos = gAgent.getPosGlobalFromAgent(getRenderPosition());
+
+ resolveHeightGlobal(root_pos, ground_under_pelvis, normal);
+ F32 foot_to_ground = (F32) (root_pos.mdV[VZ] - mPelvisToFoot - ground_under_pelvis.mdV[VZ]);
+ BOOL in_air = ( (!gWorldPointer->getRegionFromPosGlobal(ground_under_pelvis)) ||
+ foot_to_ground > FOOT_GROUND_COLLISION_TOLERANCE);
+
+ if (in_air && !mInAir)
+ {
+ mTimeInAir.reset();
+ }
+ mInAir = in_air;
+
+ // correct for the fact that the pelvis is not necessarily the center
+ // of the agent's physical representation
+ root_pos.mdV[VZ] -= (0.5f * mBodySize.mV[VZ]) - mPelvisToFoot;
+ mRoot.setWorldPosition(gAgent.getPosAgentFromGlobal(root_pos) ); // regular update
+
+ //--------------------------------------------------------------------
+ // Propagate viewer object rotation to root of avatar
+ //--------------------------------------------------------------------
+ if (!isAnyAnimationSignaled(AGENT_NO_ROTATE_ANIMS, NUM_AGENT_NO_ROTATE_ANIMS))
+ {
+ LLQuaternion iQ;
+ LLVector3 upDir( 0.0f, 0.0f, 1.0f );
+
+ // Compute a forward direction vector derived from the primitive rotation
+ // and the velocity vector. When walking or jumping, don't let body deviate
+ // more than 90 from the view, if necessary, flip the velocity vector.
+
+ LLVector3 primDir;
+ if (mIsSelf)
+ {
+ primDir = agent.getAtAxis() - projected_vec(agent.getAtAxis(), agent.getReferenceUpVector());
+ primDir.normVec();
+ }
+ else
+ {
+ primDir = getRotation().getMatrix3().getFwdRow();
+ }
+ LLVector3 velDir = getVelocity();
+ velDir.normVec();
+ if ( mSignaledAnimations.find(ANIM_AGENT_WALK) != mSignaledAnimations.end())
+ {
+ F32 vpD = velDir * primDir;
+ if (vpD < -0.5f)
+ {
+ velDir *= -1.0f;
+ }
+ }
+ LLVector3 fwdDir = lerp(primDir, velDir, clamp_rescale(speed, 0.5f, 2.0f, 0.0f, 1.0f));
+ if (mIsSelf && gAgent.cameraMouselook())
+ {
+ // make sure fwdDir stays in same general direction as primdir
+ if (gAgent.getFlying())
+ {
+ fwdDir = gCamera->getAtAxis();
+ }
+ else
+ {
+ LLVector3 at_axis = gCamera->getAtAxis();
+ LLVector3 up_vector = gAgent.getReferenceUpVector();
+ at_axis -= up_vector * (at_axis * up_vector);
+ at_axis.normVec();
+
+ F32 dot = fwdDir * at_axis;
+ if (dot < 0.f)
+ {
+ fwdDir -= 2.f * at_axis * dot;
+ fwdDir.normVec();
+ }
+ }
+
+ }
+
+ LLQuaternion root_rotation = mRoot.getWorldMatrix().quaternion();
+ F32 root_roll, root_pitch, root_yaw;
+ root_rotation.getEulerAngles(&root_roll, &root_pitch, &root_yaw);
+
+ if (gDebugAvatarRotation)
+ {
+ llinfos << "root_roll " << RAD_TO_DEG * root_roll
+ << " root_pitch " << RAD_TO_DEG * root_pitch
+ << " root_yaw " << RAD_TO_DEG * root_yaw
+ << llendl;
+ }
+
+ // When moving very slow, the pelvis is allowed to deviate from the
+ // forward direction to allow it to hold it's position while the torso
+ // and head turn. Once in motion, it must conform however.
+ BOOL self_in_mouselook = mIsSelf && gAgent.cameraMouselook();
+
+ LLVector3 pelvisDir( mRoot.getWorldMatrix().getFwdRow4().mV );
+ F32 pelvis_rot_threshold = clamp_rescale(speed, 0.1f, 1.0f, PELVIS_ROT_THRESHOLD_SLOW, PELVIS_ROT_THRESHOLD_FAST);
+
+ //Ventrella
+ //if ( gAgent.getCameraMode() == CAMERA_MODE_FOLLOW )
+ //{
+ // pelvis_rot_threshold = clamp_rescale(speed, 0.1f, 1.0f, 1.0f, 1.0f);
+ //}
+ //end Ventrella
+
+ if (self_in_mouselook)
+ {
+ pelvis_rot_threshold *= MOUSELOOK_PELVIS_FOLLOW_FACTOR;
+ }
+ pelvis_rot_threshold *= DEG_TO_RAD;
+
+ F32 angle = angle_between( pelvisDir, fwdDir );
+
+ // The avatar's root is allowed to have a yaw that deviates widely
+ // from the forward direction, but if roll or pitch are off even
+ // a little bit we need to correct the rotation.
+ if(root_roll < 1.f * DEG_TO_RAD
+ && root_pitch < 5.f * DEG_TO_RAD)
+ {
+ // smaller correction vector means pelvis follows prim direction more closely
+ if (!mTurning && angle > pelvis_rot_threshold*0.75f)
+ {
+ mTurning = TRUE;
+ }
+
+ // use tighter threshold when turning
+ if (mTurning)
+ {
+ pelvis_rot_threshold *= 0.4f;
+ }
+
+ // am I done turning?
+ if (angle < pelvis_rot_threshold)
+ {
+ mTurning = FALSE;
+ }
+
+ LLVector3 correction_vector = (pelvisDir - fwdDir) * clamp_rescale(angle, pelvis_rot_threshold*0.75f, pelvis_rot_threshold, 1.0f, 0.0f);
+ fwdDir += correction_vector;
+ }
+ else
+ {
+ mTurning = FALSE;
+ }
+
+ // Now compute the full world space rotation for the whole body (wQv)
+ LLVector3 leftDir = upDir % fwdDir;
+ leftDir.normVec();
+ fwdDir = leftDir % upDir;
+ LLQuaternion wQv( fwdDir, leftDir, upDir );
+
+ if (mIsSelf && mTurning)
+ {
+ if ((fwdDir % pelvisDir) * upDir > 0.f)
+ {
+ gAgent.setControlFlags(AGENT_CONTROL_TURN_RIGHT);
+ }
+ else
+ {
+ gAgent.setControlFlags(AGENT_CONTROL_TURN_LEFT);
+ }
+ }
+
+ // Set the root rotation, but do so incrementally so that it
+ // lags in time by some fixed amount.
+ //F32 u = LLCriticalDamp::getInterpolant(PELVIS_LAG);
+ F32 pelvis_lag_time = 0.f;
+ if (self_in_mouselook)
+ {
+ pelvis_lag_time = PELVIS_LAG_MOUSELOOK;
+ }
+ else if (mInAir)
+ {
+ pelvis_lag_time = PELVIS_LAG_FLYING;
+ // increase pelvis lag time when moving slowly
+ pelvis_lag_time *= clamp_rescale(mSpeedAccum, 0.f, 15.f, 3.f, 1.f);
+ }
+ else
+ {
+ pelvis_lag_time = PELVIS_LAG_WALKING;
+ }
+
+ //Ventrella
+ //if ( gAgent.getCameraMode() == CAMERA_MODE_FOLLOW )
+ //{
+ // pelvis_lag_time = PELVIS_LAG_WHEN_FOLLOW_CAM_IS_ON;
+ //}
+ //end Ventrella
+
+ F32 u = llclamp((deltaTime / pelvis_lag_time), 0.0f, 1.0f);
+
+ mRoot.setWorldRotation( slerp(u, mRoot.getWorldRotation(), wQv) );
+ }
+ }
+ else if (mDrawable.notNull())
+ {
+ mRoot.setPosition(mDrawable->getPosition());
+ mRoot.setRotation(mDrawable->getRotation());
+ }
+
+ //--------------------------------------------------------------------
+ // the rest should only be done when close enough to see it
+ //--------------------------------------------------------------------
+ if ( !(mIsSitting && getParent()) &&
+ ((mPixelArea < 12.0f) ||
+ (!isVisible() && (mPixelArea < MIN_PIXEL_AREA_FOR_COMPOSITE))) )
+ {
+ mRoot.setWorldRotation( getRotation() );
+ mRoot.updateWorldMatrixChildren();
+ return;
+ }
+
+ //-------------------------------------------------------------------------
+ // Update character motions
+ //-------------------------------------------------------------------------
+ // store data relevant to motions
+ mSpeed = speed;
+
+ // update animations
+ {
+ LLFastTimer t(LLFastTimer::FTM_UPDATE_ANIMATION);
+ updateMotion();
+ }
+
+ // update head position
+ updateHeadOffset();
+
+ //-------------------------------------------------------------------------
+ // Find the ground under each foot, these are used for a variety
+ // of things that follow
+ //-------------------------------------------------------------------------
+ LLVector3 ankle_left_pos_agent = mFootLeftp->getWorldPosition();
+ LLVector3 ankle_right_pos_agent = mFootRightp->getWorldPosition();
+
+ LLVector3 ankle_left_ground_agent = ankle_left_pos_agent;
+ LLVector3 ankle_right_ground_agent = ankle_right_pos_agent;
+ resolveHeightAgent(ankle_left_pos_agent, ankle_left_ground_agent, normal);
+ resolveHeightAgent(ankle_right_pos_agent, ankle_right_ground_agent, normal);
+
+ F32 leftElev = llmax(-0.2f, ankle_left_pos_agent.mV[VZ] - ankle_left_ground_agent.mV[VZ]);
+ F32 rightElev = llmax(-0.2f, ankle_right_pos_agent.mV[VZ] - ankle_right_ground_agent.mV[VZ]);
+
+ if (!mIsSitting)
+ {
+ //-------------------------------------------------------------------------
+ // Figure out which foot is on ground
+ //-------------------------------------------------------------------------
+ if (!mInAir)
+ {
+ if ((leftElev < 0.0f) || (rightElev < 0.0f))
+ {
+ ankle_left_pos_agent = mFootLeftp->getWorldPosition();
+ ankle_right_pos_agent = mFootRightp->getWorldPosition();
+ leftElev = ankle_left_pos_agent.mV[VZ] - ankle_left_ground_agent.mV[VZ];
+ rightElev = ankle_right_pos_agent.mV[VZ] - ankle_right_ground_agent.mV[VZ];
+ }
+ }
+ }
+
+ //-------------------------------------------------------------------------
+ // Generate footstep sounds when feet hit the ground
+ //-------------------------------------------------------------------------
+ const LLUUID AGENT_FOOTSTEP_ANIMS[] = {ANIM_AGENT_WALK, ANIM_AGENT_RUN, ANIM_AGENT_LAND};
+ const S32 NUM_AGENT_FOOTSTEP_ANIMS = sizeof(AGENT_FOOTSTEP_ANIMS) / sizeof(LLUUID);
+
+ if ( gAudiop && isAnyAnimationSignaled(AGENT_FOOTSTEP_ANIMS, NUM_AGENT_FOOTSTEP_ANIMS) )
+ {
+ BOOL playSound = FALSE;
+ LLVector3 foot_pos_agent;
+
+ BOOL onGroundLeft = (leftElev <= 0.05f);
+ BOOL onGroundRight = (rightElev <= 0.05f);
+
+ // did left foot hit the ground?
+ if ( onGroundLeft && !mWasOnGroundLeft )
+ {
+ foot_pos_agent = ankle_left_pos_agent;
+ playSound = TRUE;
+ }
+
+ // did right foot hit the ground?
+ if ( onGroundRight && !mWasOnGroundRight )
+ {
+ foot_pos_agent = ankle_right_pos_agent;
+ playSound = TRUE;
+ }
+
+ mWasOnGroundLeft = onGroundLeft;
+ mWasOnGroundRight = onGroundRight;
+
+ if ( playSound )
+ {
+// F32 gain = clamp_rescale( mSpeedAccum,
+// AUDIO_STEP_LO_SPEED, AUDIO_STEP_HI_SPEED,
+// AUDIO_STEP_LO_GAIN, AUDIO_STEP_HI_GAIN );
+
+ F32 gain = gSavedSettings.getF32("AudioLevelFootsteps");
+ LLUUID& step_sound_id = getStepSound();
+
+ LLVector3d foot_pos_global = gAgent.getPosGlobalFromAgent(foot_pos_agent);
+
+ if (gParcelMgr && gParcelMgr->canHearSound(foot_pos_global)
+ && gMuteListp && !gMuteListp->isMuted(getID()))
+ {
+ gAudiop->triggerSound(step_sound_id, getID(), gain, foot_pos_global);
+ }
+ }
+ }
+
+ mRoot.updateWorldMatrixChildren();
+
+ if (!mDebugText.size() && mText.notNull())
+ {
+ mText->markDead();
+ mText = NULL;
+ }
+ else if (mDebugText.size())
+ {
+ setDebugText(mDebugText);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// updateHeadOffset()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::updateHeadOffset()
+{
+ // since we only care about Z, just grab one of the eyes
+ LLVector3 midEyePt = mEyeLeftp->getWorldPosition();
+ midEyePt -= mDrawable.notNull() ? mDrawable->getWorldPosition() : mRoot.getWorldPosition();
+ midEyePt.mV[VZ] = llmax(-mPelvisToFoot + gCamera->getNear(), midEyePt.mV[VZ]);
+
+ if (mDrawable.notNull())
+ {
+ midEyePt = midEyePt * ~mDrawable->getWorldRotation();
+ }
+ if (mIsSitting)
+ {
+ mHeadOffset = midEyePt;
+ }
+ else
+ {
+ F32 u = llmax(0.f, HEAD_MOVEMENT_AVG_TIME - (1.f / gFPSClamped));
+ mHeadOffset = lerp(midEyePt, mHeadOffset, u);
+ }
+}
+
+//------------------------------------------------------------------------
+// updateVisibility()
+//------------------------------------------------------------------------
+void LLVOAvatar::updateVisibility(BOOL force_invisible)
+{
+ BOOL visible = FALSE;
+
+ if (mIsDummy)
+ {
+ visible = TRUE;
+ }
+ else if (mDrawable.isNull())
+ {
+ visible = FALSE;
+ }
+ else if (!force_invisible)
+ {
+ // calculate avatar distance wrt head
+ LLVector3 pos = mDrawable->getPositionAgent();
+ pos -= gCamera->getOrigin();
+ mDrawable->mDistanceWRTCamera = pos.magVec();
+
+ if (!mDrawable->getSpatialGroup() || mDrawable->getSpatialGroup()->isVisible())
+ {
+ visible = TRUE;
+ }
+ else
+ {
+ visible = FALSE;
+ }
+
+ if( mIsSelf )
+ {
+ if( !gAgent.areWearablesLoaded())
+ {
+ visible = FALSE;
+ }
+ }
+ else
+ if( !mFirstAppearanceMessageReceived )
+ {
+ visible = FALSE;
+ }
+
+ if (sDebugInvisible)
+ {
+ LLNameValue* firstname = getNVPair("FirstName");
+ if (firstname)
+ {
+ llinfos << "Avatar " << firstname->getString() << " updating visiblity" << llendl;
+ }
+ else
+ {
+ llinfos << "Avatar " << this << " updating visiblity" << llendl;
+ }
+
+ if (visible)
+ {
+ llinfos << "Visible" << llendl;
+ }
+ else
+ {
+ llinfos << "Not visible" << llendl;
+ }
+
+ /*if (avatar_in_frustum)
+ {
+ llinfos << "Avatar in frustum" << llendl;
+ }
+ else
+ {
+ llinfos << "Avatar not in frustum" << llendl;
+ }*/
+
+ /*if (gCamera->sphereInFrustum(sel_pos_agent, 2.0f))
+ {
+ llinfos << "Sel pos visible" << llendl;
+ }
+ if (gCamera->sphereInFrustum(wrist_right_pos_agent, 0.2f))
+ {
+ llinfos << "Wrist pos visible" << llendl;
+ }
+ if (gCamera->sphereInFrustum(getPositionAgent(), getMaxScale()*2.f))
+ {
+ llinfos << "Agent visible" << llendl;
+ }*/
+ llinfos << "PA: " << getPositionAgent() << llendl;
+ /*llinfos << "SPA: " << sel_pos_agent << llendl;
+ llinfos << "WPA: " << wrist_right_pos_agent << llendl;*/
+ for (LLViewerJointAttachment* attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment->getObject(0))
+ {
+ if(attachment->getObject(0)->mDrawable->isVisible())
+ {
+ llinfos << attachment->getName() << " visible" << llendl;
+ }
+ else
+ {
+ llinfos << attachment->getName() << " not visible at " << mDrawable->getWorldPosition() << " and radius " << mDrawable->getRadius() << llendl;
+ }
+ }
+ }
+ }
+ }
+
+ if (!visible && mVisible)
+ {
+ mMeshInvisibleTime.reset();
+ }
+
+ if (visible)
+ {
+ if (!mMeshValid)
+ {
+ restoreMeshData();
+ }
+ gPipeline.markVisible(mDrawable);
+ }
+ else
+ {
+ if (mMeshValid && mMeshInvisibleTime.getElapsedTimeF32() > TIME_BEFORE_MESH_CLEANUP)
+ {
+ releaseMeshData();
+ }
+ // this breaks off-screen chat bubbles
+ //if (mNameText)
+ //{
+ // mNameText->markDead();
+ // mNameText = NULL;
+ // sNumVisibleChatBubbles--;
+ //}
+ }
+
+ mVisible = visible;
+}
+
+//------------------------------------------------------------------------
+// updateAllVisibility()
+//------------------------------------------------------------------------
+//static
+void LLVOAvatar::updateAllAvatarVisiblity()
+{
+ LLVOAvatar::sNumVisibleAvatars = 0;
+ LLVOAvatar *avatarp;
+
+ F32 render_priority = (F32)LLVOAvatar::sMaxVisible;
+ for (avatarp = (LLVOAvatar*)sInstances.getFirstData(); avatarp; avatarp = (LLVOAvatar*)sInstances.getNextData())
+ {
+ if (avatarp->isDead())
+ {
+ continue;
+ }
+ if (avatarp->isSelf())
+ {
+ avatarp->mRenderPriority = 1000.f;
+ }
+ else
+ {
+ avatarp->mRenderPriority = render_priority * 10.f; // 500 -> 10
+ if (render_priority > 0.f)
+ {
+ render_priority -= 1.f;
+ }
+ }
+ avatarp->updateVisibility(LLVOAvatar::sNumVisibleAvatars > LLVOAvatar::sMaxVisible);
+
+ if (avatarp->mDrawable.isNull())
+ {
+ llwarns << "Avatar with no drawable" << llendl;
+ }
+ else if (avatarp->mDrawable->isVisible())
+ {
+ LLVOAvatar::sNumVisibleAvatars++;
+ }
+ }
+}
+
+//------------------------------------------------------------------------
+// needsRenderBeam()
+//------------------------------------------------------------------------
+BOOL LLVOAvatar::needsRenderBeam()
+{
+ if (gNoRender)
+ {
+ return FALSE;
+ }
+ LLTool *tool = gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) );
+
+ BOOL is_touching_or_grabbing = (tool == gToolGrab && gToolGrab->isEditing());
+ if (gToolGrab->getEditingObject() &&
+ gToolGrab->getEditingObject()->isAttachment())
+ {
+ // don't render selection beam on hud objects
+ is_touching_or_grabbing = FALSE;
+ }
+ return is_touching_or_grabbing || (mState & AGENT_STATE_EDITING && gSelectMgr->shouldShowSelection());
+}
+
+//-----------------------------------------------------------------------------
+// renderSkinned()
+//-----------------------------------------------------------------------------
+U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass)
+{
+ U32 num_indices = 0;
+
+ if (!mIsBuilt)
+ {
+ return num_indices;
+ }
+
+ if (sDebugInvisible)
+ {
+ LLNameValue* firstname = getNVPair("FirstName");
+ if (firstname)
+ {
+ llinfos << "Avatar " << firstname->getString() << " in render" << llendl;
+ }
+ else
+ {
+ llinfos << "Avatar " << this << " in render" << llendl;
+ }
+ if (!mIsBuilt)
+ {
+ llinfos << "Not built!" << llendl;
+ }
+ else if (!gAgent.needsRenderAvatar())
+ {
+ llinfos << "Doesn't need avatar render!" << llendl;
+ }
+ else
+ {
+ llinfos << "Rendering!" << llendl;
+ }
+ }
+
+ if (!mIsBuilt)
+ {
+ return num_indices;
+ }
+
+ if (mIsSelf && !gAgent.needsRenderAvatar())
+ {
+ return num_indices;
+ }
+
+ // render collision normal
+ if (sShowFootPlane && mDrawable.notNull())
+ {
+ LLVector3 slaved_pos = mDrawable->getPositionAgent();
+ LLVector3 foot_plane_normal(mFootPlane.mV[VX], mFootPlane.mV[VY], mFootPlane.mV[VZ]);
+ F32 dist_from_plane = (slaved_pos * foot_plane_normal) - mFootPlane.mV[VW];
+ LLVector3 collide_point = slaved_pos;
+ collide_point.mV[VZ] -= foot_plane_normal.mV[VZ] * (dist_from_plane + COLLISION_TOLERANCE - FOOT_COLLIDE_FUDGE);
+
+ glBegin(GL_LINES);
+ {
+ F32 SQUARE_SIZE = 0.2f;
+ glColor4f(1.f, 0.f, 0.f, 1.f);
+
+ glVertex3f(collide_point.mV[VX] - SQUARE_SIZE, collide_point.mV[VY] - SQUARE_SIZE, collide_point.mV[VZ]);
+ glVertex3f(collide_point.mV[VX] + SQUARE_SIZE, collide_point.mV[VY] - SQUARE_SIZE, collide_point.mV[VZ]);
+
+ glVertex3f(collide_point.mV[VX] + SQUARE_SIZE, collide_point.mV[VY] - SQUARE_SIZE, collide_point.mV[VZ]);
+ glVertex3f(collide_point.mV[VX] + SQUARE_SIZE, collide_point.mV[VY] + SQUARE_SIZE, collide_point.mV[VZ]);
+
+ glVertex3f(collide_point.mV[VX] + SQUARE_SIZE, collide_point.mV[VY] + SQUARE_SIZE, collide_point.mV[VZ]);
+ glVertex3f(collide_point.mV[VX] - SQUARE_SIZE, collide_point.mV[VY] + SQUARE_SIZE, collide_point.mV[VZ]);
+
+ glVertex3f(collide_point.mV[VX] - SQUARE_SIZE, collide_point.mV[VY] + SQUARE_SIZE, collide_point.mV[VZ]);
+ glVertex3f(collide_point.mV[VX] - SQUARE_SIZE, collide_point.mV[VY] - SQUARE_SIZE, collide_point.mV[VZ]);
+
+ glVertex3f(collide_point.mV[VX], collide_point.mV[VY], collide_point.mV[VZ]);
+ glVertex3f(collide_point.mV[VX] + mFootPlane.mV[VX], collide_point.mV[VY] + mFootPlane.mV[VY], collide_point.mV[VZ] + mFootPlane.mV[VZ]);
+
+ }glEnd();
+ }
+ //--------------------------------------------------------------------
+ // render all geomety attached to the skeleton
+ //--------------------------------------------------------------------
+ static LLStat render_stat;
+
+ LLViewerJointMesh::sRenderPass = pass;
+
+ if (pass == AVATAR_RENDER_PASS_SINGLE)
+ {
+ if (!mIsSelf || gAgent.needsRenderHead())
+ {
+ num_indices += mHeadLOD.render(mAdjustedPixelArea);
+ }
+ num_indices += mUpperBodyLOD.render(mAdjustedPixelArea);
+ num_indices += mLowerBodyLOD.render(mAdjustedPixelArea);
+ if( isWearingWearableType( WT_SKIRT ) )
+ {
+ glAlphaFunc(GL_GREATER,0.25f);
+ num_indices += mSkirtLOD.render(mAdjustedPixelArea);
+ glAlphaFunc(GL_GREATER,0.01f);
+ }
+
+ if (!mIsSelf || gAgent.needsRenderHead())
+ {
+ num_indices += mEyeLashLOD.render(mAdjustedPixelArea);
+ num_indices += mHairLOD.render(mAdjustedPixelArea);
+ }
+ }
+ else if (pass == AVATAR_RENDER_PASS_CLOTHING_INNER)
+ {
+ if (!mIsSelf || gAgent.needsRenderHead())
+ {
+ num_indices += mHeadLOD.render(mAdjustedPixelArea);
+ }
+ LLViewerJointMesh::sClothingInnerColor = mTexSkinColor->getColor() * 0.5f;
+ LLViewerJointMesh::sClothingMaskImageName = mUpperMaskTexName;
+ num_indices += mUpperBodyLOD.render(mAdjustedPixelArea);
+ LLViewerJointMesh::sClothingMaskImageName = mLowerMaskTexName;
+ num_indices += mLowerBodyLOD.render(mAdjustedPixelArea);
+ LLViewerJointMesh::sClothingMaskImageName = 0;
+ if( isWearingWearableType( WT_SKIRT ) )
+ {
+ glAlphaFunc(GL_GREATER,0.25f);
+ num_indices += mSkirtLOD.render(mAdjustedPixelArea);
+ glAlphaFunc(GL_GREATER,0.01f);
+ }
+
+ if (!mIsSelf || gAgent.needsRenderHead())
+ {
+ num_indices += mEyeLashLOD.render(mAdjustedPixelArea);
+ num_indices += mHairLOD.render(mAdjustedPixelArea);
+ }
+ }
+ else if (pass == AVATAR_RENDER_PASS_CLOTHING_OUTER)
+ {
+ LLViewerJointMesh::sClothingInnerColor = mTexSkinColor->getColor() * 0.5f;
+ LLViewerJointMesh::sClothingMaskImageName = mUpperMaskTexName;
+ num_indices += mUpperBodyLOD.render(mAdjustedPixelArea);
+ LLViewerJointMesh::sClothingMaskImageName = mLowerMaskTexName;
+ num_indices += mLowerBodyLOD.render(mAdjustedPixelArea);
+ LLViewerJointMesh::sClothingMaskImageName = 0;
+ }
+
+ LLViewerJointMesh::sRenderPass = AVATAR_RENDER_PASS_SINGLE;
+
+ //llinfos << "Avatar render: " << render_timer.getElapsedTimeF32() << llendl;
+
+ //render_stat.addValue(render_timer.getElapsedTimeF32()*1000.f);
+
+ return num_indices;
+}
+
+//-----------------------------------------------------------------------------
+// renderRigid()
+//-----------------------------------------------------------------------------
+U32 LLVOAvatar::renderRigid()
+{
+ U32 num_indices = 0;
+
+ if (!mIsBuilt)
+ {
+ return 0;
+ }
+
+ if (mIsSelf && (!gAgent.needsRenderAvatar() || !gAgent.needsRenderHead()))
+ {
+ return 0;
+ }
+
+ if (!mIsBuilt)
+ {
+ return 0;
+ }
+
+ num_indices += mEyeBallLeftLOD.render(mAdjustedPixelArea);
+ num_indices += mEyeBallRightLOD.render(mAdjustedPixelArea);
+
+ return num_indices;
+}
+
+//-----------------------------------------------------------------------------
+// renderCollisionVolumes()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::renderCollisionVolumes()
+{
+ for (S32 i = 0; i < mNumCollisionVolumes; i++)
+ {
+ mCollisionVolumes[i].render();
+ }
+}
+
+//------------------------------------------------------------------------
+// LLVOAvatar::updateTextures()
+//------------------------------------------------------------------------
+void LLVOAvatar::updateTextures(LLAgent &agent)
+{
+ BOOL render_avatar = TRUE;
+
+ if (mIsDummy || gNoRender)
+ {
+ return;
+ }
+
+ BOOL head_baked = ( getTEImage( TEX_HEAD_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL upper_baked = ( getTEImage( TEX_UPPER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL lower_baked = ( getTEImage( TEX_LOWER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL eyes_baked = ( getTEImage( TEX_EYES_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL skirt_baked = ( getTEImage( TEX_SKIRT_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+
+ if( mIsSelf )
+ {
+ render_avatar = TRUE;
+ }
+ else
+ {
+ render_avatar = isVisible() && !mCulled;
+ }
+
+ // bind the texture so that they'll be decoded
+ // slightly inefficient, we can short-circuit this
+ // if we have to
+ if( render_avatar && !gGLManager.mIsDisabled )
+ {
+ if( head_baked && ! mHeadBakedLoaded )
+ {
+ getTEImage( TEX_HEAD_BAKED )->bind();
+ }
+ if( upper_baked && ! mUpperBakedLoaded )
+ {
+ getTEImage( TEX_UPPER_BAKED )->bind();
+ }
+ if( lower_baked && ! mLowerBakedLoaded )
+ {
+ getTEImage( TEX_LOWER_BAKED )->bind();
+ }
+ if( eyes_baked && ! mEyesBakedLoaded )
+ {
+ getTEImage( TEX_EYES_BAKED )->bind();
+ }
+ if( skirt_baked && ! mSkirtBakedLoaded )
+ {
+ getTEImage( TEX_SKIRT_BAKED )->bind();
+ }
+ }
+
+ /*
+ // JAMESDEBUG
+ if (mIsSelf)
+ {
+ S32 null_count = 0;
+ S32 default_count = 0;
+ for (U32 i = 0; i < getNumTEs(); i++)
+ {
+ const LLTextureEntry* te = getTE(i);
+ if (te)
+ {
+ if (te->getID() == LLUUID::null)
+ {
+ null_count++;
+ }
+ else if (te->getID() == IMG_DEFAULT_AVATAR)
+ {
+ default_count++;
+ }
+ }
+ }
+ llinfos << "JAMESDEBUG my avatar TE null " << null_count << " default " << default_count << llendl;
+ }
+ */
+
+ for (U32 i = 0; i < getNumTEs(); i++)
+ {
+ LLViewerImage *imagep = getTEImage(i);
+ if (imagep)
+ {
+ // Debugging code - maybe non-self avatars are downloading textures?
+ //llinfos << "avatar self " << mIsSelf << " tex " << i
+ // << " decode " << imagep->getDecodePriority()
+ // << " boost " << boost_avatar
+ // << " size " << imagep->getWidth() << "x" << imagep->getHeight()
+ // << " discard " << imagep->getDiscardLevel()
+ // << " desired " << imagep->getDesiredDiscardLevel()
+ // << llendl;
+
+ const LLTextureEntry *te = getTE(i);
+ F32 texel_area_ratio = fabs(te->mScaleS * te->mScaleT);
+// BOOL boost_aux = (imagep->needsAux() && (!imagep->mFullWidth || !imagep->mFullHeight));
+ S32 boost_level = mIsSelf ? LLViewerImage::BOOST_AVATAR_BAKED_SELF : LLViewerImage::BOOST_AVATAR_BAKED;
+
+ // Spam if this is a baked texture, not set to default image, without valid host info
+ if (isTextureIndexBaked(i)
+ && imagep->getID() != IMG_DEFAULT_AVATAR
+ && !imagep->getTargetHost().isOk())
+ {
+ llwarns << "LLVOAvatar::updateTextures No host for texture " << imagep->getID()
+ << " for avatar " << (mIsSelf ? "<myself>" : getID().getString())
+ << " on host " << getRegion()->getHost() << llendl;
+ }
+
+ switch( i )
+ {
+ // Head
+ case TEX_HEAD_BODYPAINT:
+ addLocalTextureStats( LOCTEX_HEAD_BODYPAINT, imagep, texel_area_ratio, render_avatar, head_baked );
+ break;
+
+ // Upper
+ case TEX_UPPER_JACKET:
+ addLocalTextureStats( LOCTEX_UPPER_JACKET, imagep, texel_area_ratio, render_avatar, upper_baked );
+ break;
+
+ case TEX_UPPER_SHIRT:
+ addLocalTextureStats( LOCTEX_UPPER_SHIRT, imagep, texel_area_ratio, render_avatar, upper_baked );
+ break;
+
+ case TEX_UPPER_GLOVES:
+ addLocalTextureStats( LOCTEX_UPPER_GLOVES, imagep, texel_area_ratio, render_avatar, upper_baked );
+ break;
+
+ case TEX_UPPER_UNDERSHIRT:
+ addLocalTextureStats( LOCTEX_UPPER_UNDERSHIRT, imagep, texel_area_ratio, render_avatar, upper_baked );
+ break;
+
+ case TEX_UPPER_BODYPAINT:
+ addLocalTextureStats( LOCTEX_UPPER_BODYPAINT, imagep, texel_area_ratio, render_avatar, upper_baked );
+ break;
+
+ // Lower
+ case TEX_LOWER_JACKET:
+ addLocalTextureStats( LOCTEX_LOWER_JACKET, imagep, texel_area_ratio, render_avatar, lower_baked );
+ break;
+
+ case TEX_LOWER_PANTS:
+ addLocalTextureStats( LOCTEX_LOWER_PANTS, imagep, texel_area_ratio, render_avatar, lower_baked );
+ break;
+
+ case TEX_LOWER_SHOES:
+ addLocalTextureStats( LOCTEX_LOWER_SHOES, imagep, texel_area_ratio, render_avatar, lower_baked );
+ break;
+
+ case TEX_LOWER_SOCKS:
+ addLocalTextureStats( LOCTEX_LOWER_SOCKS, imagep, texel_area_ratio, render_avatar, lower_baked );
+ break;
+
+ case TEX_LOWER_UNDERPANTS:
+ addLocalTextureStats( LOCTEX_LOWER_UNDERPANTS, imagep, texel_area_ratio, render_avatar, lower_baked );
+ break;
+
+ case TEX_LOWER_BODYPAINT:
+ addLocalTextureStats( LOCTEX_LOWER_BODYPAINT, imagep, texel_area_ratio, render_avatar, lower_baked );
+ break;
+
+ // Eyes
+ case TEX_EYES_IRIS:
+ addLocalTextureStats( LOCTEX_EYES_IRIS, imagep, texel_area_ratio, render_avatar, eyes_baked );
+ break;
+
+ // Skirt
+ case TEX_SKIRT:
+ addLocalTextureStats( LOCTEX_SKIRT, imagep, texel_area_ratio, render_avatar, skirt_baked );
+ break;
+
+ // Baked
+ case TEX_HEAD_BAKED:
+ if (head_baked)
+ {
+ imagep->setBoostLevel(boost_level);
+ imagep->addTextureStats(mPixelArea, texel_area_ratio);
+ }
+ break;
+
+ case TEX_UPPER_BAKED:
+ if (upper_baked)
+ {
+ imagep->setBoostLevel(boost_level);
+ imagep->addTextureStats(mPixelArea, texel_area_ratio);
+ }
+ break;
+
+ case TEX_LOWER_BAKED:
+ if (lower_baked)
+ {
+ imagep->setBoostLevel(boost_level);
+ imagep->addTextureStats(mPixelArea, texel_area_ratio);
+ }
+ break;
+
+ case TEX_EYES_BAKED:
+ if (eyes_baked)
+ {
+ imagep->setBoostLevel(boost_level);
+ imagep->addTextureStats(mPixelArea, texel_area_ratio);
+ }
+ break;
+
+ case TEX_SKIRT_BAKED:
+ if (skirt_baked)
+ {
+ imagep->setBoostLevel(boost_level);
+ imagep->addTextureStats(mPixelArea, texel_area_ratio);
+ }
+ break;
+
+ case TEX_HAIR:
+ // Hair is neither a local texture used for baking, nor the output
+ // of the baking process. It's just a texture that happens to be
+ // used to draw avatars. Hence BOOST_AVATAR. JC
+ if (mIsSelf)
+ {
+ imagep->setBoostLevel(LLViewerImage::BOOST_AVATAR_SELF);
+ }
+ else
+ {
+ imagep->setBoostLevel(LLViewerImage::BOOST_AVATAR);
+ }
+ imagep->addTextureStats(mPixelArea, texel_area_ratio);
+ break;
+
+ default:
+ llassert(0);
+ break;
+ }
+ }
+ }
+
+ if( render_avatar )
+ {
+ mShadowImagep->addTextureStats(mPixelArea, 1.f);
+ }
+}
+
+
+void LLVOAvatar::addLocalTextureStats( LLVOAvatar::ELocTexIndex idx, LLViewerImage* imagep,
+ F32 texel_area_ratio, BOOL render_avatar, BOOL covered_by_baked )
+{
+ if (!covered_by_baked &&
+ render_avatar && // always true if mIsSelf
+ mLocalTexture[ idx ].notNull() && mLocalTexture[idx]->getID() != IMG_DEFAULT_AVATAR)
+ {
+ F32 desired_pixels;
+ if( mIsSelf )
+ {
+ desired_pixels = llmin(mPixelArea, (F32)LOCTEX_IMAGE_AREA_SELF );
+ imagep->setBoostLevel(LLViewerImage::BOOST_AVATAR_SELF);
+ }
+ else
+ {
+ desired_pixels = llmin(mPixelArea, (F32)LOCTEX_IMAGE_AREA_OTHER );
+ imagep->setBoostLevel(LLViewerImage::BOOST_AVATAR);
+ }
+ imagep->addTextureStats( desired_pixels, texel_area_ratio );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// resolveHeight()
+//-----------------------------------------------------------------------------
+
+void LLVOAvatar::resolveHeightAgent(const LLVector3 &in_pos_agent, LLVector3 &out_pos_agent, LLVector3 &out_norm)
+{
+ LLVector3d in_pos_global, out_pos_global;
+
+ in_pos_global = gAgent.getPosGlobalFromAgent(in_pos_agent);
+ resolveHeightGlobal(in_pos_global, out_pos_global, out_norm);
+ out_pos_agent = gAgent.getPosAgentFromGlobal(out_pos_global);
+}
+
+
+void LLVOAvatar::resolveRayCollisionAgent(const LLVector3d start_pt, const LLVector3d end_pt, LLVector3d &out_pos, LLVector3 &out_norm)
+{
+ LLViewerObject *obj;
+ gWorldPointer->resolveStepHeightGlobal(this, start_pt, end_pt, out_pos, out_norm, &obj);
+}
+
+
+void LLVOAvatar::resolveHeightGlobal(const LLVector3d &inPos, LLVector3d &outPos, LLVector3 &outNorm)
+{
+ LLVector3d zVec(0.0f, 0.0f, 0.5f);
+ LLVector3d p0 = inPos + zVec;
+ LLVector3d p1 = inPos - zVec;
+ LLViewerObject *obj;
+ gWorldPointer->resolveStepHeightGlobal(this, p0, p1, outPos, outNorm, &obj);
+ if (!obj)
+ {
+ mStepOnLand = TRUE;
+ mStepMaterial = 0;
+ mStepObjectVelocity.setVec(0.0f, 0.0f, 0.0f);
+ }
+ else
+ {
+ mStepOnLand = FALSE;
+ mStepMaterial = obj->getMaterial();
+
+ // We want the primitive velocity, not our velocity... (which actually subtracts the
+ // step object velocity)
+ LLVector3 angularVelocity = obj->getAngularVelocity();
+ LLVector3 relativePos = gAgent.getPosAgentFromGlobal(outPos) - obj->getPositionAgent();
+
+ LLVector3 linearComponent = angularVelocity % relativePos;
+// llinfos << "Linear Component of Rotation Velocity " << linearComponent << llendl;
+ mStepObjectVelocity = obj->getVelocity() + linearComponent;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// getStepSound()
+//-----------------------------------------------------------------------------
+LLUUID& LLVOAvatar::getStepSound()
+{
+ if ( mStepOnLand )
+ {
+ return sStepSoundOnLand;
+ }
+
+ return sStepSounds[mStepMaterial];
+}
+
+
+//-----------------------------------------------------------------------------
+// processAnimationStateChanges()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::processAnimationStateChanges()
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ if (gNoRender)
+ {
+ return;
+ }
+
+ if ( isAnyAnimationSignaled(AGENT_WALK_ANIMS, NUM_AGENT_WALK_ANIMS) )
+ {
+ startMotion(ANIM_AGENT_WALK_ADJUST);
+ stopMotion(ANIM_AGENT_FLY_ADJUST);
+ }
+ else if (mInAir && !mIsSitting)
+ {
+ stopMotion(ANIM_AGENT_WALK_ADJUST);
+ startMotion(ANIM_AGENT_FLY_ADJUST);
+ }
+ else
+ {
+ stopMotion(ANIM_AGENT_WALK_ADJUST);
+ stopMotion(ANIM_AGENT_FLY_ADJUST);
+ }
+
+ if ( isAnyAnimationSignaled(AGENT_GUN_AIM_ANIMS, NUM_AGENT_GUN_AIM_ANIMS) )
+ {
+ startMotion(ANIM_AGENT_TARGET);
+ stopMotion(ANIM_AGENT_BODY_NOISE);
+ }
+ else
+ {
+ stopMotion(ANIM_AGENT_TARGET);
+ startMotion(ANIM_AGENT_BODY_NOISE);
+ }
+
+ // clear all current animations
+ AnimIterator anim_it;
+ for (anim_it = mPlayingAnimations.begin(); anim_it != mPlayingAnimations.end();)
+ {
+ AnimIterator found_anim = mSignaledAnimations.find(anim_it->first);
+
+ // playing, but not signaled, so stop
+ if (found_anim == mSignaledAnimations.end())
+ {
+ processSingleAnimationStateChange(anim_it->first, FALSE);
+ mPlayingAnimations.erase(anim_it++);
+ continue;
+ }
+
+ ++anim_it;
+ }
+
+ // start up all new anims
+ for (anim_it = mSignaledAnimations.begin(); anim_it != mSignaledAnimations.end();)
+ {
+ AnimIterator found_anim = mPlayingAnimations.find(anim_it->first);
+
+ // signaled but not playing, or different sequence id, start motion
+ if (found_anim == mPlayingAnimations.end() || found_anim->second != anim_it->second)
+ {
+ if (processSingleAnimationStateChange(anim_it->first, TRUE))
+ {
+ mPlayingAnimations[anim_it->first] = anim_it->second;
+ ++anim_it;
+ continue;
+ }
+ }
+
+ ++anim_it;
+ }
+
+ // clear source information for animations which have been stopped
+ if (mIsSelf)
+ {
+ AnimSourceIterator source_it = mAnimationSources.begin();
+
+ for (source_it = mAnimationSources.begin(); source_it != mAnimationSources.end();)
+ {
+ if (mSignaledAnimations.find(source_it->second) == mSignaledAnimations.end())
+ {
+ mAnimationSources.erase(source_it++);
+ }
+ else
+ {
+ ++source_it;
+ }
+ }
+ }
+
+ stop_glerror();
+}
+
+
+//-----------------------------------------------------------------------------
+// processSingleAnimationStateChange();
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::processSingleAnimationStateChange( const LLUUID& anim_id, BOOL start )
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ BOOL result = FALSE;
+
+ if ( start ) // start animation
+ {
+ if (anim_id == ANIM_AGENT_TYPE)
+ {
+ if (gAudiop)
+ {
+ LLVector3d char_pos_global = gAgent.getPosGlobalFromAgent(getCharacterPosition());
+ if (gParcelMgr && gParcelMgr->canHearSound(char_pos_global)
+ && gMuteListp && !gMuteListp->isMuted(getID()))
+ {
+ // RN: uncomment this to play on typing sound at fixed volume once sound engine is fixed
+ // to support both spatialized and non-spatialized instances of the same sound
+ //if (mIsSelf)
+ //{
+ // gAudiop->triggerSound(LLUUID(gSavedSettings.getString("UISndTyping")), 0.8f);
+ //}
+ //else
+ {
+ LLUUID sound_id = LLUUID(gSavedSettings.getString("UISndTyping"));
+ gAudiop->triggerSound(sound_id, getID(), 1.f, char_pos_global);
+ }
+ }
+ }
+ }
+ else if (anim_id == ANIM_AGENT_SIT_GROUND_CONSTRAINED)
+ {
+ mIsSitting = TRUE;
+ }
+
+
+ if (startMotion(anim_id))
+ {
+ result = TRUE;
+ }
+ else
+ {
+ llwarns << "Failed to start motion!" << llendl;
+ }
+ }
+ else //stop animation
+ {
+ if (anim_id == ANIM_AGENT_SIT_GROUND_CONSTRAINED)
+ {
+ mIsSitting = FALSE;
+ }
+ stopMotion(anim_id);
+ result = TRUE;
+ }
+
+ return result;
+}
+
+//-----------------------------------------------------------------------------
+// isAnyAnimationSignaled()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::isAnyAnimationSignaled(const LLUUID *anim_array, const S32 num_anims)
+{
+ for (S32 i = 0; i < num_anims; i++)
+ {
+ if(mSignaledAnimations.find(anim_array[i]) != mSignaledAnimations.end())
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// resetAnimations()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::resetAnimations()
+{
+ LLKeyframeMotion::flushKeyframeCache();
+ flushAllMotions();
+}
+
+//-----------------------------------------------------------------------------
+// startMotion()
+// id is the asset if of the animation to start
+// time_offset is the offset into the animation at which to start playing
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::startMotion(const LLUUID& id, F32 time_offset)
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ // start special case female walk for female avatars
+ if (getSex() == SEX_FEMALE)
+ {
+ if (id == ANIM_AGENT_WALK)
+ {
+ return LLCharacter::startMotion(ANIM_AGENT_FEMALE_WALK, time_offset);
+ }
+ else if (id == ANIM_AGENT_SIT)
+ {
+ return LLCharacter::startMotion(ANIM_AGENT_SIT_FEMALE, time_offset);
+ }
+ }
+
+ if (mIsSelf && id == ANIM_AGENT_AWAY)
+ {
+ gAgent.setAFK();
+ }
+
+ return LLCharacter::startMotion(id, time_offset);
+}
+
+//-----------------------------------------------------------------------------
+// stopMotion()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::stopMotion(const LLUUID& id, BOOL stop_immediate)
+{
+ if (mIsSelf)
+ {
+ if (id == ANIM_AGENT_STAND)
+ {
+ LLAgent::stopFidget();
+ }
+ else if (id == ANIM_AGENT_AWAY)
+ {
+ gAgent.clearAFK();
+ }
+ }
+
+ if (id == ANIM_AGENT_WALK)
+ {
+ LLCharacter::stopMotion(ANIM_AGENT_FEMALE_WALK, stop_immediate);
+ }
+ else if (id == ANIM_AGENT_SIT)
+ {
+ LLCharacter::stopMotion(ANIM_AGENT_SIT_FEMALE, stop_immediate);
+ }
+
+ return LLCharacter::stopMotion(id, stop_immediate);
+}
+
+//-----------------------------------------------------------------------------
+// stopMotionFromSource()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::stopMotionFromSource(const LLUUID& source_id)
+{
+ if (!mIsSelf)
+ {
+ return;
+ }
+ AnimSourceIterator motion_it;
+
+ for(motion_it = mAnimationSources.find(source_id); motion_it != mAnimationSources.end();)
+ {
+ gAgent.sendAnimationRequest( motion_it->second, ANIM_REQUEST_STOP );
+ mAnimationSources.erase(motion_it++);
+ }
+
+ LLViewerObject* object = gObjectList.findObject(source_id);
+ if (object)
+ {
+ object->mFlags &= ~FLAGS_ANIM_SOURCE;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getVolumePos()
+//-----------------------------------------------------------------------------
+LLVector3 LLVOAvatar::getVolumePos(S32 joint_index, LLVector3& volume_offset)
+{
+ if (joint_index > mNumCollisionVolumes)
+ {
+ return LLVector3::zero;
+ }
+
+ return mCollisionVolumes[joint_index].getVolumePos(volume_offset);
+}
+
+//-----------------------------------------------------------------------------
+// findCollisionVolume()
+//-----------------------------------------------------------------------------
+LLJoint* LLVOAvatar::findCollisionVolume(U32 volume_id)
+{
+ if ((S32)volume_id > mNumCollisionVolumes)
+ {
+ return NULL;
+ }
+
+ return &mCollisionVolumes[volume_id];
+}
+
+//-----------------------------------------------------------------------------
+// findCollisionVolume()
+//-----------------------------------------------------------------------------
+S32 LLVOAvatar::getCollisionVolumeID(std::string &name)
+{
+ for (S32 i = 0; i < mNumCollisionVolumes; i++)
+ {
+ if (mCollisionVolumes[i].getName() == name)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// addDebugText()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::addDebugText(const char* text)
+{
+ mDebugText.append(1, '\n');
+ mDebugText.append(text);
+}
+
+//-----------------------------------------------------------------------------
+// getID()
+//-----------------------------------------------------------------------------
+const LLUUID& LLVOAvatar::getID()
+{
+ return mID;
+}
+
+//-----------------------------------------------------------------------------
+// getJoint()
+//-----------------------------------------------------------------------------
+// RN: avatar joints are multi-rooted to include screen-based attachments
+LLJoint *LLVOAvatar::getJoint( const std::string &name )
+{
+ LLJoint* jointp = NULL;
+ if (mScreenp)
+ {
+ jointp = mScreenp->findJoint(name);
+ }
+ if (!jointp)
+ {
+ jointp = mRoot.findJoint(name);
+ }
+ return jointp;
+}
+
+//-----------------------------------------------------------------------------
+// getCharacterPosition()
+//-----------------------------------------------------------------------------
+LLVector3 LLVOAvatar::getCharacterPosition()
+{
+ if (mDrawable.notNull())
+ {
+ return mDrawable->getPositionAgent();
+ }
+ else
+ {
+ return getPositionAgent();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::getCharacterRotation()
+//-----------------------------------------------------------------------------
+LLQuaternion LLVOAvatar::getCharacterRotation()
+{
+ return getRotation();
+}
+
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::getCharacterVelocity()
+//-----------------------------------------------------------------------------
+LLVector3 LLVOAvatar::getCharacterVelocity()
+{
+ return getVelocity() - mStepObjectVelocity;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::getCharacterAngularVelocity()
+//-----------------------------------------------------------------------------
+LLVector3 LLVOAvatar::getCharacterAngularVelocity()
+{
+ return getAngularVelocity();
+}
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::getGround()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::getGround(const LLVector3 &in_pos_agent, LLVector3 &out_pos_agent, LLVector3 &outNorm)
+{
+ LLVector3d z_vec(0.0f, 0.0f, 1.0f);
+ LLVector3d p0_global, p1_global;
+
+ if (gNoRender || mIsDummy)
+ {
+ outNorm.setVec(z_vec);
+ out_pos_agent = in_pos_agent;
+ return;
+ }
+
+ p0_global = gAgent.getPosGlobalFromAgent(in_pos_agent) + z_vec;
+ p1_global = gAgent.getPosGlobalFromAgent(in_pos_agent) - z_vec;
+ LLViewerObject *obj;
+ LLVector3d out_pos_global;
+ gWorldPointer->resolveStepHeightGlobal(this, p0_global, p1_global, out_pos_global, outNorm, &obj);
+ out_pos_agent = gAgent.getPosAgentFromGlobal(out_pos_global);
+}
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::getTimeDilation()
+//-----------------------------------------------------------------------------
+F32 LLVOAvatar::getTimeDilation()
+{
+ return mTimeDilation;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::getPixelArea()
+//-----------------------------------------------------------------------------
+F32 LLVOAvatar::getPixelArea()
+{
+ if (mIsDummy)
+ {
+ return 100000.f;
+ }
+ return mPixelArea;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::getHeadMesh()
+//-----------------------------------------------------------------------------
+LLPolyMesh* LLVOAvatar::getHeadMesh()
+{
+ return mHeadMesh0.getMesh();
+}
+
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::getUpperBodyMesh()
+//-----------------------------------------------------------------------------
+LLPolyMesh* LLVOAvatar::getUpperBodyMesh()
+{
+ return mUpperBodyMesh0.getMesh();
+}
+
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::getPosGlobalFromAgent()
+//-----------------------------------------------------------------------------
+LLVector3d LLVOAvatar::getPosGlobalFromAgent(const LLVector3 &position)
+{
+ return gAgent.getPosGlobalFromAgent(position);
+}
+
+//-----------------------------------------------------------------------------
+// getPosAgentFromGlobal()
+//-----------------------------------------------------------------------------
+LLVector3 LLVOAvatar::getPosAgentFromGlobal(const LLVector3d &position)
+{
+ return gAgent.getPosAgentFromGlobal(position);
+}
+
+//-----------------------------------------------------------------------------
+// allocateCharacterJoints()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::allocateCharacterJoints( U32 num )
+{
+ delete [] mSkeleton;
+ mSkeleton = NULL;
+ mNumJoints = 0;
+
+ mSkeleton = new LLViewerJoint[num];
+
+ for(S32 joint_num = 0; joint_num < (S32)num; joint_num++)
+ {
+ mSkeleton[joint_num].setJointNum(joint_num);
+ }
+
+ if (!mSkeleton)
+ {
+ return FALSE;
+ }
+
+ mNumJoints = num;
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// allocateCollisionVolumes()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::allocateCollisionVolumes( U32 num )
+{
+ delete [] mCollisionVolumes;
+ mCollisionVolumes = NULL;
+ mNumCollisionVolumes = 0;
+
+ mCollisionVolumes = new LLViewerJointCollisionVolume[num];
+ if (!mCollisionVolumes)
+ {
+ return FALSE;
+ }
+
+ mNumCollisionVolumes = num;
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// getCharacterJoint()
+//-----------------------------------------------------------------------------
+LLJoint *LLVOAvatar::getCharacterJoint( U32 num )
+{
+ if ((S32)num >= mNumJoints
+ || num < 0)
+ {
+ return NULL;
+ }
+ return (LLJoint*)&mSkeleton[num];
+}
+
+
+//-----------------------------------------------------------------------------
+// requestStopMotion()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::requestStopMotion( LLMotion* motion )
+{
+ // Only agent avatars should handle the stop motion notifications.
+ if ( mIsSelf )
+ {
+ // Notify agent that motion has stopped
+ gAgent.requestStopMotion( motion );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// loadAvatar()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::loadAvatar()
+{
+// LLFastTimer t(LLFastTimer::FTM_LOAD_AVATAR);
+
+ // avatar_skeleton.xml
+ if( !buildSkeleton(sSkeletonInfo) )
+ {
+ llwarns << "avatar file: buildSkeleton() failed" << llendl;
+ return FALSE;
+ }
+
+ // avatar_lad.xml : <skeleton>
+ if( !loadSkeletonNode() )
+ {
+ llwarns << "avatar file: loadNodeSkeleton() failed" << llendl;
+ return FALSE;
+ }
+
+ // avatar_lad.xml : <mesh>
+ if( !loadMeshNodes() )
+ {
+ llwarns << "avatar file: loadNodeMesh() failed" << llendl;
+ return FALSE;
+ }
+
+ // avatar_lad.xml : <global_color>
+ if( sAvatarInfo->mTexSkinColorInfo )
+ {
+ mTexSkinColor = new LLTexGlobalColor( this );
+ if( !mTexSkinColor->setInfo( sAvatarInfo->mTexSkinColorInfo ) )
+ {
+ llwarns << "avatar file: mTexSkinColor->setInfo() failed" << llendl;
+ return FALSE;
+ }
+ }
+ else
+ {
+ llwarns << "<global_color> name=\"skin_color\" not found" << llendl;
+ return FALSE;
+ }
+ if( sAvatarInfo->mTexHairColorInfo )
+ {
+ mTexHairColor = new LLTexGlobalColor( this );
+ if( !mTexHairColor->setInfo( sAvatarInfo->mTexHairColorInfo ) )
+ {
+ llwarns << "avatar file: mTexHairColor->setInfo() failed" << llendl;
+ return FALSE;
+ }
+ }
+ else
+ {
+ llwarns << "<global_color> name=\"hair_color\" not found" << llendl;
+ return FALSE;
+ }
+ if( sAvatarInfo->mTexEyeColorInfo )
+ {
+ mTexEyeColor = new LLTexGlobalColor( this );
+ if( !mTexEyeColor->setInfo( sAvatarInfo->mTexEyeColorInfo ) )
+ {
+ llwarns << "avatar file: mTexEyeColor->setInfo() failed" << llendl;
+ return FALSE;
+ }
+ }
+ else
+ {
+ llwarns << "<global_color> name=\"eye_color\" not found" << llendl;
+ return FALSE;
+ }
+
+ // avatar_lad.xml : <layer_set>
+ if (sAvatarInfo->mLayerInfoList.empty())
+ {
+ llwarns << "avatar file: missing <layer_set> node" << llendl;
+ }
+ else
+ {
+ LLVOAvatarInfo::layer_info_list_t::iterator iter;
+ for (iter = sAvatarInfo->mLayerInfoList.begin();
+ iter != sAvatarInfo->mLayerInfoList.end(); iter++)
+ {
+ LLTexLayerSetInfo *info = *iter;
+ LLTexLayerSet* layer_set = new LLTexLayerSet( this );
+ if (!layer_set->setInfo(info))
+ {
+ stop_glerror();
+ delete layer_set;
+ llwarns << "avatar file: layer_set->parseData() failed" << llendl;
+ return FALSE;
+ }
+ if( layer_set->isBodyRegion( "head" ) )
+ {
+ mHeadLayerSet = layer_set;
+ }
+ else if( layer_set->isBodyRegion( "upper_body" ) )
+ {
+ mUpperBodyLayerSet = layer_set;
+ }
+ else if( layer_set->isBodyRegion( "lower_body" ) )
+ {
+ mLowerBodyLayerSet = layer_set;
+ }
+ else if( layer_set->isBodyRegion( "eyes" ) )
+ {
+ mEyesLayerSet = layer_set;
+ }
+ else if( layer_set->isBodyRegion( "skirt" ) )
+ {
+ mSkirtLayerSet = layer_set;
+ }
+ else
+ {
+ llwarns << "<layer_set> has invalid body_region attribute" << llendl;
+ delete layer_set;
+ return FALSE;
+ }
+ }
+ }
+
+ // avatar_lad.xml : <driver_parameters>
+ {
+ LLVOAvatarInfo::driver_info_list_t::iterator iter;
+ for (iter = sAvatarInfo->mDriverInfoList.begin();
+ iter != sAvatarInfo->mDriverInfoList.end(); iter++)
+ {
+ LLDriverParamInfo *info = *iter;
+ LLDriverParam* driver_param = new LLDriverParam( this );
+ if (driver_param->setInfo(info))
+ {
+ addVisualParam( driver_param );
+ }
+ else
+ {
+ delete driver_param;
+ llwarns << "avatar file: driver_param->parseData() failed" << llendl;
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// loadSkeletonNode(): loads <skeleton> node from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::loadSkeletonNode ()
+{
+ mRoot.addChild( &mSkeleton[0] );
+
+ mRoot.addChild( &mHeadLOD );
+ mHeadLOD.mUpdateXform = FALSE;
+ mHeadLOD.addChild( &mHeadMesh0 );
+ mHeadLOD.addChild( &mHeadMesh1 );
+ mHeadLOD.addChild( &mHeadMesh2 );
+ mHeadLOD.addChild( &mHeadMesh3 );
+ mHeadLOD.addChild( &mHeadMesh4 );
+
+ mRoot.addChild( &mEyeLashLOD );
+ mEyeLashLOD.mUpdateXform = FALSE;
+ mEyeLashLOD.addChild( &mEyeLashMesh0 );
+
+ mRoot.addChild( &mUpperBodyLOD );
+ mUpperBodyLOD.mUpdateXform = FALSE;
+ mUpperBodyLOD.addChild( &mUpperBodyMesh0 );
+ mUpperBodyLOD.addChild( &mUpperBodyMesh1 );
+ mUpperBodyLOD.addChild( &mUpperBodyMesh2 );
+ mUpperBodyLOD.addChild( &mUpperBodyMesh3 );
+ mUpperBodyLOD.addChild( &mUpperBodyMesh4 );
+
+ mRoot.addChild( &mLowerBodyLOD );
+ mLowerBodyLOD.mUpdateXform = FALSE;
+ mLowerBodyLOD.addChild( &mLowerBodyMesh0 );
+ mLowerBodyLOD.addChild( &mLowerBodyMesh1 );
+ mLowerBodyLOD.addChild( &mLowerBodyMesh2 );
+ mLowerBodyLOD.addChild( &mLowerBodyMesh3 );
+ mLowerBodyLOD.addChild( &mLowerBodyMesh4 );
+
+ mRoot.addChild( &mSkirtLOD );
+ mSkirtLOD.mUpdateXform = FALSE;
+ mSkirtLOD.addChild( &mSkirtMesh0 );
+ mSkirtLOD.addChild( &mSkirtMesh1 );
+ mSkirtLOD.addChild( &mSkirtMesh2 );
+ mSkirtLOD.addChild( &mSkirtMesh3 );
+ mSkirtLOD.addChild( &mSkirtMesh4 );
+
+ LLViewerJoint *skull = (LLViewerJoint*)mRoot.findJoint("mSkull");
+ if (skull)
+ {
+ skull->addChild( &mHairLOD );
+ mHairLOD.mUpdateXform = FALSE;
+ mHairLOD.addChild( &mHairMesh0 );
+ mHairLOD.addChild( &mHairMesh1 );
+ mHairLOD.addChild( &mHairMesh2 );
+ mHairLOD.addChild( &mHairMesh3 );
+ mHairLOD.addChild( &mHairMesh4 );
+ mHairLOD.addChild( &mHairMesh5 );
+ }
+
+ LLViewerJoint *eyeL = (LLViewerJoint*)mRoot.findJoint("mEyeLeft");
+ if (eyeL)
+ {
+ eyeL->addChild( &mEyeBallLeftLOD );
+ mEyeBallLeftLOD.mUpdateXform = FALSE;
+ mEyeBallLeftLOD.addChild( &mEyeBallLeftMesh0 );
+ mEyeBallLeftLOD.addChild( &mEyeBallLeftMesh1 );
+ }
+
+ LLViewerJoint *eyeR = (LLViewerJoint*)mRoot.findJoint("mEyeRight");
+ if (eyeR)
+ {
+ eyeR->addChild( &mEyeBallRightLOD );
+ mEyeBallRightLOD.mUpdateXform = FALSE;
+ mEyeBallRightLOD.addChild( &mEyeBallRightMesh0 );
+ mEyeBallRightLOD.addChild( &mEyeBallRightMesh1 );
+ }
+
+ // SKELETAL DISTORTIONS
+ {
+ LLVOAvatarInfo::skeletal_distortion_info_list_t::iterator iter;
+ for (iter = sAvatarInfo->mSkeletalDistortionInfoList.begin();
+ iter != sAvatarInfo->mSkeletalDistortionInfoList.end(); iter++)
+ {
+ LLPolySkeletalDistortionInfo *info = *iter;
+ LLPolySkeletalDistortion *param = new LLPolySkeletalDistortion(this);
+ if (!param->setInfo(info))
+ {
+ delete param;
+ return FALSE;
+ }
+ else
+ {
+ addVisualParam(param);
+ }
+ }
+ }
+
+ // ATTACHMENTS
+ {
+ LLVOAvatarInfo::attachment_info_list_t::iterator iter;
+ for (iter = sAvatarInfo->mAttachmentInfoList.begin();
+ iter != sAvatarInfo->mAttachmentInfoList.end(); iter++)
+ {
+ LLVOAvatarInfo::LLVOAvatarAttachmentInfo *info = *iter;
+ LLViewerJointAttachment* attachment = new LLViewerJointAttachment();
+
+ attachment->setName(info->mName);
+ LLJoint *parentJoint = getJoint(info->mJointName);
+ if (!parentJoint)
+ {
+ llwarns << "No parent joint by name " << info->mJointName << " found for attachment point " << info->mName << llendl;
+ delete attachment;
+ continue;
+ }
+
+ if (info->mHasPosition)
+ {
+ attachment->setOriginalPosition(info->mPosition);
+ }
+
+ if (info->mHasRotation)
+ {
+ LLQuaternion rotation;
+ rotation.setQuat(info->mRotationEuler.mV[VX] * DEG_TO_RAD,
+ info->mRotationEuler.mV[VY] * DEG_TO_RAD,
+ info->mRotationEuler.mV[VZ] * DEG_TO_RAD);
+ attachment->setRotation(rotation);
+ }
+
+ int group = info->mGroup;
+ if (group >= 0)
+ {
+ if (group < 0 || group >= 9)
+ {
+ llwarns << "Invalid group number (" << group << ") for attachment point " << info->mName << llendl;
+ }
+ else
+ {
+ attachment->setGroup(group);
+ }
+ }
+
+ S32 attachmentID = info->mAttachmentID;
+ if (attachmentID < 1 || attachmentID > 255)
+ {
+ llwarns << "Attachment point out of range [1-255]: " << attachmentID << " on attachment point " << info->mName << llendl;
+ delete attachment;
+ continue;
+ }
+ if (mAttachmentPoints.checkData(attachmentID))
+ {
+ llwarns << "Attachment point redefined with id " << attachmentID << " on attachment point " << info->mName << llendl;
+ delete attachment;
+ continue;
+ }
+
+ attachment->setPieSlice(info->mPieMenuSlice);
+ attachment->setVisibleInFirstPerson(info->mVisibleFirstPerson);
+ attachment->setIsHUDAttachment(info->mIsHUDAttachment);
+
+ mAttachmentPoints[attachmentID] = attachment;
+
+ // now add attachment joint
+ parentJoint->addChild(attachment);
+ }
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// loadMeshNodes(): loads <mesh> nodes from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::loadMeshNodes()
+{
+ LLVOAvatarInfo::mesh_info_list_t::iterator iter;
+ for (iter = sAvatarInfo->mMeshInfoList.begin();
+ iter != sAvatarInfo->mMeshInfoList.end(); iter++)
+ {
+ LLVOAvatarInfo::LLVOAvatarMeshInfo *info = *iter;
+ LLString &type = info->mType;
+ S32 lod = info->mLOD;
+
+ LLViewerJointMesh* mesh = NULL;
+ if (type == "hairMesh")
+ {
+ switch (lod)
+ {
+ case 0:
+ mesh = &mHairMesh0;
+ break;
+ case 1:
+ mesh = &mHairMesh1;
+ break;
+ case 2:
+ mesh = &mHairMesh2;
+ break;
+ case 3:
+ mesh = &mHairMesh3;
+ break;
+ case 4:
+ mesh = &mHairMesh4;
+ break;
+ case 5:
+ mesh = &mHairMesh5;
+ break;
+ default:
+ llwarns << "Avatar file: <mesh> has invalid lod setting " << lod << llendl;
+ return FALSE;
+ }
+ }
+ else if (type == "headMesh")
+ {
+ switch (lod)
+ {
+ case 0:
+ mesh = &mHeadMesh0;
+ break;
+ case 1:
+ mesh = &mHeadMesh1;
+ break;
+ case 2:
+ mesh = &mHeadMesh2;
+ break;
+ case 3:
+ mesh = &mHeadMesh3;
+ break;
+ case 4:
+ mesh = &mHeadMesh4;
+ break;
+ default:
+ llwarns << "Avatar file: <mesh> has invalid lod setting " << lod << llendl;
+ return FALSE;
+ }
+ }
+ else if (type == "upperBodyMesh")
+ {
+ switch (lod)
+ {
+ case 0:
+ mesh = &mUpperBodyMesh0;
+ break;
+ case 1:
+ mesh = &mUpperBodyMesh1;
+ break;
+ case 2:
+ mesh = &mUpperBodyMesh2;
+ break;
+ case 3:
+ mesh = &mUpperBodyMesh3;
+ break;
+ case 4:
+ mesh = &mUpperBodyMesh4;
+ break;
+ default:
+ llwarns << "Avatar file: <mesh> has invalid lod setting " << lod << llendl;
+ return FALSE;
+ }
+ }
+ else if (type == "lowerBodyMesh")
+ {
+ switch (lod)
+ {
+ case 0:
+ mesh = &mLowerBodyMesh0;
+ break;
+ case 1:
+ mesh = &mLowerBodyMesh1;
+ break;
+ case 2:
+ mesh = &mLowerBodyMesh2;
+ break;
+ case 3:
+ mesh = &mLowerBodyMesh3;
+ break;
+ case 4:
+ mesh = &mLowerBodyMesh4;
+ break;
+ default:
+ llwarns << "Avatar file: <mesh> has invalid lod setting " << lod << llendl;
+ return FALSE;
+ }
+ }
+ else if (type == "skirtMesh")
+ {
+ switch (lod)
+ {
+ case 0:
+ mesh = &mSkirtMesh0;
+ break;
+ case 1:
+ mesh = &mSkirtMesh1;
+ break;
+ case 2:
+ mesh = &mSkirtMesh2;
+ break;
+ case 3:
+ mesh = &mSkirtMesh3;
+ break;
+ case 4:
+ mesh = &mSkirtMesh4;
+ break;
+ default:
+ llwarns << "Avatar file: <mesh> has invalid lod setting " << lod << llendl;
+ return FALSE;
+ }
+ }
+ else if (type == "eyelashMesh")
+ {
+ mesh = &mEyeLashMesh0;
+ }
+ else if (type == "eyeBallLeftMesh")
+ {
+ switch (lod)
+ {
+ case 0:
+ mesh = &mEyeBallLeftMesh0;
+ break;
+ case 1:
+ mesh = &mEyeBallLeftMesh1;
+ break;
+ default:
+ llwarns << "Avatar file: <mesh> has invalid lod setting " << lod << llendl;
+ return FALSE;
+ }
+ }
+ else if (type == "eyeBallRightMesh")
+ {
+ switch (lod)
+ {
+ case 0:
+ mesh = &mEyeBallRightMesh0;
+ break;
+ case 1:
+ mesh = &mEyeBallRightMesh1;
+ break;
+ default:
+ llwarns << "Avatar file: <mesh> has invalid lod setting " << lod << llendl;
+ return FALSE;
+ }
+ }
+
+ if( !mesh )
+ {
+ llwarns << "Ignoring unrecognized mesh type: " << type << llendl;
+ return FALSE;
+ }
+
+ // llinfos << "Parsing mesh data for " << type << "..." << llendl;
+
+ mesh->setColor( 0.8f, 0.8f, 0.8f, 1.0f );
+
+ LLPolyMesh *poly_mesh = NULL;
+
+ if (!info->mReferenceMeshName.empty())
+ {
+ mesh_map_t::iterator iter = mMeshes.find(info->mReferenceMeshName);
+ if (iter != mMeshes.end())
+ {
+ poly_mesh = LLPolyMesh::getMesh(info->mMeshFileName, iter->second);
+ poly_mesh->setAvatar(this);
+ }
+ else
+ {
+ // This should never happen
+ }
+ }
+ else
+ {
+ poly_mesh = LLPolyMesh::getMesh(info->mMeshFileName);
+ poly_mesh->setAvatar(this);
+ }
+
+ if( !poly_mesh )
+ {
+ llwarns << "Failed to load mesh of type " << type << llendl;
+ return FALSE;
+ }
+
+ // Multimap insert
+ mMeshes.insert(std::pair<LLString, LLPolyMesh*>(info->mMeshFileName, poly_mesh));
+
+ mesh->setMesh( poly_mesh );
+
+ mesh->setLOD( info->mMinPixelArea );
+
+ LLVOAvatarInfo::LLVOAvatarMeshInfo::morph_info_list_t::iterator iter;
+ for (iter = info->mPolyMorphTargetInfoList.begin();
+ iter != info->mPolyMorphTargetInfoList.end(); iter++)
+ {
+ LLVOAvatarInfo::LLVOAvatarMeshInfo::morph_info_pair_t *info_pair = &(*iter);
+ LLPolyMorphTarget *param = new LLPolyMorphTarget(mesh->getMesh());
+ if (!param->setInfo(info_pair->first))
+ {
+ delete param;
+ return FALSE;
+ }
+ else
+ {
+ if (info_pair->second)
+ {
+ addSharedVisualParam(param);
+ }
+ else
+ {
+ addVisualParam(param);
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// updateVisualParams()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::updateVisualParams()
+{
+ if (gNoRender)
+ {
+ return;
+ }
+
+ setSex( (getVisualParamWeight( "male" ) > 0.5f) ? SEX_MALE : SEX_FEMALE );
+
+ LLCharacter::updateVisualParams();
+
+ if (mLastSkeletonSerialNum != mSkeletonSerialNum)
+ {
+ computeBodySize();
+ mLastSkeletonSerialNum = mSkeletonSerialNum;
+ mRoot.updateWorldMatrixChildren();
+ }
+
+ dirtyMesh();
+ updateHeadOffset();
+}
+
+//-----------------------------------------------------------------------------
+// isActive()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::isActive() const
+{
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// setPixelAreaAndAngle()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::setPixelAreaAndAngle(LLAgent &agent)
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ LLVector3 viewer_pos_agent = agent.getCameraPositionAgent();
+ LLVector3 pos_agent;
+
+ pos_agent = getRenderPosition();
+
+ F32 dx = viewer_pos_agent.mV[VX] - pos_agent.mV[VX];
+ F32 dy = viewer_pos_agent.mV[VY] - pos_agent.mV[VY];
+ F32 dz = viewer_pos_agent.mV[VZ] - pos_agent.mV[VZ];
+
+ F32 max_scale = getMaxScale();
+ F32 mid_scale = getMidScale();
+ F32 min_scale = llmin( getScale().mV[VX], llmin( getScale().mV[VY], getScale().mV[VZ] ) );
+
+ // IW: esitmate - when close to large objects, computing range based on distance from center is no good
+ // to try to get a min distance from face, subtract min_scale/2 from the range.
+ // This means we'll load too much detail sometimes, but that's better than not enough
+ // I don't think there's a better way to do this without calculating distance per-poly
+ F32 range = sqrt(dx*dx + dy*dy + dz*dz) - min_scale/2;
+
+ if (range < 0.001f) // range == zero
+ {
+ mAppAngle = 180.f;
+ mPixelArea = gCamera->getViewHeightInPixels() *
+ gCamera->getViewHeightInPixels() *
+ gCamera->getAspect();
+ }
+ else
+ {
+ mAppAngle = (F32) atan2( max_scale, range) * RAD_TO_DEG;
+
+ F32 pixels_per_meter = gCamera->getPixelMeterRatio() / range;
+
+ mPixelArea = (pixels_per_meter * max_scale) * (pixels_per_meter * mid_scale);
+// if( !mIsSelf )
+// {
+// llinfos << "range " << range << llendl;
+// llinfos << "pixels_per_meter " << pixels_per_meter << llendl;
+// llinfos << "scale " << max_scale << "x" << mid_scale << llendl;
+// llinfos << "pixel area " << mPixelArea << llendl;
+// }
+ }
+
+ // We always want to look good to ourselves
+ if( mIsSelf )
+ {
+ mPixelArea = llmax( mPixelArea, F32(LOCTEX_IMAGE_SIZE_SELF / 16) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// updateJointLODs()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::updateJointLODs()
+{
+ if (!mMeshValid)
+ {
+ return;
+ }
+
+ F32 lod_factor = (sLODFactor * AVATAR_LOD_TWEAK_RANGE + (1.f - AVATAR_LOD_TWEAK_RANGE));
+ F32 avatar_num_min_factor = clamp_rescale(sLODFactor, 0.f, 1.f, 0.25f, 0.6f);
+ F32 avatar_num_factor = clamp_rescale((F32)sNumVisibleAvatars, 8, 25, 1.f, avatar_num_min_factor);
+
+ {
+ if (mIsSelf)
+ {
+ if(gAgent.cameraCustomizeAvatar() || gAgent.cameraMouselook())
+ {
+ mAdjustedPixelArea = 1000000;
+ }
+ else
+ {
+ mAdjustedPixelArea = mPixelArea;
+ }
+ }
+ else if (mIsDummy)
+ {
+ mAdjustedPixelArea = 1000000;
+ }
+ else
+ {
+ // reported avatar pixel area is dependent on avatar render load, based on number of visible avatars
+ mAdjustedPixelArea = (F32)mPixelArea * lod_factor * lod_factor * avatar_num_factor * avatar_num_factor;
+ }
+
+ // now select meshes to render based on adjusted pixel area, and perform AGP data push as necessary
+ BOOL res = mRoot.updateLOD(mAdjustedPixelArea, TRUE);
+ if (res)
+ {
+ sNumLODChangesThisFrame++;
+ updateMeshData();
+ }
+ }
+
+ return;
+}
+
+//-----------------------------------------------------------------------------
+// createDrawable()
+//-----------------------------------------------------------------------------
+LLDrawable *LLVOAvatar::createDrawable(LLPipeline *pipeline)
+{
+ pipeline->allocDrawable(this);
+ mDrawable->setLit(FALSE);
+
+ LLDrawPool *poolp = gPipeline.getPool(LLDrawPool::POOL_AVATAR);
+
+ // Only a single face (one per avatar)
+ mDrawable->setState(LLDrawable::ACTIVE);
+ mDrawable->addFace(poolp, NULL);
+
+ poolp = gPipeline.getPool(LLDrawPool::POOL_ALPHA);
+
+ LLFace *facep;
+
+ // Add faces for the foot shadows
+ facep = mDrawable->addFace(poolp, mShadowImagep);
+ mShadow0Facep = facep;
+
+ facep = mDrawable->addFace(poolp, mShadowImagep);
+ mShadow1Facep = facep;
+
+ gPipeline.markMaterialed(mDrawable);
+ dirtyMesh();
+ return mDrawable;
+}
+
+
+//-----------------------------------------------------------------------------
+// updateGeometry()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::updateGeometry(LLDrawable *drawable)
+{
+ if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_AVATAR)))
+ {
+ return TRUE;
+ }
+
+ if (!mMeshValid)
+ {
+ return TRUE;
+ }
+
+ if (!drawable)
+ {
+ llerrs << "LLVOAvatar::updateGeometry() called with NULL drawable" << llendl;
+ }
+
+ // Update the shadow, tractor, and text label geometry.
+
+ updateShadowFaces();
+
+ if (!drawable->isVisible())
+ {
+ return TRUE;
+ }
+
+ LLFace* facep = drawable->getFace(0);
+ if (!mDirtyMesh && !facep->getDirty())
+ {
+ return TRUE;
+ }
+
+ // U32 num_vertices = 0;
+
+ updateMeshData();
+
+ mDirtyMesh = FALSE;
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// updateShadowFaces()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::updateShadowFaces()
+{
+ LLFace *face0p = mShadow0Facep;
+ LLFace *face1p = mShadow1Facep;
+ //
+ // render avatar shadows
+ //
+ if (mInAir)
+ {
+ face0p->setSize(0, 0);
+ face1p->setSize(0, 0);
+ return;
+ }
+
+ LLSprite sprite(mShadowImageID);
+ sprite.setFollow(FALSE);
+ const F32 cos_angle = gSky.getSunDirection().mV[2];
+ F32 cos_elev = sqrt(1 - cos_angle * cos_angle);
+ if (cos_angle < 0) cos_elev = -cos_elev;
+ sprite.setSize(0.4f + cos_elev * 0.8f, 0.3f);
+ LLVector3 sun_vec = gSky.mVOSkyp->getToSun();
+
+ if (mShadowImagep->getHasGLTexture())
+ {
+ LLVector3 normal;
+ LLVector3d shadow_pos;
+ LLVector3 shadow_pos_agent;
+ F32 foot_height;
+
+ if (mFootLeftp)
+ {
+ LLVector3 joint_world_pos = mFootLeftp->getWorldPosition();
+ // this only does a ray straight down from the foot, as our client-side ray-tracing is very limited now
+ // but we make an explicit ray trace call in expectation of future improvements
+ resolveRayCollisionAgent(gAgent.getPosGlobalFromAgent(joint_world_pos),
+ gAgent.getPosGlobalFromAgent(gSky.getSunDirection() + joint_world_pos), shadow_pos, normal);
+ shadow_pos_agent = gAgent.getPosAgentFromGlobal(shadow_pos);
+ foot_height = joint_world_pos.mV[VZ] - shadow_pos_agent.mV[VZ];
+
+ // Pull sprite in direction of surface normal
+ shadow_pos_agent += normal * SHADOW_OFFSET_AMT;
+
+ // Render sprite
+ sprite.setNormal(normal);
+ if (mIsSelf && gAgent.getCameraMode() == CAMERA_MODE_MOUSELOOK)
+ {
+ sprite.setColor(0.f, 0.f, 0.f, 0.f);
+ }
+ else
+ {
+ sprite.setColor(0.f, 0.f, 0.f, clamp_rescale(foot_height, MIN_SHADOW_HEIGHT, MAX_SHADOW_HEIGHT, 0.5f, 0.f));
+ }
+ sprite.setPosition(shadow_pos_agent);
+
+ LLVector3 foot_to_knee = mKneeLeftp->getWorldPosition() - joint_world_pos;
+ //foot_to_knee.normVec();
+ foot_to_knee -= projected_vec(foot_to_knee, sun_vec);
+ sprite.setYaw(azimuth(sun_vec - foot_to_knee));
+
+ sprite.updateFace(*face0p);
+ }
+
+ if (mFootRightp)
+ {
+ LLVector3 joint_world_pos = mFootRightp->getWorldPosition();
+ // this only does a ray straight down from the foot, as our client-side ray-tracing is very limited now
+ // but we make an explicit ray trace call in expectation of future improvements
+ resolveRayCollisionAgent(gAgent.getPosGlobalFromAgent(joint_world_pos),
+ gAgent.getPosGlobalFromAgent(gSky.getSunDirection() + joint_world_pos), shadow_pos, normal);
+ shadow_pos_agent = gAgent.getPosAgentFromGlobal(shadow_pos);
+ foot_height = joint_world_pos.mV[VZ] - shadow_pos_agent.mV[VZ];
+
+ // Pull sprite in direction of surface normal
+ shadow_pos_agent += normal * SHADOW_OFFSET_AMT;
+
+ // Render sprite
+ sprite.setNormal(normal);
+ if (mIsSelf && gAgent.getCameraMode() == CAMERA_MODE_MOUSELOOK)
+ {
+ sprite.setColor(0.f, 0.f, 0.f, 0.f);
+ }
+ else
+ {
+ sprite.setColor(0.f, 0.f, 0.f, clamp_rescale(foot_height, MIN_SHADOW_HEIGHT, MAX_SHADOW_HEIGHT, 0.5f, 0.f));
+ }
+ sprite.setPosition(shadow_pos_agent);
+
+ LLVector3 foot_to_knee = mKneeRightp->getWorldPosition() - joint_world_pos;
+ //foot_to_knee.normVec();
+ foot_to_knee -= projected_vec(foot_to_knee, sun_vec);
+ sprite.setYaw(azimuth(sun_vec - foot_to_knee));
+
+ sprite.updateFace(*face1p);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// updateSexDependentLayerSets()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::updateSexDependentLayerSets( BOOL set_by_user )
+{
+ invalidateComposite( mHeadLayerSet, set_by_user );
+ invalidateComposite( mLowerBodyLayerSet, set_by_user );
+ invalidateComposite( mUpperBodyLayerSet, set_by_user );
+ updateMeshTextures();
+}
+
+//-----------------------------------------------------------------------------
+// dirtyMesh()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::dirtyMesh()
+{
+ mDirtyMesh = TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// requestLayerSetUpdate()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::requestLayerSetUpdate( LLVOAvatar::ELocTexIndex i )
+{
+ switch( i )
+ {
+ case LOCTEX_HEAD_BODYPAINT:
+ if( mHeadLayerSet )
+ {
+ mHeadLayerSet->requestUpdate();
+ }
+ break;
+
+ case LOCTEX_UPPER_BODYPAINT:
+ case LOCTEX_UPPER_SHIRT:
+ case LOCTEX_UPPER_GLOVES:
+ case LOCTEX_UPPER_UNDERSHIRT:
+ if( mUpperBodyLayerSet )
+ {
+ mUpperBodyLayerSet->requestUpdate();
+ }
+ break;
+
+ case LOCTEX_LOWER_BODYPAINT:
+ case LOCTEX_LOWER_PANTS:
+ case LOCTEX_LOWER_SHOES:
+ case LOCTEX_LOWER_SOCKS:
+ case LOCTEX_LOWER_UNDERPANTS:
+ if( mLowerBodyLayerSet )
+ {
+ mLowerBodyLayerSet->requestUpdate();
+ }
+ break;
+
+ case LOCTEX_EYES_IRIS:
+ if( mEyesLayerSet )
+ {
+ mEyesLayerSet->requestUpdate();
+ }
+ break;
+
+
+ case LOCTEX_SKIRT:
+ if( mSkirtLayerSet )
+ {
+ mSkirtLayerSet->requestUpdate();
+ }
+ break;
+
+
+ case LOCTEX_LOWER_JACKET:
+ if( mUpperBodyLayerSet )
+ {
+ mUpperBodyLayerSet->requestUpdate();
+ }
+
+ if( mLowerBodyLayerSet )
+ {
+ mLowerBodyLayerSet->requestUpdate();
+ }
+ break;
+ }
+
+}
+
+void LLVOAvatar::setParent(LLViewerObject* parent)
+{
+ if (parent == NULL)
+ {
+ getOffObject();
+ LLViewerObject::setParent(parent);
+ if (isSelf())
+ {
+ gAgent.resetCamera();
+ }
+ }
+ else
+ {
+ LLViewerObject::setParent(parent);
+ sitOnObject(parent);
+ }
+}
+
+void LLVOAvatar::addChild(LLViewerObject *childp)
+{
+ LLViewerObject::addChild(childp);
+ attachObject(childp);
+}
+
+void LLVOAvatar::removeChild(LLViewerObject *childp)
+{
+ LLViewerObject::removeChild(childp);
+ detachObject(childp);
+}
+
+//-----------------------------------------------------------------------------
+// attachObject()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::attachObject(LLViewerObject *viewer_object)
+{
+ S32 attachmentID = ATTACHMENT_ID_FROM_STATE(viewer_object->getState());
+ //clamp((S32)(viewer_object->getState() & AGENT_ATTACH_MASK) >> AGENT_ATTACH_OFFSET, 1, 0xff);
+
+ //if (mIsSelf)
+ //{
+ // gSelectMgr->deselectObjectAndFamily(viewer_object);
+ //}
+
+ LLViewerJointAttachment* attachment = mAttachmentPoints.getIfThere(attachmentID);
+
+ if (!attachment)
+ {
+ llwarns << "Tried to attach object to invalid attachment point: " << attachmentID << llendl;
+ return FALSE;
+ }
+
+// LLQuaternion object_world_rot = viewer_object->getWorldRotation();
+
+ if (!attachment->addObject(viewer_object))
+ {
+ return FALSE;
+ }
+
+ if (viewer_object->isSelected())
+ {
+ gSelectMgr->updateSelectionCenter();
+ gSelectMgr->updatePointAt();
+ }
+
+// LLQuaternion desired_rot = (object_world_rot * ~attachment->getWorldRotation());
+
+ lldebugs << "Attaching object (" << attachmentID << ") item_id=" << attachment->getItemID() << " task_id=" << viewer_object->getID() << "to " << attachment->getName() << llendl;
+
+ if (mIsSelf)
+ {
+ updateAttachmentVisibility(gAgent.getCameraMode());
+
+ // Then make sure the inventory is in sync with the avatar.
+ gInventory.addChangedMask( LLInventoryObserver::LABEL, attachment->getItemID() );
+ gInventory.notifyObservers();
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// lazyAttach()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::lazyAttach()
+{
+ for(LLViewerJointAttachment* attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment->getAttachmentDirty())
+ {
+ attachment->lazyAttach();
+ if (mIsSelf)
+ {
+ updateAttachmentVisibility(gAgent.getCameraMode());
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// detachObject()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::detachObject(LLViewerObject *viewer_object)
+{
+ for(LLViewerJointAttachment* attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ // only one object per attachment point for now
+ if (attachment->getObject(0) == viewer_object)
+ {
+ LLUUID item_id = attachment->getItemID();
+ attachment->removeObject(viewer_object);
+ if (mIsSelf)
+ {
+ // the simulator should automatically handle
+ // permissiosn revokation
+
+ stopMotionFromSource(viewer_object->getID());
+ LLFollowCamMgr::setCameraActive(viewer_object->getID(), FALSE);
+
+ for (S32 i = 0; i < (S32)viewer_object->mChildList.size(); i++)
+ {
+ LLViewerObject* child_objectp = viewer_object->mChildList[i];
+ // the simulator should automatically handle
+ // permissions revokation
+
+ stopMotionFromSource(child_objectp->getID());
+ LLFollowCamMgr::setCameraActive(child_objectp->getID(), FALSE);
+ }
+
+ }
+ lldebugs << "Detaching object " << viewer_object->mID << " from " << attachment->getName() << llendl;
+ if (mIsSelf)
+ {
+ // Then make sure the inventory is in sync with the avatar.
+ gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
+ gInventory.notifyObservers();
+ }
+ return TRUE;
+ }
+ }
+
+
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// sitOnObject()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::sitOnObject(LLViewerObject *sit_object)
+{
+ if (mDrawable.isNull())
+ {
+ return;
+ }
+ LLQuaternion inv_obj_rot = ~sit_object->getRenderRotation();
+ LLVector3 obj_pos = sit_object->getRenderPosition();
+
+ LLVector3 rel_pos = getRenderPosition() - obj_pos;
+ rel_pos.rotVec(inv_obj_rot);
+
+ mDrawable->mXform.setPosition(rel_pos);
+ mDrawable->mXform.setRotation(mDrawable->getWorldRotation() * inv_obj_rot);
+
+ gPipeline.markMoved(mDrawable, TRUE);
+ mIsSitting = TRUE;
+ mRoot.getXform()->setParent(&sit_object->mDrawable->mXform); // LLVOAvatar::sitOnObject
+ mRoot.setPosition(getPosition());
+ mRoot.updateWorldMatrixChildren();
+
+ stopMotion(ANIM_AGENT_BODY_NOISE);
+
+ if (mIsSelf)
+ {
+ // Might be first sit
+ LLFirstUse::useSit();
+
+ gAgent.setFlying(FALSE);
+ gAgent.setThirdPersonHeadOffset(LLVector3::zero);
+ //interpolate to new camera position
+ gAgent.startCameraAnimation();
+ // make sure we are not trying to autopilot
+ gAgent.stopAutoPilot();
+ gAgent.setupSitCamera();
+ if (gAgent.mForceMouselook) gAgent.changeCameraToMouselook();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getOffObject()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::getOffObject()
+{
+ if (mDrawable.isNull())
+ {
+ return;
+ }
+
+ LLViewerObject* sit_object = (LLViewerObject*)getParent();
+
+ if (sit_object)
+ {
+ stopMotionFromSource(sit_object->getID());
+ LLFollowCamMgr::setCameraActive(sit_object->getID(), FALSE);
+
+ for (S32 i = 0; i < (S32)sit_object->mChildList.size(); i++)
+ {
+ LLViewerObject* child_objectp = sit_object->mChildList[i];
+
+ stopMotionFromSource(child_objectp->getID());
+ LLFollowCamMgr::setCameraActive(child_objectp->getID(), FALSE);
+ }
+ }
+
+ // assumes that transform will not be updated with drawable still having a parent
+ LLVector3 cur_position_world = mDrawable->getWorldPosition();
+ LLQuaternion cur_rotation_world = mDrawable->getWorldRotation();
+
+ // set *local* position based on last *world* position, since we're unparenting the avatar
+ mDrawable->mXform.setPosition(cur_position_world);
+ mDrawable->mXform.setRotation(cur_rotation_world);
+
+ gPipeline.markMoved(mDrawable, TRUE);
+
+ mIsSitting = FALSE;
+ mRoot.getXform()->setParent(NULL); // LLVOAvatar::getOffObject
+ mRoot.setPosition(cur_position_world);
+ mRoot.setRotation(cur_rotation_world);
+ mRoot.getXform()->update();
+
+ startMotion(ANIM_AGENT_BODY_NOISE);
+
+ if (mIsSelf)
+ {
+ LLQuaternion av_rot = gAgent.getFrameAgent().getQuaternion();
+ LLQuaternion obj_rot = sit_object ? sit_object->getRenderRotation() : LLQuaternion::DEFAULT;
+ av_rot = av_rot * obj_rot;
+ LLVector3 at_axis = LLVector3::x_axis;
+ at_axis = at_axis * av_rot;
+ at_axis.mV[VZ] = 0.f;
+ at_axis.normVec();
+ gAgent.resetAxes(at_axis);
+
+ //reset orientation
+// mRoot.setRotation(avWorldRot);
+ gAgent.setThirdPersonHeadOffset(LLVector3(0.f, 0.f, 1.f));
+
+ gAgent.setSitCamera(LLUUID::null);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// findAvatarFromAttachment()
+//-----------------------------------------------------------------------------
+// static
+LLVOAvatar* LLVOAvatar::findAvatarFromAttachment( LLViewerObject* obj )
+{
+ if( obj->isAttachment() )
+ {
+ do
+ {
+ obj = (LLViewerObject*) obj->getParent();
+ }
+ while( obj && !obj->isAvatar() );
+
+ if( obj && !obj->isDead() )
+ {
+ return (LLVOAvatar*)obj;
+ }
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// isWearingAttachment()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::isWearingAttachment( const LLUUID& inv_item_id )
+{
+ for (LLViewerJointAttachment *attachment_point = mAttachmentPoints.getFirstData();
+ attachment_point;
+ attachment_point = mAttachmentPoints.getNextData())
+ {
+ if( attachment_point->getItemID() == inv_item_id )
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// getWornAttachment()
+//-----------------------------------------------------------------------------
+LLViewerObject* LLVOAvatar::getWornAttachment( const LLUUID& inv_item_id )
+{
+ for (LLViewerJointAttachment *attachment_point = mAttachmentPoints.getFirstData();
+ attachment_point;
+ attachment_point = mAttachmentPoints.getNextData())
+ {
+ if( attachment_point->getItemID() == inv_item_id )
+ {
+ return attachment_point->getObject(0);
+ }
+ }
+ return NULL;
+}
+
+const LLString LLVOAvatar::getAttachedPointName(const LLUUID& inv_item_id)
+{
+ for (LLViewerJointAttachment *attachment_point = mAttachmentPoints.getFirstData();
+ attachment_point;
+ attachment_point = mAttachmentPoints.getNextData())
+ {
+ if( attachment_point->getItemID() == inv_item_id )
+ {
+ return (LLString)attachment_point->getName();
+ }
+ }
+
+ return LLString::null;
+}
+
+
+//-----------------------------------------------------------------------------
+// static
+// onLocalTextureLoaded()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::onLocalTextureLoaded( BOOL success, LLViewerImage *src_vi, LLImageRaw* src_raw, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata )
+{
+ //llinfos << "onLocalTextureLoaded: " << src_vi->getID() << llendl;
+
+ const LLUUID& src_id = src_vi->getID();
+ LLAvatarTexData *data = (LLAvatarTexData *)userdata;
+ if (success)
+ {
+ LLVOAvatar *self = (LLVOAvatar *)gObjectList.findObject(data->mAvatarID);
+ LLVOAvatar::ELocTexIndex idx = data->mIndex;
+ if( self &&
+ (!self->mLocalTextureBaked[ idx ]) &&
+ (self->mLocalTexture[ idx ].notNull()) &&
+ (self->mLocalTexture[ idx ]->getID() == src_id) &&
+ (discard_level < self->mLocalTextureDiscard[idx]))
+ {
+ self->mLocalTextureDiscard[idx] = discard_level;
+ self->requestLayerSetUpdate( idx );
+ if( self->mIsSelf && gAgent.cameraCustomizeAvatar() )
+ {
+ LLVisualParamHint::requestHintUpdates();
+ }
+ self->updateMeshTextures();
+ }
+ }
+
+ if( final || !success )
+ {
+ delete data;
+ }
+}
+
+void LLVOAvatar::updateComposites()
+{
+ if( mHeadLayerSet )
+ {
+ mHeadLayerSet->updateComposite();
+ }
+
+ if( mUpperBodyLayerSet )
+ {
+ mUpperBodyLayerSet->updateComposite();
+ }
+
+ if( mLowerBodyLayerSet )
+ {
+ mLowerBodyLayerSet->updateComposite();
+ }
+
+ if( mEyesLayerSet )
+ {
+ mEyesLayerSet->updateComposite();
+ }
+
+ if( mSkirtLayerSet && isWearingWearableType( WT_SKIRT ))
+ {
+ mSkirtLayerSet->updateComposite();
+ }
+}
+
+LLColor4 LLVOAvatar::getGlobalColor( const LLString& color_name )
+{
+ if( color_name=="skin_color" && mTexSkinColor )
+ {
+ return mTexSkinColor->getColor();
+ }
+ else
+ if( color_name=="hair_color" && mTexHairColor )
+ {
+ return mTexHairColor->getColor();
+ }
+ if( color_name=="eye_color" && mTexEyeColor )
+ {
+ return mTexEyeColor->getColor();
+ }
+ else
+ {
+// return LLColor4( .5f, .5f, .5f, .5f );
+ return LLColor4( 0.f, 1.f, 1.f, 1.f ); // good debugging color
+ }
+}
+
+
+void LLVOAvatar::invalidateComposite( LLTexLayerSet* layerset, BOOL set_by_user )
+{
+ if( !layerset || !layerset->getUpdatesEnabled() )
+ {
+ return;
+ }
+
+ /* Debug spam. JC
+ const char* layer_name = "";
+ if (layerset == mHeadLayerSet)
+ {
+ layer_name = "head";
+ }
+ else if (layerset == mUpperBodyLayerSet)
+ {
+ layer_name = "upperbody";
+ }
+ else if (layerset == mLowerBodyLayerSet)
+ {
+ layer_name = "lowerbody";
+ }
+ else if (layerset == mEyesLayerSet)
+ {
+ layer_name = "eyes";
+ }
+ else if (layerset == mSkirtLayerSet)
+ {
+ layer_name = "skirt";
+ }
+ else
+ {
+ layer_name = "unknown";
+ }
+ llinfos << "LLVOAvatar::invalidComposite() " << layer_name << llendl;
+ */
+
+ layerset->requestUpdate();
+
+ if( set_by_user )
+ {
+ llassert( mIsSelf );
+
+ ETextureIndex baked_te = getBakedTE( layerset );
+ if( gAgent.cameraCustomizeAvatar() )
+ {
+ mSavedTE[ baked_te ].setNull();
+ }
+ else
+ {
+ setTEImage( baked_te, gImageList.getImage(IMG_DEFAULT_AVATAR) );
+ layerset->requestUpload();
+ }
+ }
+}
+
+
+void LLVOAvatar::onGlobalColorChanged( LLTexGlobalColor* global_color, BOOL set_by_user )
+{
+ if( global_color == mTexSkinColor )
+ {
+// llinfos << "invalidateComposite cause: onGlobalColorChanged( skin color )" << llendl;
+ invalidateComposite( mHeadLayerSet, set_by_user );
+ invalidateComposite( mUpperBodyLayerSet, set_by_user );
+ invalidateComposite( mLowerBodyLayerSet, set_by_user );
+ }
+ else
+ if( global_color == mTexHairColor )
+ {
+// llinfos << "invalidateComposite cause: onGlobalColorChanged( hair color )" << llendl;
+ invalidateComposite( mHeadLayerSet, set_by_user );
+
+ LLColor4 color = mTexHairColor->getColor();
+ mHairMesh0.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh1.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh2.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh3.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh4.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh5.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ }
+ else
+ if( global_color == mTexEyeColor )
+ {
+// llinfos << "invalidateComposite cause: onGlobalColorChanged( eyecolor )" << llendl;
+ invalidateComposite( mEyesLayerSet, set_by_user );
+ }
+ updateMeshTextures();
+}
+
+void LLVOAvatar::forceBakeAllTextures(bool slam_for_debug)
+{
+ llinfos << "TAT: forced full rebake. " << llendl;
+
+ for (S32 i = 0; i < BAKED_TEXTURE_COUNT; i++)
+ {
+ ETextureIndex baked_index = sBakedTextureIndices[i];
+ LLTexLayerSet* layer_set = getLayerSet(baked_index);
+ if (layer_set)
+ {
+ if (slam_for_debug)
+ {
+ layer_set->setUpdatesEnabled(TRUE);
+ layer_set->cancelUpload();
+ }
+
+ BOOL set_by_user = TRUE;
+ invalidateComposite(layer_set, set_by_user);
+ gViewerStats->incStat(LLViewerStats::ST_TEX_REBAKES);
+ }
+ else
+ {
+ llwarns << "TAT: NO LAYER SET FOR " << (S32)baked_index << llendl;
+ }
+ }
+
+ // Don't know if this is needed
+ updateMeshTextures();
+}
+
+
+// static
+void LLVOAvatar::processRebakeAvatarTextures(LLMessageSystem* msg, void**)
+{
+ LLUUID texture_id;
+ msg->getUUID("TextureData", "TextureID", texture_id);
+
+ LLVOAvatar* self = gAgent.getAvatarObject();
+ if (!self) return;
+
+ // If this is a texture corresponding to one of our baked entries,
+ // just rebake that layer set.
+ BOOL found = FALSE;
+ for (S32 i = 0; i < BAKED_TEXTURE_COUNT; i++)
+ {
+ ETextureIndex baked_index = sBakedTextureIndices[i];
+ if (texture_id == self->getTEImage(baked_index)->getID())
+ {
+ LLTexLayerSet* layer_set = self->getLayerSet(baked_index);
+ if (layer_set)
+ {
+ llinfos << "TAT: rebake - matched entry " << (S32)baked_index << llendl;
+ // Apparently set_by_user == force upload
+ BOOL set_by_user = TRUE;
+ self->invalidateComposite(layer_set, set_by_user);
+ found = TRUE;
+ gViewerStats->incStat(LLViewerStats::ST_TEX_REBAKES);
+ }
+ }
+ }
+
+ // If texture not found, rebake all entries.
+ if (!found)
+ {
+ self->forceBakeAllTextures();
+ }
+ else
+ {
+ // Not sure if this is necessary, but forceBakeAllTextures() does it.
+ self->updateMeshTextures();
+ }
+}
+
+
+BOOL LLVOAvatar::getLocalTextureRaw(S32 index, LLImageRaw* image_raw)
+{
+ BOOL success = FALSE;
+
+ if( (0 <= index) && (index < LOCTEX_NUM_ENTRIES) )
+ {
+ if (mLocalTexture[ index ].isNull() || mLocalTexture[ index ]->getID() == IMG_DEFAULT_AVATAR )
+ {
+ success = TRUE;
+ }
+ else
+ {
+ if( mLocalTexture[ index ]->readBackRaw(-1, image_raw) )
+ {
+ success = TRUE;
+ }
+ else
+ {
+ // No data loaded yet
+ setLocalTexture( (ELocTexIndex)index, getTEImage( index ), FALSE );
+ }
+ }
+ }
+ return success;
+}
+
+BOOL LLVOAvatar::getLocalTextureGL(S32 index, LLImageGL** image_gl_pp)
+{
+ BOOL success = FALSE;
+ *image_gl_pp = NULL;
+
+ if( (0 <= index) && (index < LOCTEX_NUM_ENTRIES) )
+ {
+ if( mLocalTexture[ index ].isNull() || mLocalTexture[ index ]->getID() == IMG_DEFAULT_AVATAR)
+ {
+ success = TRUE;
+ }
+ else
+ {
+ *image_gl_pp = mLocalTexture[ index ];
+ success = TRUE;
+ }
+ }
+
+ if( !success )
+ {
+// llinfos << "getLocalTextureGL(" << index << ") had no data" << llendl;
+ }
+ return success;
+}
+
+const LLUUID& LLVOAvatar::getLocalTextureID( S32 index )
+{
+ if (index >= 0 && mLocalTexture[index].notNull())
+ {
+ return mLocalTexture[index]->getID();
+ }
+ else
+ {
+ return IMG_DEFAULT_AVATAR;
+ }
+}
+
+// static
+void LLVOAvatar::dumpTotalLocalTextureByteCount()
+{
+ S32 total_gl_bytes = 0;
+ for( LLVOAvatar* cur = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
+ cur;
+ cur = (LLVOAvatar*)LLCharacter::sInstances.getNextData() )
+ {
+ S32 gl_bytes = 0;
+ cur->getLocalTextureByteCount(&gl_bytes );
+ total_gl_bytes += gl_bytes;
+ }
+ llinfos << "Total Avatar LocTex GL:" << (total_gl_bytes/1024) << "KB" << llendl;
+}
+
+BOOL LLVOAvatar::isVisible()
+{
+ return mDrawable.notNull() && mDrawable->isVisible();
+}
+
+
+//-----------------------------------------------------------------------------
+// findMotion()
+//-----------------------------------------------------------------------------
+LLMotion* LLVOAvatar::findMotion(const LLUUID& id)
+{
+ return mMotionController.findMotion(id);
+}
+
+// Counts the memory footprint of local textures.
+void LLVOAvatar::getLocalTextureByteCount( S32* gl_bytes )
+{
+ *gl_bytes = 0;
+ for( S32 i = 0; i < LOCTEX_NUM_ENTRIES; i++ )
+ {
+ LLViewerImage* image_gl = mLocalTexture[i];
+ if( image_gl )
+ {
+ S32 bytes = (S32)image_gl->getWidth() * image_gl->getHeight() * image_gl->getComponents();
+
+ if( image_gl->getHasGLTexture() )
+ {
+ *gl_bytes += bytes;
+ }
+ }
+ }
+}
+
+
+BOOL LLVOAvatar::bindScratchTexture( LLGLenum format )
+{
+ U32 texture_bytes = 0;
+ GLuint gl_name = getScratchTexName( format, &texture_bytes );
+ if( gl_name )
+ {
+ LLImageGL::bindExternalTexture( gl_name, 0, GL_TEXTURE_2D );
+ stop_glerror();
+
+ F32* last_bind_time = LLVOAvatar::sScratchTexLastBindTime.getIfThere( format );
+ if( last_bind_time )
+ {
+ if( *last_bind_time != LLImageGL::sLastFrameTime )
+ {
+ *last_bind_time = LLImageGL::sLastFrameTime;
+ LLImageGL::updateBoundTexMem(texture_bytes);
+ }
+ }
+ else
+ {
+ LLImageGL::updateBoundTexMem(texture_bytes);
+ LLVOAvatar::sScratchTexLastBindTime.addData( format, new F32(LLImageGL::sLastFrameTime) );
+ }
+
+
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+LLGLuint LLVOAvatar::getScratchTexName( LLGLenum format, U32* texture_bytes )
+{
+ S32 components;
+ GLenum internal_format;
+ switch( format )
+ {
+ case GL_LUMINANCE: components = 1; internal_format = GL_LUMINANCE8; break;
+ case GL_ALPHA: components = 1; internal_format = GL_ALPHA8; break;
+ case GL_COLOR_INDEX: components = 1; internal_format = GL_COLOR_INDEX8_EXT; break;
+ case GL_LUMINANCE_ALPHA: components = 2; internal_format = GL_LUMINANCE8_ALPHA8; break;
+ case GL_RGB: components = 3; internal_format = GL_RGB8; break;
+ case GL_RGBA: components = 4; internal_format = GL_RGBA8; break;
+ default: llassert(0); components = 4; internal_format = GL_RGBA8; break;
+ }
+
+ *texture_bytes = components * VOAVATAR_SCRATCH_TEX_WIDTH * VOAVATAR_SCRATCH_TEX_HEIGHT;
+
+ if( LLVOAvatar::sScratchTexNames.checkData( format ) )
+ {
+ return *( LLVOAvatar::sScratchTexNames.getData( format ) );
+ }
+ else
+ {
+
+ LLGLSUIDefault gls_ui;
+
+ GLuint name = 0;
+ glGenTextures(1, &name );
+ stop_glerror();
+
+ LLImageGL::bindExternalTexture( name, 0, GL_TEXTURE_2D );
+ stop_glerror();
+
+ glTexImage2D(
+ GL_TEXTURE_2D, 0, internal_format,
+ VOAVATAR_SCRATCH_TEX_WIDTH, VOAVATAR_SCRATCH_TEX_HEIGHT,
+ 0, format, GL_UNSIGNED_BYTE, NULL );
+ stop_glerror();
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
+ stop_glerror();
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+ stop_glerror();
+
+ LLVOAvatar::sScratchTexNames.addData( format, new LLGLuint( name ) );
+
+ LLVOAvatar::sScratchTexBytes += *texture_bytes;
+ LLImageGL::sGlobalTextureMemory += *texture_bytes;
+ return name;
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// setLocalTextureTE()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::setLocTexTE( U8 te, LLViewerImage* image, BOOL set_by_user )
+{
+ if( !mIsSelf )
+ {
+ llassert( 0 );
+ return;
+ }
+
+ if( te >= TEX_NUM_ENTRIES )
+ {
+ llassert(0);
+ return;
+ }
+
+ if( getTEImage( te )->getID() == image->getID() )
+ {
+ return;
+ }
+
+ if (isTextureIndexBaked(te))
+ {
+ llassert(0);
+ return;
+ }
+
+ LLTexLayerSet* layer_set = getLayerSet((ETextureIndex)te);
+ if (layer_set)
+ {
+ invalidateComposite(layer_set, set_by_user);
+ }
+
+ setTEImage( te, image );
+ updateMeshTextures();
+
+ if( gAgent.cameraCustomizeAvatar() )
+ {
+ LLVisualParamHint::requestHintUpdates();
+ }
+}
+
+void LLVOAvatar::setupComposites()
+{
+ // Don't invalidate the baked textures we had on start-up.
+ BOOL head_baked = ( getTEImage( TEX_HEAD_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL upper_baked = ( getTEImage( TEX_UPPER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL lower_baked = ( getTEImage( TEX_LOWER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL eyes_baked = ( getTEImage( TEX_EYES_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL skirt_baked = ( getTEImage( TEX_SKIRT_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+
+ if (mHeadLayerSet)
+ {
+ mHeadLayerSet->setUpdatesEnabled( !head_baked );
+ }
+ if (mUpperBodyLayerSet)
+ {
+ mUpperBodyLayerSet->setUpdatesEnabled( !upper_baked );
+ }
+ if (mLowerBodyLayerSet)
+ {
+ mLowerBodyLayerSet->setUpdatesEnabled( !lower_baked );
+ }
+ if (mEyesLayerSet)
+ {
+ mEyesLayerSet->setUpdatesEnabled( !eyes_baked );
+ }
+ if (mSkirtLayerSet)
+ {
+ mSkirtLayerSet->setUpdatesEnabled( !skirt_baked );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// updateMeshTextures()
+// Uses the current TE values to set the meshes' and layersets' textures.
+//-----------------------------------------------------------------------------
+void LLVOAvatar::updateMeshTextures()
+{
+// llinfos << "updateMeshTextures" << llendl;
+ if (gNoRender)
+ {
+ return;
+ }
+ // if user has never specified a texture, assign the default
+ LLViewerImage* default_tex = gImageList.getImage(IMG_DEFAULT);
+ U8 num_TEs = getNumTEs();
+ for (U32 i=0; i<num_TEs; i++)
+ {
+ LLViewerImage* te_image = getTEImage(i);
+ if( (NULL == te_image) || te_image->getID().isNull() || (te_image->getID() == IMG_DEFAULT) )
+ {
+ if( TEX_HAIR == i )
+ {
+ setTEImage(i, default_tex );
+ }
+ else
+ {
+ setTEImage(i, gImageList.getImage(IMG_DEFAULT_AVATAR)); // a special texture that's never rendered.
+ }
+ }
+ }
+
+ // During face edit mode, we don't use baked textures
+ BOOL self_customize = mIsSelf && gAgent.cameraCustomizeAvatar();
+
+ BOOL head_baked = (getTEImage( TEX_HEAD_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL upper_baked = (getTEImage( TEX_UPPER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL lower_baked = (getTEImage( TEX_LOWER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL eyes_baked = (getTEImage( TEX_EYES_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL skirt_baked = (getTEImage( TEX_SKIRT_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+
+ // Nothing should be baked if we're in customize avatar mode.
+ llassert( !( self_customize &&
+ ( head_baked || upper_baked || lower_baked || eyes_baked ) ) );
+
+ BOOL use_lkg_head_baked = FALSE;
+ BOOL use_lkg_upper_baked = FALSE;
+ BOOL use_lkg_lower_baked = FALSE;
+ BOOL use_lkg_eyes_baked = FALSE;
+ BOOL use_lkg_skirt_baked = FALSE;
+
+ BOOL other_culled = !mIsSelf && mCulled;
+ if( other_culled )
+ {
+ use_lkg_head_baked = !head_baked && (mLastHeadBakedID != IMG_DEFAULT_AVATAR);
+ use_lkg_upper_baked = !upper_baked && (mLastUpperBodyBakedID != IMG_DEFAULT_AVATAR);
+ use_lkg_lower_baked = !lower_baked && (mLastLowerBodyBakedID != IMG_DEFAULT_AVATAR);
+ use_lkg_eyes_baked = !eyes_baked && (mLastEyesBakedID != IMG_DEFAULT_AVATAR);
+ use_lkg_skirt_baked = !skirt_baked && (mLastSkirtBakedID != IMG_DEFAULT_AVATAR);
+
+ if( mHeadLayerSet )
+ {
+ mHeadLayerSet->destroyComposite();
+ }
+
+ if( mUpperBodyLayerSet )
+ {
+ mUpperBodyLayerSet->destroyComposite();
+ }
+
+ if( mLowerBodyLayerSet )
+ {
+ mLowerBodyLayerSet->destroyComposite();
+ }
+
+ if( mEyesLayerSet )
+ {
+ mEyesLayerSet->destroyComposite();
+ }
+
+ if( mSkirtLayerSet )
+ {
+ mSkirtLayerSet->destroyComposite();
+ }
+
+ }
+ else
+ if( !self_customize )
+ {
+ // When you're changing clothes and you're not in Appearance mode,
+ // use the last-known good baked texture until you finish the first
+ // render of the new layerset.
+ use_lkg_head_baked = !head_baked && (mLastHeadBakedID != IMG_DEFAULT_AVATAR) && mHeadLayerSet && !mHeadLayerSet->getComposite()->isInitialized();
+ use_lkg_upper_baked = !upper_baked && (mLastUpperBodyBakedID != IMG_DEFAULT_AVATAR) && mUpperBodyLayerSet && !mUpperBodyLayerSet->getComposite()->isInitialized();
+ use_lkg_lower_baked = !lower_baked && (mLastLowerBodyBakedID != IMG_DEFAULT_AVATAR) && mLowerBodyLayerSet && !mLowerBodyLayerSet->getComposite()->isInitialized();
+ use_lkg_eyes_baked = !eyes_baked && (mLastEyesBakedID != IMG_DEFAULT_AVATAR) && mEyesLayerSet && !mEyesLayerSet->getComposite()->isInitialized();
+ use_lkg_skirt_baked = !skirt_baked && (mLastSkirtBakedID != IMG_DEFAULT_AVATAR) && mSkirtLayerSet && !mSkirtLayerSet->getComposite()->isInitialized();
+
+ if( use_lkg_head_baked )
+ {
+ mHeadLayerSet->setUpdatesEnabled( TRUE );
+ }
+
+ if( use_lkg_upper_baked )
+ {
+ mUpperBodyLayerSet->setUpdatesEnabled( TRUE );
+ }
+
+ if( use_lkg_lower_baked )
+ {
+ mLowerBodyLayerSet->setUpdatesEnabled( TRUE );
+ }
+
+ if( use_lkg_eyes_baked )
+ {
+ mEyesLayerSet->setUpdatesEnabled( TRUE );
+ }
+
+ if( use_lkg_skirt_baked )
+ {
+ mSkirtLayerSet->setUpdatesEnabled( TRUE );
+ }
+ }
+
+ // Baked textures should be requested from the sim this avatar is on. JC
+ LLHost target_host = getObjectHost();
+ if (!target_host.isOk())
+ {
+ llwarns << "updateMeshTextures: invalid host for object: " << getID() << llendl;
+ }
+
+ // Head
+ if( use_lkg_head_baked )
+ {
+ LLViewerImage* baked = gImageList.getImageFromHost( mLastHeadBakedID, target_host );
+ mHeadMesh0.setTexture( baked );
+ mHeadMesh1.setTexture( baked );
+ mHeadMesh2.setTexture( baked );
+ mHeadMesh3.setTexture( baked );
+ mHeadMesh4.setTexture( baked );
+ mEyeLashMesh0.setTexture( baked );
+ }
+ else
+ if( !self_customize && head_baked )
+ {
+ LLViewerImage* baked = getTEImage( TEX_HEAD_BAKED );
+ if( baked->getID() == mLastHeadBakedID )
+ {
+ // Even though the file may not be finished loading, we'll consider it loaded and use it (rather than doing compositing).
+ useBakedTexture( baked->getID() );
+ }
+ else
+ {
+ mHeadBakedLoaded = FALSE;
+ mHeadMaskDiscard = -1;
+ baked->setNeedsAux(TRUE);
+ baked->setLoadedCallback(onBakedTextureMasksLoaded, MORPH_MASK_REQUESTED_DISCARD, TRUE, new LLTextureMaskData( mID ));
+ baked->setLoadedCallback(onBakedTextureLoaded, SWITCH_TO_BAKED_DISCARD, FALSE, new LLUUID( mID ) );
+ }
+ }
+ else
+ if( mHeadLayerSet && !other_culled )
+ {
+ mHeadLayerSet->createComposite();
+ mHeadLayerSet->setUpdatesEnabled( TRUE );
+ mHeadMesh0.setLayerSet( mHeadLayerSet );
+ mHeadMesh1.setLayerSet( mHeadLayerSet );
+ mHeadMesh2.setLayerSet( mHeadLayerSet );
+ mHeadMesh3.setLayerSet( mHeadLayerSet );
+ mHeadMesh4.setLayerSet( mHeadLayerSet );
+ mEyeLashMesh0.setLayerSet( mHeadLayerSet );
+ }
+ else
+ {
+ mHeadMesh0.setTexture( default_tex );
+ mHeadMesh1.setTexture( default_tex );
+ mHeadMesh2.setTexture( default_tex );
+ mHeadMesh3.setTexture( default_tex );
+ mHeadMesh4.setTexture( default_tex );
+ mEyeLashMesh0.setTexture( default_tex );
+ }
+
+ // Upper body
+ if( use_lkg_upper_baked )
+ {
+ LLViewerImage* baked = gImageList.getImageFromHost( mLastUpperBodyBakedID, target_host );
+ mUpperBodyMesh0.setTexture( baked );
+ mUpperBodyMesh1.setTexture( baked );
+ mUpperBodyMesh2.setTexture( baked );
+ mUpperBodyMesh3.setTexture( baked );
+ mUpperBodyMesh4.setTexture( baked );
+ }
+ else
+ if( !self_customize && upper_baked )
+ {
+ LLViewerImage* baked = getTEImage( TEX_UPPER_BAKED );
+
+ if( baked->getID() == mLastUpperBodyBakedID )
+ {
+ // Even though the file may not be finished loading, we'll consider it loaded and use it (rather than doing compositing).
+ useBakedTexture( baked->getID() );
+ }
+ else
+ {
+ mUpperBakedLoaded = FALSE;
+ mUpperMaskDiscard = -1;
+ baked->setNeedsAux(TRUE);
+ baked->setLoadedCallback(onBakedTextureMasksLoaded, MORPH_MASK_REQUESTED_DISCARD, TRUE, new LLTextureMaskData( mID ));
+ baked->setLoadedCallback(onBakedTextureLoaded, SWITCH_TO_BAKED_DISCARD, FALSE, new LLUUID( mID ) );
+ }
+ }
+ else
+ if( mUpperBodyLayerSet && !other_culled )
+ {
+ mUpperBodyLayerSet->createComposite();
+ mUpperBodyLayerSet->setUpdatesEnabled( TRUE );
+ mUpperBodyMesh0.setLayerSet( mUpperBodyLayerSet );
+ mUpperBodyMesh1.setLayerSet( mUpperBodyLayerSet );
+ mUpperBodyMesh2.setLayerSet( mUpperBodyLayerSet );
+ mUpperBodyMesh3.setLayerSet( mUpperBodyLayerSet );
+ mUpperBodyMesh4.setLayerSet( mUpperBodyLayerSet );
+ }
+ else
+ {
+ mUpperBodyMesh0.setTexture( default_tex );
+ mUpperBodyMesh1.setTexture( default_tex );
+ mUpperBodyMesh2.setTexture( default_tex );
+ mUpperBodyMesh3.setTexture( default_tex );
+ mUpperBodyMesh4.setTexture( default_tex );
+ }
+
+ // Lower body
+ if( use_lkg_lower_baked )
+ {
+ LLViewerImage* baked = gImageList.getImageFromHost( mLastLowerBodyBakedID, target_host );
+ mLowerBodyMesh0.setTexture( baked );
+ mLowerBodyMesh1.setTexture( baked );
+ mLowerBodyMesh2.setTexture( baked );
+ mLowerBodyMesh3.setTexture( baked );
+ mLowerBodyMesh4.setTexture( baked );
+ }
+ else
+ if( !self_customize && lower_baked )
+ {
+ LLViewerImage* baked = getTEImage( TEX_LOWER_BAKED );
+ if( baked->getID() == mLastLowerBodyBakedID )
+ {
+ // Even though the file may not be finished loading, we'll consider it loaded and use it (rather than doing compositing).
+ useBakedTexture( baked->getID() );
+ }
+ else
+ {
+ mLowerBakedLoaded = FALSE;
+ mLowerMaskDiscard = -1;
+ baked->setNeedsAux(TRUE);
+ baked->setLoadedCallback(onBakedTextureMasksLoaded, MORPH_MASK_REQUESTED_DISCARD, TRUE, new LLTextureMaskData( mID ));
+ baked->setLoadedCallback(onBakedTextureLoaded, SWITCH_TO_BAKED_DISCARD, FALSE, new LLUUID( mID ) );
+ }
+ }
+ else
+ if( mLowerBodyLayerSet && !other_culled )
+ {
+ mLowerBodyLayerSet->createComposite();
+ mLowerBodyLayerSet->setUpdatesEnabled( TRUE );
+ mLowerBodyMesh0.setLayerSet( mLowerBodyLayerSet );
+ mLowerBodyMesh1.setLayerSet( mLowerBodyLayerSet );
+ mLowerBodyMesh2.setLayerSet( mLowerBodyLayerSet );
+ mLowerBodyMesh3.setLayerSet( mLowerBodyLayerSet );
+ mLowerBodyMesh4.setLayerSet( mLowerBodyLayerSet );
+ }
+ else
+ {
+ mLowerBodyMesh0.setTexture( default_tex );
+ mLowerBodyMesh1.setTexture( default_tex );
+ mLowerBodyMesh2.setTexture( default_tex );
+ mLowerBodyMesh3.setTexture( default_tex );
+ mLowerBodyMesh4.setTexture( default_tex );
+ }
+
+ // Eyes
+ if( use_lkg_eyes_baked )
+ {
+ LLViewerImage* baked = gImageList.getImageFromHost( mLastEyesBakedID, target_host );
+ mEyeBallLeftMesh0.setTexture( baked );
+ mEyeBallLeftMesh1.setTexture( baked );
+ mEyeBallRightMesh0.setTexture( baked );
+ mEyeBallRightMesh1.setTexture( baked );
+ }
+ else
+ if( !self_customize && eyes_baked )
+ {
+ LLViewerImage* baked = getTEImage( TEX_EYES_BAKED );
+ if( baked->getID() == mLastEyesBakedID )
+ {
+ // Even though the file may not be finished loading, we'll consider it loaded and use it (rather than doing compositing).
+ useBakedTexture( baked->getID() );
+ }
+ else
+ {
+ mEyesBakedLoaded = FALSE;
+ baked->setLoadedCallback(onBakedTextureLoaded, SWITCH_TO_BAKED_DISCARD, FALSE, new LLUUID( mID ) );
+ }
+ }
+ else
+ if( mEyesLayerSet && !other_culled )
+ {
+ mEyesLayerSet->createComposite();
+ mEyesLayerSet->setUpdatesEnabled( TRUE );
+ mEyeBallLeftMesh0.setLayerSet( mEyesLayerSet );
+ mEyeBallLeftMesh1.setLayerSet( mEyesLayerSet );
+ mEyeBallRightMesh0.setLayerSet( mEyesLayerSet );
+ mEyeBallRightMesh1.setLayerSet( mEyesLayerSet );
+ }
+ else
+ {
+ mEyeBallLeftMesh0.setTexture( default_tex );
+ mEyeBallLeftMesh1.setTexture( default_tex );
+ mEyeBallRightMesh0.setTexture( default_tex );
+ mEyeBallRightMesh1.setTexture( default_tex );
+ }
+
+ // Skirt
+ if( use_lkg_skirt_baked )
+ {
+ LLViewerImage* baked = gImageList.getImageFromHost( mLastSkirtBakedID, target_host );
+ mSkirtMesh0.setTexture( baked );
+ mSkirtMesh1.setTexture( baked );
+ mSkirtMesh2.setTexture( baked );
+ mSkirtMesh3.setTexture( baked );
+ mSkirtMesh4.setTexture( baked );
+ }
+ else
+ if( !self_customize && skirt_baked )
+ {
+ LLViewerImage* baked = getTEImage( TEX_SKIRT_BAKED );
+ if( baked->getID() == mLastSkirtBakedID )
+ {
+ // Even though the file may not be finished loading, we'll consider it loaded and use it (rather than doing compositing).
+ useBakedTexture( baked->getID() );
+ }
+ else
+ {
+ mSkirtBakedLoaded = FALSE;
+ baked->setLoadedCallback(onBakedTextureLoaded, SWITCH_TO_BAKED_DISCARD, FALSE, new LLUUID( mID ) );
+ }
+ }
+ else
+ if( mSkirtLayerSet && !other_culled)
+ {
+ mSkirtLayerSet->createComposite();
+ mSkirtLayerSet->setUpdatesEnabled( TRUE );
+ mSkirtMesh0.setLayerSet( mSkirtLayerSet );
+ mSkirtMesh1.setLayerSet( mSkirtLayerSet );
+ mSkirtMesh2.setLayerSet( mSkirtLayerSet );
+ mSkirtMesh3.setLayerSet( mSkirtLayerSet );
+ mSkirtMesh4.setLayerSet( mSkirtLayerSet );
+ }
+ else
+ {
+ mSkirtMesh0.setTexture( default_tex );
+ mSkirtMesh1.setTexture( default_tex );
+ mSkirtMesh2.setTexture( default_tex );
+ mSkirtMesh3.setTexture( default_tex );
+ mSkirtMesh4.setTexture( default_tex );
+ }
+
+ mHairMesh0.setTexture( getTEImage( TEX_HAIR ) );
+ mHairMesh1.setTexture( getTEImage( TEX_HAIR ) );
+ mHairMesh2.setTexture( getTEImage( TEX_HAIR ) );
+ mHairMesh3.setTexture( getTEImage( TEX_HAIR ) );
+ mHairMesh4.setTexture( getTEImage( TEX_HAIR ) );
+ mHairMesh5.setTexture( getTEImage( TEX_HAIR ) );
+
+ if( mTexHairColor )
+ {
+ LLColor4 color = mTexHairColor->getColor();
+ mHairMesh0.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh1.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh2.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh3.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh4.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh5.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ }
+
+ // Head
+ BOOL head_baked_ready = (head_baked && mHeadBakedLoaded) || other_culled;
+ setLocalTexture( LOCTEX_HEAD_BODYPAINT, getTEImage( TEX_HEAD_BODYPAINT ), head_baked_ready );
+
+ // Upper body
+ BOOL upper_baked_ready = (upper_baked && mUpperBakedLoaded) || other_culled;
+ setLocalTexture( LOCTEX_UPPER_SHIRT, getTEImage( TEX_UPPER_SHIRT ), upper_baked_ready );
+ setLocalTexture( LOCTEX_UPPER_BODYPAINT, getTEImage( TEX_UPPER_BODYPAINT ), upper_baked_ready );
+ setLocalTexture( LOCTEX_UPPER_JACKET, getTEImage( TEX_UPPER_JACKET ), upper_baked_ready );
+ setLocalTexture( LOCTEX_UPPER_GLOVES, getTEImage( TEX_UPPER_GLOVES ), upper_baked_ready );
+ setLocalTexture( LOCTEX_UPPER_UNDERSHIRT, getTEImage( TEX_UPPER_UNDERSHIRT ), upper_baked_ready );
+
+ // Lower body
+ BOOL lower_baked_ready = (lower_baked && mLowerBakedLoaded) || other_culled;
+ setLocalTexture( LOCTEX_LOWER_PANTS, getTEImage( TEX_LOWER_PANTS ), lower_baked_ready );
+ setLocalTexture( LOCTEX_LOWER_BODYPAINT, getTEImage( TEX_LOWER_BODYPAINT ), lower_baked_ready );
+ setLocalTexture( LOCTEX_LOWER_SHOES, getTEImage( TEX_LOWER_SHOES ), lower_baked_ready );
+ setLocalTexture( LOCTEX_LOWER_SOCKS, getTEImage( TEX_LOWER_SOCKS ), lower_baked_ready );
+ setLocalTexture( LOCTEX_LOWER_JACKET, getTEImage( TEX_LOWER_JACKET ), lower_baked_ready );
+ setLocalTexture( LOCTEX_LOWER_UNDERPANTS, getTEImage( TEX_LOWER_UNDERPANTS ), lower_baked_ready );
+
+ // Eyes
+ BOOL eyes_baked_ready = (eyes_baked && mEyesBakedLoaded) || other_culled;
+ setLocalTexture( LOCTEX_EYES_IRIS, getTEImage( TEX_EYES_IRIS ), eyes_baked_ready );
+
+ // Skirt
+ BOOL skirt_baked_ready = (skirt_baked && mSkirtBakedLoaded) || other_culled;
+ setLocalTexture( LOCTEX_SKIRT, getTEImage( TEX_SKIRT ), skirt_baked_ready );
+
+ removeMissingBakedTextures();
+}
+
+//-----------------------------------------------------------------------------
+// setLocalTexture()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::setLocalTexture( ELocTexIndex idx, LLViewerImage* tex, BOOL baked_version_ready )
+{
+ S32 desired_discard = mIsSelf ? 0 : 2;
+ if (!baked_version_ready)
+ {
+ if (tex != mLocalTexture[idx] || mLocalTextureBaked[idx])
+ {
+ mLocalTextureDiscard[idx] = MAX_DISCARD_LEVEL+1;
+ }
+ if (tex->getID() != IMG_DEFAULT_AVATAR)
+ {
+ if (mLocalTextureDiscard[idx] > desired_discard)
+ {
+ S32 tex_discard = tex->getDiscardLevel();
+ if (tex_discard >= 0 && tex_discard <= desired_discard)
+ {
+ mLocalTextureDiscard[idx] = tex_discard;
+ requestLayerSetUpdate( idx );
+ if( mIsSelf && gAgent.cameraCustomizeAvatar() )
+ {
+ LLVisualParamHint::requestHintUpdates();
+ }
+ }
+ else
+ {
+ tex->setLoadedCallback( onLocalTextureLoaded, desired_discard, TRUE, new LLAvatarTexData(getID(), idx) );
+ }
+ }
+ tex->setMinDiscardLevel(desired_discard);
+ }
+ }
+ mLocalTextureBaked[idx] = baked_version_ready;
+ mLocalTexture[idx] = tex;
+}
+
+//-----------------------------------------------------------------------------
+// requestLayerSetUploads()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::requestLayerSetUploads()
+{
+ BOOL upper_baked = (getTEImage( TEX_UPPER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL lower_baked = (getTEImage( TEX_LOWER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL head_baked = (getTEImage( TEX_HEAD_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL eyes_baked = (getTEImage( TEX_EYES_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL skirt_baked = (getTEImage( TEX_SKIRT_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+
+ if( !head_baked && mHeadLayerSet )
+ {
+ mHeadLayerSet->requestUpload();
+ }
+
+ if( !upper_baked && mUpperBodyLayerSet )
+ {
+ mUpperBodyLayerSet->requestUpload();
+ }
+
+ if( !lower_baked && mLowerBodyLayerSet )
+ {
+ mLowerBodyLayerSet->requestUpload();
+ }
+
+ if( !eyes_baked && mEyesLayerSet )
+ {
+ mEyesLayerSet->requestUpload();
+ }
+
+ if( !skirt_baked && mSkirtLayerSet )
+ {
+ mSkirtLayerSet->requestUpload();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// setCompositeUpdatesEnabled()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::setCompositeUpdatesEnabled( BOOL b )
+{
+ if( mHeadLayerSet )
+ {
+ mHeadLayerSet->setUpdatesEnabled( b );
+ }
+
+ if( mUpperBodyLayerSet )
+ {
+ mUpperBodyLayerSet->setUpdatesEnabled( b );
+ }
+
+ if( mLowerBodyLayerSet )
+ {
+ mLowerBodyLayerSet->setUpdatesEnabled( b );
+ }
+
+ if( mEyesLayerSet )
+ {
+ mEyesLayerSet->setUpdatesEnabled( b );
+ }
+
+ if( mSkirtLayerSet )
+ {
+ mSkirtLayerSet->setUpdatesEnabled( b );
+ }
+
+}
+
+void LLVOAvatar::addChat(const LLChat& chat)
+{
+ std::deque<LLChat>::iterator chat_iter;
+
+ mChats.push_back(chat);
+
+ S32 chat_length = 0;
+ for( chat_iter = mChats.begin(); chat_iter != mChats.end(); ++chat_iter)
+ {
+ chat_length += chat_iter->mText.size();
+ }
+
+ // remove any excess chat
+ chat_iter = mChats.begin();
+ while ((chat_length > MAX_BUBBLE_CHAT_LENGTH || mChats.size() > MAX_BUBBLE_CHAT_UTTERANCES) && chat_iter != mChats.end())
+ {
+ chat_length -= chat_iter->mText.size();
+ mChats.pop_front();
+ chat_iter = mChats.begin();
+ }
+
+ mChatTimer.reset();
+}
+
+void LLVOAvatar::clearChat()
+{
+ mChats.clear();
+}
+
+S32 LLVOAvatar::getLocalDiscardLevel( S32 index )
+{
+ if (index >= 0 && mLocalTexture[index].notNull() && mLocalTexture[index]->getID() != IMG_DEFAULT_AVATAR)
+ {
+ return mLocalTexture[index]->getDiscardLevel();
+ }
+ else
+ {
+ // We don't care about this (no image associated with the layer) treat as fully loaded.
+ return 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// isLocalTextureDataFinal()
+// Returns true is the highest quality discard level exists for every texture
+// in the layerset.
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::isLocalTextureDataFinal( LLTexLayerSet* layerset )
+{
+ if( layerset == mHeadLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_HEAD_BODYPAINT ) == 0;
+ }
+ else if( layerset == mUpperBodyLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_UPPER_SHIRT ) == 0 &&
+ getLocalDiscardLevel( LOCTEX_UPPER_BODYPAINT ) == 0 &&
+ getLocalDiscardLevel( LOCTEX_UPPER_JACKET ) == 0 &&
+ getLocalDiscardLevel( LOCTEX_UPPER_GLOVES ) == 0 &&
+ getLocalDiscardLevel( LOCTEX_UPPER_UNDERSHIRT ) == 0;
+ }
+ else if( layerset == mLowerBodyLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_LOWER_PANTS ) == 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_BODYPAINT ) == 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_SHOES ) == 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_SOCKS ) == 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_JACKET ) == 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_UNDERPANTS ) == 0;
+ }
+ else if( layerset == mEyesLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_EYES_IRIS ) == 0;
+ }
+ else if( layerset == mSkirtLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_SKIRT ) == 0;
+ }
+
+ llassert(0);
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// isLocalTextureDataAvailable()
+// Returns true is at least the lowest quality discard level exists for every texture
+// in the layerset.
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::isLocalTextureDataAvailable( LLTexLayerSet* layerset )
+{
+ if( layerset == mHeadLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_HEAD_BODYPAINT ) >= 0;
+ }
+ else if( layerset == mUpperBodyLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_UPPER_SHIRT ) >= 0 &&
+ getLocalDiscardLevel( LOCTEX_UPPER_BODYPAINT ) >= 0 &&
+ getLocalDiscardLevel( LOCTEX_UPPER_JACKET ) >= 0 &&
+ getLocalDiscardLevel( LOCTEX_UPPER_GLOVES ) >= 0 &&
+ getLocalDiscardLevel( LOCTEX_UPPER_UNDERSHIRT ) >= 0;
+ }
+ else if( layerset == mLowerBodyLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_LOWER_PANTS ) >= 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_BODYPAINT ) >= 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_SHOES ) >= 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_SOCKS ) >= 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_JACKET ) >= 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_UNDERPANTS ) >= 0;
+ }
+ else if( layerset == mEyesLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_EYES_IRIS ) >= 0;
+ }
+ else if( layerset == mSkirtLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_SKIRT ) >= 0;
+ }
+
+ llassert(0);
+ return FALSE;
+}
+
+
+//-----------------------------------------------------------------------------
+// getBakedTE()
+// Used by the LayerSet. (Layer sets don't in general know what textures depend on them.)
+//-----------------------------------------------------------------------------
+LLVOAvatar::ETextureIndex LLVOAvatar::getBakedTE( LLTexLayerSet* layerset )
+{
+ if( layerset == mHeadLayerSet )
+ {
+ return TEX_HEAD_BAKED;
+ }
+ else
+ if( layerset == mUpperBodyLayerSet )
+ {
+ return TEX_UPPER_BAKED;
+ }
+ else
+ if( layerset == mLowerBodyLayerSet )
+ {
+ return TEX_LOWER_BAKED;
+ }
+ else
+ if( layerset == mEyesLayerSet )
+ {
+ return TEX_EYES_BAKED;
+ }
+ else
+ if( layerset == mSkirtLayerSet )
+ {
+ return TEX_SKIRT_BAKED;
+ }
+
+ llassert(0);
+ return TEX_HEAD_BAKED;
+}
+
+//-----------------------------------------------------------------------------
+// setNewBakedTexture()
+// A new baked texture has been successfully uploaded and we can start using it now.
+//-----------------------------------------------------------------------------
+void LLVOAvatar::setNewBakedTexture( ETextureIndex te, const LLUUID& uuid )
+{
+ // Baked textures live on other sims.
+ LLHost target_host = getObjectHost();
+ setTEImage( te, gImageList.getImageFromHost( uuid, target_host ) );
+ updateMeshTextures();
+ dirtyMesh();
+
+ LLVOAvatar::cullAvatarsByPixelArea();
+
+ switch( te )
+ {
+ case TEX_HEAD_BAKED:
+ llinfos << "New baked texture: HEAD" << llendl;
+ break;
+ case TEX_UPPER_BAKED:
+ llinfos << "New baked texture: UPPER" << llendl;
+ break;
+ case TEX_LOWER_BAKED:
+ llinfos << "New baked texture: LOWER" << llendl;
+ break;
+ case TEX_EYES_BAKED:
+ llinfos << "New baked texture: EYES" << llendl;
+ break;
+ case TEX_SKIRT_BAKED:
+ llinfos << "New baked texture: SKIRT" << llendl;
+ break;
+ default:
+ llwarns << "New baked texture: unknown te " << te << llendl;
+ break;
+ }
+
+ // dumpAvatarTEs( "setNewBakedTexture() send" );
+ // RN: throttle uploads
+ if (!hasPendingBakedUploads())
+ {
+ gAgent.sendAgentSetAppearance();
+ }
+}
+
+bool LLVOAvatar::hasPendingBakedUploads()
+{
+ bool head_pending = (mHeadLayerSet && mHeadLayerSet->getComposite()->uploadPending());
+ bool upper_pending = (mUpperBodyLayerSet && mUpperBodyLayerSet->getComposite()->uploadPending());
+ bool lower_pending = (mLowerBodyLayerSet && mLowerBodyLayerSet->getComposite()->uploadPending());
+ bool eyes_pending = (mEyesLayerSet && mEyesLayerSet->getComposite()->uploadPending());
+ bool skirt_pending = (mSkirtLayerSet && mSkirtLayerSet->getComposite()->uploadPending());
+
+ //llinfos << "TAT: LLVOAvatar::hasPendingBakedUploads()"
+ // << " head_pending " << head_pending
+ // << " upper_pending " << upper_pending
+ // << " lower_pending " << lower_pending
+ // << " eyes_pending " << eyes_pending
+ // << " skirt_pending " << skirt_pending
+ // << llendl;
+
+ if (head_pending || upper_pending || lower_pending || eyes_pending || skirt_pending)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setCachedBakedTexture()
+// A baked texture id was received from a cache query, make it active
+//-----------------------------------------------------------------------------
+void LLVOAvatar::setCachedBakedTexture( ETextureIndex te, const LLUUID& uuid )
+{
+ setTETexture( te, uuid );
+
+ switch(te)
+ {
+ case TEX_HEAD_BAKED:
+ if( mHeadLayerSet )
+ {
+ mHeadLayerSet->cancelUpload();
+ }
+ break;
+ case TEX_UPPER_BAKED:
+ if( mUpperBodyLayerSet )
+ {
+ mUpperBodyLayerSet->cancelUpload();
+ }
+ break;
+ case TEX_LOWER_BAKED:
+ if( mLowerBodyLayerSet )
+ {
+ mLowerBodyLayerSet->cancelUpload();
+ }
+ break;
+ case TEX_EYES_BAKED:
+ if( mEyesLayerSet )
+ {
+ mEyesLayerSet->cancelUpload();
+ }
+ break;
+ case TEX_SKIRT_BAKED:
+ if( mSkirtLayerSet )
+ {
+ mSkirtLayerSet->cancelUpload();
+ }
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// static
+// onCustomizeStart()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::onCustomizeStart()
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( avatar )
+ {
+ for( S32 i = 0; i < BAKED_TEXTURE_COUNT; i++ )
+ {
+ S32 tex_index = sBakedTextureIndices[i];
+ avatar->mSavedTE[ tex_index ] = avatar->getTEImage(tex_index)->getID();
+ avatar->setTEImage( tex_index, gImageList.getImage(IMG_DEFAULT_AVATAR) );
+ }
+
+ avatar->updateMeshTextures();
+
+// avatar->dumpAvatarTEs( "onCustomizeStart() send" );
+ gAgent.sendAgentSetAppearance();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// static
+// onCustomizeEnd()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::onCustomizeEnd()
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( !avatar ) return;
+
+ LLHost target_host = avatar->getObjectHost();
+ for( S32 i = 0; i < BAKED_TEXTURE_COUNT; i++ )
+ {
+ S32 tex_index = sBakedTextureIndices[i];
+ const LLUUID& saved = avatar->mSavedTE[ tex_index ];
+ if( !saved.isNull() )
+ {
+ avatar->setTEImage( tex_index, gImageList.getImageFromHost( saved, target_host ) );
+ }
+ }
+
+ avatar->updateMeshTextures();
+
+ if( !gQuit )
+ {
+ avatar->requestLayerSetUploads();
+ }
+
+ gAgent.sendAgentSetAppearance();
+ }
+
+BOOL LLVOAvatar::teToColorParams( ETextureIndex te, const char* param_name[3] )
+{
+ switch( te )
+ {
+ case TEX_UPPER_SHIRT:
+ param_name[0] = "shirt_red";
+ param_name[1] = "shirt_green";
+ param_name[2] = "shirt_blue";
+ break;
+
+ case TEX_LOWER_PANTS:
+ param_name[0] = "pants_red";
+ param_name[1] = "pants_green";
+ param_name[2] = "pants_blue";
+ break;
+
+ case TEX_LOWER_SHOES:
+ param_name[0] = "shoes_red";
+ param_name[1] = "shoes_green";
+ param_name[2] = "shoes_blue";
+ break;
+
+ case TEX_LOWER_SOCKS:
+ param_name[0] = "socks_red";
+ param_name[1] = "socks_green";
+ param_name[2] = "socks_blue";
+ break;
+
+ case TEX_UPPER_JACKET:
+ case TEX_LOWER_JACKET:
+ param_name[0] = "jacket_red";
+ param_name[1] = "jacket_green";
+ param_name[2] = "jacket_blue";
+ break;
+
+ case TEX_UPPER_GLOVES:
+ param_name[0] = "gloves_red";
+ param_name[1] = "gloves_green";
+ param_name[2] = "gloves_blue";
+ break;
+
+ case TEX_UPPER_UNDERSHIRT:
+ param_name[0] = "undershirt_red";
+ param_name[1] = "undershirt_green";
+ param_name[2] = "undershirt_blue";
+ break;
+
+ case TEX_LOWER_UNDERPANTS:
+ param_name[0] = "underpants_red";
+ param_name[1] = "underpants_green";
+ param_name[2] = "underpants_blue";
+ break;
+
+ case TEX_SKIRT:
+ param_name[0] = "skirt_red";
+ param_name[1] = "skirt_green";
+ param_name[2] = "skirt_blue";
+ break;
+
+ default:
+ llassert(0);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void LLVOAvatar::setClothesColor( ETextureIndex te, const LLColor4& new_color, BOOL set_by_user )
+{
+ const char* param_name[3];
+ if( teToColorParams( te, param_name ) )
+ {
+ setVisualParamWeight( param_name[0], new_color.mV[VX], set_by_user );
+ setVisualParamWeight( param_name[1], new_color.mV[VY], set_by_user );
+ setVisualParamWeight( param_name[2], new_color.mV[VZ], set_by_user );
+ }
+}
+
+LLColor4 LLVOAvatar::getClothesColor( ETextureIndex te )
+{
+ LLColor4 color;
+ const char* param_name[3];
+ if( teToColorParams( te, param_name ) )
+ {
+ color.mV[VX] = getVisualParamWeight( param_name[0] );
+ color.mV[VY] = getVisualParamWeight( param_name[1] );
+ color.mV[VZ] = getVisualParamWeight( param_name[2] );
+ }
+ return color;
+}
+
+
+
+
+void LLVOAvatar::dumpAvatarTEs( const char* context )
+{
+ llinfos << (mIsSelf ? "Self: " : "Other: ") << context << llendl;
+ for( S32 i=0; i<TEX_NUM_ENTRIES; i++ )
+ {
+ const char* te_name[] = {
+ "TEX_HEAD_BODYPAINT ",
+ "TEX_UPPER_SHIRT ",
+ "TEX_LOWER_PANTS ",
+ "TEX_EYES_IRIS ",
+ "TEX_HAIR ",
+ "TEX_UPPER_BODYPAINT ",
+ "TEX_LOWER_BODYPAINT ",
+ "TEX_LOWER_SHOES ",
+ "TEX_HEAD_BAKED ",
+ "TEX_UPPER_BAKED ",
+ "TEX_LOWER_BAKED ",
+ "TEX_EYES_BAKED ",
+ "TEX_LOWER_SOCKS ",
+ "TEX_UPPER_JACKET ",
+ "TEX_LOWER_JACKET ",
+ "TEX_UPPER_GLOVES ",
+ "TEX_UPPER_UNDERSHIRT ",
+ "TEX_LOWER_UNDERPANTS ",
+ "TEX_SKIRT ",
+ "TEX_SKIRT_BAKED "
+ };
+
+ LLViewerImage* te_image = getTEImage(i);
+ if( !te_image )
+ {
+ llinfos << " " << te_name[i] << ": null ptr" << llendl;
+ }
+ else
+ if( te_image->getID().isNull() )
+ {
+ llinfos << " " << te_name[i] << ": null UUID" << llendl;
+ }
+ else
+ if( te_image->getID() == IMG_DEFAULT )
+ {
+ llinfos << " " << te_name[i] << ": IMG_DEFAULT" << llendl;
+ }
+ else
+ if( te_image->getID() == IMG_DEFAULT_AVATAR )
+ {
+ llinfos << " " << te_name[i] << ": IMG_DEFAULT_AVATAR" << llendl;
+ }
+ else
+ {
+ llinfos << " " << te_name[i] << ": " << te_image->getID() << llendl;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// updateAttachmentVisibility()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::updateAttachmentVisibility(U32 camera_mode)
+{
+ for (LLViewerJointAttachment *attachmentPoint = mAttachmentPoints.getFirstData();
+ attachmentPoint;
+ attachmentPoint = mAttachmentPoints.getNextData())
+ {
+ if (attachmentPoint->getIsHUDAttachment())
+ {
+ attachmentPoint->setAttachmentVisibility(TRUE);
+ }
+ else
+ {
+ switch (camera_mode)
+ {
+ case CAMERA_MODE_MOUSELOOK:
+ if (LLVOAvatar::sVisibleInFirstPerson && attachmentPoint->getVisibleInFirstPerson())
+ {
+ attachmentPoint->setAttachmentVisibility(TRUE);
+ }
+ else
+ {
+ attachmentPoint->setAttachmentVisibility(FALSE);
+ }
+ break;
+ default:
+ attachmentPoint->setAttachmentVisibility(TRUE);
+ break;
+ }
+ }
+ }
+}
+
+// Given a texture entry, determine which wearable type owns it.
+// static
+LLUUID LLVOAvatar::getDefaultTEImageID( S32 te )
+{
+ switch( te )
+ {
+ case TEX_UPPER_SHIRT: return LLUUID( gSavedSettings.getString("UIImgDefaultShirtUUID") );
+ case TEX_LOWER_PANTS: return LLUUID( gSavedSettings.getString("UIImgDefaultPantsUUID") );
+ case TEX_EYES_IRIS: return LLUUID( gSavedSettings.getString("UIImgDefaultEyesUUID") );
+ case TEX_HAIR: return LLUUID( gSavedSettings.getString("UIImgDefaultHairUUID") );
+ case TEX_LOWER_SHOES: return LLUUID( gSavedSettings.getString("UIImgDefaultShoesUUID") );
+ case TEX_LOWER_SOCKS: return LLUUID( gSavedSettings.getString("UIImgDefaultSocksUUID") );
+ case TEX_UPPER_GLOVES: return LLUUID( gSavedSettings.getString("UIImgDefaultGlovesUUID") );
+
+ case TEX_UPPER_JACKET:
+ case TEX_LOWER_JACKET: return LLUUID( gSavedSettings.getString("UIImgDefaultJacketUUID") );
+
+ case TEX_UPPER_UNDERSHIRT:
+ case TEX_LOWER_UNDERPANTS: return LLUUID( gSavedSettings.getString("UIImgDefaultUnderwearUUID") );
+
+ case TEX_SKIRT: return LLUUID( gSavedSettings.getString("UIImgDefaultSkirtUUID") );
+
+ default: return IMG_DEFAULT_AVATAR;
+ }
+}
+
+
+
+// Given a texture entry, determine which wearable type owns it.
+// static
+EWearableType LLVOAvatar::getTEWearableType( S32 te )
+{
+ switch( te )
+ {
+ case TEX_UPPER_SHIRT:
+ return WT_SHIRT;
+
+ case TEX_LOWER_PANTS:
+ return WT_PANTS;
+
+ case TEX_EYES_IRIS:
+ return WT_EYES;
+
+ case TEX_HAIR:
+ return WT_HAIR;
+
+ case TEX_HEAD_BODYPAINT:
+ case TEX_UPPER_BODYPAINT:
+ case TEX_LOWER_BODYPAINT:
+ return WT_SKIN;
+
+ case TEX_LOWER_SHOES:
+ return WT_SHOES;
+
+ case TEX_LOWER_SOCKS:
+ return WT_SOCKS;
+
+ case TEX_UPPER_JACKET:
+ case TEX_LOWER_JACKET:
+ return WT_JACKET;
+
+ case TEX_UPPER_GLOVES:
+ return WT_GLOVES;
+
+ case TEX_UPPER_UNDERSHIRT:
+ return WT_UNDERSHIRT;
+
+ case TEX_LOWER_UNDERPANTS:
+ return WT_UNDERPANTS;
+
+ case TEX_SKIRT:
+ return WT_SKIRT;
+
+ default:
+ return WT_INVALID;
+ }
+}
+
+// Unlike most wearable functions, this works for both self and other.
+BOOL LLVOAvatar::isWearingWearableType( EWearableType type )
+{
+ if (mIsDummy) return TRUE;
+
+ ETextureIndex indicator_te;
+ switch( type )
+ {
+ case WT_SHIRT:
+ indicator_te = TEX_UPPER_SHIRT;
+ break;
+
+ case WT_PANTS:
+ indicator_te = TEX_LOWER_PANTS;
+ break;
+
+ case WT_SHOES:
+ indicator_te = TEX_LOWER_SHOES;
+ break;
+
+ case WT_SOCKS:
+ indicator_te = TEX_LOWER_SOCKS;
+ break;
+
+ case WT_JACKET:
+ indicator_te = TEX_UPPER_JACKET;
+ // Note: no need to test both upper and lower jacket
+ break;
+
+ case WT_GLOVES:
+ indicator_te = TEX_UPPER_GLOVES;
+ break;
+
+ case WT_UNDERSHIRT:
+ indicator_te = TEX_UPPER_UNDERSHIRT;
+ break;
+
+ case WT_UNDERPANTS:
+ indicator_te = TEX_LOWER_UNDERPANTS;
+ break;
+
+ case WT_SKIRT:
+ indicator_te = TEX_SKIRT;
+ break;
+
+ case WT_SHAPE:
+ case WT_SKIN:
+ case WT_HAIR:
+ case WT_EYES:
+ return TRUE; // everyone has all bodyparts
+
+ default:
+ return FALSE;
+ }
+
+ return ( getTEImage(indicator_te)->getID() != IMG_DEFAULT_AVATAR );
+}
+
+
+//-----------------------------------------------------------------------------
+// clampAttachmentPositions()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::clampAttachmentPositions()
+{
+ if (isDead()) return;
+ for(LLViewerJointAttachment *attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment)
+ {
+ attachment->clampObjectPosition();
+ }
+ }
+}
+
+BOOL LLVOAvatar::hasHUDAttachment()
+{
+ for(LLViewerJointAttachment *attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment->getIsHUDAttachment() && attachment->getObject(0))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+LLBBox LLVOAvatar::getHUDBBox()
+{
+ LLBBox bbox;
+ for(LLViewerJointAttachment *attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment->getIsHUDAttachment() && attachment->getObject(0))
+ {
+ LLViewerObject* hud_object = attachment->getObject(0);
+
+ // initialize bounding box to contain identity orientation and center point for attached object
+ bbox.addPointLocal(hud_object->getPosition());
+ // add rotated bounding box for attached object
+ bbox.addBBoxAgent(hud_object->getBoundingBoxAgent());
+ for (U32 i = 0; i < hud_object->mChildList.size(); i++)
+ {
+ bbox.addBBoxAgent(hud_object->mChildList[i]->getBoundingBoxAgent());
+ }
+ }
+ }
+
+ return bbox;
+}
+
+void LLVOAvatar::rebuildHUD()
+{
+ if (!mIsSelf)
+ {
+ return;
+ }
+
+ for(LLViewerJointAttachment *attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment->getIsHUDAttachment() && attachment->getObject(0))
+ {
+ LLViewerObject* hud_object = attachment->getObject(0);
+ LLDrawable* hud_drawable = hud_object->mDrawable;
+
+ if (hud_drawable)
+ {
+ // this assumes that an AGP sync will happen because face has been backlisted,
+ // so that pool has been rebuilt this frame and is scheduled for a sync
+ for(S32 face_index = 0; face_index < hud_drawable->getNumFaces(); ++face_index)
+ {
+ LLFace* facep = hud_drawable->getFace(face_index);
+ if (facep->isState(LLFace::BACKLIST))
+ {
+ facep->restore();
+ }
+ }
+ }
+ for (U32 child_num = 0; child_num < hud_object->mChildList.size(); ++child_num)
+ {
+ LLViewerObject* childp = hud_object->mChildList[child_num];
+ LLDrawable* child_drawable = childp->mDrawable;
+
+ if (child_drawable)
+ {
+ for(S32 face_index = 0; face_index < child_drawable->getNumFaces(); ++face_index)
+ {
+ LLFace* facep = child_drawable->getFace(face_index);
+ if (facep->isState(LLFace::BACKLIST))
+ {
+ facep->restore();
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// onFirstTEMessageReceived()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::onFirstTEMessageReceived()
+{
+ if( !mFirstTEMessageReceived )
+ {
+ mFirstTEMessageReceived = TRUE;
+
+ BOOL head_baked = ( getTEImage( TEX_HEAD_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL upper_baked = ( getTEImage( TEX_UPPER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL lower_baked = ( getTEImage( TEX_LOWER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL eyes_baked = ( getTEImage( TEX_EYES_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL skirt_baked = ( getTEImage( TEX_SKIRT_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+
+ // Use any baked textures that we have even if they haven't downloaded yet.
+ // (That is, don't do a transition from unbaked to baked.)
+ if( head_baked )
+ {
+ mLastHeadBakedID = getTEImage( TEX_HEAD_BAKED )->getID();
+ LLViewerImage* image = getTEImage( TEX_HEAD_BAKED );
+ image->setNeedsAux(TRUE);
+ image->setLoadedCallback( onBakedTextureMasksLoaded, MORPH_MASK_REQUESTED_DISCARD, TRUE, new LLTextureMaskData( mID ));
+ image->setLoadedCallback( onInitialBakedTextureLoaded, MAX_DISCARD_LEVEL, FALSE, new LLUUID( mID ) );
+ }
+
+ if( upper_baked )
+ {
+ mLastUpperBodyBakedID = getTEImage( TEX_UPPER_BAKED )->getID();
+ LLViewerImage* image = getTEImage( TEX_UPPER_BAKED );
+ image->setNeedsAux(TRUE);
+ image->setLoadedCallback( onBakedTextureMasksLoaded, MORPH_MASK_REQUESTED_DISCARD, TRUE, new LLTextureMaskData( mID ));
+ image->setLoadedCallback( onInitialBakedTextureLoaded, MAX_DISCARD_LEVEL, FALSE, new LLUUID( mID ) );
+ }
+
+ if( lower_baked )
+ {
+ mLastLowerBodyBakedID = getTEImage( TEX_LOWER_BAKED )->getID();
+ LLViewerImage* image = getTEImage( TEX_LOWER_BAKED );
+ image->setNeedsAux(TRUE);
+ image->setLoadedCallback( onBakedTextureMasksLoaded, MORPH_MASK_REQUESTED_DISCARD, TRUE, new LLTextureMaskData( mID ));
+ image->setLoadedCallback( onInitialBakedTextureLoaded, MAX_DISCARD_LEVEL, FALSE, new LLUUID( mID ) );
+ }
+
+ if( eyes_baked )
+ {
+ mLastEyesBakedID = getTEImage( TEX_EYES_BAKED )->getID();
+ LLViewerImage* image = getTEImage( TEX_EYES_BAKED );
+ image->setLoadedCallback( onInitialBakedTextureLoaded, MAX_DISCARD_LEVEL, FALSE, new LLUUID( mID ) );
+ }
+
+ if( skirt_baked )
+ {
+ mLastSkirtBakedID = getTEImage( TEX_SKIRT_BAKED )->getID();
+ LLViewerImage* image = getTEImage( TEX_SKIRT_BAKED );
+ image->setLoadedCallback( onInitialBakedTextureLoaded, MAX_DISCARD_LEVEL, FALSE, new LLUUID( mID ) );
+ }
+
+ updateMeshTextures();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// processAvatarAppearance()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys )
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+// llinfos << "processAvatarAppearance start " << mID << llendl;
+ BOOL is_first_appearance_message = !mFirstAppearanceMessageReceived;
+
+ mFirstAppearanceMessageReceived = TRUE;
+
+ if( mIsSelf )
+ {
+ llwarns << "Received AvatarAppearance for self" << llendl;
+ if( mFirstTEMessageReceived )
+ {
+// llinfos << "processAvatarAppearance end " << mID << llendl;
+ return;
+ }
+ }
+
+ if (gNoRender)
+ {
+ return;
+ }
+
+ ESex old_sex = getSex();
+
+// llinfos << "ady LLVOAvatar::processAvatarAppearance()" << llendl;
+// dumpAvatarTEs( "PRE processAvatarAppearance()" );
+ unpackTEMessage(mesgsys, _PREHASH_ObjectData);
+// dumpAvatarTEs( "POST processAvatarAppearance()" );
+
+// llinfos << "Received AvatarAppearance: " << (mIsSelf ? "(self): " : "(other): " ) <<
+// (( getTEImage( TEX_HEAD_BAKED )->getID() != IMG_DEFAULT_AVATAR ) ? "HEAD " : "head " ) <<
+// (( getTEImage( TEX_UPPER_BAKED )->getID() != IMG_DEFAULT_AVATAR ) ? "UPPER " : "upper " ) <<
+// (( getTEImage( TEX_LOWER_BAKED )->getID() != IMG_DEFAULT_AVATAR ) ? "LOWER " : "lower " ) <<
+// (( getTEImage( TEX_EYES_BAKED )->getID() != IMG_DEFAULT_AVATAR ) ? "EYES" : "eyes" ) << llendl;
+
+ if( !mFirstTEMessageReceived )
+ {
+ onFirstTEMessageReceived();
+ }
+
+ setCompositeUpdatesEnabled( FALSE );
+ updateMeshTextures(); // enables updates for laysets without baked textures.
+
+ // parse visual params
+ S32 num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_VisualParam);
+ if( num_blocks > 1 )
+ {
+ BOOL params_changed = FALSE;
+ BOOL interp_params = FALSE;
+
+ LLVisualParam* param = getFirstVisualParam();
+ if (!param)
+ {
+ llwarns << "No visual params!" << llendl;
+ }
+ else
+ {
+ for( S32 i = 0; i < num_blocks; i++ )
+ {
+ while( param && (param->getGroup() != VISUAL_PARAM_GROUP_TWEAKABLE) )
+ {
+ param = getNextVisualParam();
+ }
+
+ if( !param )
+ {
+ llwarns << "Number of params in AvatarAppearance msg does not match number of params in avatar xml file." << llendl;
+ return;
+ }
+
+ U8 value;
+ mesgsys->getU8Fast(_PREHASH_VisualParam, _PREHASH_ParamValue, value, i);
+ F32 newWeight = U8_to_F32(value, param->getMinWeight(), param->getMaxWeight());
+
+ if (is_first_appearance_message || (param->getWeight() != newWeight))
+ {
+ //llinfos << "Received update for param " << param->getDisplayName() << " at value " << newWeight << llendl;
+ params_changed = TRUE;
+ if(is_first_appearance_message)
+ {
+ param->setWeight(newWeight, FALSE);
+ }
+ else
+ {
+ interp_params = TRUE;
+ param->setAnimationTarget(newWeight, FALSE);
+ }
+ }
+
+ param = getNextVisualParam();
+ }
+ }
+
+ while( param && (param->getGroup() != VISUAL_PARAM_GROUP_TWEAKABLE) )
+ {
+ param = getNextVisualParam();
+ }
+ if( param )
+ {
+ llwarns << "Number of params in AvatarAppearance msg does not match number of params in avatar xml file." << llendl;
+ return;
+ }
+
+ if (params_changed)
+ {
+ if (interp_params)
+ {
+ startAppearanceAnimation(FALSE, FALSE);
+ }
+ updateVisualParams();
+
+ ESex new_sex = getSex();
+ if( old_sex != new_sex )
+ {
+ updateSexDependentLayerSets( FALSE );
+ }
+ }
+ }
+ else
+ {
+ llwarns << "AvatarAppearance msg received without any parameters" << llendl;
+ }
+
+ setCompositeUpdatesEnabled( TRUE );
+
+ llassert( getSex() == ((getVisualParamWeight( "male" ) > 0.5f) ? SEX_MALE : SEX_FEMALE) );
+
+ // If all of the avatars are completely baked, release the global image caches to conserve memory.
+ LLVOAvatar::cullAvatarsByPixelArea();
+
+// llinfos << "processAvatarAppearance end " << mID << llendl;
+}
+
+// static
+void LLVOAvatar::getAnimLabels( LLDynamicArray<const char*>* labels )
+{
+ S32 i;
+ for( i = 0; i < gUserAnimStatesCount; i++ )
+ {
+ labels->put( gUserAnimStates[i].mLabel );
+ }
+
+ // Special case to trigger away (AFK) state
+ labels->put( "Away From Keyboard" );
+}
+
+// static
+void LLVOAvatar::getAnimNames( LLDynamicArray<const char*>* names )
+{
+ S32 i;
+
+ for( i = 0; i < gUserAnimStatesCount; i++ )
+ {
+ names->put( gUserAnimStates[i].mName );
+ }
+
+ // Special case to trigger away (AFK) state
+ names->put( "enter_away_from_keyboard_state" );
+}
+
+void LLVOAvatar::onBakedTextureMasksLoaded( BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata )
+{
+ //llinfos << "onBakedTextureMasksLoaded: " << src_vi->getID() << llendl;
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ LLUUID id = src_vi->getID();
+
+ if (!userdata)
+ {
+ return;
+ }
+
+ LLTextureMaskData* maskData = (LLTextureMaskData*) userdata;
+ LLVOAvatar* self = (LLVOAvatar*) gObjectList.findObject( maskData->mAvatarID );
+
+ // if discard level is 2 less than last discard level we processed, or we hit 0,
+ // then generate morph masks
+ if( self && success && (discard_level < maskData->mLastDiscardLevel - 2 || discard_level == 0) )
+ {
+ LLViewerImage* head_baked = self->getTEImage( TEX_HEAD_BAKED );
+ LLViewerImage* upper_baked = self->getTEImage( TEX_UPPER_BAKED );
+ LLViewerImage* lower_baked = self->getTEImage( TEX_LOWER_BAKED );
+
+ if( aux_src && aux_src->getComponents() == 1 )
+ {
+ if (!aux_src->getData())
+ {
+ llwarns << "No auxiliary source data for onBakedTextureMasksLoaded" << llendl;
+ src_vi->startImageDecode();
+ return;
+ }
+
+ U32 gl_name;
+ glGenTextures(1, (GLuint*) &gl_name );
+ stop_glerror();
+
+ LLImageGL::bindExternalTexture( gl_name, 0, GL_TEXTURE_2D );
+ stop_glerror();
+
+ glTexImage2D(
+ GL_TEXTURE_2D, 0, GL_ALPHA8,
+ aux_src->getWidth(), aux_src->getHeight(),
+ 0, GL_ALPHA, GL_UNSIGNED_BYTE, aux_src->getData());
+ stop_glerror();
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ if( id == head_baked->getID() )
+ {
+ if (self->mHeadLayerSet)
+ {
+ //llinfos << "onBakedTextureMasksLoaded for head " << id << " discard = " << discard_level << llendl;
+ self->mHeadLayerSet->applyMorphMask(aux_src->getData(), aux_src->getWidth(), aux_src->getHeight(), 1);
+ maskData->mLastDiscardLevel = discard_level;
+ self->mHeadMaskDiscard = discard_level;
+ if (self->mHeadMaskTexName)
+ {
+ glDeleteTextures(1, (GLuint*) &self->mHeadMaskTexName);
+ }
+ self->mHeadMaskTexName = gl_name;
+ }
+ else
+ {
+ llwarns << "onBakedTextureMasksLoaded: no mHeadLayerSet." << llendl;
+ }
+ }
+ else
+ if( id == upper_baked->getID() )
+ {
+ if ( self->mUpperBodyLayerSet)
+ {
+ //llinfos << "onBakedTextureMasksLoaded for upper body " << id << " discard = " << discard_level << llendl;
+ self->mUpperBodyLayerSet->applyMorphMask(aux_src->getData(), aux_src->getWidth(), aux_src->getHeight(), 1);
+ maskData->mLastDiscardLevel = discard_level;
+ self->mUpperMaskDiscard = discard_level;
+ if (self->mUpperMaskTexName)
+ {
+ glDeleteTextures(1, (GLuint*) &self->mUpperMaskTexName);
+ }
+ self->mUpperMaskTexName = gl_name;
+ }
+ else
+ {
+ llwarns << "onBakedTextureMasksLoaded: no mHeadLayerSet." << llendl;
+ }
+ }
+ else
+ if( id == lower_baked->getID() )
+ {
+ if ( self->mLowerBodyLayerSet )
+ {
+ //llinfos << "onBakedTextureMasksLoaded for lower body " << id << " discard = " << discard_level << llendl;
+ self->mLowerBodyLayerSet->applyMorphMask(aux_src->getData(), aux_src->getWidth(), aux_src->getHeight(), 1);
+ maskData->mLastDiscardLevel = discard_level;
+ self->mLowerMaskDiscard = discard_level;
+ if (self->mLowerMaskTexName)
+ {
+ glDeleteTextures(1, (GLuint*) &self->mLowerMaskTexName);
+ }
+ self->mLowerMaskTexName = gl_name;
+ }
+ else
+ {
+ llwarns << "onBakedTextureMasksLoaded: no mHeadLayerSet." << llendl;
+ }
+ }
+ else
+ {
+ llinfos << "onBakedTextureMasksLoaded(): unexpected image id: " << id << llendl;
+ }
+
+ self->dirtyMesh();
+ }
+ else
+ {
+ // this can happen when someone uses an old baked texture possibly provided by
+ // viewer-side baked texture caching
+ llwarns << "Masks loaded callback but NO aux source!" << llendl;
+ }
+ }
+
+ if (final || !success)
+ {
+ delete maskData;
+ }
+
+}
+
+// static
+void LLVOAvatar::onInitialBakedTextureLoaded( BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata )
+{
+ LLUUID *avatar_idp = (LLUUID *)userdata;
+ LLVOAvatar *selfp = (LLVOAvatar *)gObjectList.findObject(*avatar_idp);
+
+ if (!success && selfp)
+ {
+ selfp->removeMissingBakedTextures();
+ }
+ if (final || !success )
+ {
+ delete avatar_idp;
+ }
+}
+
+void LLVOAvatar::onBakedTextureLoaded(BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata)
+{
+ //llinfos << "onBakedTextureLoaded: " << src_vi->getID() << llendl;
+
+ LLUUID id = src_vi->getID();
+ LLUUID *avatar_idp = (LLUUID *)userdata;
+ LLVOAvatar *selfp = (LLVOAvatar *)gObjectList.findObject(*avatar_idp);
+
+ if (selfp && !success)
+ {
+ selfp->removeMissingBakedTextures();
+ }
+
+ if( final || !success )
+ {
+ delete avatar_idp;
+ }
+
+ if( selfp && success && final )
+ {
+ selfp->useBakedTexture( id );
+ }
+}
+
+
+// Called when baked texture is loaded and also when we start up with a baked texture
+void LLVOAvatar::useBakedTexture( const LLUUID& id )
+{
+// llinfos << "useBakedTexture" << llendl;
+ LLViewerImage* head_baked = getTEImage( TEX_HEAD_BAKED );
+ LLViewerImage* upper_baked = getTEImage( TEX_UPPER_BAKED );
+ LLViewerImage* lower_baked = getTEImage( TEX_LOWER_BAKED );
+ LLViewerImage* eyes_baked = getTEImage( TEX_EYES_BAKED );
+ LLViewerImage* skirt_baked = getTEImage( TEX_SKIRT_BAKED );
+
+ if( id == head_baked->getID() )
+ {
+ mHeadBakedLoaded = TRUE;
+
+ mLastHeadBakedID = id;
+ mHeadMesh0.setTexture( head_baked );
+ mHeadMesh1.setTexture( head_baked );
+ mHeadMesh2.setTexture( head_baked );
+ mHeadMesh3.setTexture( head_baked );
+ mHeadMesh4.setTexture( head_baked );
+ mEyeLashMesh0.setTexture( head_baked );
+ if( mHeadLayerSet )
+ {
+ mHeadLayerSet->destroyComposite();
+ }
+ setLocalTexture( LOCTEX_HEAD_BODYPAINT, getTEImage( TEX_HEAD_BODYPAINT ), TRUE );
+ }
+ else
+ if( id == upper_baked->getID() )
+ {
+ mUpperBakedLoaded = TRUE;
+
+ mLastUpperBodyBakedID = id;
+ mUpperBodyMesh0.setTexture( upper_baked );
+ mUpperBodyMesh1.setTexture( upper_baked );
+ mUpperBodyMesh2.setTexture( upper_baked );
+ mUpperBodyMesh3.setTexture( upper_baked );
+ mUpperBodyMesh4.setTexture( upper_baked );
+ if( mUpperBodyLayerSet )
+ {
+ mUpperBodyLayerSet->destroyComposite();
+ }
+
+ setLocalTexture( LOCTEX_UPPER_SHIRT, getTEImage( TEX_UPPER_SHIRT ), TRUE );
+ setLocalTexture( LOCTEX_UPPER_BODYPAINT, getTEImage( TEX_UPPER_BODYPAINT ), TRUE );
+ setLocalTexture( LOCTEX_UPPER_JACKET, getTEImage( TEX_UPPER_JACKET ), TRUE );
+ setLocalTexture( LOCTEX_UPPER_GLOVES, getTEImage( TEX_UPPER_GLOVES ), TRUE );
+ setLocalTexture( LOCTEX_UPPER_UNDERSHIRT, getTEImage( TEX_UPPER_UNDERSHIRT ), TRUE );
+ }
+ else
+ if( id == lower_baked->getID() )
+ {
+ mLowerBakedLoaded = TRUE;
+
+ mLastLowerBodyBakedID = id;
+ mLowerBodyMesh0.setTexture( lower_baked );
+ mLowerBodyMesh1.setTexture( lower_baked );
+ mLowerBodyMesh2.setTexture( lower_baked );
+ mLowerBodyMesh3.setTexture( lower_baked );
+ mLowerBodyMesh4.setTexture( lower_baked );
+ if( mLowerBodyLayerSet )
+ {
+ mLowerBodyLayerSet->destroyComposite();
+ }
+
+ setLocalTexture( LOCTEX_LOWER_PANTS, getTEImage( TEX_LOWER_PANTS ), TRUE );
+ setLocalTexture( LOCTEX_LOWER_BODYPAINT, getTEImage( TEX_LOWER_BODYPAINT ), TRUE );
+ setLocalTexture( LOCTEX_LOWER_SHOES, getTEImage( TEX_LOWER_SHOES ), TRUE );
+ setLocalTexture( LOCTEX_LOWER_SOCKS, getTEImage( TEX_LOWER_SOCKS ), TRUE );
+ setLocalTexture( LOCTEX_LOWER_JACKET, getTEImage( TEX_LOWER_JACKET ), TRUE );
+ setLocalTexture( LOCTEX_LOWER_UNDERPANTS, getTEImage( TEX_LOWER_UNDERPANTS ), TRUE );
+ }
+ else
+ if( id == eyes_baked->getID() )
+ {
+ mEyesBakedLoaded = TRUE;
+
+ mLastEyesBakedID = id;
+ mEyeBallLeftMesh0.setTexture( eyes_baked );
+ mEyeBallLeftMesh1.setTexture( eyes_baked );
+ mEyeBallRightMesh0.setTexture( eyes_baked );
+ mEyeBallRightMesh1.setTexture( eyes_baked );
+ if( mEyesLayerSet )
+ {
+ mEyesLayerSet->destroyComposite();
+ }
+
+ setLocalTexture( LOCTEX_EYES_IRIS, getTEImage( TEX_EYES_IRIS ), TRUE );
+ }
+ else
+ if( id == skirt_baked->getID() )
+ {
+ mSkirtBakedLoaded = TRUE;
+
+ mLastSkirtBakedID = id;
+ mSkirtMesh0.setTexture( skirt_baked );
+ mSkirtMesh1.setTexture( skirt_baked );
+ mSkirtMesh2.setTexture( skirt_baked );
+ mSkirtMesh3.setTexture( skirt_baked );
+ mSkirtMesh4.setTexture( skirt_baked );
+ if( mSkirtLayerSet )
+ {
+ mSkirtLayerSet->destroyComposite();
+ }
+
+ setLocalTexture( LOCTEX_SKIRT, getTEImage( TEX_SKIRT ), TRUE );
+ }
+
+ dirtyMesh();
+}
+
+// static
+void LLVOAvatar::dumpArchetypeXML( void* )
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ apr_file_t* file = ll_apr_file_open(gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,"new archetype.xml"), LL_APR_WB );
+ if( !file )
+ {
+ return;
+ }
+
+ apr_file_printf( file, "<?xml version=\"1.0\" encoding=\"US-ASCII\" standalone=\"yes\"?>\n" );
+ apr_file_printf( file, "<linden_genepool version=\"1.0\">\n" );
+ apr_file_printf( file, "\n\t<archetype name=\"???\">\n" );
+
+ // only body parts, not clothing.
+ for( S32 type = WT_SHAPE; type <= WT_EYES; type++ )
+ {
+ const char* wearable_name = LLWearable::typeToTypeName( (EWearableType) type );
+ apr_file_printf( file, "\n\t\t<!-- wearable: %s -->\n", wearable_name );
+
+ for( LLVisualParam* param = avatar->getFirstVisualParam(); param; param = avatar->getNextVisualParam() )
+ {
+ LLViewerVisualParam* viewer_param = (LLViewerVisualParam*)param;
+ if( (viewer_param->getWearableType() == type) &&
+ (viewer_param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE) )
+ {
+ apr_file_printf( file, "\t\t<param id=\"%d\" name=\"%s\" value=\"%.3f\"/>\n",
+ viewer_param->getID(), viewer_param->getName().c_str(), viewer_param->getWeight() );
+ }
+ }
+
+ for( S32 te = 0; te < TEX_NUM_ENTRIES; te++ )
+ {
+ if( LLVOAvatar::getTEWearableType( te ) == type )
+ {
+ LLViewerImage* te_image = avatar->getTEImage( te );
+ if( te_image )
+ {
+ char uuid_str[UUID_STR_LENGTH];
+ te_image->getID().toString( uuid_str );
+ apr_file_printf( file, "\t\t<texture te=\"%i\" uuid=\"%s\"/>\n", te, uuid_str);
+ }
+ }
+ }
+ }
+ apr_file_printf( file, "\t</archetype>\n" );
+ apr_file_printf( file, "\n</linden_genepool>\n" );
+ apr_file_close( file );
+}
+
+
+// Assumes LLVOAvatar::sInstances has already been sorted.
+S32 LLVOAvatar::getUnbakedPixelAreaRank()
+{
+ S32 rank = 1;
+ for( LLVOAvatar* inst = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
+ inst;
+ inst = (LLVOAvatar*)LLCharacter::sInstances.getNextData() )
+ {
+ if( inst == this )
+ {
+ return rank;
+ }
+ else
+ if( !inst->isDead() && !inst->isFullyBaked() )
+ {
+ rank++;
+ }
+ }
+
+ llassert(0);
+ return 0;
+}
+
+// static
+void LLVOAvatar::cullAvatarsByPixelArea()
+{
+ LLVOAvatar::sInstances.bubbleSortList();
+
+
+ // Update the avatars that have changed status
+ S32 rank = 1;
+
+ for( LLVOAvatar* inst = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
+ inst;
+ inst = (LLVOAvatar*)LLCharacter::sInstances.getNextData() )
+ {
+ BOOL culled;
+ if( inst->isDead() )
+ {
+ culled = TRUE;
+ }
+ else if( inst->isSelf() || inst->isFullyBaked() )
+ {
+ culled = FALSE;
+ }
+ else
+ {
+ culled = (rank > LLVOAvatar::sMaxOtherAvatarsToComposite) || (inst->mPixelArea < MIN_PIXEL_AREA_FOR_COMPOSITE);
+ rank++;
+ }
+
+ if( inst->mCulled != culled )
+ {
+ inst->mCulled = culled;
+
+ lldebugs << "avatar " << inst->getID() << (culled ? " start culled" : " start not culled" ) << llendl;
+
+ inst->updateMeshTextures();
+ }
+ }
+
+ if( LLVOAvatar::areAllNearbyInstancesBaked() )
+ {
+ LLVOAvatar::deleteCachedImages();
+ }
+}
+
+const LLUUID& LLVOAvatar::grabLocalTexture(ETextureIndex index)
+{
+ if (canGrabLocalTexture(index))
+ {
+ return getTEImage( index )->getID();
+ }
+ return LLUUID::null;
+}
+
+BOOL LLVOAvatar::canGrabLocalTexture(ETextureIndex index)
+{
+ // Check if the texture hasn't been baked yet.
+ if ( getTEImage( index )->getID() == IMG_DEFAULT_AVATAR )
+ {
+ lldebugs << "getTEImage( " << (U32) index << " )->getID() == IMG_DEFAULT_AVATAR" << llendl;
+ return FALSE;
+ }
+
+ // Check permissions of textures that show up in the
+ // baked texture. We don't want people copying people's
+ // work via baked textures.
+ std::vector<ETextureIndex> textures;
+ switch (index)
+ {
+ case TEX_EYES_BAKED:
+ textures.push_back(TEX_EYES_IRIS);
+ break;
+ case TEX_HEAD_BAKED:
+ textures.push_back(TEX_HEAD_BODYPAINT);
+ break;
+ case TEX_UPPER_BAKED:
+ textures.push_back(TEX_UPPER_BODYPAINT);
+ textures.push_back(TEX_UPPER_UNDERSHIRT);
+ textures.push_back(TEX_UPPER_SHIRT);
+ textures.push_back(TEX_UPPER_JACKET);
+ textures.push_back(TEX_UPPER_GLOVES);
+ break;
+ case TEX_LOWER_BAKED:
+ textures.push_back(TEX_LOWER_BODYPAINT);
+ textures.push_back(TEX_LOWER_UNDERPANTS);
+ textures.push_back(TEX_LOWER_PANTS);
+ textures.push_back(TEX_LOWER_JACKET);
+ textures.push_back(TEX_LOWER_SOCKS);
+ textures.push_back(TEX_LOWER_SHOES);
+ break;
+ case TEX_SKIRT_BAKED:
+ textures.push_back(TEX_SKIRT);
+ break;
+ default:
+ return FALSE;
+ break;
+ }
+
+ std::vector<ETextureIndex>::iterator iter = textures.begin();
+ std::vector<ETextureIndex>::iterator end = textures.end();
+ for (; iter != end; ++iter)
+ {
+ ETextureIndex t_index = (*iter);
+ lldebugs << "Checking index " << (U32) t_index << llendl;
+ const LLUUID& texture_id = getTEImage( t_index )->getID();
+ if (texture_id != IMG_DEFAULT_AVATAR)
+ {
+ // Search inventory for this texture.
+ LLViewerInventoryCategory::cat_array_t cats;
+ LLViewerInventoryItem::item_array_t items;
+ LLAssetIDMatches asset_id_matches(texture_id);
+ gInventory.collectDescendentsIf(LLUUID::null,
+ cats,
+ items,
+ LLInventoryModel::INCLUDE_TRASH,
+ asset_id_matches);
+
+ BOOL can_grab = FALSE;
+ lldebugs << "item count for asset " << texture_id << ": " << items.count() << llendl;
+ if (items.count())
+ {
+ // search for full permissions version
+ for (S32 i = 0; i < items.count(); i++)
+ {
+ LLInventoryItem* itemp = items[i];
+ LLPermissions item_permissions = itemp->getPermissions();
+ if ( item_permissions.allowOperationBy(
+ PERM_MODIFY, gAgent.getID(), gAgent.getGroupID()) &&
+ item_permissions.allowOperationBy(
+ PERM_COPY, gAgent.getID(), gAgent.getGroupID()) &&
+ item_permissions.allowOperationBy(
+ PERM_TRANSFER, gAgent.getID(), gAgent.getGroupID()) )
+ {
+ can_grab = TRUE;
+ break;
+ }
+ }
+ }
+ if (!can_grab) return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+void LLVOAvatar::dumpLocalTextures()
+{
+ llinfos << "Local Textures:" << llendl;
+
+ char* names[] = {
+ "Shirt ",
+ "UpperTatoo",
+ "Pants ",
+ "LowerTatoo",
+ "Head Tatoo",
+ "Shoes ",
+ "Socks ",
+ "Upper Jckt",
+ "Lower Jckt",
+ "Gloves ",
+ "Undershirt",
+ "Underpants",
+ "Iris ",
+ "Skirt "};
+
+ ETextureIndex baked_equiv[] = {
+ TEX_UPPER_BAKED,
+ TEX_UPPER_BAKED,
+ TEX_LOWER_BAKED,
+ TEX_LOWER_BAKED,
+ TEX_HEAD_BAKED,
+ TEX_LOWER_BAKED,
+ TEX_LOWER_BAKED,
+ TEX_UPPER_BAKED,
+ TEX_LOWER_BAKED,
+ TEX_UPPER_BAKED,
+ TEX_UPPER_BAKED,
+ TEX_LOWER_BAKED,
+ TEX_EYES_BAKED,
+ TEX_SKIRT_BAKED };
+
+
+ for( S32 i = 0; i < LOCTEX_NUM_ENTRIES; i++ )
+ {
+ if( getTEImage( baked_equiv[i] )->getID() != IMG_DEFAULT_AVATAR )
+ {
+#if LL_RELEASE_FOR_DOWNLOAD
+ // End users don't get to trivially see avatar texture IDs, makes textures
+ // easier to steal. JC
+ llinfos << "LocTex " << names[i] << ": Baked " << llendl;
+#else
+ llinfos << "LocTex " << names[i] << ": Baked " << getTEImage( baked_equiv[i] )->getID() << llendl;
+#endif
+ }
+ else if (mLocalTexture[i].notNull())
+ {
+ if( mLocalTexture[i]->getID() == IMG_DEFAULT_AVATAR )
+ {
+ llinfos << "LocTex " << names[i] << ": None" << llendl;
+ }
+ else
+ {
+ LLViewerImage* image = mLocalTexture[i];
+ F32 data_progress = 0.0f;
+ F32 decode_progress = image->getDecodeProgress(&data_progress);
+
+ llinfos << "LocTex " << names[i] << ": "
+ << "Discard " << image->getDiscardLevel() << ", "
+ << "(" << image->getWidth() << ", " << image->getHeight() << ") "
+#if !LL_RELEASE_FOR_DOWNLOAD
+ // End users don't get to trivially see avatar texture IDs,
+ // makes textures easier to steal
+ << image->getID() << " "
+#endif
+ << "Data: " << (data_progress * 100) << "% "
+ << "Decode: " << (decode_progress * 100) << "% "
+ << "Priority: " << image->getDecodePriority() << " "
+ << (image->needsDecode() ? "pending decode" : "not pending decode")
+ << llendl;
+ }
+ }
+ else
+ {
+ llinfos << "LocTex " << names[i] << ": No LLViewerImage" << llendl;
+ }
+ }
+}
+
+void LLVOAvatar::startAppearanceAnimation(BOOL set_by_user, BOOL play_sound)
+{
+ if(!mAppearanceAnimating)
+ {
+ mAppearanceAnimSetByUser = set_by_user;
+ mAppearanceAnimating = TRUE;
+ mAppearanceMorphTimer.reset();
+ mLastAppearanceBlendTime = 0.f;
+ }
+}
+
+
+void LLVOAvatar::removeMissingBakedTextures()
+{
+ if (!mIsSelf)
+ {
+ return;
+ }
+ BOOL removed = FALSE;
+
+ for( S32 i = 0; i < BAKED_TEXTURE_COUNT; i++ )
+ {
+ S32 te = sBakedTextureIndices[i];
+
+ if( getTEImage( te )->isMissingAsset() )
+ {
+ setTEImage( te, gImageList.getImage(IMG_DEFAULT_AVATAR) );
+ removed = TRUE;
+ }
+ }
+
+ if( removed )
+ {
+ invalidateComposite( mEyesLayerSet, FALSE );
+ invalidateComposite( mHeadLayerSet, FALSE );
+ invalidateComposite( mUpperBodyLayerSet, FALSE );
+ invalidateComposite( mLowerBodyLayerSet, FALSE );
+ invalidateComposite( mSkirtLayerSet, FALSE );
+ updateMeshTextures();
+ requestLayerSetUploads();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// LLVOAvatarInfo
+//-----------------------------------------------------------------------------
+
+LLVOAvatarInfo::LLVOAvatarInfo()
+ : mTexSkinColorInfo(0), mTexHairColorInfo(0), mTexEyeColorInfo(0)
+{
+}
+
+LLVOAvatarInfo::~LLVOAvatarInfo()
+{
+ std::for_each(mMeshInfoList.begin(), mMeshInfoList.end(), DeletePointer());
+ std::for_each(mSkeletalDistortionInfoList.begin(), mSkeletalDistortionInfoList.end(), DeletePointer());
+ std::for_each(mAttachmentInfoList.begin(), mAttachmentInfoList.end(), DeletePointer());
+ delete mTexSkinColorInfo;
+ delete mTexHairColorInfo;
+ delete mTexEyeColorInfo;
+ std::for_each(mLayerInfoList.begin(), mLayerInfoList.end(), DeletePointer());
+ std::for_each(mDriverInfoList.begin(), mDriverInfoList.end(), DeletePointer());
+}
+
+//-----------------------------------------------------------------------------
+// LLVOAvatarBoneInfo::parseXml()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatarBoneInfo::parseXml(LLXmlTreeNode* node)
+{
+ if (node->hasName("bone"))
+ {
+ mIsJoint = TRUE;
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if (!node->getFastAttributeString(name_string, mName))
+ {
+ llwarns << "Bone without name" << llendl;
+ return FALSE;
+ }
+ }
+ else if (node->hasName("collision_volume"))
+ {
+ mIsJoint = FALSE;
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if (!node->getFastAttributeString(name_string, mName))
+ {
+ mName = "Collision Volume";
+ }
+ }
+ else
+ {
+ llerrs << "Invalid node " << node->getName() << llendl;
+ return FALSE;
+ }
+
+ static LLStdStringHandle pos_string = LLXmlTree::addAttributeString("pos");
+ if (!node->getFastAttributeVector3(pos_string, mPos))
+ {
+ llwarns << "Bone without position" << llendl;
+ return FALSE;
+ }
+
+ static LLStdStringHandle rot_string = LLXmlTree::addAttributeString("rot");
+ if (!node->getFastAttributeVector3(rot_string, mRot))
+ {
+ llwarns << "Bone without rotation" << llendl;
+ return FALSE;
+ }
+
+ static LLStdStringHandle scale_string = LLXmlTree::addAttributeString("scale");
+ if (!node->getFastAttributeVector3(scale_string, mScale))
+ {
+ llwarns << "Bone without scale" << llendl;
+ return FALSE;
+ }
+
+ if (mIsJoint)
+ {
+ static LLStdStringHandle pivot_string = LLXmlTree::addAttributeString("pivot");
+ if (!node->getFastAttributeVector3(pivot_string, mPivot))
+ {
+ llwarns << "Bone without pivot" << llendl;
+ return FALSE;
+ }
+ }
+
+ // parse children
+ LLXmlTreeNode* child;
+ for( child = node->getFirstChild(); child; child = node->getNextChild() )
+ {
+ LLVOAvatarBoneInfo *child_info = new LLVOAvatarBoneInfo;
+ if (!child_info->parseXml(child))
+ {
+ delete child_info;
+ return FALSE;
+ }
+ mChildList.push_back(child_info);
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLVOAvatarSkeletonInfo::parseXml()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatarSkeletonInfo::parseXml(LLXmlTreeNode* node)
+{
+ static LLStdStringHandle num_bones_string = LLXmlTree::addAttributeString("num_bones");
+ if (!node->getFastAttributeS32(num_bones_string, mNumBones))
+ {
+ llerrs << "Couldn't find number of bones." << llendl;
+ return FALSE;
+ }
+
+ static LLStdStringHandle num_collision_volumes_string = LLXmlTree::addAttributeString("num_collision_volumes");
+ node->getFastAttributeS32(num_collision_volumes_string, mNumCollisionVolumes);
+
+ LLXmlTreeNode* child;
+ for( child = node->getFirstChild(); child; child = node->getNextChild() )
+ {
+ LLVOAvatarBoneInfo *info = new LLVOAvatarBoneInfo;
+ if (!info->parseXml(child))
+ {
+ delete info;
+ llerrs << "Error parsing bone in skeleton file" << llendl;
+ }
+ mBoneInfoList.push_back(info);
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// parseXmlSkeletonNode(): parses <skeleton> nodes from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatarInfo::parseXmlSkeletonNode(LLXmlTreeNode* root)
+{
+ LLXmlTreeNode* node = root->getChildByName( "skeleton" );
+ if( !node )
+ {
+ llwarns << "avatar file: missing <skeleton>" << llendl;
+ return FALSE;
+ }
+
+ LLXmlTreeNode* child;
+
+ // SKELETON DISTORTIONS
+ for (child = node->getChildByName( "param" );
+ child;
+ child = node->getNextNamedChild())
+ {
+ if (!child->getChildByName("param_skeleton"))
+ {
+ if (child->getChildByName("param_morph"))
+ {
+ llwarns << "Can't specify morph param in skeleton definition." << llendl;
+ }
+ else
+ {
+ llwarns << "Unknown param type." << llendl;
+ }
+ continue;
+ }
+
+ LLPolySkeletalDistortionInfo *info = new LLPolySkeletalDistortionInfo;
+ if (!info->parseXml(child))
+ {
+ delete info;
+ return FALSE;
+ }
+
+ mSkeletalDistortionInfoList.push_back(info);
+ }
+
+ // ATTACHMENT POINTS
+ for (child = node->getChildByName( "attachment_point" );
+ child;
+ child = node->getNextNamedChild())
+ {
+ LLVOAvatarAttachmentInfo* info = new LLVOAvatarAttachmentInfo();
+
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if (!child->getFastAttributeString(name_string, info->mName))
+ {
+ llwarns << "No name supplied for attachment point." << llendl;
+ delete info;
+ continue;
+ }
+
+ static LLStdStringHandle joint_string = LLXmlTree::addAttributeString("joint");
+ if (!child->getFastAttributeString(joint_string, info->mJointName))
+ {
+ llwarns << "No bone declared in attachment point " << info->mName << llendl;
+ delete info;
+ continue;
+ }
+
+ static LLStdStringHandle position_string = LLXmlTree::addAttributeString("position");
+ if (child->getFastAttributeVector3(position_string, info->mPosition))
+ {
+ info->mHasPosition = TRUE;
+ }
+
+ static LLStdStringHandle rotation_string = LLXmlTree::addAttributeString("rotation");
+ if (child->getFastAttributeVector3(rotation_string, info->mRotationEuler))
+ {
+ info->mHasRotation = TRUE;
+ }
+ static LLStdStringHandle group_string = LLXmlTree::addAttributeString("group");
+ if (child->getFastAttributeS32(group_string, info->mGroup))
+ {
+ if (info->mGroup == -1)
+ info->mGroup = -1111; // -1 = none parsed, < -1 = bad value
+ }
+
+ static LLStdStringHandle id_string = LLXmlTree::addAttributeString("id");
+ if (!child->getFastAttributeS32(id_string, info->mAttachmentID))
+ {
+ llwarns << "No id supplied for attachment point " << info->mName << llendl;
+ delete info;
+ continue;
+ }
+
+ static LLStdStringHandle slot_string = LLXmlTree::addAttributeString("pie_slice");
+ child->getFastAttributeS32(slot_string, info->mPieMenuSlice);
+
+ static LLStdStringHandle visible_in_first_person_string = LLXmlTree::addAttributeString("visible_in_first_person");
+ child->getFastAttributeBOOL(visible_in_first_person_string, info->mVisibleFirstPerson);
+
+ static LLStdStringHandle hud_attachment_string = LLXmlTree::addAttributeString("hud");
+ child->getFastAttributeBOOL(hud_attachment_string, info->mIsHUDAttachment);
+
+ mAttachmentInfoList.push_back(info);
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// parseXmlMeshNodes(): parses <mesh> nodes from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatarInfo::parseXmlMeshNodes(LLXmlTreeNode* root)
+{
+ for (LLXmlTreeNode* node = root->getChildByName( "mesh" );
+ node;
+ node = root->getNextNamedChild())
+ {
+ LLVOAvatarMeshInfo *info = new LLVOAvatarMeshInfo;
+
+ // attribute: type
+ static LLStdStringHandle type_string = LLXmlTree::addAttributeString("type");
+ if( !node->getFastAttributeString( type_string, info->mType ) )
+ {
+ llwarns << "Avatar file: <mesh> is missing type attribute. Ignoring element. " << llendl;
+ delete info;
+ return FALSE; // Ignore this element
+ }
+
+ static LLStdStringHandle lod_string = LLXmlTree::addAttributeString("lod");
+ if (!node->getFastAttributeS32( lod_string, info->mLOD ))
+ {
+ llwarns << "Avatar file: <mesh> is missing lod attribute. Ignoring element. " << llendl;
+ delete info;
+ return FALSE; // Ignore this element
+ }
+
+ static LLStdStringHandle file_name_string = LLXmlTree::addAttributeString("file_name");
+ if( !node->getFastAttributeString( file_name_string, info->mMeshFileName ) )
+ {
+ llwarns << "Avatar file: <mesh> is missing file_name attribute. Ignoring: " << info->mType << llendl;
+ delete info;
+ return FALSE; // Ignore this element
+ }
+
+ static LLStdStringHandle reference_string = LLXmlTree::addAttributeString("reference");
+ node->getFastAttributeString( reference_string, info->mReferenceMeshName );
+
+ // attribute: min_pixel_area
+ static LLStdStringHandle min_pixel_area_string = LLXmlTree::addAttributeString("min_pixel_area");
+ static LLStdStringHandle min_pixel_width_string = LLXmlTree::addAttributeString("min_pixel_width");
+ if (!node->getFastAttributeF32( min_pixel_area_string, info->mMinPixelArea ))
+ {
+ F32 min_pixel_area = 0.1f;
+ if (node->getFastAttributeF32( min_pixel_width_string, min_pixel_area ))
+ {
+ // this is square root of pixel area (sensible to use linear space in defining lods)
+ min_pixel_area = min_pixel_area * min_pixel_area;
+ }
+ info->mMinPixelArea = min_pixel_area;
+ }
+
+ // Parse visual params for this node only if we haven't already
+ for (LLXmlTreeNode* child = node->getChildByName( "param" );
+ child;
+ child = node->getNextNamedChild())
+ {
+ if (!child->getChildByName("param_morph"))
+ {
+ if (child->getChildByName("param_skeleton"))
+ {
+ llwarns << "Can't specify skeleton param in a mesh definition." << llendl;
+ }
+ else
+ {
+ llwarns << "Unknown param type." << llendl;
+ }
+ continue;
+ }
+
+ LLPolyMorphTargetInfo *morphinfo = new LLPolyMorphTargetInfo();
+ if (!morphinfo->parseXml(child))
+ {
+ delete morphinfo;
+ delete info;
+ return -1;
+ }
+ BOOL shared = FALSE;
+ static LLStdStringHandle shared_string = LLXmlTree::addAttributeString("shared");
+ child->getFastAttributeBOOL(shared_string, shared);
+
+ info->mPolyMorphTargetInfoList.push_back(LLVOAvatarMeshInfo::morph_info_pair_t(morphinfo, shared));
+ }
+
+ mMeshInfoList.push_back(info);
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// parseXmlColorNodes(): parses <global_color> nodes from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatarInfo::parseXmlColorNodes(LLXmlTreeNode* root)
+{
+ for (LLXmlTreeNode* color_node = root->getChildByName( "global_color" );
+ color_node;
+ color_node = root->getNextNamedChild())
+ {
+ LLString global_color_name;
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if (color_node->getFastAttributeString( name_string, global_color_name ) )
+ {
+ if( global_color_name == "skin_color" )
+ {
+ if (mTexSkinColorInfo)
+ {
+ llwarns << "avatar file: multiple instances of skin_color" << llendl;
+ return FALSE;
+ }
+ mTexSkinColorInfo = new LLTexGlobalColorInfo;
+ if( !mTexSkinColorInfo->parseXml( color_node ) )
+ {
+ delete mTexSkinColorInfo; mTexSkinColorInfo = 0;
+ llwarns << "avatar file: mTexSkinColor->parseXml() failed" << llendl;
+ return FALSE;
+ }
+ }
+ else if( global_color_name == "hair_color" )
+ {
+ if (mTexHairColorInfo)
+ {
+ llwarns << "avatar file: multiple instances of hair_color" << llendl;
+ return FALSE;
+ }
+ mTexHairColorInfo = new LLTexGlobalColorInfo;
+ if( !mTexHairColorInfo->parseXml( color_node ) )
+ {
+ delete mTexHairColorInfo; mTexHairColorInfo = 0;
+ llwarns << "avatar file: mTexHairColor->parseXml() failed" << llendl;
+ return FALSE;
+ }
+ }
+ else if( global_color_name == "eye_color" )
+ {
+ if (mTexEyeColorInfo)
+ {
+ llwarns << "avatar file: multiple instances of eye_color" << llendl;
+ return FALSE;
+ }
+ mTexEyeColorInfo = new LLTexGlobalColorInfo;
+ if( !mTexEyeColorInfo->parseXml( color_node ) )
+ {
+ llwarns << "avatar file: mTexEyeColor->parseXml() failed" << llendl;
+ return FALSE;
+ }
+ }
+ }
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// parseXmlLayerNodes(): parses <layer_set> nodes from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatarInfo::parseXmlLayerNodes(LLXmlTreeNode* root)
+{
+ for (LLXmlTreeNode* layer_node = root->getChildByName( "layer_set" );
+ layer_node;
+ layer_node = root->getNextNamedChild())
+ {
+ LLTexLayerSetInfo* layer_info = new LLTexLayerSetInfo();
+ if( layer_info->parseXml( layer_node ) )
+ {
+ mLayerInfoList.push_back(layer_info);
+ }
+ else
+ {
+ delete layer_info;
+ llwarns << "avatar file: layer_set->parseXml() failed" << llendl;
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// parseXmlDriverNodes(): parses <driver_parameters> nodes from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatarInfo::parseXmlDriverNodes(LLXmlTreeNode* root)
+{
+ LLXmlTreeNode* driver = root->getChildByName( "driver_parameters" );
+ if( driver )
+ {
+ for (LLXmlTreeNode* grand_child = driver->getChildByName( "param" );
+ grand_child;
+ grand_child = driver->getNextNamedChild())
+ {
+ if( grand_child->getChildByName( "param_driver" ) )
+ {
+ LLDriverParamInfo* driver_info = new LLDriverParamInfo();
+ if( driver_info->parseXml( grand_child ) )
+ {
+ mDriverInfoList.push_back(driver_info);
+ }
+ else
+ {
+ delete driver_info;
+ llwarns << "avatar file: driver_param->parseXml() failed" << llendl;
+ return FALSE;
+ }
+ }
+ }
+ }
+ return TRUE;
+}
+
+void LLVOAvatar::writeCAL3D(std::string& path, std::string& file_base)
+{
+ char filename[MAX_PATH];
+
+ // reset animated morphs
+ setVisualParamWeight("Blink_Left", 0.f);
+ setVisualParamWeight("Blink_Right", 0.f);
+ setVisualParamWeight("Hands_Relaxed", 1.f);
+ setVisualParamWeight("Hands_Point", 0.f);
+ setVisualParamWeight("Hands_Fist", 0.f);
+ setVisualParamWeight("Hands_Relaxed_L", 0.f);
+ setVisualParamWeight("Hands_Point_L", 0.f);
+ setVisualParamWeight("Hands_Fist_L", 0.f);
+ setVisualParamWeight("Hands_Relaxed_R", 0.f);
+ setVisualParamWeight("Hands_Point_R", 0.f);
+ setVisualParamWeight("Hands_Fist_R", 0.f);
+ setVisualParamWeight("Hands_Salute_R", 0.f);
+ setVisualParamWeight("Hands_Typing", 0.f);
+ setVisualParamWeight("Hands_Peace_R", 0.f);
+ setVisualParamWeight("Hands_Spread_R", 0.f);
+ updateVisualParams();
+
+ sprintf(filename, "%s\\%s_skeleton.xsf", path.c_str(), file_base.c_str());
+ apr_file_t* fp = ll_apr_file_open(filename, LL_APR_W);
+ if (!fp)
+ {
+ llwarns << "Unable to write avatar file " << filename << llendl;
+ return;
+ }
+ apr_file_printf(fp, "<SKELETON VERSION=\"1000\" NUMBONES=\"%d\">\n", sSkeletonInfo->getNumBones() - sSkeletonInfo->getNumCollisionVolumes());
+ mRoot.writeCAL3D(fp);
+ apr_file_printf(fp, "</SKELETON>\n");
+ apr_file_close(fp);
+
+ sprintf(filename, "%s\\%s_mesh_body.xmf", path.c_str(), file_base.c_str());
+ //gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,"avatar.cal").c_str()
+ fp = ll_apr_file_open(filename, LL_APR_W);
+ if (!fp)
+ {
+ llwarns << "Unable to write avatar file " << filename << llendl;
+ return;
+ }
+
+ BOOL has_skirt = isWearingWearableType(WT_SKIRT);
+
+ apr_file_printf(fp, "<MESH VERSION=\"1000\" NUMSUBMESH=\"%d\">\n", has_skirt ? 8 : 7);
+ mHairMesh0.writeCAL3D(fp, 5, this);
+ mHeadMesh0.writeCAL3D(fp, 0, this);
+ mEyeLashMesh0.writeCAL3D(fp, 0, this);
+ mUpperBodyMesh0.writeCAL3D(fp, 1, this);
+ mLowerBodyMesh0.writeCAL3D(fp, 2, this);
+ mEyeBallLeftMesh0.writeCAL3D(fp, 3, this);
+ mEyeBallRightMesh0.writeCAL3D(fp, 3, this);
+ if (has_skirt)
+ {
+ mSkirtMesh0.writeCAL3D(fp, 4, this);
+ }
+ apr_file_printf(fp, "</MESH>\n");
+ apr_file_close(fp);
+
+ // write out material files
+ LLPointer<LLImageTGA> tga_image = new LLImageTGA;
+
+ for (S32 i = 0; i < (has_skirt ? BAKED_TEXTURE_COUNT : BAKED_TEXTURE_COUNT - 1); i++)
+ {
+ sprintf(filename, "%s\\%s_material_tex_%d.tga", path.c_str(), file_base.c_str(), i);
+
+ LLViewerImage* viewer_imagep = mTEImages[sBakedTextureIndices[i]];
+ if (!viewer_imagep->getHasGLTexture())
+ {
+ llinfos << "No image data available for " << filename << llendl;
+ continue;
+ }
+ LLPointer<LLImageRaw> raw_image = new LLImageRaw;
+ viewer_imagep->readBackRaw(-1, raw_image);
+ BOOL success = tga_image->encode(raw_image);
+ success = tga_image->save(filename);
+ }
+
+ // output image for hair
+ sprintf(filename, "%s\\%s_material_tex_5.tga", path.c_str(), file_base.c_str());
+ LLViewerImage* viewer_imagep = mTEImages[TEX_HAIR];
+ if (!viewer_imagep->getHasGLTexture())
+ {
+ llinfos << "No image data available for " << filename << llendl;
+ }
+ else
+ {
+ LLPointer<LLImageRaw> raw_image = new LLImageRaw;
+ viewer_imagep->readBackRaw(-1, raw_image);
+ BOOL success = tga_image->encode(raw_image);
+ success = tga_image->save(filename);
+ }
+
+ // save out attachments
+ sprintf(filename, "%s\\%s_mesh_attachments.xmf", path.c_str(), file_base.c_str());
+ fp = ll_apr_file_open(filename, LL_APR_W);
+ if (!fp)
+ {
+ llwarns << "Unable to write attachments file " << filename << llendl;
+ return;
+ }
+
+ typedef std::multimap<LLUUID, LLMaterialExportInfo*>::iterator material_it_t;
+ std::multimap<LLUUID, LLMaterialExportInfo*> material_map;
+
+ S32 num_attachment_objects = 0;
+ for(LLViewerJointAttachment *attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ LLViewerObject *attached_object = attachment->getObject(0);
+ if (attached_object && !attached_object->isDead() && attached_object->mDrawable.notNull() &&
+ attached_object->getPCode() == LL_PCODE_VOLUME)
+ {
+ num_attachment_objects += attached_object->mDrawable->getNumFaces();
+ for (U32 i = 0; i < attached_object->mChildList.size(); i++)
+ {
+ LLViewerObject* child_object = attached_object->mChildList[i];
+ num_attachment_objects += child_object->mDrawable->getNumFaces();
+ }
+ }
+ }
+
+ apr_file_printf(fp, "<MESH VERSION=\"1000\" NUMSUBMESH=\"%d\">\n", num_attachment_objects);
+
+ S32 material_index = 6;
+ S32 texture_index = 6;
+ for(LLViewerJointAttachment *attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ LLViewerObject *attached_object = attachment->getObject(0);
+ if (attached_object && !attached_object->isDead() && attached_object->getPCode() == LL_PCODE_VOLUME)
+ {
+ LLVOVolume* attached_volume = (LLVOVolume*)attached_object;
+ LLVector3 pos = attachment->getPosition();
+ LLJoint* cur_joint = attachment->getParent();
+ while (cur_joint)
+ {
+ pos += cur_joint->getSkinOffset();
+ cur_joint = (LLViewerJoint*)cur_joint->getParent();
+ }
+ pos *= 100.f;
+ S32 attached_joint_num = attachment->getParent()->mJointNum;
+ LLQuaternion rot = attachment->getRotation();
+ attached_volume->writeCAL3D(fp, path, file_base, attached_joint_num, pos, rot, material_index, texture_index, material_map);
+ }
+ }
+ apr_file_printf(fp, "</MESH>\n");
+ apr_file_close(fp);
+
+ // now dump sample animation
+ LLKeyframeMotion* walk_motion =
+ getSex() == SEX_MALE ? (LLKeyframeMotion*)findMotion(ANIM_AGENT_WALK) : (LLKeyframeMotion*)findMotion(ANIM_AGENT_FEMALE_WALK);
+ if (FALSE)//(walk_motion)
+ {
+ sprintf(filename, "%s\\%s_anim.xaf", path.c_str(), file_base.c_str());
+ apr_file_t* fp = ll_apr_file_open(filename, LL_APR_W);
+ if (!fp)
+ {
+ llwarns << "Unable to write avatar animation file " << filename << llendl;
+ return;
+ }
+
+ walk_motion->writeCAL3D(fp);
+
+ apr_file_close(fp);
+ }
+
+ // finally, write out .cfg file
+ sprintf(filename, "%s\\%s_avatar.cfg", path.c_str(), file_base.c_str());
+ fp = ll_apr_file_open(filename, LL_APR_W);
+ if (!fp)
+ {
+ llwarns << "Unable to write avatar config file " << filename << llendl;
+ return;
+ }
+
+ // this version exports animation
+ //apr_file_printf(fp, "#\n# cal3d model configuration file\n#\n# model: %s_avatar\n#\n\nscale=1.0\n\nskeleton=%s_skeleton.xsf\n\nanimation=%s_anim.xaf\n\n", file_base.c_str(), file_base.c_str(), file_base.c_str());
+ apr_file_printf(fp, "#\n# cal3d model configuration file\n#\n# model: %s_avatar\n#\n\nscale=1.0\n\nskeleton=%s_skeleton.xsf\n\n", file_base.c_str(), file_base.c_str());
+ apr_file_printf(fp, "mesh=%s_mesh_body.xmf\nmesh=%s_mesh_attachments.xmf\n", file_base.c_str(), file_base.c_str());
+
+ for (S32 i = 0; i < material_index; i++)
+ {
+ apr_file_printf(fp, "material=%s_material_%d.xrf\n", file_base.c_str(), i);
+ }
+ apr_file_close(fp);
+
+ for(S32 i = 0; i < 6; i++)
+ {
+ sprintf(filename, "%s\\%s_material_%d.xrf", path.c_str(), file_base.c_str(), i);
+ apr_file_t* fp = ll_apr_file_open(filename, LL_APR_W);
+ if (!fp)
+ {
+ llwarns << "Unable to write material definition file " << filename << llendl;
+ return;
+ }
+
+ // for hair material, use hair color...otherwise use white for entire body
+ LLColor4U material_color = (i == 5) ? mTexHairColor->getColor() : LLColor4U::white;
+
+ apr_file_printf(fp, "<HEADER MAGIC=\"XRF\" VERSION=\"900\" />\n<MATERIAL NUMMAPS=\"1\">\n");
+ apr_file_printf(fp, " <AMBIENT>%d %d %d %d</AMBIENT>\n", material_color.mV[VX], material_color.mV[VY], material_color.mV[VZ], material_color.mV[VW]);
+ apr_file_printf(fp, " <DIFFUSE>%d %d %d %d</DIFFUSE>\n", material_color.mV[VX], material_color.mV[VY], material_color.mV[VZ], material_color.mV[VW]);
+ apr_file_printf(fp, " <SPECULAR>0 0 0 0</SPECULAR>\n");
+ apr_file_printf(fp, " <SHININESS>1.0</SHININESS>\n");
+ apr_file_printf(fp, " <MAP>%s_material_tex_%d.tga</MAP>\n", file_base.c_str(), i);
+ apr_file_printf(fp, "</MATERIAL>\n");
+
+ apr_file_close(fp);
+ }
+
+ // write out material files
+ for(material_it_t material_it = material_map.begin(); material_it != material_map.end(); ++material_it)
+ {
+ LLMaterialExportInfo* export_info = material_it->second;
+
+ sprintf(filename, "%s\\%s_material_%d.xrf", path.c_str(), file_base.c_str(), export_info->mMaterialIndex);
+ apr_file_t* fp = ll_apr_file_open(filename, LL_APR_W);
+ if (!fp)
+ {
+ llwarns << "Unable to write material definition file " << filename << llendl;
+ return;
+ }
+
+ LLColor4U material_color = export_info->mColor;
+
+ apr_file_printf(fp, "<HEADER MAGIC=\"XRF\" VERSION=\"900\" />\n<MATERIAL NUMMAPS=\"1\">\n");
+ apr_file_printf(fp, " <AMBIENT>%d %d %d %d</AMBIENT>\n", material_color.mV[VX], material_color.mV[VY], material_color.mV[VZ], material_color.mV[VW]);
+ apr_file_printf(fp, " <DIFFUSE>%d %d %d %d</DIFFUSE>\n", material_color.mV[VX], material_color.mV[VY], material_color.mV[VZ], material_color.mV[VW]);
+ apr_file_printf(fp, " <SPECULAR>0 0 0 0</SPECULAR>\n");
+ apr_file_printf(fp, " <SHININESS>1.0</SHININESS>\n");
+ apr_file_printf(fp, " <MAP>%s_material_tex_%d.tga</MAP>\n", file_base.c_str(), export_info->mTextureIndex);
+ apr_file_printf(fp, "</MATERIAL>\n");
+
+ apr_file_close(fp);
+ }
+
+
+ std::for_each(material_map.begin(), material_map.end(), DeletePairedPointer());
+ material_map.clear();
+}
+
+// warning: order(N) not order(1)
+S32 LLVOAvatar::getAttachmentCount()
+{
+ S32 count = mAttachmentPoints.getLength();
+ return count;
+}
+
+//virtual
+void LLVOAvatar::updateRegion(LLViewerRegion *regionp)
+{
+ if (mIsSelf)
+ {
+ if (regionp->getHandle() != mLastRegionHandle)
+ {
+ if (mLastRegionHandle != 0)
+ {
+ ++mRegionCrossingCount;
+ F64 delta = (F64)mRegionCrossingTimer.getElapsedTimeF32();
+ F64 avg = (mRegionCrossingCount == 1) ? 0 : gViewerStats->getStat(LLViewerStats::ST_CROSSING_AVG);
+ F64 delta_avg = (delta + avg*(mRegionCrossingCount-1)) / mRegionCrossingCount;
+ gViewerStats->setStat(LLViewerStats::ST_CROSSING_AVG, delta_avg);
+
+ F64 max = (mRegionCrossingCount == 1) ? 0 : gViewerStats->getStat(LLViewerStats::ST_CROSSING_MAX);
+ max = llmax(delta, max);
+ gViewerStats->setStat(LLViewerStats::ST_CROSSING_MAX, max);
+ }
+ mLastRegionHandle = regionp->getHandle();
+ }
+ mRegionCrossingTimer.reset();
+ }
+}
+
+LLString LLVOAvatar::getFullname() const
+{
+ LLString name;
+
+ LLNameValue* first = getNVPair("FirstName");
+ LLNameValue* last = getNVPair("LastName");
+ if (first && last)
+ {
+ name += first->getString();
+ name += " ";
+ name += last->getString();
+ }
+
+ return name;
+}
+
+LLTexLayerSet* LLVOAvatar::getLayerSet(ETextureIndex index) const
+{
+ switch( index )
+ {
+ case TEX_HEAD_BAKED:
+ case TEX_HEAD_BODYPAINT:
+ return mHeadLayerSet;
+
+ case TEX_UPPER_BAKED:
+ case TEX_UPPER_SHIRT:
+ case TEX_UPPER_BODYPAINT:
+ case TEX_UPPER_JACKET:
+ case TEX_UPPER_GLOVES:
+ case TEX_UPPER_UNDERSHIRT:
+ return mUpperBodyLayerSet;
+
+ case TEX_LOWER_BAKED:
+ case TEX_LOWER_PANTS:
+ case TEX_LOWER_BODYPAINT:
+ case TEX_LOWER_SHOES:
+ case TEX_LOWER_SOCKS:
+ case TEX_LOWER_JACKET:
+ case TEX_LOWER_UNDERPANTS:
+ return mLowerBodyLayerSet;
+
+ case TEX_EYES_BAKED:
+ case TEX_EYES_IRIS:
+ return mEyesLayerSet;
+
+ case TEX_SKIRT_BAKED:
+ case TEX_SKIRT:
+ return mSkirtLayerSet;
+
+ case TEX_HAIR:
+ default:
+ return NULL;
+ }
+}
+
+LLHost LLVOAvatar::getObjectHost() const
+{
+ LLViewerRegion* region = getRegion();
+ if (region && !isDead())
+ {
+ return region->getHost();
+ }
+ else
+ {
+ return LLHost::invalid;
+ }
+}