diff options
author | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
---|---|---|
committer | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
commit | 420b91db29485df39fd6e724e782c449158811cb (patch) | |
tree | b471a94563af914d3ed3edd3e856d21cb1b69945 /indra/newview/llvoavatar.cpp |
Print done when done.
Diffstat (limited to 'indra/newview/llvoavatar.cpp')
-rw-r--r-- | indra/newview/llvoavatar.cpp | 9426 |
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; + } +} |