/** * @file llagent.cpp * @brief LLAgent class implementation * * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc. * $License$ */ #include "llviewerprecompiledheaders.h" #include "stdtypes.h" #include "stdenums.h" #include "llagent.h" #include "llcoordframe.h" #include "indra_constants.h" #include "llmath.h" #include "llcriticaldamp.h" #include "llfocusmgr.h" #include "llglheaders.h" #include "llparcel.h" #include "llpermissions.h" #include "llregionhandle.h" #include "m3math.h" #include "m4math.h" #include "message.h" #include "llquaternion.h" #include "v3math.h" #include "v4math.h" //#include "vmath.h" #include "imageids.h" #include "llbox.h" #include "llbutton.h" #include "llcameraview.h" #include "llcallingcard.h" #include "llchatbar.h" #include "llconsole.h" #include "lldrawable.h" #include "llface.h" #include "llfirstuse.h" #include "llfloater.h" #include "llfloateravatarinfo.h" #include "llfloaterbuildoptions.h" #include "llfloaterchat.h" #include "llfloatercustomize.h" #include "llfloaterdirectory.h" #include "llfloatergroupinfo.h" #include "llfloatergroups.h" #include "llfloatermap.h" #include "llfloatermute.h" #include "llfloatersnapshot.h" #include "llfloatertools.h" #include "llfloaterworldmap.h" #include "llgroupmgr.h" #include "llhudeffectlookat.h" #include "llhudmanager.h" #include "llinventorymodel.h" #include "llinventoryview.h" #include "lljoystickbutton.h" #include "llmenugl.h" #include "llmorphview.h" #include "llmoveview.h" #include "llnotify.h" #include "llquantize.h" #include "llselectmgr.h" #include "llsky.h" #include "llsphere.h" #include "llstatusbar.h" #include "llimview.h" #include "lltool.h" #include "lltoolcomp.h" // for gToolGun #include "lltoolfocus.h" #include "lltoolgrab.h" #include "lltoolmgr.h" #include "lltoolpie.h" #include "lltoolview.h" #include "llui.h" // for make_ui_sound #include "llviewercamera.h" #include "llviewerinventory.h" #include "llviewermenu.h" #include "llviewernetwork.h" #include "llviewerobjectlist.h" #include "llviewerparcelmgr.h" #include "llviewerparceloverlay.h" #include "llviewerregion.h" #include "llviewerstats.h" #include "llviewerwindow.h" #include "llvoavatar.h" #include "llvoground.h" #include "llvosky.h" #include "llwearable.h" #include "llwearablelist.h" #include "llworld.h" #include "llworldmap.h" #include "pipeline.h" #include "roles_constants.h" #include "viewer.h" // Ventrella #include "llfollowcam.h" // end Ventrella extern LLMenuBarGL* gMenuBarView; extern U8 gLastPickAlpha; extern F32 gFrameDTClamped; //drone wandering constants const F32 MAX_WANDER_TIME = 20.f; // seconds const F32 MAX_HEADING_HALF_ERROR = 0.2f; // radians const F32 WANDER_MAX_SLEW_RATE = 2.f * DEG_TO_RAD; // radians / frame const F32 WANDER_TARGET_MIN_DISTANCE = 10.f; // meters // Autopilot constants const F32 AUTOPILOT_HEADING_HALF_ERROR = 10.f * DEG_TO_RAD; // radians const F32 AUTOPILOT_MAX_SLEW_RATE = 1.f * DEG_TO_RAD; // radians / frame const F32 AUTOPILOT_STOP_DISTANCE = 2.f; // meters const F32 AUTOPILOT_HEIGHT_ADJUST_DISTANCE = 8.f; // meters const F32 AUTOPILOT_MIN_TARGET_HEIGHT_OFF_GROUND = 1.f; // meters const F32 AUTOPILOT_MAX_TIME_NO_PROGRESS = 1.5f; // seconds // face editing constants const LLVector3d FACE_EDIT_CAMERA_OFFSET(0.4f, -0.05f, 0.07f); const LLVector3d FACE_EDIT_TARGET_OFFSET(0.f, 0.f, 0.05f); // Mousewheel camera zoom const F32 MIN_ZOOM_FRACTION = 0.25f; const F32 INITIAL_ZOOM_FRACTION = 1.f; const F32 MAX_ZOOM_FRACTION = 8.f; const F32 METERS_PER_WHEEL_CLICK = 1.f; const F32 MAX_TIME_DELTA = 1.f; const F32 CAMERA_ZOOM_HALF_LIFE = 0.07f; // seconds const F32 FOV_ZOOM_HALF_LIFE = 0.07f; // seconds const F32 CAMERA_FOCUS_HALF_LIFE = 0.f;//0.02f; const F32 CAMERA_LAG_HALF_LIFE = 0.25f; const F32 MIN_CAMERA_LAG = 0.5f; const F32 MAX_CAMERA_LAG = 5.f; const F32 CAMERA_COLLIDE_EPSILON = 0.1f; const F32 MIN_CAMERA_DISTANCE = 0.1f; const F32 AVATAR_ZOOM_MIN_X_FACTOR = 0.55f; const F32 AVATAR_ZOOM_MIN_Y_FACTOR = 0.7f; const F32 AVATAR_ZOOM_MIN_Z_FACTOR = 1.15f; const F32 MAX_CAMERA_DISTANCE_FROM_AGENT = 50.f; const F32 HEAD_BUFFER_SIZE = 0.3f; const F32 CUSTOMIZE_AVATAR_CAMERA_ANIM_SLOP = 0.2f; const F32 LAND_MIN_ZOOM = 0.15f; const F32 AVATAR_MIN_ZOOM = 0.5f; const F32 OBJECT_MIN_ZOOM = 0.02f; const F32 APPEARANCE_MIN_ZOOM = 0.39f; const F32 APPEARANCE_MAX_ZOOM = 8.f; // fidget constants const F32 MIN_FIDGET_TIME = 8.f; // seconds const F32 MAX_FIDGET_TIME = 20.f; // seconds const S32 MAX_NUM_CHAT_POSITIONS = 10; const F32 GROUND_TO_AIR_CAMERA_TRANSITION_TIME = 0.5f; const F32 GROUND_TO_AIR_CAMERA_TRANSITION_START_TIME = 0.5f; const F32 MAX_VELOCITY_AUTO_LAND_SQUARED = 4.f * 4.f; const F32 MAX_FOCUS_OFFSET = 20.f; const F32 OBJECT_EXTENTS_PADDING = 0.5f; const F32 MIN_RADIUS_ALPHA_SIZZLE = 0.5f; const F64 CHAT_AGE_FAST_RATE = 3.0; const S32 MAX_WEARABLES_PER_LAYERSET = 7; const EWearableType WEARABLE_BAKE_TEXTURE_MAP[BAKED_TEXTURE_COUNT][MAX_WEARABLES_PER_LAYERSET] = { { WT_SHAPE, WT_SKIN, WT_HAIR, WT_INVALID, WT_INVALID, WT_INVALID, WT_INVALID }, // TEX_HEAD_BAKED { WT_SHAPE, WT_SKIN, WT_SHIRT, WT_JACKET, WT_GLOVES, WT_UNDERSHIRT, WT_INVALID }, // TEX_UPPER_BAKED { WT_SHAPE, WT_SKIN, WT_PANTS, WT_SHOES, WT_SOCKS, WT_JACKET, WT_UNDERPANTS }, // TEX_LOWER_BAKED { WT_EYES, WT_INVALID, WT_INVALID, WT_INVALID, WT_INVALID, WT_INVALID, WT_INVALID }, // TEX_EYES_BAKED { WT_SKIRT, WT_INVALID, WT_INVALID, WT_INVALID, WT_INVALID, WT_INVALID, WT_INVALID } // TEX_SKIRT_BAKED }; const LLUUID BAKED_TEXTURE_HASH[BAKED_TEXTURE_COUNT] = { LLUUID("18ded8d6-bcfc-e415-8539-944c0f5ea7a6"), LLUUID("338c29e3-3024-4dbb-998d-7c04cf4fa88f"), LLUUID("91b4a2c7-1b1a-ba16-9a16-1f8f8dcc1c3f"), LLUUID("b2cf28af-b840-1071-3c6a-78085d8128b5"), LLUUID("ea800387-ea1a-14e0-56cb-24f2022f969a") }; // // Statics // BOOL LLAgent::sDebugDisplayTarget = FALSE; const F32 LLAgent::TYPING_TIMEOUT_SECS = 5.f; class LLAgentFriendObserver : public LLFriendObserver { public: LLAgentFriendObserver() {} virtual ~LLAgentFriendObserver() {} virtual void changed(U32 mask); }; void LLAgentFriendObserver::changed(U32 mask) { // if there's a change we're interested in. if((mask & (LLFriendObserver::POWERS)) != 0) { gAgent.friendsChanged(); } } // ************************************************************ // Enabled this definition to compile a 'hacked' viewer that // locally believes the end user has godlike powers. // #define HACKED_GODLIKE_VIEWER // For a toggled version, see viewer.h for the // TOGGLE_HACKED_GODLIKE_VIEWER define, instead. // ************************************************************ // Constructors and Destructors // JC - Please try to make this order match the order in the header // file. Otherwise it's hard to find variables that aren't initialized. //----------------------------------------------------------------------------- // LLAgent() //----------------------------------------------------------------------------- LLAgent::LLAgent() : mViewerPort(NET_USE_OS_ASSIGNED_PORT), mDrawDistance( DEFAULT_FAR_PLANE ), mAccess(SIM_ACCESS_PG), mGroupPowers(0), mGroupID(), //mGroupInsigniaID(), mMapOriginX(0), mMapOriginY(0), mMapWidth(0), mMapHeight(0), mLookAt(NULL), mPointAt(NULL), mInitialized(FALSE), mNumPendingQueries(0), mForceMouselook(FALSE), mTeleportState( TELEPORT_NONE ), mRegionp(NULL), mAgentOriginGlobal(), mPositionGlobal(), mDistanceTraveled(0), mLastPositionGlobal(LLVector3d::zero), mAvatarObject(NULL), mRenderState(0), mTypingTimer(), mCameraMode( CAMERA_MODE_THIRD_PERSON ), mLastCameraMode( CAMERA_MODE_THIRD_PERSON ), mViewsPushed(FALSE), mbAlwaysRun(FALSE), mShowAvatar(TRUE), mCameraAnimating( FALSE ), mAnimationCameraStartGlobal(), mAnimationFocusStartGlobal(), mAnimationTimer(), mAnimationDuration(0.33f), mCameraFOVZoomFactor(0.f), mCameraCurrentFOVZoomFactor(0.f), mCameraFocusOffset(), mCameraOffsetDefault(), // mCameraOffsetNorm(), mCameraCollidePlane(), mCurrentCameraDistance(2.f), // meters, set in init() mTargetCameraDistance(2.f), mCameraZoomFraction(1.f), // deprecated mThirdPersonHeadOffset(0.f, 0.f, 1.f), mSitCameraEnabled(FALSE), mFocusOnAvatar(TRUE), mFocusGlobal(), mFocusTargetGlobal(), mFocusObject(NULL), mFocusObjectOffset(), mFocusDotRadius( 0.1f ), // meters mTrackFocusObject(TRUE), mFrameAgent(), mCrouching(FALSE), mIsBusy(FALSE), // movement keys below mControlFlags(0x00000000), mbFlagsDirty(FALSE), mbFlagsNeedReset(FALSE), mbJump(FALSE), mWanderTimer(), mWanderTargetGlobal( LLVector3d::zero ), mAutoPilot(FALSE), mAutoPilotFlyOnStop(FALSE), mAutoPilotTargetGlobal(), mAutoPilotStopDistance(1.f), mAutoPilotUseRotation(FALSE), mAutoPilotTargetFacing(LLVector3::zero), mAutoPilotTargetDist(0.f), mAutoPilotFinishedCallback(NULL), mAutoPilotCallbackData(NULL), mEffectColor(0.f, 1.f, 1.f, 1.f), mHaveHomePosition(FALSE), mHomeRegionHandle( 0 ), mNearChatRadius(10.f), mGodLevel( GOD_NOT ), mNextFidgetTime(0.f), mCurrentFidget(0), mFirstLogin(FALSE), mGenderChosen(FALSE), mAgentWearablesUpdateSerialNum(0), mWearablesLoaded(FALSE), mTextureCacheQueryID(0), mAppearanceSerialNum(0) { U32 i; for (i = 0; i < TOTAL_CONTROLS; i++) { mControlsTakenCount[i] = 0; mControlsTakenPassedOnCount[i] = 0; } // Initialize movement keys mAtKey = 0; // Either 1, 0, or -1... indicates that movement-key is pressed mWalkKey = 0; // like AtKey, but causes less forward thrust mLeftKey = 0; mUpKey = 0; mYawKey = 0.f; mPitchKey = 0; mOrbitLeftKey = 0.f; mOrbitRightKey = 0.f; mOrbitUpKey = 0.f; mOrbitDownKey = 0.f; mOrbitInKey = 0.f; mOrbitOutKey = 0.f; mPanUpKey = 0.f; mPanDownKey = 0.f; mPanLeftKey = 0.f; mPanRightKey = 0.f; mPanInKey = 0.f; mPanOutKey = 0.f; mActiveCacheQueries = new S32[BAKED_TEXTURE_COUNT]; for (i = 0; i < (U32)BAKED_TEXTURE_COUNT; i++) { mActiveCacheQueries[i] = 0; } //Ventrella mCameraUpVector = LLVector3::z_axis;// default is straight up mFollowCam.setMaxCameraDistantFromSubject( MAX_CAMERA_DISTANCE_FROM_AGENT ); //end ventrella } // Requires gSavedSettings to be initialized. //----------------------------------------------------------------------------- // init() //----------------------------------------------------------------------------- void LLAgent::init() { mDrawDistance = gSavedSettings.getF32("RenderFarClip"); gCamera = new LLViewerCamera(); gCamera->setView(DEFAULT_FIELD_OF_VIEW); // Leave at 0.1 meters until we have real near clip management gCamera->setNear(0.1f); gCamera->setFar(mDrawDistance); // if you want to change camera settings, do so in camera.h gCamera->setAspect( gViewerWindow->getDisplayAspectRatio() ); // default, overridden in LLViewerWindow::reshape gCamera->setViewHeightInPixels(768); // default, overridden in LLViewerWindow::reshape setFlying( gSavedSettings.getBOOL("FlyingAtExit") ); mCameraFocusOffsetTarget = LLVector4(gSavedSettings.getVector3("CameraOffsetBuild")); mCameraOffsetDefault = gSavedSettings.getVector3("CameraOffsetDefault"); // mCameraOffsetNorm = mCameraOffsetDefault; // mCameraOffsetNorm.normVec(); mCameraCollidePlane.clearVec(); mCurrentCameraDistance = mCameraOffsetDefault.magVec(); mTargetCameraDistance = mCurrentCameraDistance; mCameraZoomFraction = 1.f; mTrackFocusObject = gSavedSettings.getBOOL("TrackFocusObject"); // LLDebugVarMessageBox::show("Camera Lag", &CAMERA_FOCUS_HALF_LIFE, 0.5f, 0.01f); gSavedSettings.getControl("RenderHideGroupTitle")->addListener(&mHideGroupTitleListener); gSavedSettings.getControl("EffectColor")->addListener(&mEffectColorListener); mEffectColor = gSavedSettings.getColor4("EffectColor"); mInitialized = TRUE; } //----------------------------------------------------------------------------- // cleanup() //----------------------------------------------------------------------------- void LLAgent::cleanup() { setSitCamera(LLUUID::null); mAvatarObject = NULL; mLookAt = NULL; mPointAt = NULL; mRegionp = NULL; setFocusObject(NULL); mFadeObjects.clear(); } //----------------------------------------------------------------------------- // LLAgent() //----------------------------------------------------------------------------- LLAgent::~LLAgent() { cleanup(); delete [] mActiveCacheQueries; mActiveCacheQueries = NULL; delete gCamera; gCamera = NULL; } // Change camera back to third person, stop the autopilot, // deselect stuff, etc. //----------------------------------------------------------------------------- // resetView() //----------------------------------------------------------------------------- void LLAgent::resetView(BOOL reset_camera) { if (mAutoPilot) { stopAutoPilot(TRUE); } if (!gNoRender) { gSelectMgr->unhighlightAll(); // By popular request, keep land selection while walking around. JC // gParcelMgr->deselectLand(); // force deselect when walking and attachment is selected // this is so people don't wig out when their avatar moves without animating if (gSelectMgr->getSelection()->isAttachment()) { gSelectMgr->deselectAll(); } // Hide all popup menus gMenuHolder->hideMenus(); } if (reset_camera && !gSavedSettings.getBOOL("FreezeTime")) { if (!gViewerWindow->getLeftMouseDown() && cameraThirdPerson()) { // leaving mouse-steer mode LLVector3 agent_at_axis = getAtAxis(); agent_at_axis -= projected_vec(agent_at_axis, getReferenceUpVector()); agent_at_axis.normVec(); gAgent.resetAxes(lerp(getAtAxis(), agent_at_axis, LLCriticalDamp::getInterpolant(0.3f))); } setFocusOnAvatar(TRUE, ANIMATE); } if (mAvatarObject.notNull()) { mAvatarObject->mHUDTargetZoom = 1.f; } } void LLAgent::ageChat() { if (mAvatarObject) { // get amount of time since I last chatted F64 elapsed_time = (F64)mAvatarObject->mChatTimer.getElapsedTimeF32(); // add in frame time * 3 (so it ages 4x) mAvatarObject->mChatTimer.setAge(elapsed_time + (F64)gFrameDTClamped * (CHAT_AGE_FAST_RATE - 1.0)); } } // Allow camera to be moved somewhere other than behind avatar. //----------------------------------------------------------------------------- // unlockView() //----------------------------------------------------------------------------- void LLAgent::unlockView() { if (getFocusOnAvatar()) { if (mAvatarObject) { setFocusGlobal( LLVector3d::zero, mAvatarObject->mID ); } setFocusOnAvatar(FALSE, FALSE); // no animation } } //----------------------------------------------------------------------------- // moveAt() //----------------------------------------------------------------------------- void LLAgent::moveAt(S32 direction) { // age chat timer so it fades more quickly when you are intentionally moving ageChat(); setKey(direction, mAtKey); if (direction > 0) { setControlFlags(AGENT_CONTROL_AT_POS | AGENT_CONTROL_FAST_AT); } else if (direction < 0) { setControlFlags(AGENT_CONTROL_AT_NEG | AGENT_CONTROL_FAST_AT); } resetView(); } //----------------------------------------------------------------------------- // moveAtNudge() //----------------------------------------------------------------------------- void LLAgent::moveAtNudge(S32 direction) { // age chat timer so it fades more quickly when you are intentionally moving ageChat(); setKey(direction, mWalkKey); if (direction > 0) { setControlFlags(AGENT_CONTROL_NUDGE_AT_POS); } else if (direction < 0) { setControlFlags(AGENT_CONTROL_NUDGE_AT_NEG); } resetView(); } //----------------------------------------------------------------------------- // moveLeft() //----------------------------------------------------------------------------- void LLAgent::moveLeft(S32 direction) { // age chat timer so it fades more quickly when you are intentionally moving ageChat(); setKey(direction, mLeftKey); if (direction > 0) { setControlFlags(AGENT_CONTROL_LEFT_POS | AGENT_CONTROL_FAST_LEFT); } else if (direction < 0) { setControlFlags(AGENT_CONTROL_LEFT_NEG | AGENT_CONTROL_FAST_LEFT); } resetView(); } //----------------------------------------------------------------------------- // moveLeftNudge() //----------------------------------------------------------------------------- void LLAgent::moveLeftNudge(S32 direction) { // age chat timer so it fades more quickly when you are intentionally moving ageChat(); setKey(direction, mLeftKey); if (direction > 0) { setControlFlags(AGENT_CONTROL_NUDGE_LEFT_POS); } else if (direction < 0) { setControlFlags(AGENT_CONTROL_NUDGE_LEFT_NEG); } resetView(); } //----------------------------------------------------------------------------- // moveUp() //----------------------------------------------------------------------------- void LLAgent::moveUp(S32 direction) { // age chat timer so it fades more quickly when you are intentionally moving ageChat(); setKey(direction, mUpKey); if (direction > 0) { setControlFlags(AGENT_CONTROL_UP_POS | AGENT_CONTROL_FAST_UP); } else if (direction < 0) { setControlFlags(AGENT_CONTROL_UP_NEG | AGENT_CONTROL_FAST_UP); } resetView(); } //----------------------------------------------------------------------------- // moveYaw() //----------------------------------------------------------------------------- void LLAgent::moveYaw(F32 mag) { mYawKey = mag; if (mag > 0) { setControlFlags(AGENT_CONTROL_YAW_POS); } else if (mag < 0) { setControlFlags(AGENT_CONTROL_YAW_NEG); } resetView(); } //----------------------------------------------------------------------------- // movePitch() //----------------------------------------------------------------------------- void LLAgent::movePitch(S32 direction) { setKey(direction, mPitchKey); if (direction > 0) { setControlFlags(AGENT_CONTROL_PITCH_POS ); } else if (direction < 0) { setControlFlags(AGENT_CONTROL_PITCH_NEG); } } // Does this parcel allow you to fly? BOOL LLAgent::canFly() { if (isGodlike()) return TRUE; if (!gParcelMgr) return FALSE; LLViewerRegion* regionp = getRegion(); if (regionp && regionp->getBlockFly()) return FALSE; LLParcel* parcel = gParcelMgr->getAgentParcel(); if (!parcel) return FALSE; // Allow owners to fly on their own land. if (LLViewerParcelMgr::isParcelOwnedByAgent(parcel, GP_LAND_ALLOW_FLY)) { return TRUE; } return parcel->getAllowFly(); } //----------------------------------------------------------------------------- // setFlying() //----------------------------------------------------------------------------- void LLAgent::setFlying(BOOL fly) { if (mAvatarObject.notNull()) { if(mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_STANDUP) != mAvatarObject->mSignaledAnimations.end()) { return; } // don't allow taking off while sitting if (fly && mAvatarObject->mIsSitting) { return; } } if (fly) { BOOL was_flying = getFlying(); if (gParcelMgr) { if (!canFly() && !was_flying) { // parcel doesn't let you start fly // gods can always fly // and it's OK if you're already flying make_ui_sound("UISndBadKeystroke"); return; } } if( !was_flying ) { gViewerStats->incStat(LLViewerStats::ST_FLY_COUNT); } setControlFlags(AGENT_CONTROL_FLY); gSavedSettings.setBOOL("FlyBtnState", TRUE); } else { clearControlFlags(AGENT_CONTROL_FLY); gSavedSettings.setBOOL("FlyBtnState", FALSE); } mbFlagsDirty = TRUE; } // UI based mechanism of setting fly state //----------------------------------------------------------------------------- // toggleFlying() //----------------------------------------------------------------------------- void LLAgent::toggleFlying() { BOOL fly = !(mControlFlags & AGENT_CONTROL_FLY); setFlying( fly ); resetView(); } //----------------------------------------------------------------------------- // setRegion() //----------------------------------------------------------------------------- void LLAgent::setRegion(LLViewerRegion *regionp) { llassert(regionp); if (mRegionp != regionp) { // JC - Avoid this, causes out-of-bounds array write deep within // Windows. // char host_name[MAX_STRING]; // regionp->getHost().getHostName(host_name, MAX_STRING); char ip[MAX_STRING]; /*Flawfinder: ignore*/ regionp->getHost().getString(ip, MAX_STRING); llinfos << "Moving agent into region: " << regionp->getName() << " located at " << ip << llendl; if (mRegionp) { // We've changed regions, we're now going to change our agent coordinate frame. mAgentOriginGlobal = regionp->getOriginGlobal(); LLVector3d agent_offset_global = mRegionp->getOriginGlobal(); LLVector3 delta; delta.setVec(regionp->getOriginGlobal() - mRegionp->getOriginGlobal()); setPositionAgent(getPositionAgent() - delta); LLVector3 camera_position_agent = gCamera->getOrigin(); gCamera->setOrigin(camera_position_agent - delta); // Update all of the regions. gWorldPointer->updateAgentOffset(agent_offset_global); // Hack to keep sky in the agent's region, otherwise it may get deleted - DJS 08/02/02 if (gSky.mVOSkyp) { gSky.mVOSkyp->setRegion(regionp); } if (gSky.mVOStarsp) { gSky.mVOStarsp->setRegion(regionp); } if (gSky.mVOGroundp) { gSky.mVOGroundp->setRegion(regionp); } } else { // First time initialization. // We've changed regions, we're now going to change our agent coordinate frame. mAgentOriginGlobal = regionp->getOriginGlobal(); LLVector3 delta; delta.setVec(regionp->getOriginGlobal()); setPositionAgent(getPositionAgent() - delta); LLVector3 camera_position_agent = gCamera->getOrigin(); gCamera->setOrigin(camera_position_agent - delta); // Update all of the regions. gWorldPointer->updateAgentOffset(mAgentOriginGlobal); } } mRegionp = regionp; // Must shift hole-covering water object locations because local // coordinate frame changed. gWorldPointer->updateWaterObjects(); // keep a list of regions we've been too // this is just an interesting stat, logged at the dataserver // we could trake this at the dataserver side, but that's harder U64 handle = regionp->getHandle(); mRegionsVisited.insert(handle); gSelectMgr->updateSelectionCenter(); } //----------------------------------------------------------------------------- // getRegion() //----------------------------------------------------------------------------- LLViewerRegion *LLAgent::getRegion() const { return mRegionp; } const LLHost& LLAgent::getRegionHost() const { if (mRegionp) { return mRegionp->getHost(); } else { return LLHost::invalid; } } //----------------------------------------------------------------------------- // inPrelude() //----------------------------------------------------------------------------- BOOL LLAgent::inPrelude() { return mRegionp && mRegionp->isPrelude(); } //----------------------------------------------------------------------------- // canManageEstate() //----------------------------------------------------------------------------- BOOL LLAgent::canManageEstate() const { return mRegionp && mRegionp->canManageEstate(); } //----------------------------------------------------------------------------- // sendMessage() //----------------------------------------------------------------------------- void LLAgent::sendMessage() { if (gDisconnected) { llwarns << "Trying to send message when disconnected!" << llendl; return; } if (!mRegionp) { llerrs << "No region for agent yet!" << llendl; } gMessageSystem->sendMessage(mRegionp->getHost()); } //----------------------------------------------------------------------------- // sendReliableMessage() //----------------------------------------------------------------------------- void LLAgent::sendReliableMessage() { if (gDisconnected) { llwarns << "Trying to send message when disconnected!" << llendl; return; } if (!mRegionp) { llwarns << "LLAgent::sendReliableMessage No region for agent yet, not sending message!" << llendl; return; } gMessageSystem->sendReliable(mRegionp->getHost()); } //----------------------------------------------------------------------------- // getVelocity() //----------------------------------------------------------------------------- LLVector3 LLAgent::getVelocity() const { if (mAvatarObject) { return mAvatarObject->getVelocity(); } else { return LLVector3::zero; } } //----------------------------------------------------------------------------- // setPositionAgent() //----------------------------------------------------------------------------- void LLAgent::setPositionAgent(const LLVector3 &pos_agent) { if (!pos_agent.isFinite()) { llerrs << "setPositionAgent is not a number" << llendl; } if (!mAvatarObject.isNull() && mAvatarObject->getParent()) { LLVector3 pos_agent_sitting; LLVector3d pos_agent_d; LLViewerObject *parent = (LLViewerObject*)mAvatarObject->getParent(); pos_agent_sitting = mAvatarObject->getPosition() * parent->getRotation() + parent->getPositionAgent(); pos_agent_d.setVec(pos_agent_sitting); mFrameAgent.setOrigin(pos_agent_sitting); mPositionGlobal = pos_agent_d + mAgentOriginGlobal; } else { mFrameAgent.setOrigin(pos_agent); LLVector3d pos_agent_d; pos_agent_d.setVec(pos_agent); mPositionGlobal = pos_agent_d + mAgentOriginGlobal; } } //----------------------------------------------------------------------------- // slamLookAt() //----------------------------------------------------------------------------- void LLAgent::slamLookAt(const LLVector3 &look_at) { LLVector3 look_at_norm = look_at; look_at_norm.mV[VZ] = 0.f; look_at_norm.normVec(); resetAxes(look_at_norm); } //----------------------------------------------------------------------------- // getPositionGlobal() //----------------------------------------------------------------------------- const LLVector3d &LLAgent::getPositionGlobal() { if (!mAvatarObject.isNull() && !mAvatarObject->mDrawable.isNull()) { mPositionGlobal = getPosGlobalFromAgent(mAvatarObject->getRenderPosition()); } else { mPositionGlobal = getPosGlobalFromAgent(mFrameAgent.getOrigin()); } return mPositionGlobal; } //----------------------------------------------------------------------------- // getPositionAgent() //----------------------------------------------------------------------------- const LLVector3 &LLAgent::getPositionAgent() { if(!mAvatarObject.isNull() && !mAvatarObject->mDrawable.isNull()) { mFrameAgent.setOrigin(mAvatarObject->getRenderPosition()); } return mFrameAgent.getOrigin(); } //----------------------------------------------------------------------------- // getRegionsVisited() //----------------------------------------------------------------------------- const S32 LLAgent::getRegionsVisited() const { return mRegionsVisited.size(); } //----------------------------------------------------------------------------- // getDistanceTraveled() //----------------------------------------------------------------------------- const F64 LLAgent::getDistanceTraveled() const { return mDistanceTraveled; } //----------------------------------------------------------------------------- // getPosAgentFromGlobal() //----------------------------------------------------------------------------- LLVector3 LLAgent::getPosAgentFromGlobal(const LLVector3d &pos_global) const { LLVector3 pos_agent; pos_agent.setVec(pos_global - mAgentOriginGlobal); return pos_agent; } //----------------------------------------------------------------------------- // getPosGlobalFromAgent() //----------------------------------------------------------------------------- LLVector3d LLAgent::getPosGlobalFromAgent(const LLVector3 &pos_agent) const { LLVector3d pos_agent_d; pos_agent_d.setVec(pos_agent); return pos_agent_d + mAgentOriginGlobal; } //----------------------------------------------------------------------------- // resetAxes() //----------------------------------------------------------------------------- void LLAgent::resetAxes() { mFrameAgent.resetAxes(); } // Copied from LLCamera::setOriginAndLookAt // Look_at must be unit vector //----------------------------------------------------------------------------- // resetAxes() //----------------------------------------------------------------------------- void LLAgent::resetAxes(const LLVector3 &look_at) { LLVector3 skyward = getReferenceUpVector(); // if look_at has zero length, fail // if look_at and skyward are parallel, fail // // Test both of these conditions with a cross product. LLVector3 cross(look_at % skyward); if (cross.isNull()) { llinfos << "LLAgent::resetAxes cross-product is zero" << llendl; return; } // Make sure look_at and skyward are not parallel // and neither are zero length LLVector3 left(skyward % look_at); LLVector3 up(look_at % left); mFrameAgent.setAxes(look_at, left, up); } //----------------------------------------------------------------------------- // rotate() //----------------------------------------------------------------------------- void LLAgent::rotate(F32 angle, const LLVector3 &axis) { mFrameAgent.rotate(angle, axis); } //----------------------------------------------------------------------------- // rotate() //----------------------------------------------------------------------------- void LLAgent::rotate(F32 angle, F32 x, F32 y, F32 z) { mFrameAgent.rotate(angle, x, y, z); } //----------------------------------------------------------------------------- // rotate() //----------------------------------------------------------------------------- void LLAgent::rotate(const LLMatrix3 &matrix) { mFrameAgent.rotate(matrix); } //----------------------------------------------------------------------------- // rotate() //----------------------------------------------------------------------------- void LLAgent::rotate(const LLQuaternion &quaternion) { mFrameAgent.rotate(quaternion); } //----------------------------------------------------------------------------- // getReferenceUpVector() //----------------------------------------------------------------------------- LLVector3 LLAgent::getReferenceUpVector() { // this vector is in the coordinate frame of the avatar's parent object, or the world if none LLVector3 up_vector = LLVector3::z_axis; if (mAvatarObject.notNull() && mAvatarObject->getParent() && mAvatarObject->mDrawable.notNull()) { U32 camera_mode = mCameraAnimating ? mLastCameraMode : mCameraMode; // and in third person... if (camera_mode == CAMERA_MODE_THIRD_PERSON) { // make the up vector point to the absolute +z axis up_vector = up_vector * ~((LLViewerObject*)mAvatarObject->getParent())->getRenderRotation(); } else if (camera_mode == CAMERA_MODE_MOUSELOOK) { // make the up vector point to the avatar's +z axis up_vector = up_vector * mAvatarObject->mDrawable->getRotation(); } } return up_vector; } // Radians, positive is forward into ground //----------------------------------------------------------------------------- // pitch() //----------------------------------------------------------------------------- void LLAgent::pitch(F32 angle) { // don't let user pitch if pointed almost all the way down or up // A dot B = mag(A) * mag(B) * cos(angle between A and B) // so... cos(angle between A and B) = A dot B / mag(A) / mag(B) // = A dot B for unit vectors LLVector3 skyward = getReferenceUpVector(); F32 look_down_limit; F32 look_up_limit = 10.f * DEG_TO_RAD; F32 angle_from_skyward = acos( mFrameAgent.getAtAxis() * skyward ); if (mAvatarObject.notNull() && mAvatarObject->mIsSitting) { look_down_limit = 130.f * DEG_TO_RAD; } else { look_down_limit = 170.f * DEG_TO_RAD; } // clamp pitch to limits if ((angle >= 0.f) && (angle_from_skyward + angle > look_down_limit)) { angle = look_down_limit - angle_from_skyward; } else if ((angle < 0.f) && (angle_from_skyward + angle < look_up_limit)) { angle = look_up_limit - angle_from_skyward; } mFrameAgent.pitch(angle); } //----------------------------------------------------------------------------- // roll() //----------------------------------------------------------------------------- void LLAgent::roll(F32 angle) { mFrameAgent.roll(angle); } //----------------------------------------------------------------------------- // yaw() //----------------------------------------------------------------------------- void LLAgent::yaw(F32 angle) { if (!rotateGrabbed()) { mFrameAgent.rotate(angle, getReferenceUpVector()); } } // Returns a quat that represents the rotation of the agent in the absolute frame //----------------------------------------------------------------------------- // getQuat() //----------------------------------------------------------------------------- LLQuaternion LLAgent::getQuat() const { return mFrameAgent.getQuaternion(); } //----------------------------------------------------------------------------- // calcFocusOffset() //----------------------------------------------------------------------------- LLVector3d LLAgent::calcFocusOffset(LLViewerObject *object, S32 x, S32 y) { // calculate offset based on view direction BOOL is_avatar = object->isAvatar(); LLMatrix4 obj_matrix = is_avatar ? ((LLVOAvatar*)object)->mPelvisp->getWorldMatrix() : object->getRenderMatrix(); LLQuaternion obj_rot = is_avatar ? ((LLVOAvatar*)object)->mPelvisp->getWorldRotation() : object->getRenderRotation(); LLVector3 obj_pos = is_avatar ? ((LLVOAvatar*)object)->mPelvisp->getWorldPosition() : object->getRenderPosition(); LLQuaternion inv_obj_rot = ~obj_rot; LLVector3 obj_dir_abs = obj_pos - gCamera->getOrigin(); obj_dir_abs.rotVec(inv_obj_rot); obj_dir_abs.normVec(); obj_dir_abs.abs(); LLVector3 object_extents = object->getScale(); // make sure they object extents are non-zero object_extents.clamp(0.001f, F32_MAX); LLVector3 object_half_extents = object_extents * 0.5f; obj_dir_abs.mV[VX] = obj_dir_abs.mV[VX] / object_extents.mV[VX]; obj_dir_abs.mV[VY] = obj_dir_abs.mV[VY] / object_extents.mV[VY]; obj_dir_abs.mV[VZ] = obj_dir_abs.mV[VZ] / object_extents.mV[VZ]; LLVector3 normal; if (obj_dir_abs.mV[VX] > obj_dir_abs.mV[VY] && obj_dir_abs.mV[VX] > obj_dir_abs.mV[VZ]) { normal.setVec(obj_matrix.getFwdRow4()); } else if (obj_dir_abs.mV[VY] > obj_dir_abs.mV[VZ]) { normal.setVec(obj_matrix.getLeftRow4()); } else { normal.setVec(obj_matrix.getUpRow4()); } normal.normVec(); LLVector3d focus_pt_global; // RN: should we check return value for valid pick? gViewerWindow->mousePointOnPlaneGlobal(focus_pt_global, x, y, gAgent.getPosGlobalFromAgent(obj_pos), normal); LLVector3 focus_pt = gAgent.getPosAgentFromGlobal(focus_pt_global); // find vector from camera to focus point in object coordinates LLVector3 camera_focus_vec = focus_pt - gCamera->getOrigin(); // convert to object-local space camera_focus_vec.rotVec(inv_obj_rot); // find vector from object origin to focus point in object coordinates LLVector3 focus_delta = focus_pt - obj_pos; // convert to object-local space focus_delta.rotVec(inv_obj_rot); // calculate clip percentage needed to get focus offset back in bounds along the camera_focus axis LLVector3 clip_fraction; for (U32 axis = VX; axis <= VZ; axis++) { F32 clip_amt; if (focus_delta.mV[axis] > 0.f) { clip_amt = llmax(0.f, focus_delta.mV[axis] - object_half_extents.mV[axis]); } else { clip_amt = llmin(0.f, focus_delta.mV[axis] + object_half_extents.mV[axis]); } // don't divide by very small nunber if (llabs(camera_focus_vec.mV[axis]) < 0.0001f) { clip_fraction.mV[axis] = 0.f; } else { clip_fraction.mV[axis] = clip_amt / camera_focus_vec.mV[axis]; } } LLVector3 abs_clip_fraction = clip_fraction; abs_clip_fraction.abs(); // find greatest shrinkage factor and // rescale focus offset to inside object extents if (abs_clip_fraction.mV[VX] > abs_clip_fraction.mV[VY] && abs_clip_fraction.mV[VX] > abs_clip_fraction.mV[VZ]) { focus_delta -= clip_fraction.mV[VX] * camera_focus_vec; } else if (abs_clip_fraction.mV[VY] > abs_clip_fraction.mV[VZ]) { focus_delta -= clip_fraction.mV[VY] * camera_focus_vec; } else { focus_delta -= clip_fraction.mV[VZ] * camera_focus_vec; } // convert back to world space focus_delta.rotVec(obj_rot); if (!is_avatar) { //unproject relative clicked coordinate from window coordinate using GL glPushMatrix(); glMatrixMode(GL_PROJECTION); glLoadMatrixf((const GLfloat*) gCamera->getProjection().mMatrix); glMatrixMode(GL_MODELVIEW); glLoadMatrixf((const GLfloat*) gCamera->getModelview().mMatrix); glMultMatrixf((const GLfloat*) obj_matrix.mMatrix); GLint viewport[4]; GLdouble modelview[16]; GLdouble projection[16]; GLfloat winX, winY, winZ; GLdouble posX, posY, posZ; glGetDoublev( GL_MODELVIEW_MATRIX, modelview ); glGetDoublev( GL_PROJECTION_MATRIX, projection ); glGetIntegerv( GL_VIEWPORT, viewport ); winX = ((F32)x) * gViewerWindow->getDisplayScale().mV[VX]; winY = ((F32)y) * gViewerWindow->getDisplayScale().mV[VY]; glReadPixels( llfloor(winX), llfloor(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ ); gluUnProject( winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ); glPopMatrix(); LLVector3 obj_rel = LLVector3((F32)posX, (F32)posY, (F32)posZ); LLVector3 obj_center = LLVector3(0, 0, 0); obj_rel = obj_rel * object->getRenderMatrix(); //mDrawable->getWorldMatrix(); obj_center = obj_center * object->getRenderMatrix();//mDrawable->getWorldMatrix(); obj_rel -= object->getRenderPosition();//mDrawable->getWorldPosition(); //now that we have the object relative position, we should bias toward the center of the object //based on the distance of the camera to the focus point vs. the distance of the camera to the focus F32 relDist = llabs(obj_rel * gCamera->getAtAxis()); F32 viewDist = dist_vec(obj_center + obj_rel, gCamera->getOrigin()); LLBBox obj_bbox = object->getBoundingBoxAgent(); F32 bias = 0.f; LLVector3 virtual_camera_pos = gAgent.getPosAgentFromGlobal(mFocusTargetGlobal + (getCameraPositionGlobal() - mFocusTargetGlobal) / (1.f + mCameraFOVZoomFactor)); if(obj_bbox.containsPointAgent(virtual_camera_pos)) { // if the camera is inside the object (large, hollow objects, for example) // force focus point all the way to destination depth, away from object center bias = 1.f; } else { // perform magic number biasing of focus point towards surface vs. planar center bias = clamp_rescale(relDist/viewDist, 0.1f, 0.7f, 0.0f, 1.0f); } obj_rel = lerp(focus_delta, obj_rel, bias); return LLVector3d(obj_rel); } return LLVector3d(focus_delta.mV[VX], focus_delta.mV[VY], focus_delta.mV[VZ]); } //----------------------------------------------------------------------------- // calcCameraMinDistance() //----------------------------------------------------------------------------- BOOL LLAgent::calcCameraMinDistance(F32 &obj_min_distance) { BOOL soft_limit = FALSE; // is the bounding box to be treated literally (volumes) or as an approximation (avatars) if (!mFocusObject || mFocusObject->isDead()) { obj_min_distance = 0.f; return TRUE; } if (mFocusObject->mDrawable.isNull()) { #ifdef LL_RELEASE_FOR_DOWNLOAD llwarns << "Focus object with no drawable!" << llendl; #else mFocusObject->dump(); llerrs << "Focus object with no drawable!" << llendl; #endif obj_min_distance = 0.f; return TRUE; } LLQuaternion inv_object_rot = ~mFocusObject->getRenderRotation(); LLVector3 target_offset_origin = mFocusObjectOffset; LLVector3 camera_offset_target(getCameraPositionAgent() - getPosAgentFromGlobal(mFocusTargetGlobal)); // convert offsets into object local space camera_offset_target.rotVec(inv_object_rot); target_offset_origin.rotVec(inv_object_rot); // push around object extents based on target offset LLVector3 object_extents = mFocusObject->getScale(); if (mFocusObject->isAvatar()) { // fudge factors that lets you zoom in on avatars a bit more (which don't do FOV zoom) object_extents.mV[VX] *= AVATAR_ZOOM_MIN_X_FACTOR; object_extents.mV[VY] *= AVATAR_ZOOM_MIN_Y_FACTOR; object_extents.mV[VZ] *= AVATAR_ZOOM_MIN_Z_FACTOR; soft_limit = TRUE; } LLVector3 abs_target_offset = target_offset_origin; abs_target_offset.abs(); LLVector3 target_offset_dir = target_offset_origin; F32 object_radius = mFocusObject->getVObjRadius(); BOOL target_outside_object_extents = FALSE; for (U32 i = VX; i <= VZ; i++) { if (abs_target_offset.mV[i] * 2.f > object_extents.mV[i] + OBJECT_EXTENTS_PADDING) { target_outside_object_extents = TRUE; } if (camera_offset_target.mV[i] > 0.f) { object_extents.mV[i] -= target_offset_origin.mV[i] * 2.f; } else { object_extents.mV[i] += target_offset_origin.mV[i] * 2.f; } } // don't shrink the object extents so far that the object inverts object_extents.clamp(0.001f, F32_MAX); // move into first octant LLVector3 camera_offset_target_abs_norm = camera_offset_target; camera_offset_target_abs_norm.abs(); // make sure offset is non-zero camera_offset_target_abs_norm.clamp(0.001f, F32_MAX); camera_offset_target_abs_norm.normVec(); // find camera position relative to normalized object extents LLVector3 camera_offset_target_scaled = camera_offset_target_abs_norm; camera_offset_target_scaled.mV[VX] /= object_extents.mV[VX]; camera_offset_target_scaled.mV[VY] /= object_extents.mV[VY]; camera_offset_target_scaled.mV[VZ] /= object_extents.mV[VZ]; if (camera_offset_target_scaled.mV[VX] > camera_offset_target_scaled.mV[VY] && camera_offset_target_scaled.mV[VX] > camera_offset_target_scaled.mV[VZ]) { if (camera_offset_target_abs_norm.mV[VX] < 0.001f) { obj_min_distance = object_extents.mV[VX] * 0.5f; } else { obj_min_distance = object_extents.mV[VX] * 0.5f / camera_offset_target_abs_norm.mV[VX]; } } else if (camera_offset_target_scaled.mV[VY] > camera_offset_target_scaled.mV[VZ]) { if (camera_offset_target_abs_norm.mV[VY] < 0.001f) { obj_min_distance = object_extents.mV[VY] * 0.5f; } else { obj_min_distance = object_extents.mV[VY] * 0.5f / camera_offset_target_abs_norm.mV[VY]; } } else { if (camera_offset_target_abs_norm.mV[VZ] < 0.001f) { obj_min_distance = object_extents.mV[VZ] * 0.5f; } else { obj_min_distance = object_extents.mV[VZ] * 0.5f / camera_offset_target_abs_norm.mV[VZ]; } } LLVector3 object_split_axis; LLVector3 target_offset_scaled = target_offset_origin; target_offset_scaled.abs(); target_offset_scaled.normVec(); target_offset_scaled.mV[VX] /= object_extents.mV[VX]; target_offset_scaled.mV[VY] /= object_extents.mV[VY]; target_offset_scaled.mV[VZ] /= object_extents.mV[VZ]; if (target_offset_scaled.mV[VX] > target_offset_scaled.mV[VY] && target_offset_scaled.mV[VX] > target_offset_scaled.mV[VZ]) { object_split_axis = LLVector3::x_axis; } else if (target_offset_scaled.mV[VY] > target_offset_scaled.mV[VZ]) { object_split_axis = LLVector3::y_axis; } else { object_split_axis = LLVector3::z_axis; } LLVector3 camera_offset_object(getCameraPositionAgent() - mFocusObject->getPositionAgent()); // length projected orthogonal to target offset F32 camera_offset_dist = (camera_offset_object - target_offset_dir * (camera_offset_object * target_offset_dir)).magVec(); // calculate whether the target point would be "visible" if it were outside the bounding box // on the opposite of the splitting plane defined by object_split_axis; BOOL exterior_target_visible = FALSE; if (camera_offset_dist > object_radius) { // target is visible from camera, so turn off fov zoom exterior_target_visible = TRUE; } F32 camera_offset_clip = camera_offset_object * object_split_axis; F32 target_offset_clip = target_offset_dir * object_split_axis; // target has moved outside of object extents // check to see if camera and target are on same side if (target_outside_object_extents) { if (camera_offset_clip > 0.f && target_offset_clip > 0.f) { return FALSE; } else if (camera_offset_clip < 0.f && target_offset_clip < 0.f) { return FALSE; } } // clamp obj distance to diagonal of 10 by 10 cube obj_min_distance = llmin(obj_min_distance, 10.f * F_SQRT3); obj_min_distance += gCamera->getNear() + (soft_limit ? 0.1f : 0.2f); return TRUE; } F32 LLAgent::getCameraZoomFraction() { // 0.f -> camera zoomed all the way out // 1.f -> camera zoomed all the way in LLObjectSelectionHandle selection = gSelectMgr->getSelection(); if (selection->getObjectCount() && selection->getSelectType() == SELECT_TYPE_HUD) { // already [0,1] return mAvatarObject->mHUDTargetZoom; } else if (mFocusOnAvatar && cameraThirdPerson()) { return clamp_rescale(mCameraZoomFraction, MIN_ZOOM_FRACTION, MAX_ZOOM_FRACTION, 1.f, 0.f); } else if (cameraCustomizeAvatar()) { F32 distance = (F32)mCameraFocusOffsetTarget.magVec(); return clamp_rescale(distance, APPEARANCE_MIN_ZOOM, APPEARANCE_MAX_ZOOM, 1.f, 0.f ); } else { F32 min_zoom; const F32 DIST_FUDGE = 16.f; // meters F32 max_zoom = gWorldPointer ? llmin(mDrawDistance - DIST_FUDGE, gWorldPointer->getRegionWidthInMeters() - DIST_FUDGE, MAX_CAMERA_DISTANCE_FROM_AGENT) : MAX_CAMERA_DISTANCE_FROM_AGENT; F32 distance = (F32)mCameraFocusOffsetTarget.magVec(); if (mFocusObject.notNull()) { if (mFocusObject->isAvatar()) { min_zoom = AVATAR_MIN_ZOOM; } else { min_zoom = OBJECT_MIN_ZOOM; } } else { min_zoom = LAND_MIN_ZOOM; } return clamp_rescale(distance, min_zoom, max_zoom, 1.f, 0.f); } } void LLAgent::setCameraZoomFraction(F32 fraction) { // 0.f -> camera zoomed all the way out // 1.f -> camera zoomed all the way in LLObjectSelectionHandle selection = gSelectMgr->getSelection(); if (selection->getObjectCount() && selection->getSelectType() == SELECT_TYPE_HUD) { mAvatarObject->mHUDTargetZoom = fraction; } else if (mFocusOnAvatar && cameraThirdPerson()) { mCameraZoomFraction = rescale(fraction, 0.f, 1.f, MAX_ZOOM_FRACTION, MIN_ZOOM_FRACTION); } else if (cameraCustomizeAvatar()) { LLVector3d camera_offset_dir = mCameraFocusOffsetTarget; camera_offset_dir.normVec(); mCameraFocusOffsetTarget = camera_offset_dir * rescale(fraction, 0.f, 1.f, APPEARANCE_MAX_ZOOM, APPEARANCE_MIN_ZOOM); } else { F32 min_zoom = LAND_MIN_ZOOM; const F32 DIST_FUDGE = 16.f; // meters F32 max_zoom = llmin(mDrawDistance - DIST_FUDGE, gWorldPointer->getRegionWidthInMeters() - DIST_FUDGE, MAX_CAMERA_DISTANCE_FROM_AGENT); if (mFocusObject.notNull()) { if (mFocusObject.notNull()) { if (mFocusObject->isAvatar()) { min_zoom = AVATAR_MIN_ZOOM; } else { min_zoom = OBJECT_MIN_ZOOM; } } } LLVector3d camera_offset_dir = mCameraFocusOffsetTarget; camera_offset_dir.normVec(); mCameraFocusOffsetTarget = camera_offset_dir * rescale(fraction, 0.f, 1.f, max_zoom, min_zoom); } startCameraAnimation(); } //----------------------------------------------------------------------------- // cameraOrbitAround() //----------------------------------------------------------------------------- void LLAgent::cameraOrbitAround(const F32 radians) { LLObjectSelectionHandle selection = gSelectMgr->getSelection(); if (selection->getObjectCount() && selection->getSelectType() == SELECT_TYPE_HUD) { // do nothing for hud selection } else if (mFocusOnAvatar && (mCameraMode == CAMERA_MODE_THIRD_PERSON || mCameraMode == CAMERA_MODE_FOLLOW)) { mFrameAgent.rotate(radians, getReferenceUpVector()); } else { mCameraFocusOffsetTarget.rotVec(radians, 0.f, 0.f, 1.f); cameraZoomIn(1.f); } } //----------------------------------------------------------------------------- // cameraOrbitOver() //----------------------------------------------------------------------------- void LLAgent::cameraOrbitOver(const F32 angle) { LLObjectSelectionHandle selection = gSelectMgr->getSelection(); if (selection->getObjectCount() && selection->getSelectType() == SELECT_TYPE_HUD) { // do nothing for hud selection } else if (mFocusOnAvatar && mCameraMode == CAMERA_MODE_THIRD_PERSON) { pitch(angle); } else { LLVector3 camera_offset_unit(mCameraFocusOffsetTarget); camera_offset_unit.normVec(); F32 angle_from_up = acos( camera_offset_unit * getReferenceUpVector() ); LLVector3d left_axis; left_axis.setVec(gCamera->getLeftAxis()); F32 new_angle = llclamp(angle_from_up - angle, 1.f * DEG_TO_RAD, 179.f * DEG_TO_RAD); mCameraFocusOffsetTarget.rotVec(angle_from_up - new_angle, left_axis); cameraZoomIn(1.f); } } //----------------------------------------------------------------------------- // cameraZoomIn() //----------------------------------------------------------------------------- void LLAgent::cameraZoomIn(const F32 fraction) { if (gDisconnected) { return; } LLObjectSelectionHandle selection = gSelectMgr->getSelection(); if (selection->getObjectCount() && selection->getSelectType() == SELECT_TYPE_HUD) { // just update hud zoom level mAvatarObject->mHUDTargetZoom /= fraction; return; } LLVector3d camera_offset(mCameraFocusOffsetTarget); LLVector3d camera_offset_unit(mCameraFocusOffsetTarget); F32 min_zoom = LAND_MIN_ZOOM; F32 current_distance = (F32)camera_offset_unit.normVec(); F32 new_distance = current_distance * fraction; // Don't move through focus point if (mFocusObject) { LLVector3 camera_offset_dir((F32)camera_offset_unit.mdV[VX], (F32)camera_offset_unit.mdV[VY], (F32)camera_offset_unit.mdV[VZ]); if (mFocusObject->isAvatar()) { calcCameraMinDistance(min_zoom); } else { min_zoom = OBJECT_MIN_ZOOM; } } new_distance = llmax(new_distance, min_zoom); // Don't zoom too far back const F32 DIST_FUDGE = 16.f; // meters F32 max_distance = llmin(mDrawDistance - DIST_FUDGE, gWorldPointer->getRegionWidthInMeters() - DIST_FUDGE ); if (new_distance > max_distance) { new_distance = max_distance; /* // Unless camera is unlocked if (!LLViewerCamera::sDisableCameraConstraints) { return; } */ } if( cameraCustomizeAvatar() ) { new_distance = llclamp( new_distance, APPEARANCE_MIN_ZOOM, APPEARANCE_MAX_ZOOM ); } mCameraFocusOffsetTarget = new_distance * camera_offset_unit; } //----------------------------------------------------------------------------- // cameraOrbitIn() //----------------------------------------------------------------------------- void LLAgent::cameraOrbitIn(const F32 meters) { if (mFocusOnAvatar && mCameraMode == CAMERA_MODE_THIRD_PERSON) { F32 camera_offset_dist = llmax(0.001f, mCameraOffsetDefault.magVec()); mCameraZoomFraction = (mTargetCameraDistance - meters) / camera_offset_dist; if (!gSavedSettings.getBOOL("FreezeTime") && mCameraZoomFraction < MIN_ZOOM_FRACTION && meters > 0.f) { // No need to animate, camera is already there. changeCameraToMouselook(FALSE); } mCameraZoomFraction = llclamp(mCameraZoomFraction, MIN_ZOOM_FRACTION, MAX_ZOOM_FRACTION); } else { LLVector3d camera_offset(mCameraFocusOffsetTarget); LLVector3d camera_offset_unit(mCameraFocusOffsetTarget); F32 current_distance = (F32)camera_offset_unit.normVec(); F32 new_distance = current_distance - meters; F32 min_zoom = LAND_MIN_ZOOM; // Don't move through focus point if (mFocusObject.notNull()) { if (mFocusObject->isAvatar()) { min_zoom = AVATAR_MIN_ZOOM; } else { min_zoom = OBJECT_MIN_ZOOM; } } new_distance = llmax(new_distance, min_zoom); // Don't zoom too far back const F32 DIST_FUDGE = 16.f; // meters F32 max_distance = llmin(mDrawDistance - DIST_FUDGE, gWorldPointer->getRegionWidthInMeters() - DIST_FUDGE ); if (new_distance > max_distance) { // Unless camera is unlocked if (!LLViewerCamera::sDisableCameraConstraints) { return; } } if( CAMERA_MODE_CUSTOMIZE_AVATAR == getCameraMode() ) { llclamp( new_distance, APPEARANCE_MIN_ZOOM, APPEARANCE_MAX_ZOOM ); } // Compute new camera offset mCameraFocusOffsetTarget = new_distance * camera_offset_unit; cameraZoomIn(1.f); } } //----------------------------------------------------------------------------- // cameraPanIn() //----------------------------------------------------------------------------- void LLAgent::cameraPanIn(F32 meters) { LLVector3d at_axis; at_axis.setVec(gCamera->getAtAxis()); mFocusTargetGlobal += meters * at_axis; mFocusGlobal = mFocusTargetGlobal; // don't enforce zoom constraints as this is the only way for users to get past them easily updateFocusOffset(); } //----------------------------------------------------------------------------- // cameraPanLeft() //----------------------------------------------------------------------------- void LLAgent::cameraPanLeft(F32 meters) { LLVector3d left_axis; left_axis.setVec(gCamera->getLeftAxis()); mFocusTargetGlobal += meters * left_axis; mFocusGlobal = mFocusTargetGlobal; cameraZoomIn(1.f); updateFocusOffset(); } //----------------------------------------------------------------------------- // cameraPanUp() //----------------------------------------------------------------------------- void LLAgent::cameraPanUp(F32 meters) { LLVector3d up_axis; up_axis.setVec(gCamera->getUpAxis()); mFocusTargetGlobal += meters * up_axis; mFocusGlobal = mFocusTargetGlobal; cameraZoomIn(1.f); updateFocusOffset(); } //----------------------------------------------------------------------------- // setKey() //----------------------------------------------------------------------------- void LLAgent::setKey(const S32 direction, S32 &key) { if (direction > 0) { key = 1; } else if (direction < 0) { key = -1; } else { key = 0; } } //----------------------------------------------------------------------------- // getControlFlags() //----------------------------------------------------------------------------- U32 LLAgent::getControlFlags() { /* // HACK -- avoids maintenance of control flags when camera mode is turned on or off, // only worries about it when the flags are measured if (mCameraMode == CAMERA_MODE_MOUSELOOK) { if ( !(mControlFlags & AGENT_CONTROL_MOUSELOOK) ) { mControlFlags |= AGENT_CONTROL_MOUSELOOK; } } */ return mControlFlags; } //----------------------------------------------------------------------------- // setControlFlags() //----------------------------------------------------------------------------- void LLAgent::setControlFlags(U32 mask) { mControlFlags |= mask; mbFlagsDirty = TRUE; } //----------------------------------------------------------------------------- // clearControlFlags() //----------------------------------------------------------------------------- void LLAgent::clearControlFlags(U32 mask) { U32 old_flags = mControlFlags; mControlFlags &= ~mask; if (old_flags != mControlFlags) { mbFlagsDirty = TRUE; } } //----------------------------------------------------------------------------- // controlFlagsDirty() //----------------------------------------------------------------------------- BOOL LLAgent::controlFlagsDirty() const { return mbFlagsDirty; } //----------------------------------------------------------------------------- // enableControlFlagReset() //----------------------------------------------------------------------------- void LLAgent::enableControlFlagReset() { mbFlagsNeedReset = TRUE; } //----------------------------------------------------------------------------- // resetControlFlags() //----------------------------------------------------------------------------- void LLAgent::resetControlFlags() { if (mbFlagsNeedReset) { mbFlagsNeedReset = FALSE; mbFlagsDirty = FALSE; // reset all of the ephemeral flags // some flags are managed elsewhere mControlFlags &= AGENT_CONTROL_AWAY | AGENT_CONTROL_FLY | AGENT_CONTROL_MOUSELOOK; } } //----------------------------------------------------------------------------- // setAFK() //----------------------------------------------------------------------------- void LLAgent::setAFK() { // Drones can't go AFK if (gNoRender) { return; } if (!gAllowAFK) { return; } if (!gAgent.getRegion()) { // Don't set AFK if we're not talking to a region yet. return; } if (!(mControlFlags & AGENT_CONTROL_AWAY)) { sendAnimationRequest(ANIM_AGENT_AWAY, ANIM_REQUEST_START); setControlFlags(AGENT_CONTROL_AWAY | AGENT_CONTROL_STOP); gAwayTimer.start(); if (gAFKMenu) { gAFKMenu->setLabel("Set Not Away"); } } } //----------------------------------------------------------------------------- // clearAFK() //----------------------------------------------------------------------------- void LLAgent::clearAFK() { gAwayTriggerTimer.reset(); // Gods can sometimes get into away state (via gestures) // without setting the appropriate control flag. JC LLVOAvatar* av = mAvatarObject; if (mControlFlags & AGENT_CONTROL_AWAY || (av && (av->mSignaledAnimations.find(ANIM_AGENT_AWAY) != av->mSignaledAnimations.end()))) { sendAnimationRequest(ANIM_AGENT_AWAY, ANIM_REQUEST_STOP); clearControlFlags(AGENT_CONTROL_AWAY); if (gAFKMenu) { gAFKMenu->setLabel("Set Away"); } } } //----------------------------------------------------------------------------- // getAFK() //----------------------------------------------------------------------------- BOOL LLAgent::getAFK() const { return (mControlFlags & AGENT_CONTROL_AWAY) != 0; } //----------------------------------------------------------------------------- // setBusy() //----------------------------------------------------------------------------- void LLAgent::setBusy() { sendAnimationRequest(ANIM_AGENT_BUSY, ANIM_REQUEST_START); mIsBusy = TRUE; if (gBusyMenu) { gBusyMenu->setLabel("Set Not Busy"); } if (gFloaterMute) { gFloaterMute->updateButtons(); } } //----------------------------------------------------------------------------- // clearBusy() //----------------------------------------------------------------------------- void LLAgent::clearBusy() { mIsBusy = FALSE; sendAnimationRequest(ANIM_AGENT_BUSY, ANIM_REQUEST_STOP); if (gBusyMenu) { gBusyMenu->setLabel("Set Busy"); } if (gFloaterMute) { gFloaterMute->updateButtons(); } } //----------------------------------------------------------------------------- // getBusy() //----------------------------------------------------------------------------- BOOL LLAgent::getBusy() const { return mIsBusy; } //----------------------------------------------------------------------------- // updateWanderTarget() //----------------------------------------------------------------------------- void LLAgent::updateWanderTarget() { S32 num_regions; LLViewerRegion* rand_region; F32 rand_x; F32 rand_y; if (mWanderTimer.checkExpirationAndReset(ll_frand(MAX_WANDER_TIME))) { // Pick a random spot to wander towards num_regions = gWorldPointer->mActiveRegionList.getLength(); S32 region_num = llround(ll_frand() * num_regions); rand_region = gWorldPointer->mActiveRegionList.getFirstData(); S32 i = 0; while (i < region_num) { rand_region = gWorldPointer->mActiveRegionList.getNextData(); i++; } rand_x = ll_frand(rand_region->getWidth()); rand_y = ll_frand(rand_region->getWidth()); stopAutoPilot(); startAutoPilotGlobal(rand_region->getPosGlobalFromRegion(LLVector3(rand_x, rand_y, 0.f))); } } //----------------------------------------------------------------------------- // startAutoPilotGlobal() //----------------------------------------------------------------------------- void LLAgent::startAutoPilotGlobal(const LLVector3d &target_global, const std::string& behavior_name, const LLQuaternion *target_rotation, void (*finish_callback)(BOOL, void *), void *callback_data, F32 stop_distance, F32 rot_threshold) { if (!gAgent.getAvatarObject() || !gWorldPointer) { return; } mAutoPilotFinishedCallback = finish_callback; mAutoPilotCallbackData = callback_data; mAutoPilotRotationThreshold = rot_threshold; mAutoPilotBehaviorName = behavior_name; LLVector3d delta_pos( target_global ); delta_pos -= getPositionGlobal(); F64 distance = delta_pos.magVec(); LLVector3d trace_target = target_global; trace_target.mdV[VZ] -= 10.f; LLVector3d intersection; LLVector3 normal; LLViewerObject *hit_obj; F32 heightDelta = gWorldPointer->resolveStepHeightGlobal(NULL, target_global, trace_target, intersection, normal, &hit_obj); if (stop_distance > 0.f) { mAutoPilotStopDistance = stop_distance; } else { // Guess at a reasonable stop distance. mAutoPilotStopDistance = fsqrtf( distance ); if (mAutoPilotStopDistance < 0.5f) { mAutoPilotStopDistance = 0.5f; } } mAutoPilotFlyOnStop = getFlying(); if (distance > 30.0) { setFlying(TRUE); } if ( distance > 1.f && heightDelta > (sqrtf(mAutoPilotStopDistance) + 1.f)) { setFlying(TRUE); mAutoPilotFlyOnStop = TRUE; } mAutoPilot = TRUE; mAutoPilotTargetGlobal = target_global; // trace ray down to find height of destination from ground LLVector3d traceEndPt = target_global; traceEndPt.mdV[VZ] -= 20.f; LLVector3d targetOnGround; LLVector3 groundNorm; LLViewerObject *obj; gWorldPointer->resolveStepHeightGlobal(NULL, target_global, traceEndPt, targetOnGround, groundNorm, &obj); F64 target_height = llmax((F64)gAgent.getAvatarObject()->getPelvisToFoot(), target_global.mdV[VZ] - targetOnGround.mdV[VZ]); // clamp z value of target to minimum height above ground mAutoPilotTargetGlobal.mdV[VZ] = targetOnGround.mdV[VZ] + target_height; mAutoPilotTargetDist = (F32)dist_vec(gAgent.getPositionGlobal(), mAutoPilotTargetGlobal); if (target_rotation) { mAutoPilotUseRotation = TRUE; mAutoPilotTargetFacing = LLVector3::x_axis * *target_rotation; mAutoPilotTargetFacing.mV[VZ] = 0.f; mAutoPilotTargetFacing.normVec(); } else { mAutoPilotUseRotation = FALSE; } mAutoPilotNoProgressFrameCount = 0; } //----------------------------------------------------------------------------- // startFollowPilot() //----------------------------------------------------------------------------- void LLAgent::startFollowPilot(const LLUUID &leader_id) { if (!mAutoPilot) return; mLeaderID = leader_id; if ( mLeaderID.isNull() ) return; LLViewerObject* object = gObjectList.findObject(mLeaderID); if (!object) { mLeaderID = LLUUID::null; return; } startAutoPilotGlobal(object->getPositionGlobal()); } //----------------------------------------------------------------------------- // stopAutoPilot() //----------------------------------------------------------------------------- void LLAgent::stopAutoPilot(BOOL user_cancel) { if (mAutoPilot) { mAutoPilot = FALSE; if (mAutoPilotUseRotation && !user_cancel) { resetAxes(mAutoPilotTargetFacing); } //NB: auto pilot can terminate for a reason other than reaching the destination if (mAutoPilotFinishedCallback) { mAutoPilotFinishedCallback(!user_cancel && dist_vec(gAgent.getPositionGlobal(), mAutoPilotTargetGlobal) < mAutoPilotStopDistance, mAutoPilotCallbackData); } mLeaderID = LLUUID::null; // If the user cancelled, don't change the fly state if (!user_cancel) { setFlying(mAutoPilotFlyOnStop); } setControlFlags(AGENT_CONTROL_STOP); if (user_cancel && !mAutoPilotBehaviorName.empty()) { if (mAutoPilotBehaviorName == "Sit") LLNotifyBox::showXml("CancelledSit"); else if (mAutoPilotBehaviorName == "Attach") LLNotifyBox::showXml("CancelledAttach"); else LLNotifyBox::showXml("Cancelled"); } } } // Returns necessary agent pitch and yaw changes, radians. //----------------------------------------------------------------------------- // autoPilot() //----------------------------------------------------------------------------- void LLAgent::autoPilot(F32 *delta_yaw) { if (mAutoPilot) { if (!mLeaderID.isNull()) { LLViewerObject* object = gObjectList.findObject(mLeaderID); if (!object) { stopAutoPilot(); return; } mAutoPilotTargetGlobal = object->getPositionGlobal(); } if (!mAvatarObject) { return; } if (mAvatarObject->mInAir) { setFlying(TRUE); } LLVector3 at; at.setVec(mFrameAgent.getAtAxis()); LLVector3 target_agent = getPosAgentFromGlobal(mAutoPilotTargetGlobal); LLVector3 direction = target_agent - getPositionAgent(); F32 target_dist = direction.magVec(); if (target_dist >= mAutoPilotTargetDist) { mAutoPilotNoProgressFrameCount++; if (mAutoPilotNoProgressFrameCount > AUTOPILOT_MAX_TIME_NO_PROGRESS * gFPSClamped) { stopAutoPilot(); return; } } mAutoPilotTargetDist = target_dist; // Make this a two-dimensional solution at.mV[VZ] = 0.f; direction.mV[VZ] = 0.f; at.normVec(); F32 xy_distance = direction.normVec(); F32 yaw = 0.f; if (mAutoPilotTargetDist > mAutoPilotStopDistance) { yaw = angle_between(mFrameAgent.getAtAxis(), direction); } else if (mAutoPilotUseRotation) { // we're close now just aim at target facing yaw = angle_between(at, mAutoPilotTargetFacing); direction = mAutoPilotTargetFacing; } yaw = 4.f * yaw / gFPSClamped; // figure out which direction to turn LLVector3 scratch(at % direction); if (scratch.mV[VZ] > 0.f) { setControlFlags(AGENT_CONTROL_YAW_POS); } else { yaw = -yaw; setControlFlags(AGENT_CONTROL_YAW_NEG); } *delta_yaw = yaw; // Compute when to start slowing down and when to stop F32 stop_distance = mAutoPilotStopDistance; F32 slow_distance; if (getFlying()) { slow_distance = llmax(6.f, mAutoPilotStopDistance + 5.f); stop_distance = llmax(2.f, mAutoPilotStopDistance); } else { slow_distance = llmax(3.f, mAutoPilotStopDistance + 2.f); } // If we're flying, handle autopilot points above or below you. if (getFlying() && xy_distance < AUTOPILOT_HEIGHT_ADJUST_DISTANCE) { if (mAvatarObject) { F64 current_height = mAvatarObject->getPositionGlobal().mdV[VZ]; F32 delta_z = (F32)(mAutoPilotTargetGlobal.mdV[VZ] - current_height); F32 slope = delta_z / xy_distance; if (slope > 0.45f && delta_z > 6.f) { setControlFlags(AGENT_CONTROL_FAST_UP | AGENT_CONTROL_UP_POS); } else if (slope > 0.002f && delta_z > 0.5f) { setControlFlags(AGENT_CONTROL_UP_POS); } else if (slope < -0.45f && delta_z < -6.f && current_height > AUTOPILOT_MIN_TARGET_HEIGHT_OFF_GROUND) { setControlFlags(AGENT_CONTROL_FAST_UP | AGENT_CONTROL_UP_NEG); } else if (slope < -0.002f && delta_z < -0.5f && current_height > AUTOPILOT_MIN_TARGET_HEIGHT_OFF_GROUND) { setControlFlags(AGENT_CONTROL_UP_NEG); } } } // calculate delta rotation to target heading F32 delta_target_heading = angle_between(mFrameAgent.getAtAxis(), mAutoPilotTargetFacing); if (xy_distance > slow_distance && yaw < (F_PI / 10.f)) { // walking/flying fast setControlFlags(AGENT_CONTROL_FAST_AT | AGENT_CONTROL_AT_POS); } else if (mAutoPilotTargetDist > mAutoPilotStopDistance) { // walking/flying slow if (at * direction > 0.9f) { setControlFlags(AGENT_CONTROL_AT_POS); } else if (at * direction < -0.9f) { setControlFlags(AGENT_CONTROL_AT_NEG); } } // check to see if we need to keep rotating to target orientation if (mAutoPilotTargetDist < mAutoPilotStopDistance) { setControlFlags(AGENT_CONTROL_STOP); if(!mAutoPilotUseRotation || (delta_target_heading < mAutoPilotRotationThreshold)) { stopAutoPilot(); } } } } //----------------------------------------------------------------------------- // propagate() //----------------------------------------------------------------------------- void LLAgent::propagate(const F32 dt) { // Update UI based on agent motion LLFloaterMove *floater_move = LLFloaterMove::getInstance(); if (floater_move) { floater_move->mForwardButton ->setToggleState( mAtKey > 0 || mWalkKey > 0 ); floater_move->mBackwardButton ->setToggleState( mAtKey < 0 || mWalkKey < 0 ); floater_move->mSlideLeftButton ->setToggleState( mLeftKey > 0 ); floater_move->mSlideRightButton->setToggleState( mLeftKey < 0 ); floater_move->mTurnLeftButton ->setToggleState( mYawKey > 0.f ); floater_move->mTurnRightButton ->setToggleState( mYawKey < 0.f ); floater_move->mMoveUpButton ->setToggleState( mUpKey > 0 ); floater_move->mMoveDownButton ->setToggleState( mUpKey < 0 ); } // handle rotation based on keyboard levels const F32 YAW_RATE = 90.f * DEG_TO_RAD; // radians per second yaw( YAW_RATE * mYawKey * dt ); const F32 PITCH_RATE = 90.f * DEG_TO_RAD; // radians per second pitch(PITCH_RATE * (F32) mPitchKey * dt); // handle auto-land behavior if (mAvatarObject) { BOOL in_air = mAvatarObject->mInAir; LLVector3 land_vel = getVelocity(); land_vel.mV[VZ] = 0.f; if (!in_air && mUpKey < 0 && land_vel.magVecSquared() < MAX_VELOCITY_AUTO_LAND_SQUARED && gSavedSettings.getBOOL("AutomaticFly")) { // land automatically setFlying(FALSE); } } // clear keys mAtKey = 0; mWalkKey = 0; mLeftKey = 0; mUpKey = 0; mYawKey = 0.f; mPitchKey = 0; } //----------------------------------------------------------------------------- // updateAgentPosition() //----------------------------------------------------------------------------- void LLAgent::updateAgentPosition(const F32 dt, const F32 yaw_radians, const S32 mouse_x, const S32 mouse_y) { propagate(dt); // static S32 cameraUpdateCount = 0; rotate(yaw_radians, 0, 0, 1); // // Check for water and land collision, set underwater flag // updateLookAt(mouse_x, mouse_y); } //----------------------------------------------------------------------------- // updateLookAt() //----------------------------------------------------------------------------- void LLAgent::updateLookAt(const S32 mouse_x, const S32 mouse_y) { static LLVector3 last_at_axis; if ( mAvatarObject.isNull() ) { return; } LLQuaternion av_inv_rot = ~mAvatarObject->mRoot.getWorldRotation(); LLVector3 root_at = LLVector3::x_axis * mAvatarObject->mRoot.getWorldRotation(); if ((gViewerWindow->getMouseVelocityStat()->getCurrent() < 0.01f) && (root_at * last_at_axis > 0.95f )) { LLVector3 vel = mAvatarObject->getVelocity(); if (vel.magVecSquared() > 4.f) { setLookAt(LOOKAT_TARGET_IDLE, mAvatarObject, vel * av_inv_rot); } else { // *FIX: rotate mframeagent by sit object's rotation? LLQuaternion look_rotation = mAvatarObject->mIsSitting ? mAvatarObject->getRenderRotation() : mFrameAgent.getQuaternion(); // use camera's current rotation LLVector3 look_offset = LLVector3(2.f, 0.f, 0.f) * look_rotation * av_inv_rot; setLookAt(LOOKAT_TARGET_IDLE, mAvatarObject, look_offset); } last_at_axis = root_at; return; } last_at_axis = root_at; if (CAMERA_MODE_CUSTOMIZE_AVATAR == getCameraMode()) { setLookAt(LOOKAT_TARGET_NONE, mAvatarObject, LLVector3(-2.f, 0.f, 0.f)); } else { // Move head based on cursor position ELookAtType lookAtType = LOOKAT_TARGET_NONE; LLVector3 headLookAxis; LLCoordFrame frameCamera = *((LLCoordFrame*)gCamera); F32 x_from_center = mouse_x_from_center( mouse_x ); // range from -0.5 to 0.5 F32 y_from_center = mouse_y_from_center( mouse_y ); // range from -0.5 to 0.5 if (cameraMouselook()) { lookAtType = LOOKAT_TARGET_MOUSELOOK; } else if (cameraThirdPerson()) { frameCamera.yaw( - x_from_center * gSavedSettings.getF32("YawFromMousePosition") * DEG_TO_RAD); frameCamera.pitch( - y_from_center * gSavedSettings.getF32("PitchFromMousePosition") * DEG_TO_RAD); lookAtType = LOOKAT_TARGET_FREELOOK; } headLookAxis = frameCamera.getAtAxis(); // RN: we use world-space offset for mouselook and freelook //headLookAxis = headLookAxis * av_inv_rot; setLookAt(lookAtType, mAvatarObject, headLookAxis); } } // friends and operators std::ostream& operator<<(std::ostream &s, const LLAgent &agent) { // This is unfinished, but might never be used. // We'll just leave it for now; we can always delete it. s << " { " << " Frame = " << agent.mFrameAgent << "\n" << " }"; return s; } // ------------------- Beginning of legacy LLCamera hack ---------------------- // This section is included for legacy LLCamera support until // it is no longer needed. Some legacy code must exist in // non-legacy functions, and is labeled with "// legacy" comments. //----------------------------------------------------------------------------- // setAvatarObject() //----------------------------------------------------------------------------- void LLAgent::setAvatarObject(LLVOAvatar *avatar) { mAvatarObject = avatar; if (!avatar) { llinfos << "Setting LLAgent::mAvatarObject to NULL" << llendl; return; } if (!mLookAt) { mLookAt = (LLHUDEffectLookAt *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_LOOKAT); } if (!mPointAt) { mPointAt = (LLHUDEffectPointAt *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINTAT); } if (!mLookAt.isNull()) { mLookAt->setSourceObject(avatar); } if (!mPointAt.isNull()) { mPointAt->setSourceObject(avatar); } sendAgentWearablesRequest(); } // TRUE if your own avatar needs to be rendered. Usually only // in third person and build. //----------------------------------------------------------------------------- // needsRenderAvatar() //----------------------------------------------------------------------------- BOOL LLAgent::needsRenderAvatar() { if (cameraMouselook() && !LLVOAvatar::sVisibleInFirstPerson) { return FALSE; } return mShowAvatar && mGenderChosen; } // TRUE if we need to render your own avatar's head. BOOL LLAgent::needsRenderHead() { return mShowAvatar && !cameraMouselook(); } //----------------------------------------------------------------------------- // startTyping() //----------------------------------------------------------------------------- void LLAgent::startTyping() { mTypingTimer.reset(); if (getRenderState() & AGENT_STATE_TYPING) { // already typing, don't trigger a different animation return; } setRenderState(AGENT_STATE_TYPING); if (mChatTimer.getElapsedTimeF32() < 2.f) { LLViewerObject* chatter = gObjectList.findObject(mLastChatterID); if (chatter && chatter->isAvatar()) { gAgent.setLookAt(LOOKAT_TARGET_RESPOND, chatter, LLVector3::zero); } } if (gSavedSettings.getBOOL("PlayTypingAnim")) { sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_START); } gChatBar->sendChatFromViewer("", CHAT_TYPE_START, FALSE); } //----------------------------------------------------------------------------- // stopTyping() //----------------------------------------------------------------------------- void LLAgent::stopTyping() { if (mRenderState & AGENT_STATE_TYPING) { clearRenderState(AGENT_STATE_TYPING); sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_STOP); gChatBar->sendChatFromViewer("", CHAT_TYPE_STOP, FALSE); } } //----------------------------------------------------------------------------- // setRenderState() //----------------------------------------------------------------------------- void LLAgent::setRenderState(U8 newstate) { mRenderState |= newstate; } //----------------------------------------------------------------------------- // clearRenderState() //----------------------------------------------------------------------------- void LLAgent::clearRenderState(U8 clearstate) { mRenderState &= ~clearstate; } //----------------------------------------------------------------------------- // getRenderState() //----------------------------------------------------------------------------- U8 LLAgent::getRenderState() { if (gNoRender || gToolMgr == NULL || gSelectMgr == NULL || gKeyboard == NULL) { return 0; } // *FIX: don't do stuff in a getter! This is infinite loop city! if ((mTypingTimer.getElapsedTimeF32() > TYPING_TIMEOUT_SECS) && (mRenderState & AGENT_STATE_TYPING)) { stopTyping(); } if ((!gSelectMgr->getSelection()->isEmpty() && gSelectMgr->shouldShowSelection()) || gToolMgr->getCurrentTool()->isEditing() ) { setRenderState(AGENT_STATE_EDITING); } else { clearRenderState(AGENT_STATE_EDITING); } return mRenderState; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- static const LLFloaterView::skip_list_t& get_skip_list() { static LLFloaterView::skip_list_t skip_list; skip_list.insert(gFloaterMap); return skip_list; } //----------------------------------------------------------------------------- // endAnimationUpdateUI() //----------------------------------------------------------------------------- void LLAgent::endAnimationUpdateUI() { if (mCameraMode == mLastCameraMode) { // We're already done endAnimationUpdateUI for this transition. return; } // clean up UI from mode we're leaving if ( mLastCameraMode == CAMERA_MODE_MOUSELOOK ) { // show mouse cursor gViewerWindow->showCursor(); // show menus gMenuBarView->setVisible(TRUE); gStatusBar->setVisibleForMouselook(true); gToolMgr->setCurrentToolset(gBasicToolset); // Only pop if we have pushed... if (TRUE == mViewsPushed) { mViewsPushed = FALSE; gFloaterView->popVisibleAll(get_skip_list()); } gAgent.setLookAt(LOOKAT_TARGET_CLEAR); if( gMorphView ) { gMorphView->setVisible( FALSE ); } // Disable mouselook-specific animations if (mAvatarObject) { if( mAvatarObject->isAnyAnimationSignaled(AGENT_GUN_AIM_ANIMS, NUM_AGENT_GUN_AIM_ANIMS) ) { if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_AIM_RIFLE_R) != mAvatarObject->mSignaledAnimations.end()) { sendAnimationRequest(ANIM_AGENT_AIM_RIFLE_R, ANIM_REQUEST_STOP); sendAnimationRequest(ANIM_AGENT_HOLD_RIFLE_R, ANIM_REQUEST_START); } if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_AIM_HANDGUN_R) != mAvatarObject->mSignaledAnimations.end()) { sendAnimationRequest(ANIM_AGENT_AIM_HANDGUN_R, ANIM_REQUEST_STOP); sendAnimationRequest(ANIM_AGENT_HOLD_HANDGUN_R, ANIM_REQUEST_START); } if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_AIM_BAZOOKA_R) != mAvatarObject->mSignaledAnimations.end()) { sendAnimationRequest(ANIM_AGENT_AIM_BAZOOKA_R, ANIM_REQUEST_STOP); sendAnimationRequest(ANIM_AGENT_HOLD_BAZOOKA_R, ANIM_REQUEST_START); } if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_AIM_BOW_L) != mAvatarObject->mSignaledAnimations.end()) { sendAnimationRequest(ANIM_AGENT_AIM_BOW_L, ANIM_REQUEST_STOP); sendAnimationRequest(ANIM_AGENT_HOLD_BOW_L, ANIM_REQUEST_START); } } } } else if( mLastCameraMode == CAMERA_MODE_CUSTOMIZE_AVATAR ) { // make sure we ask to save changes gToolMgr->setCurrentToolset(gBasicToolset); // HACK: If we're quitting, and we were in customize avatar, don't // let the mini-map go visible again. JC if (!gQuitRequested) { gFloaterMap->popVisible(); } if( gMorphView ) { gMorphView->setVisible( FALSE ); } if (mAvatarObject) { sendAnimationRequest(ANIM_AGENT_CUSTOMIZE, ANIM_REQUEST_STOP); sendAnimationRequest(ANIM_AGENT_CUSTOMIZE_DONE, ANIM_REQUEST_START); } setLookAt(LOOKAT_TARGET_CLEAR); } //--------------------------------------------------------------------- // Set up UI for mode we're entering //--------------------------------------------------------------------- if (mCameraMode == CAMERA_MODE_MOUSELOOK) { // hide menus gMenuBarView->setVisible(FALSE); gStatusBar->setVisibleForMouselook(false); // clear out camera lag effect mCameraLag.clearVec(); // JC - Added for always chat in third person option gFocusMgr.setKeyboardFocus(NULL, NULL); gToolMgr->setCurrentToolset(gMouselookToolset); mViewsPushed = TRUE; gFloaterView->pushVisibleAll(FALSE, get_skip_list()); if( gMorphView ) { gMorphView->setVisible(FALSE); } gIMView->setFloaterOpen( FALSE ); gConsole->setVisible( TRUE ); if (mAvatarObject) { // Trigger mouselook-specific animations if( mAvatarObject->isAnyAnimationSignaled(AGENT_GUN_HOLD_ANIMS, NUM_AGENT_GUN_HOLD_ANIMS) ) { if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_HOLD_RIFLE_R) != mAvatarObject->mSignaledAnimations.end()) { sendAnimationRequest(ANIM_AGENT_HOLD_RIFLE_R, ANIM_REQUEST_STOP); sendAnimationRequest(ANIM_AGENT_AIM_RIFLE_R, ANIM_REQUEST_START); } if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_HOLD_HANDGUN_R) != mAvatarObject->mSignaledAnimations.end()) { sendAnimationRequest(ANIM_AGENT_HOLD_HANDGUN_R, ANIM_REQUEST_STOP); sendAnimationRequest(ANIM_AGENT_AIM_HANDGUN_R, ANIM_REQUEST_START); } if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_HOLD_BAZOOKA_R) != mAvatarObject->mSignaledAnimations.end()) { sendAnimationRequest(ANIM_AGENT_HOLD_BAZOOKA_R, ANIM_REQUEST_STOP); sendAnimationRequest(ANIM_AGENT_AIM_BAZOOKA_R, ANIM_REQUEST_START); } if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_HOLD_BOW_L) != mAvatarObject->mSignaledAnimations.end()) { sendAnimationRequest(ANIM_AGENT_HOLD_BOW_L, ANIM_REQUEST_STOP); sendAnimationRequest(ANIM_AGENT_AIM_BOW_L, ANIM_REQUEST_START); } } if (mAvatarObject->getParent()) { LLVector3 at_axis = gCamera->getAtAxis(); LLViewerObject* root_object = (LLViewerObject*)mAvatarObject->getRoot(); if (root_object->flagCameraDecoupled()) { resetAxes(at_axis); } else { resetAxes(at_axis * ~((LLViewerObject*)mAvatarObject->getParent())->getRenderRotation()); } } } } else if (mCameraMode == CAMERA_MODE_CUSTOMIZE_AVATAR) { gToolMgr->setCurrentToolset(gFaceEditToolset); gFloaterMap->pushVisible(FALSE); /* LLView *view; for (view = gFloaterView->getFirstChild(); view; view = gFloaterView->getNextChild()) { view->pushVisible(FALSE); } */ if( gMorphView ) { gMorphView->setVisible( TRUE ); } // freeze avatar if (mAvatarObject) { mPauseRequest = mAvatarObject->requestPause(); } } if (getAvatarObject()) { getAvatarObject()->updateAttachmentVisibility(mCameraMode); } gFloaterTools->dirty(); // Don't let this be called more than once if the camera // mode hasn't changed. --JC mLastCameraMode = mCameraMode; } //----------------------------------------------------------------------------- // updateCamera() //----------------------------------------------------------------------------- void LLAgent::updateCamera() { //Ventrella - changed camera_skyward to the new global "mCameraUpVector" mCameraUpVector = LLVector3::z_axis; //LLVector3 camera_skyward(0.f, 0.f, 1.f); //end Ventrella U32 camera_mode = mCameraAnimating ? mLastCameraMode : mCameraMode; validateFocusObject(); if (!mAvatarObject.isNull() && mAvatarObject->mIsSitting && camera_mode == CAMERA_MODE_MOUSELOOK) { //Ventrella //changed camera_skyward to the new global "mCameraUpVector" mCameraUpVector = mCameraUpVector * mAvatarObject->getRenderRotation(); //end Ventrella } if (cameraThirdPerson() && mFocusOnAvatar && LLFollowCamMgr::getActiveFollowCamParams()) { changeCameraToFollow(); } //Ventrella //NOTE - this needs to be integrated into a general upVector system here within llAgent. if ( camera_mode == CAMERA_MODE_FOLLOW && mFocusOnAvatar ) { mCameraUpVector = mFollowCam.getUpVector(); } //end Ventrella if (mSitCameraEnabled) { if (mSitCameraReferenceObject->isDead()) { setSitCamera(LLUUID::null); } } // Update UI with our camera inputs if (gFloaterCamera) { gFloaterCamera->mRotate->setToggleState( mOrbitRightKey > 0.f, // left mOrbitUpKey > 0.f, // top mOrbitLeftKey > 0.f, // right mOrbitDownKey > 0.f); // bottom gFloaterCamera->mZoom->setToggleState( mOrbitInKey > 0.f, // top mOrbitOutKey > 0.f); // bottom gFloaterCamera->mTrack->setToggleState( mPanLeftKey > 0.f, // left mPanUpKey > 0.f, // top mPanRightKey > 0.f, // right mPanDownKey > 0.f); // bottom } // Handle camera movement based on keyboard. const F32 ORBIT_OVER_RATE = 90.f * DEG_TO_RAD; // radians per second const F32 ORBIT_AROUND_RATE = 90.f * DEG_TO_RAD; // radians per second const F32 PAN_RATE = 5.f; // meters per second if( mOrbitUpKey || mOrbitDownKey ) { F32 input_rate = mOrbitUpKey - mOrbitDownKey; cameraOrbitOver( input_rate * ORBIT_OVER_RATE / gFPSClamped ); } if( mOrbitLeftKey || mOrbitRightKey) { F32 input_rate = mOrbitLeftKey - mOrbitRightKey; cameraOrbitAround( input_rate * ORBIT_AROUND_RATE / gFPSClamped ); } if( mOrbitInKey || mOrbitOutKey ) { F32 input_rate = mOrbitInKey - mOrbitOutKey; LLVector3d to_focus = gAgent.getPosGlobalFromAgent(gCamera->getOrigin()) - calcFocusPositionTargetGlobal(); F32 distance_to_focus = (F32)to_focus.magVec(); // Move at distance (in meters) meters per second cameraOrbitIn( input_rate * distance_to_focus / gFPSClamped ); } if( mPanInKey || mPanOutKey ) { F32 input_rate = mPanInKey - mPanOutKey; cameraPanIn( input_rate * PAN_RATE / gFPSClamped ); } if( mPanRightKey || mPanLeftKey ) { F32 input_rate = mPanRightKey - mPanLeftKey; cameraPanLeft( input_rate * -PAN_RATE / gFPSClamped ); } if( mPanUpKey || mPanDownKey ) { F32 input_rate = mPanUpKey - mPanDownKey; cameraPanUp( input_rate * PAN_RATE / gFPSClamped ); } // Clear camera keyboard keys. mOrbitLeftKey = 0.f; mOrbitRightKey = 0.f; mOrbitUpKey = 0.f; mOrbitDownKey = 0.f; mOrbitInKey = 0.f; mOrbitOutKey = 0.f; mPanRightKey = 0.f; mPanLeftKey = 0.f; mPanUpKey = 0.f; mPanDownKey = 0.f; mPanInKey = 0.f; mPanOutKey = 0.f; // lerp camera focus offset mCameraFocusOffset = lerp(mCameraFocusOffset, mCameraFocusOffsetTarget, LLCriticalDamp::getInterpolant(CAMERA_FOCUS_HALF_LIFE)); //Ventrella if ( mCameraMode == CAMERA_MODE_FOLLOW ) { if ( !mAvatarObject.isNull() ) { //-------------------------------------------------------------------------------- // this is where the avatar's position and rotation are given to followCam, and // where it is updated. All three of its attributes are updated: (1) position, // (2) focus, and (3) upvector. They can then be queried elsewhere in llAgent. //-------------------------------------------------------------------------------- // *TODO: use combined rotation of frameagent and sit object LLQuaternion avatarRotationForFollowCam = mAvatarObject->mIsSitting ? mAvatarObject->getRenderRotation() : mFrameAgent.getQuaternion(); LLFollowCamParams* current_cam = LLFollowCamMgr::getActiveFollowCamParams(); if (current_cam) { mFollowCam.copyParams(*current_cam); mFollowCam.setSubjectPositionAndRotation( mAvatarObject->getRenderPosition(), avatarRotationForFollowCam ); mFollowCam.update(); } else { changeCameraToThirdPerson(TRUE); } } } // end Ventrella BOOL hit_limit; LLVector3d camera_pos_global; LLVector3d camera_target_global = calcCameraPositionTargetGlobal(&hit_limit); mCameraVirtualPositionAgent = getPosAgentFromGlobal(camera_target_global); LLVector3d focus_target_global = calcFocusPositionTargetGlobal(); // perform field of view correction mCameraFOVZoomFactor = calcCameraFOVZoomFactor(); camera_target_global = focus_target_global + (camera_target_global - focus_target_global) * (1.f + mCameraFOVZoomFactor); // do alpha fade on focus object F32 fade_increment = mFocusObjectFadeTimer.getElapsedTimeAndResetF32(); if (mFocusObject.notNull() && !mFocusObject->isAttachment() && mFocusObject->mDrawable.notNull()) { F32 increment = fade_increment; if (mFocusObjectDist < -0.2f) { increment *= -1.f; } if (mFocusObject->getVObjRadius() > MIN_RADIUS_ALPHA_SIZZLE) { S32 num_faces = mFocusObject->mDrawable->getNumFaces(); for (S32 i = 0; i < num_faces; i++) { LLFace* facep = mFocusObject->mDrawable->getFace(i); F32 fade = facep->mAlphaFade; fade = llclamp(fade + increment, 0.f, 1.f); facep->mAlphaFade = fade; } } } // do alpha fade in on fade objects std::set< LLPointer >::iterator fade_object_it; for (fade_object_it = mFadeObjects.begin(); fade_object_it != mFadeObjects.end(); ) { LLViewerObject* fade_object = *fade_object_it; if (fade_object->isDead()) { // remove from list mFadeObjects.erase(fade_object_it++); } else { LLDrawable* drawablep = fade_object->mDrawable; if (drawablep && fade_object->getVObjRadius() > MIN_RADIUS_ALPHA_SIZZLE) { S32 num_faces = drawablep->getNumFaces(); BOOL fade_done = TRUE; for (S32 i = 0; i < num_faces; i++) { LLFace* facep = drawablep->getFace(i); F32 fade = facep->mAlphaFade; fade = llclamp(fade - fade_increment, 0.f, 1.f); facep->mAlphaFade = fade; if (fade > 0.f) { fade_done = FALSE; } } if (fade_done) { mFadeObjects.erase(fade_object_it++); } else { fade_object_it++; } } else { fade_object_it++; } } } mShowAvatar = TRUE; // can see avatar by default // Adjust position for animation if (mCameraAnimating) { F32 time = mAnimationTimer.getElapsedTimeF32(); // yet another instance of critically damped motion, hooray! // F32 fraction_of_animation = 1.f - pow(2.f, -time / CAMERA_ZOOM_HALF_LIFE); // linear interpolation F32 fraction_of_animation = time / mAnimationDuration; BOOL isfirstPerson = mCameraMode == CAMERA_MODE_MOUSELOOK; BOOL wasfirstPerson = mLastCameraMode == CAMERA_MODE_MOUSELOOK; F32 fraction_animation_to_skip; if (mAnimationCameraStartGlobal == camera_target_global) { fraction_animation_to_skip = 0.f; } else { LLVector3d cam_delta = mAnimationCameraStartGlobal - camera_target_global; fraction_animation_to_skip = HEAD_BUFFER_SIZE / (F32)cam_delta.magVec(); } F32 animation_start_fraction = (wasfirstPerson) ? fraction_animation_to_skip : 0.f; F32 animation_finish_fraction = (isfirstPerson) ? (1.f - fraction_animation_to_skip) : 1.f; if (fraction_of_animation < animation_finish_fraction) { if (fraction_of_animation < animation_start_fraction || fraction_of_animation > animation_finish_fraction ) { mShowAvatar = FALSE; } // ...adjust position for animation camera_pos_global = lerp(mAnimationCameraStartGlobal, camera_target_global, fraction_of_animation); mFocusGlobal = lerp(mAnimationFocusStartGlobal, focus_target_global, fraction_of_animation); } else { // ...animation complete mCameraAnimating = FALSE; camera_pos_global = camera_target_global; mFocusGlobal = focus_target_global; endAnimationUpdateUI(); mShowAvatar = TRUE; } if (getAvatarObject() && mCameraMode != CAMERA_MODE_MOUSELOOK) { getAvatarObject()->updateAttachmentVisibility(mCameraMode); } } else { camera_pos_global = camera_target_global; mFocusGlobal = focus_target_global; mShowAvatar = TRUE; } mCameraCurrentFOVZoomFactor = lerp(mCameraCurrentFOVZoomFactor, mCameraFOVZoomFactor, LLCriticalDamp::getInterpolant(FOV_ZOOM_HALF_LIFE)); // llinfos << "Current FOV Zoom: " << mCameraCurrentFOVZoomFactor << " Target FOV Zoom: " << mCameraFOVZoomFactor << " Object penetration: " << mFocusObjectDist << llendl; F32 ui_offset = 0.f; if( CAMERA_MODE_CUSTOMIZE_AVATAR == mCameraMode ) { ui_offset = calcCustomizeAvatarUIOffset( camera_pos_global ); } LLVector3 focus_agent = getPosAgentFromGlobal(mFocusGlobal); mCameraPositionAgent = getPosAgentFromGlobal(camera_pos_global); // Move the camera //Ventrella gCamera->updateCameraLocation(mCameraPositionAgent, mCameraUpVector, focus_agent); //gCamera->updateCameraLocation(mCameraPositionAgent, camera_skyward, focus_agent); //end Ventrella //RN: translate UI offset after camera is oriented properly gCamera->translate(gCamera->getLeftAxis() * ui_offset); // Change FOV gCamera->setView(gCamera->getDefaultFOV() / (1.f + mCameraCurrentFOVZoomFactor)); // follow camera when in customize mode if (cameraCustomizeAvatar()) { setLookAt(LOOKAT_TARGET_FOCUS, NULL, mCameraPositionAgent); } // update the travel distance stat // this isn't directly related to the camera // but this seemed like the best place to do this LLVector3d global_pos = getPositionGlobal(); if (! mLastPositionGlobal.isExactlyZero()) { LLVector3d delta = global_pos - mLastPositionGlobal; mDistanceTraveled += delta.magVec(); } mLastPositionGlobal = global_pos; if (LLVOAvatar::sVisibleInFirstPerson && mAvatarObject.notNull() && !mAvatarObject->mIsSitting && cameraMouselook()) { LLVector3 head_pos = mAvatarObject->mHeadp->getWorldPosition() + LLVector3(0.08f, 0.f, 0.05f) * mAvatarObject->mHeadp->getWorldRotation() + LLVector3(0.1f, 0.f, 0.f) * mAvatarObject->mPelvisp->getWorldRotation(); LLVector3 diff = mCameraPositionAgent - head_pos; diff = diff * ~mAvatarObject->mRoot.getWorldRotation(); LLJoint* torso_joint = mAvatarObject->mTorsop; LLJoint* chest_joint = mAvatarObject->mChestp; LLVector3 torso_scale = torso_joint->getScale(); LLVector3 chest_scale = chest_joint->getScale(); // shorten avatar skeleton to avoid foot interpenetration if (!mAvatarObject->mInAir) { LLVector3 chest_offset = LLVector3(0.f, 0.f, chest_joint->getPosition().mV[VZ]) * torso_joint->getWorldRotation(); F32 z_compensate = llclamp(-diff.mV[VZ], -0.2f, 1.f); F32 scale_factor = llclamp(1.f - ((z_compensate * 0.5f) / chest_offset.mV[VZ]), 0.5f, 1.2f); torso_joint->setScale(LLVector3(1.f, 1.f, scale_factor)); LLJoint* neck_joint = mAvatarObject->mNeckp; LLVector3 neck_offset = LLVector3(0.f, 0.f, neck_joint->getPosition().mV[VZ]) * chest_joint->getWorldRotation(); scale_factor = llclamp(1.f - ((z_compensate * 0.5f) / neck_offset.mV[VZ]), 0.5f, 1.2f); chest_joint->setScale(LLVector3(1.f, 1.f, scale_factor)); diff.mV[VZ] = 0.f; } mAvatarObject->mPelvisp->setPosition(mAvatarObject->mPelvisp->getPosition() + diff); mAvatarObject->mRoot.updateWorldMatrixChildren(); for(LLViewerJointAttachment *attachment = mAvatarObject->mAttachmentPoints.getFirstData(); attachment; attachment = mAvatarObject->mAttachmentPoints.getNextData()) { LLViewerObject *attached_object = attachment->getObject(); if (attached_object && !attached_object->isDead() && attached_object->mDrawable.notNull()) { // clear any existing "early" movements of attachment attached_object->mDrawable->clearState(LLDrawable::EARLY_MOVE); gPipeline.updateMoveNormalAsync(attached_object->mDrawable); attached_object->updateText(); } } torso_joint->setScale(torso_scale); chest_joint->setScale(chest_scale); } } void LLAgent::updateFocusOffset() { validateFocusObject(); if (mFocusObject.notNull()) { LLVector3d obj_pos = getPosGlobalFromAgent(mFocusObject->getRenderPosition()); mFocusObjectOffset.setVec(mFocusTargetGlobal - obj_pos); } } void LLAgent::validateFocusObject() { if (mFocusObject.notNull() && (mFocusObject->isDead())) { mFocusObjectOffset.clearVec(); clearFocusObject(); mCameraFOVZoomFactor = 0.f; } } //----------------------------------------------------------------------------- // calcCustomizeAvatarUIOffset() //----------------------------------------------------------------------------- F32 LLAgent::calcCustomizeAvatarUIOffset( const LLVector3d& camera_pos_global ) { F32 ui_offset = 0.f; if( gFloaterCustomize ) { const LLRect& rect = gFloaterCustomize->getRect(); // Move the camera so that the avatar isn't covered up by this floater. F32 fraction_of_fov = 0.5f - (0.5f * (1.f - llmin(1.f, ((F32)rect.getWidth() / (F32)gViewerWindow->getWindowWidth())))); F32 apparent_angle = fraction_of_fov * gCamera->getView() * gCamera->getAspect(); // radians F32 offset = tan(apparent_angle); if( rect.mLeft < (gViewerWindow->getWindowWidth() - rect.mRight) ) { // Move the avatar to the right (camera to the left) ui_offset = offset; } else { // Move the avatar to the left (camera to the right) ui_offset = -offset; } } F32 range = (F32)dist_vec(camera_pos_global, gAgent.getFocusGlobal()); mUIOffset = lerp(mUIOffset, ui_offset, LLCriticalDamp::getInterpolant(0.05f)); return mUIOffset * range; } //----------------------------------------------------------------------------- // calcFocusPositionTargetGlobal() //----------------------------------------------------------------------------- LLVector3d LLAgent::calcFocusPositionTargetGlobal() { if (mFocusObject.notNull() && mFocusObject->isDead()) { clearFocusObject(); } // Ventrella if ( mCameraMode == CAMERA_MODE_FOLLOW && mFocusOnAvatar ) { mFocusTargetGlobal = gAgent.getPosGlobalFromAgent(mFollowCam.getSimulatedFocus()); return mFocusTargetGlobal; }// End Ventrella else if (mCameraMode == CAMERA_MODE_MOUSELOOK) { LLVector3d at_axis(1.0, 0.0, 0.0); LLQuaternion agent_rot = mFrameAgent.getQuaternion(); if (!mAvatarObject.isNull() && mAvatarObject->getParent()) { LLViewerObject* root_object = (LLViewerObject*)mAvatarObject->getRoot(); if (!root_object->flagCameraDecoupled()) { agent_rot *= ((LLViewerObject*)(mAvatarObject->getParent()))->getRenderRotation(); } } at_axis = at_axis * agent_rot; mFocusTargetGlobal = calcCameraPositionTargetGlobal() + at_axis; return mFocusTargetGlobal; } else if (mCameraMode == CAMERA_MODE_CUSTOMIZE_AVATAR) { return mFocusTargetGlobal; } else if (!mFocusOnAvatar) { if (mFocusObject.notNull() && !mFocusObject->isDead() && mFocusObject->mDrawable.notNull()) { LLDrawable* drawablep = mFocusObject->mDrawable; if (mTrackFocusObject && drawablep && drawablep->isActive()) { if (!mFocusObject->isAvatar()) { if (mFocusObject->isSelected()) { gPipeline.updateMoveNormalAsync(drawablep); } else { if (drawablep->isState(LLDrawable::MOVE_UNDAMPED)) { gPipeline.updateMoveNormalAsync(drawablep); } else { gPipeline.updateMoveDampedAsync(drawablep); } } } } // if not tracking object, update offset based on new object position else { updateFocusOffset(); } LLVector3 focus_agent = mFocusObject->getRenderPosition() + mFocusObjectOffset; mFocusTargetGlobal.setVec(getPosGlobalFromAgent(focus_agent)); } return mFocusTargetGlobal; } else if (mSitCameraEnabled && mAvatarObject.notNull() && mAvatarObject->mIsSitting && mSitCameraReferenceObject.notNull()) { // sit camera LLVector3 object_pos = mSitCameraReferenceObject->getRenderPosition(); LLQuaternion object_rot = mSitCameraReferenceObject->getRenderRotation(); LLVector3 target_pos = object_pos + (mSitCameraFocus * object_rot); return getPosGlobalFromAgent(target_pos); } else { // ...offset from avatar LLVector3d focus_offset; focus_offset.setVec(gSavedSettings.getVector3("FocusOffsetDefault")); LLQuaternion agent_rot = mFrameAgent.getQuaternion(); if (!mAvatarObject.isNull() && mAvatarObject->getParent()) { agent_rot *= ((LLViewerObject*)(mAvatarObject->getParent()))->getRenderRotation(); } focus_offset = focus_offset * agent_rot; return getPositionGlobal() + focus_offset; } } void LLAgent::setupSitCamera() { // agent frame entering this function is in world coordinates if (mAvatarObject.notNull() && mAvatarObject->getParent()) { LLQuaternion parent_rot = ((LLViewerObject*)mAvatarObject->getParent())->getRenderRotation(); // slam agent coordinate frame to proper parent local version LLVector3 at_axis = mFrameAgent.getAtAxis(); at_axis.mV[VZ] = 0.f; at_axis.normVec(); resetAxes(at_axis * ~parent_rot); } } //----------------------------------------------------------------------------- // getCameraPositionAgent() //----------------------------------------------------------------------------- const LLVector3 &LLAgent::getCameraPositionAgent() const { return gCamera->getOrigin(); } //----------------------------------------------------------------------------- // getCameraPositionGlobal() //----------------------------------------------------------------------------- LLVector3d LLAgent::getCameraPositionGlobal() const { if (gCamera) { return getPosGlobalFromAgent(gCamera->getOrigin()); } else { return (LLVector3d::zero); } } //----------------------------------------------------------------------------- // calcCameraFOVZoomFactor() //----------------------------------------------------------------------------- F32 LLAgent::calcCameraFOVZoomFactor() { LLVector3 camera_offset_dir; camera_offset_dir.setVec(mCameraFocusOffset); if (mCameraMode == CAMERA_MODE_MOUSELOOK) { return 0.f; } else if (mFocusObject.notNull() && !mFocusObject->isAvatar()) { // don't FOV zoom on mostly transparent objects LLVector3 focus_offset = mFocusObjectOffset; F32 obj_min_dist = 0.f; calcCameraMinDistance(obj_min_dist); F32 current_distance = llmax(0.001f, camera_offset_dir.magVec()); mFocusObjectDist = obj_min_dist - current_distance; F32 new_fov_zoom = llclamp(mFocusObjectDist / current_distance, 0.f, 1000.f); return new_fov_zoom; } else // focusing on land or avatar { // keep old field of view until user changes focus explicitly return mCameraFOVZoomFactor; //return 0.f; } } //----------------------------------------------------------------------------- // calcCameraPositionTargetGlobal() //----------------------------------------------------------------------------- LLVector3d LLAgent::calcCameraPositionTargetGlobal(BOOL *hit_limit) { // Compute base camera position and look-at points. F32 camera_land_height; LLVector3d frame_center_global = mAvatarObject.isNull() ? getPositionGlobal() : getPosGlobalFromAgent(mAvatarObject->mRoot.getWorldPosition()); LLVector3 upAxis = getUpAxis(); BOOL isConstrained = FALSE; LLVector3d head_offset; head_offset.setVec(mThirdPersonHeadOffset); LLVector3d camera_position_global; // Ventrella if ( mCameraMode == CAMERA_MODE_FOLLOW && mFocusOnAvatar ) { camera_position_global = gAgent.getPosGlobalFromAgent(mFollowCam.getSimulatedPosition()); }// End Ventrella else if (mCameraMode == CAMERA_MODE_MOUSELOOK) { if (mAvatarObject.isNull() || mAvatarObject->mDrawable.isNull()) { llwarns << "Null avatar drawable!" << llendl; return LLVector3d::zero; } head_offset.clearVec(); if (mAvatarObject->mIsSitting && mAvatarObject->getParent()) { mAvatarObject->updateHeadOffset(); head_offset.mdV[VX] = mAvatarObject->mHeadOffset.mV[VX]; head_offset.mdV[VY] = mAvatarObject->mHeadOffset.mV[VY]; head_offset.mdV[VZ] = mAvatarObject->mHeadOffset.mV[VZ] + 0.1f; const LLMatrix4& mat = ((LLViewerObject*) mAvatarObject->getParent())->getRenderMatrix(); camera_position_global = getPosGlobalFromAgent ((mAvatarObject->getPosition()+ LLVector3(head_offset)*mAvatarObject->getRotation()) * mat); } else { head_offset.mdV[VZ] = mAvatarObject->mHeadOffset.mV[VZ]; if (mAvatarObject->mIsSitting) { head_offset.mdV[VZ] += 0.1; } camera_position_global = getPosGlobalFromAgent(mAvatarObject->getRenderPosition());//frame_center_global; head_offset = head_offset * mAvatarObject->getRenderRotation(); camera_position_global = camera_position_global + head_offset; } } else if (mCameraMode == CAMERA_MODE_THIRD_PERSON && mFocusOnAvatar) { LLVector3 local_camera_offset; F32 camera_distance = 0.f; if (mSitCameraEnabled && mAvatarObject.notNull() && mAvatarObject->mIsSitting && mSitCameraReferenceObject.notNull()) { // sit camera LLVector3 object_pos = mSitCameraReferenceObject->getRenderPosition(); LLQuaternion object_rot = mSitCameraReferenceObject->getRenderRotation(); LLVector3 target_pos = object_pos + (mSitCameraPos * object_rot); camera_position_global = getPosGlobalFromAgent(target_pos); } else { local_camera_offset = mCameraZoomFraction * mCameraOffsetDefault; // are we sitting down? if (mAvatarObject.notNull() && mAvatarObject->getParent()) { LLQuaternion parent_rot = ((LLViewerObject*)mAvatarObject->getParent())->getRenderRotation(); // slam agent coordinate frame to proper parent local version LLVector3 at_axis = mFrameAgent.getAtAxis() * parent_rot; at_axis.mV[VZ] = 0.f; at_axis.normVec(); resetAxes(at_axis * ~parent_rot); local_camera_offset = local_camera_offset * mFrameAgent.getQuaternion() * parent_rot; } else { local_camera_offset = mFrameAgent.rotateToAbsolute( local_camera_offset ); } if (!mCameraCollidePlane.isExactlyZero() && (mAvatarObject.isNull() || !mAvatarObject->mIsSitting)) { LLVector3 plane_normal; plane_normal.setVec(mCameraCollidePlane.mV); F32 offset_dot_norm = local_camera_offset * plane_normal; if (llabs(offset_dot_norm) < 0.001f) { offset_dot_norm = 0.001f; } camera_distance = local_camera_offset.normVec(); F32 pos_dot_norm = getPosAgentFromGlobal(frame_center_global + head_offset) * plane_normal; // if agent is outside the colliding half-plane if (pos_dot_norm > mCameraCollidePlane.mV[VW]) { // check to see if camera is on the opposite side (inside) the half-plane if (offset_dot_norm + pos_dot_norm < mCameraCollidePlane.mV[VW]) { // diminish offset by factor to push it back outside the half-plane camera_distance *= (pos_dot_norm - mCameraCollidePlane.mV[VW] - CAMERA_COLLIDE_EPSILON) / -offset_dot_norm; } } else { if (offset_dot_norm + pos_dot_norm > mCameraCollidePlane.mV[VW]) { camera_distance *= (mCameraCollidePlane.mV[VW] - pos_dot_norm - CAMERA_COLLIDE_EPSILON) / offset_dot_norm; } } } else { camera_distance = local_camera_offset.normVec(); } mTargetCameraDistance = llmax(camera_distance, MIN_CAMERA_DISTANCE); if (mTargetCameraDistance != mCurrentCameraDistance) { F32 camera_lerp_amt = LLCriticalDamp::getInterpolant(CAMERA_ZOOM_HALF_LIFE); mCurrentCameraDistance = lerp(mCurrentCameraDistance, mTargetCameraDistance, camera_lerp_amt); } // Make the camera distance current local_camera_offset *= mCurrentCameraDistance; // set the global camera position LLVector3d camera_offset; LLVector3 av_pos = mAvatarObject.isNull() ? LLVector3::zero : mAvatarObject->getRenderPosition(); camera_offset.setVec( local_camera_offset ); camera_position_global = frame_center_global + head_offset + camera_offset; if (!mAvatarObject.isNull()) { LLVector3d camera_lag_d; F32 lag_interp = LLCriticalDamp::getInterpolant(CAMERA_LAG_HALF_LIFE); LLVector3 target_lag; LLVector3 vel = getVelocity(); // lag by appropriate amount for flying F32 time_in_air = mAvatarObject->mTimeInAir.getElapsedTimeF32(); if(!mCameraAnimating && mAvatarObject->mInAir && time_in_air > GROUND_TO_AIR_CAMERA_TRANSITION_START_TIME) { LLVector3 frame_at_axis = mFrameAgent.getAtAxis(); frame_at_axis -= projected_vec(frame_at_axis, getReferenceUpVector()); frame_at_axis.normVec(); //transition smoothly in air mode, to avoid camera pop F32 u = (time_in_air - GROUND_TO_AIR_CAMERA_TRANSITION_START_TIME) / GROUND_TO_AIR_CAMERA_TRANSITION_TIME; u = llclamp(u, 0.f, 1.f); lag_interp *= u; if (gViewerWindow->getLeftMouseDown() && gLastHitObjectID == mAvatarObject->getID()) { // disable camera lag when using mouse-directed steering target_lag.clearVec(); } else { target_lag = vel * gSavedSettings.getF32("DynamicCameraStrength") / 30.f; } mCameraLag = lerp(mCameraLag, target_lag, lag_interp); F32 lag_dist = mCameraLag.magVec(); if (lag_dist > MAX_CAMERA_LAG) { mCameraLag = mCameraLag * MAX_CAMERA_LAG / lag_dist; } // clamp camera lag so that avatar is always in front F32 dot = (mCameraLag - (frame_at_axis * (MIN_CAMERA_LAG * u))) * frame_at_axis; if (dot < -(MIN_CAMERA_LAG * u)) { mCameraLag -= (dot + (MIN_CAMERA_LAG * u)) * frame_at_axis; } } else { mCameraLag = lerp(mCameraLag, LLVector3::zero, LLCriticalDamp::getInterpolant(0.15f)); } camera_lag_d.setVec(mCameraLag); camera_position_global = camera_position_global - camera_lag_d; } } } else { LLVector3d focusPosGlobal = calcFocusPositionTargetGlobal(); // camera gets pushed out later wrt mCameraFOVZoomFactor...this is "raw" value camera_position_global = focusPosGlobal + mCameraFocusOffset; } if (!LLViewerCamera::sDisableCameraConstraints && !gAgent.isGodlike()) { LLViewerRegion* regionp = gWorldPointer->getRegionFromPosGlobal( camera_position_global); bool constrain = true; if(regionp && regionp->canManageEstate()) { constrain = false; } if(constrain) { F32 max_dist = ( CAMERA_MODE_CUSTOMIZE_AVATAR == mCameraMode ) ? APPEARANCE_MAX_ZOOM : MAX_CAMERA_DISTANCE_FROM_AGENT; LLVector3d camera_offset = camera_position_global - gAgent.getPositionGlobal(); F32 camera_distance = (F32)camera_offset.magVec(); if(camera_distance > max_dist) { camera_position_global = gAgent.getPositionGlobal() + (max_dist / camera_distance) * camera_offset; isConstrained = TRUE; } } // JC - Could constrain camera based on parcel stuff here. // LLViewerRegion *regionp = gWorldPointer->getRegionFromPosGlobal(camera_position_global); // // if (regionp && !regionp->mParcelOverlay->isBuildCameraAllowed(regionp->getPosRegionFromGlobal(camera_position_global))) // { // camera_position_global = last_position_global; // // isConstrained = TRUE; // } } // Don't let camera go underground F32 camera_min_off_ground = getCameraMinOffGround(); if (gWorldPointer) { camera_land_height = gWorldPointer->resolveLandHeightGlobal(camera_position_global); } else { camera_land_height = 0.f; } if (camera_position_global.mdV[VZ] < camera_land_height + camera_min_off_ground) { camera_position_global.mdV[VZ] = camera_land_height + camera_min_off_ground; isConstrained = TRUE; } if (hit_limit) { *hit_limit = isConstrained; } return camera_position_global; } //----------------------------------------------------------------------------- // handleScrollWheel() //----------------------------------------------------------------------------- void LLAgent::handleScrollWheel(S32 clicks) { if ( mCameraMode == CAMERA_MODE_FOLLOW && gAgent.getFocusOnAvatar()) { if ( ! mFollowCam.getPositionLocked() ) // not if the followCam position is locked in place { mFollowCam.zoom( clicks ); if ( mFollowCam.isZoomedToMinimumDistance() ) { changeCameraToMouselook(FALSE); } } } else { LLObjectSelectionHandle selection = gSelectMgr->getSelection(); const F32 ROOT_ROOT_TWO = sqrt(F_SQRT2); // Block if camera is animating if (mCameraAnimating) { return; } if (selection->getObjectCount() && selection->getSelectType() == SELECT_TYPE_HUD) { F32 zoom_factor = (F32)pow(0.8, -clicks); cameraZoomIn(zoom_factor); } else if (mFocusOnAvatar && mCameraMode == CAMERA_MODE_THIRD_PERSON) { F32 current_zoom_fraction = mTargetCameraDistance / mCameraOffsetDefault.magVec(); current_zoom_fraction *= 1.f - pow(ROOT_ROOT_TWO, clicks); cameraOrbitIn(current_zoom_fraction * mCameraOffsetDefault.magVec()); } else { F32 current_zoom_fraction = (F32)mCameraFocusOffsetTarget.magVec(); cameraOrbitIn(current_zoom_fraction * (1.f - pow(ROOT_ROOT_TWO, clicks))); } } } //----------------------------------------------------------------------------- // getCameraMinOffGround() //----------------------------------------------------------------------------- F32 LLAgent::getCameraMinOffGround() { if (mCameraMode == CAMERA_MODE_MOUSELOOK) { return 0.f; } else { if (LLViewerCamera::sDisableCameraConstraints) { return -1000.f; } else { return 0.5f; } } } //----------------------------------------------------------------------------- // resetCamera() //----------------------------------------------------------------------------- void LLAgent::resetCamera() { // Remove any pitch from the avatar LLVector3 at = mFrameAgent.getAtAxis(); at.mV[VZ] = 0.f; at.normVec(); gAgent.resetAxes(at); // have to explicitly clear field of view zoom now mCameraFOVZoomFactor = 0.f; updateCamera(); } //----------------------------------------------------------------------------- // changeCameraToMouselook() //----------------------------------------------------------------------------- void LLAgent::changeCameraToMouselook(BOOL animate) { // visibility changes at end of animation gViewerWindow->getWindow()->resetBusyCount(); // unpause avatar animation mPauseRequest = NULL; gToolMgr->setCurrentToolset(gMouselookToolset); gSavedSettings.setBOOL("FirstPersonBtnState", FALSE); gSavedSettings.setBOOL("MouselookBtnState", TRUE); gSavedSettings.setBOOL("ThirdPersonBtnState", FALSE); gSavedSettings.setBOOL("BuildBtnState", FALSE); if (mAvatarObject) { mAvatarObject->stopMotion( ANIM_AGENT_BODY_NOISE ); mAvatarObject->stopMotion( ANIM_AGENT_BREATHE_ROT ); } //gViewerWindow->stopGrab(); gSelectMgr->deselectAll(); gViewerWindow->hideCursor(); gViewerWindow->moveCursorToCenter(); if( mCameraMode != CAMERA_MODE_MOUSELOOK ) { gViewerWindow->setKeyboardFocus( NULL, NULL ); mLastCameraMode = mCameraMode; mCameraMode = CAMERA_MODE_MOUSELOOK; U32 old_flags = mControlFlags; setControlFlags(AGENT_CONTROL_MOUSELOOK); if (old_flags != mControlFlags) { mbFlagsDirty = TRUE; } if (animate) { startCameraAnimation(); } else { mCameraAnimating = FALSE; endAnimationUpdateUI(); } } } //----------------------------------------------------------------------------- // changeCameraToDefault() //----------------------------------------------------------------------------- void LLAgent::changeCameraToDefault() { if (LLFollowCamMgr::getActiveFollowCamParams()) { changeCameraToFollow(); } else { changeCameraToThirdPerson(); } } // Ventrella //----------------------------------------------------------------------------- // changeCameraToFollow() //----------------------------------------------------------------------------- void LLAgent::changeCameraToFollow(BOOL animate) { if( mCameraMode != CAMERA_MODE_FOLLOW ) { if (mCameraMode == CAMERA_MODE_MOUSELOOK) { animate = FALSE; } startCameraAnimation(); mLastCameraMode = mCameraMode; mCameraMode = CAMERA_MODE_FOLLOW; // bang-in the current focus, position, and up vector of the follow cam mFollowCam.reset( mCameraPositionAgent, gCamera->getPointOfInterest(), LLVector3::z_axis ); if (gBasicToolset) { gToolMgr->setCurrentToolset(gBasicToolset); } if (mAvatarObject) { mAvatarObject->mPelvisp->setPosition(LLVector3::zero); mAvatarObject->startMotion( ANIM_AGENT_BODY_NOISE ); mAvatarObject->startMotion( ANIM_AGENT_BREATHE_ROT ); } gSavedSettings.setBOOL("FirstPersonBtnState", FALSE); gSavedSettings.setBOOL("MouselookBtnState", FALSE); gSavedSettings.setBOOL("ThirdPersonBtnState", TRUE); gSavedSettings.setBOOL("BuildBtnState", FALSE); // unpause avatar animation mPauseRequest = NULL; U32 old_flags = mControlFlags; clearControlFlags(AGENT_CONTROL_MOUSELOOK); if (old_flags != mControlFlags) { mbFlagsDirty = TRUE; } //RN: this doesn't seem to be necessary and destroys the UE for script-driven cameras //gViewerWindow->setKeyboardFocus( NULL, NULL ); //gViewerWindow->setMouseCapture( NULL, NULL ); if (animate) { startCameraAnimation(); } else { mCameraAnimating = FALSE; endAnimationUpdateUI(); } } } //----------------------------------------------------------------------------- // changeCameraToThirdPerson() //----------------------------------------------------------------------------- void LLAgent::changeCameraToThirdPerson(BOOL animate) { //printf( "changeCameraToThirdPerson\n" ); gViewerWindow->getWindow()->resetBusyCount(); mCameraZoomFraction = INITIAL_ZOOM_FRACTION; if (mAvatarObject) { mAvatarObject->mPelvisp->setPosition(LLVector3::zero); mAvatarObject->startMotion( ANIM_AGENT_BODY_NOISE ); mAvatarObject->startMotion( ANIM_AGENT_BREATHE_ROT ); } gSavedSettings.setBOOL("FirstPersonBtnState", FALSE); gSavedSettings.setBOOL("MouselookBtnState", FALSE); gSavedSettings.setBOOL("ThirdPersonBtnState", TRUE); gSavedSettings.setBOOL("BuildBtnState", FALSE); LLVector3 at_axis; // unpause avatar animation mPauseRequest = NULL; if( mCameraMode != CAMERA_MODE_THIRD_PERSON ) { if (gBasicToolset) { gToolMgr->setCurrentToolset(gBasicToolset); } mCameraLag.clearVec(); if (mCameraMode == CAMERA_MODE_MOUSELOOK) { mCurrentCameraDistance = MIN_CAMERA_DISTANCE; mTargetCameraDistance = MIN_CAMERA_DISTANCE; animate = FALSE; } mLastCameraMode = mCameraMode; mCameraMode = CAMERA_MODE_THIRD_PERSON; U32 old_flags = mControlFlags; clearControlFlags(AGENT_CONTROL_MOUSELOOK); if (old_flags != mControlFlags) { mbFlagsDirty = TRUE; } //RN: this doesn't seem to be necessary and destroys the UE for script-driven cameras //gViewerWindow->setKeyboardFocus( NULL, NULL ); //gViewerWindow->setMouseCapture( NULL, NULL ); } // Remove any pitch from the avatar if (!mAvatarObject.isNull() && mAvatarObject->getParent()) { LLQuaternion obj_rot = ((LLViewerObject*)mAvatarObject->getParent())->getRenderRotation(); at_axis = gCamera->getAtAxis(); at_axis.mV[VZ] = 0.f; at_axis.normVec(); resetAxes(at_axis * ~obj_rot); } else { at_axis = mFrameAgent.getAtAxis(); at_axis.mV[VZ] = 0.f; at_axis.normVec(); resetAxes(at_axis); } if (animate) { startCameraAnimation(); } else { mCameraAnimating = FALSE; endAnimationUpdateUI(); } } //----------------------------------------------------------------------------- // changeCameraToCustomizeAvatar() //----------------------------------------------------------------------------- void LLAgent::changeCameraToCustomizeAvatar(BOOL animate) { setControlFlags(AGENT_CONTROL_STAND_UP); // force stand up gViewerWindow->getWindow()->resetBusyCount(); if (gFaceEditToolset) { gToolMgr->setCurrentToolset(gFaceEditToolset); } gSavedSettings.setBOOL("FirstPersonBtnState", FALSE); gSavedSettings.setBOOL("MouselookBtnState", FALSE); gSavedSettings.setBOOL("ThirdPersonBtnState", FALSE); gSavedSettings.setBOOL("BuildBtnState", FALSE); if (animate) { startCameraAnimation(); } // Remove any pitch from the avatar LLVector3 at = mFrameAgent.getAtAxis(); at.mV[VZ] = 0.f; at.normVec(); gAgent.resetAxes(at); if( mCameraMode != CAMERA_MODE_CUSTOMIZE_AVATAR ) { mLastCameraMode = mCameraMode; mCameraMode = CAMERA_MODE_CUSTOMIZE_AVATAR; U32 old_flags = mControlFlags; clearControlFlags(AGENT_CONTROL_MOUSELOOK); if (old_flags != mControlFlags) { mbFlagsDirty = TRUE; } gViewerWindow->setKeyboardFocus( NULL, NULL ); gViewerWindow->setMouseCapture( NULL, NULL ); LLVOAvatar::onCustomizeStart(); } if (animate && !mAvatarObject.isNull()) { sendAnimationRequest(ANIM_AGENT_CUSTOMIZE, ANIM_REQUEST_START); LLMotion* turn_motion = mAvatarObject->findMotion(ANIM_AGENT_CUSTOMIZE); if (turn_motion) { mAnimationDuration = turn_motion->getDuration() + CUSTOMIZE_AVATAR_CAMERA_ANIM_SLOP; } else { mAnimationDuration = gSavedSettings.getF32("ZoomTime"); } gAgent.setFocusGlobal(LLVector3d::zero); } else { mCameraAnimating = FALSE; endAnimationUpdateUI(); } } // // Focus point management // //----------------------------------------------------------------------------- // startCameraAnimation() //----------------------------------------------------------------------------- void LLAgent::startCameraAnimation() { mAnimationCameraStartGlobal = getCameraPositionGlobal(); mAnimationFocusStartGlobal = mFocusGlobal; mAnimationTimer.reset(); mCameraAnimating = TRUE; mAnimationDuration = gSavedSettings.getF32("ZoomTime"); } //----------------------------------------------------------------------------- // stopCameraAnimation() //----------------------------------------------------------------------------- void LLAgent::stopCameraAnimation() { mCameraAnimating = FALSE; } void LLAgent::clearFocusObject() { if (mFocusObject.notNull()) { startCameraAnimation(); setFocusObject(NULL); mFocusObjectOffset.clearVec(); } } void LLAgent::setFocusObject(LLViewerObject* object) { if (mFocusObject.notNull() && mFocusObject->mDrawable.notNull() && mFocusObject->getPCode() == LL_PCODE_VOLUME && mFocusObject != object) { LLPointer fade_object_ptr(mFocusObject); if (fade_object_ptr.notNull() && mFadeObjects.find(fade_object_ptr) == mFadeObjects.end()) { mFadeObjects.insert(fade_object_ptr); } } mFocusObject = object; } // Focus on a point, but try to keep camera position stable. //----------------------------------------------------------------------------- // setFocusGlobal() //----------------------------------------------------------------------------- void LLAgent::setFocusGlobal(const LLVector3d& focus, const LLUUID &object_id) { setFocusObject(gObjectList.findObject(object_id)); LLVector3d old_focus = mFocusTargetGlobal; LLViewerObject *focus_obj = mFocusObject; // if focus has changed if (old_focus != focus) { if (focus.isExactlyZero()) { if (!mAvatarObject.isNull()) { mFocusTargetGlobal = getPosGlobalFromAgent(mAvatarObject->mHeadp->getWorldPosition()); } else { mFocusTargetGlobal = getPositionGlobal(); } mCameraFocusOffsetTarget = getCameraPositionGlobal() - mFocusTargetGlobal; mCameraFocusOffset = mCameraFocusOffsetTarget; setLookAt(LOOKAT_TARGET_CLEAR); } else { mFocusTargetGlobal = focus; if (!focus_obj) { mCameraFOVZoomFactor = 0.f; } mCameraFocusOffsetTarget = gAgent.getPosGlobalFromAgent(mCameraVirtualPositionAgent) - mFocusTargetGlobal; startCameraAnimation(); if (focus_obj) { if (focus_obj->isAvatar()) { setLookAt(LOOKAT_TARGET_FOCUS, focus_obj); } else { setLookAt(LOOKAT_TARGET_FOCUS, focus_obj, (getPosAgentFromGlobal(focus) - focus_obj->getRenderPosition()) * ~focus_obj->getRenderRotation()); } } else { setLookAt(LOOKAT_TARGET_FOCUS, NULL, getPosAgentFromGlobal(mFocusTargetGlobal)); } } } else // focus == mFocusTargetGlobal { if (focus.isExactlyZero()) { if (!mAvatarObject.isNull()) { mFocusTargetGlobal = getPosGlobalFromAgent(mAvatarObject->mHeadp->getWorldPosition()); } else { mFocusTargetGlobal = getPositionGlobal(); } } mCameraFocusOffsetTarget = (getCameraPositionGlobal() - mFocusTargetGlobal) / (1.f + mCameraFOVZoomFactor);; mCameraFocusOffset = mCameraFocusOffsetTarget; } if (mFocusObject.notNull()) { // for attachments, make offset relative to avatar, not the attachment if (mFocusObject->isAttachment()) { while (!mFocusObject->isAvatar()) { mFocusObject = (LLViewerObject*) mFocusObject->getParent(); } setFocusObject((LLViewerObject*)mFocusObject); } updateFocusOffset(); } } // Used for avatar customization //----------------------------------------------------------------------------- // setCameraPosAndFocusGlobal() //----------------------------------------------------------------------------- void LLAgent::setCameraPosAndFocusGlobal(const LLVector3d& camera_pos, const LLVector3d& focus, const LLUUID &object_id) { LLVector3d old_focus = mFocusTargetGlobal; F64 focus_delta_squared = (old_focus - focus).magVecSquared(); const F64 ANIM_EPSILON_SQUARED = 0.0001; if( focus_delta_squared > ANIM_EPSILON_SQUARED ) { startCameraAnimation(); if( CAMERA_MODE_CUSTOMIZE_AVATAR == mCameraMode ) { // Compensate for the fact that the camera has already been offset to make room for LLFloaterCustomize. mAnimationCameraStartGlobal -= LLVector3d(gCamera->getLeftAxis() * calcCustomizeAvatarUIOffset( mAnimationCameraStartGlobal )); } } //gCamera->setOrigin( gAgent.getPosAgentFromGlobal( camera_pos ) ); setFocusObject(gObjectList.findObject(object_id)); mFocusTargetGlobal = focus; mCameraFocusOffsetTarget = camera_pos - focus; mCameraFocusOffset = mCameraFocusOffsetTarget; if (mFocusObject) { if (mFocusObject->isAvatar()) { setLookAt(LOOKAT_TARGET_FOCUS, mFocusObject); } else { setLookAt(LOOKAT_TARGET_FOCUS, mFocusObject, (getPosAgentFromGlobal(focus) - mFocusObject->getRenderPosition()) * ~mFocusObject->getRenderRotation()); } } else { setLookAt(LOOKAT_TARGET_FOCUS, NULL, getPosAgentFromGlobal(mFocusTargetGlobal)); } if( mCameraAnimating ) { const F64 ANIM_METERS_PER_SECOND = 10.0; const F64 MIN_ANIM_SECONDS = 0.5; F64 anim_duration = llmax( MIN_ANIM_SECONDS, sqrt(focus_delta_squared) / ANIM_METERS_PER_SECOND ); setAnimationDuration( (F32)anim_duration ); } updateFocusOffset(); } //----------------------------------------------------------------------------- // setSitCamera() //----------------------------------------------------------------------------- void LLAgent::setSitCamera(const LLUUID &object_id, const LLVector3 &camera_pos, const LLVector3 &camera_focus) { BOOL camera_enabled = !object_id.isNull(); if (camera_enabled) { LLViewerObject *reference_object = gObjectList.findObject(object_id); if (reference_object) { //convert to root object relative? mSitCameraPos = camera_pos; mSitCameraFocus = camera_focus; mSitCameraReferenceObject = reference_object; mSitCameraEnabled = TRUE; } } else { mSitCameraPos.clearVec(); mSitCameraFocus.clearVec(); mSitCameraReferenceObject = NULL; mSitCameraEnabled = FALSE; } } //----------------------------------------------------------------------------- // setFocusOnAvatar() //----------------------------------------------------------------------------- void LLAgent::setFocusOnAvatar(BOOL focus_on_avatar, BOOL animate) { if (focus_on_avatar != mFocusOnAvatar) { if (animate) { startCameraAnimation(); } else { stopCameraAnimation(); } } //RN: when focused on the avatar, we're not "looking" at it // looking implies intent while focusing on avatar means // you're just walking around with a camera on you...eesh. if (focus_on_avatar && !mFocusOnAvatar) { setFocusGlobal(LLVector3d::zero); mCameraFOVZoomFactor = 0.f; if (mCameraMode == CAMERA_MODE_THIRD_PERSON) { LLVector3 at_axis; if (!mAvatarObject.isNull() && mAvatarObject->getParent()) { LLQuaternion obj_rot = ((LLViewerObject*)mAvatarObject->getParent())->getRenderRotation(); at_axis = gCamera->getAtAxis(); at_axis.mV[VZ] = 0.f; at_axis.normVec(); resetAxes(at_axis * ~obj_rot); } else { at_axis = gCamera->getAtAxis(); at_axis.mV[VZ] = 0.f; at_axis.normVec(); resetAxes(at_axis); } } } mFocusOnAvatar = focus_on_avatar; } //----------------------------------------------------------------------------- // heardChat() //----------------------------------------------------------------------------- void LLAgent::heardChat(const LLChat& chat) { if (chat.mChatType == CHAT_TYPE_START || chat.mChatType == CHAT_TYPE_STOP) { return; } mLastChatterID = chat.mFromID; mChatTimer.reset(); mNearChatRadius = CHAT_NORMAL_RADIUS / 2.f; } //----------------------------------------------------------------------------- // lookAtLastChat() //----------------------------------------------------------------------------- void LLAgent::lookAtLastChat() { // Block if camera is animating or not in normal third person camera mode if (mCameraAnimating || !cameraThirdPerson()) { return; } LLViewerObject *chatter = gObjectList.findObject(mLastChatterID); if (chatter) { LLVector3 delta_pos; if (chatter->isAvatar()) { LLVOAvatar *chatter_av = (LLVOAvatar*)chatter; if (!mAvatarObject.isNull() && chatter_av->mHeadp) { delta_pos = chatter_av->mHeadp->getWorldPosition() - mAvatarObject->mHeadp->getWorldPosition(); } else { delta_pos = chatter->getPositionAgent() - getPositionAgent(); } delta_pos.normVec(); setControlFlags(AGENT_CONTROL_STOP); changeCameraToThirdPerson(); LLVector3 new_camera_pos = mAvatarObject->mHeadp->getWorldPosition(); LLVector3 left = delta_pos % LLVector3::z_axis; left.normVec(); LLVector3 up = left % delta_pos; up.normVec(); new_camera_pos -= delta_pos * 0.4f; new_camera_pos += left * 0.3f; new_camera_pos += up * 0.2f; if (chatter_av->mHeadp) { setFocusGlobal(getPosGlobalFromAgent(chatter_av->mHeadp->getWorldPosition()), mLastChatterID); mCameraFocusOffsetTarget = getPosGlobalFromAgent(new_camera_pos) - gAgent.getPosGlobalFromAgent(chatter_av->mHeadp->getWorldPosition()); } else { setFocusGlobal(chatter->getPositionGlobal(), mLastChatterID); mCameraFocusOffsetTarget = getPosGlobalFromAgent(new_camera_pos) - chatter->getPositionGlobal(); } setFocusOnAvatar(FALSE, TRUE); } else { delta_pos = chatter->getRenderPosition() - getPositionAgent(); delta_pos.normVec(); setControlFlags(AGENT_CONTROL_STOP); changeCameraToThirdPerson(); LLVector3 new_camera_pos = mAvatarObject->mHeadp->getWorldPosition(); LLVector3 left = delta_pos % LLVector3::z_axis; left.normVec(); LLVector3 up = left % delta_pos; up.normVec(); new_camera_pos -= delta_pos * 0.4f; new_camera_pos += left * 0.3f; new_camera_pos += up * 0.2f; setFocusGlobal(chatter->getPositionGlobal(), mLastChatterID); mCameraFocusOffsetTarget = getPosGlobalFromAgent(new_camera_pos) - chatter->getPositionGlobal(); setFocusOnAvatar(FALSE, TRUE); } } } const F32 SIT_POINT_EXTENTS = 0.2f; // Grabs current position void LLAgent::setStartPosition(U32 location_id) { LLViewerObject *object; if ( !(gAgentID == LLUUID::null) ) { // we've got an ID for an agent viewerobject object = gObjectList.findObject(gAgentID); if (object) { // we've got the viewer object // Sometimes the agent can be velocity interpolated off of // this simulator. Clamp it to the region the agent is // in, a little bit in on each side. const F32 INSET = 0.5f; //meters const F32 REGION_WIDTH = gWorldPointer->getRegionWidthInMeters(); LLVector3 agent_pos = getPositionAgent(); if (mAvatarObject) { // the z height is at the agent's feet agent_pos.mV[VZ] -= 0.5f * mAvatarObject->mBodySize.mV[VZ]; } agent_pos.mV[VX] = llclamp( agent_pos.mV[VX], INSET, REGION_WIDTH - INSET ); agent_pos.mV[VY] = llclamp( agent_pos.mV[VY], INSET, REGION_WIDTH - INSET ); // Don't let them go below ground, or too high. agent_pos.mV[VZ] = llclamp( agent_pos.mV[VZ], mRegionp->getLandHeightRegion( agent_pos ), gWorldPointer->getRegionMaxHeight() ); LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_SetStartLocationRequest); msg->nextBlockFast( _PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, getID()); msg->addUUIDFast(_PREHASH_SessionID, getSessionID()); msg->nextBlockFast( _PREHASH_StartLocationData); // corrected by sim msg->addStringFast(_PREHASH_SimName, ""); msg->addU32Fast(_PREHASH_LocationID, location_id); msg->addVector3Fast(_PREHASH_LocationPos, agent_pos); msg->addVector3Fast(_PREHASH_LocationLookAt,mFrameAgent.getAtAxis()); // Reliable only helps when setting home location. Last // location is sent on quit, and we don't have time to ack // the packets. msg->sendReliable(mRegionp->getHost()); const U32 HOME_INDEX = 1; if( HOME_INDEX == location_id ) { setHomePosRegion( mRegionp->getHandle(), getPositionAgent() ); } } else { llinfos << "setStartPosition - Can't find agent viewerobject id " << gAgentID << llendl; } } } void LLAgent::requestStopMotion( LLMotion* motion ) { // Notify all avatars that a motion has stopped. // This is needed to clear the animation state bits LLUUID anim_state = motion->getID(); // if motion is not looping, it could have stopped by running out of time // so we need to tell the server this // llinfos << "Sending stop for motion " << motion->getName() << llendl; sendAnimationRequest( anim_state, ANIM_REQUEST_STOP ); // handle automatic state transitions (based on completion of animation playback) if (anim_state == ANIM_AGENT_STANDUP) { // send stand up command setControlFlags(AGENT_CONTROL_FINISH_ANIM); // now trigger dusting self off animation if (mAvatarObject.notNull() && !mAvatarObject->mBelowWater && rand() % 3 == 0) sendAnimationRequest( ANIM_AGENT_BRUSH, ANIM_REQUEST_START ); } else if (anim_state == ANIM_AGENT_PRE_JUMP || anim_state == ANIM_AGENT_LAND || anim_state == ANIM_AGENT_MEDIUM_LAND) { setControlFlags(AGENT_CONTROL_FINISH_ANIM); } } BOOL LLAgent::isGodlike() const { #ifdef HACKED_GODLIKE_VIEWER return TRUE; #else if(mAdminOverride) return TRUE; return mGodLevel > GOD_NOT; #endif } U8 LLAgent::getGodLevel() const { #ifdef HACKED_GODLIKE_VIEWER return GOD_MAINTENANCE; #else if(mAdminOverride) return GOD_FULL; return mGodLevel; #endif } void LLAgent::buildFullname(std::string& name) const { if (mAvatarObject) { name = mAvatarObject->getFullname(); } } void LLAgent::buildFullnameAndTitle(std::string& name) const { if (isGroupMember()) { name = mGroupTitle; name += ' '; } else { name.erase(0, name.length()); } if (mAvatarObject) { name += mAvatarObject->getFullname(); } } BOOL LLAgent::isInGroup(const LLUUID& group_id) const { S32 count = mGroups.count(); for(S32 i = 0; i < count; ++i) { if(mGroups.get(i).mID == group_id) { return TRUE; } } return FALSE; } // This implementation should mirror LLAgentInfo::hasPowerInGroup BOOL LLAgent::hasPowerInGroup(const LLUUID& group_id, U64 power) const { // GP_NO_POWERS can also mean no power is enough to grant an ability. if (GP_NO_POWERS == power) return FALSE; S32 count = mGroups.count(); for(S32 i = 0; i < count; ++i) { if(mGroups.get(i).mID == group_id) { return (BOOL)((mGroups.get(i).mPowers & power) > 0); } } return FALSE; } BOOL LLAgent::hasPowerInActiveGroup(U64 power) const { return (mGroupID.notNull() && (hasPowerInGroup(mGroupID, power))); } U64 LLAgent::getPowerInGroup(const LLUUID& group_id) const { S32 count = mGroups.count(); for(S32 i = 0; i < count; ++i) { if(mGroups.get(i).mID == group_id) { return (mGroups.get(i).mPowers); } } return GP_NO_POWERS; } BOOL LLAgent::getGroupData(const LLUUID& group_id, LLGroupData& data) const { S32 count = mGroups.count(); for(S32 i = 0; i < count; ++i) { if(mGroups.get(i).mID == group_id) { data = mGroups.get(i); return TRUE; } } return FALSE; } S32 LLAgent::getGroupContribution(const LLUUID& group_id) const { S32 count = mGroups.count(); for(S32 i = 0; i < count; ++i) { if(mGroups.get(i).mID == group_id) { S32 contribution = mGroups.get(i).mContribution; return contribution; } } return 0; } BOOL LLAgent::setGroupContribution(const LLUUID& group_id, S32 contribution) { S32 count = mGroups.count(); for(S32 i = 0; i < count; ++i) { if(mGroups.get(i).mID == group_id) { mGroups.get(i).mContribution = contribution; LLMessageSystem* msg = gMessageSystem; msg->newMessage("SetGroupContribution"); msg->nextBlock("AgentData"); msg->addUUID("AgentID", gAgentID); msg->addUUID("SessionID", gAgentSessionID); msg->nextBlock("Data"); msg->addUUID("GroupID", group_id); msg->addS32("Contribution", contribution); sendReliableMessage(); return TRUE; } } return FALSE; } BOOL LLAgent::setGroupAcceptNotices(const LLUUID& group_id, BOOL accept_notices) { S32 count = mGroups.count(); for(S32 i = 0; i < count; ++i) { if(mGroups.get(i).mID == group_id) { mGroups.get(i).mAcceptNotices = accept_notices; LLMessageSystem* msg = gMessageSystem; msg->newMessage("SetGroupAcceptNotices"); msg->nextBlock("AgentData"); msg->addUUID("AgentID", gAgentID); msg->addUUID("SessionID", gAgentSessionID); msg->nextBlock("Data"); msg->addUUID("GroupID", group_id); msg->addBOOL("AcceptNotices", accept_notices); sendReliableMessage(); return TRUE; } } return FALSE; } // utility to build a location string void LLAgent::buildLocationString(std::string& str) { const LLVector3& agent_pos_region = getPositionAgent(); S32 pos_x = S32(agent_pos_region.mV[VX]); S32 pos_y = S32(agent_pos_region.mV[VY]); S32 pos_z = S32(agent_pos_region.mV[VZ]); // Round the numbers based on the velocity LLVector3 agent_velocity = getVelocity(); F32 velocity_mag_sq = agent_velocity.magVecSquared(); const F32 FLY_CUTOFF = 6.f; // meters/sec const F32 FLY_CUTOFF_SQ = FLY_CUTOFF * FLY_CUTOFF; const F32 WALK_CUTOFF = 1.5f; // meters/sec const F32 WALK_CUTOFF_SQ = WALK_CUTOFF * WALK_CUTOFF; if (velocity_mag_sq > FLY_CUTOFF_SQ) { pos_x -= pos_x % 4; pos_y -= pos_y % 4; } else if (velocity_mag_sq > WALK_CUTOFF_SQ) { pos_x -= pos_x % 2; pos_y -= pos_y % 2; } // create a defult name and description for the landmark std::string buffer; if( !strcmp("", gParcelMgr->getAgentParcelName()) ) { // the parcel doesn't have a name buffer = llformat("%.32s (%d, %d, %d)", getRegion()->getName().c_str(), pos_x, pos_y, pos_z); } else { // the parcel has a name, so include it in the landmark name buffer = llformat("%.32s, %.32s (%d, %d, %d)", gParcelMgr->getAgentParcelName(), getRegion()->getName().c_str(), pos_x, pos_y, pos_z); } str = buffer; } LLQuaternion LLAgent::getHeadRotation() { if (mAvatarObject.isNull() || !mAvatarObject->mPelvisp || !mAvatarObject->mHeadp) { return LLQuaternion::DEFAULT; } if (!gAgent.cameraMouselook()) { return mAvatarObject->getRotation(); } // We must be in mouselook LLVector3 look_dir( gCamera->getAtAxis() ); LLVector3 up = look_dir % mFrameAgent.getLeftAxis(); LLVector3 left = up % look_dir; LLQuaternion rot(look_dir, left, up); if (mAvatarObject->getParent()) { rot = rot * ~mAvatarObject->getParent()->getRotation(); } return rot; } void LLAgent::sendAnimationRequests(LLDynamicArray &anim_ids, EAnimRequest request) { if (gAgentID.isNull()) { return; } S32 num_valid_anims = 0; LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_AgentAnimation); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, getID()); msg->addUUIDFast(_PREHASH_SessionID, getSessionID()); for (S32 i = 0; i < anim_ids.count(); i++) { if (anim_ids[i].isNull()) { continue; } msg->nextBlockFast(_PREHASH_AnimationList); msg->addUUIDFast(_PREHASH_AnimID, (anim_ids[i]) ); msg->addBOOLFast(_PREHASH_StartAnim, (request == ANIM_REQUEST_START) ? TRUE : FALSE); num_valid_anims++; } if (num_valid_anims) { sendReliableMessage(); } } void LLAgent::sendAnimationRequest(const LLUUID &anim_id, EAnimRequest request) { if (gAgentID.isNull() || anim_id.isNull() || !mRegionp) { return; } LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_AgentAnimation); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, getID()); msg->addUUIDFast(_PREHASH_SessionID, getSessionID()); msg->nextBlockFast(_PREHASH_AnimationList); msg->addUUIDFast(_PREHASH_AnimID, (anim_id) ); msg->addBOOLFast(_PREHASH_StartAnim, (request == ANIM_REQUEST_START) ? TRUE : FALSE); sendReliableMessage(); } void LLAgent::friendsChanged() { LLCollectProxyBuddies collector; LLAvatarTracker::instance().applyFunctor(collector); mProxyForAgents = collector.mProxy; } BOOL LLAgent::isGrantedProxy(const LLPermissions& perm) { return (mProxyForAgents.count(perm.getOwner()) > 0); } BOOL LLAgent::allowOperation(PermissionBit op, const LLPermissions& perm, U64 group_proxy_power, U8 god_minimum) { // Check god level. if (getGodLevel() >= god_minimum) return TRUE; if (!perm.isOwned()) return FALSE; // A group member with group_proxy_power can act as owner. BOOL is_group_owned; LLUUID owner_id; perm.getOwnership(owner_id, is_group_owned); LLUUID group_id(perm.getGroup()); LLUUID agent_proxy(getID()); if (is_group_owned) { if (hasPowerInGroup(group_id, group_proxy_power)) { // Let the member assume the group's id for permission requests. agent_proxy = owner_id; } } else { // Check for granted mod permissions. if ((PERM_OWNER != op) && isGrantedProxy(perm)) { agent_proxy = owner_id; } } // This is the group id to use for permission requests. // Only group members may use this field. LLUUID group_proxy = LLUUID::null; if (group_id.notNull() && isInGroup(group_id)) { group_proxy = group_id; } // We now have max ownership information. if (PERM_OWNER == op) { // This this was just a check for ownership, we can now return the answer. return (agent_proxy == owner_id); } return perm.allowOperationBy(op, agent_proxy, group_proxy); } void LLAgent::getName(LLString& name) { // Note: assumes that name points to a buffer of at least DB_FULL_NAME_BUF_SIZE bytes. name.clear(); if (mAvatarObject) { LLNameValue *first_nv = mAvatarObject->getNVPair("FirstName"); LLNameValue *last_nv = mAvatarObject->getNVPair("LastName"); if (first_nv && last_nv) { name = first_nv->printData() + " " + last_nv->printData(); } else { llwarns << "Agent is missing FirstName and/or LastName nv pair." << llendl; } } else { name = gSavedSettings.getString("FirstName") + " " + gSavedSettings.getString("LastName"); } } const LLColor4 &LLAgent::getEffectColor() { return mEffectColor; } void LLAgent::setEffectColor(const LLColor4 &color) { mEffectColor = color; } void LLAgent::initOriginGlobal(const LLVector3d &origin_global) { mAgentOriginGlobal = origin_global; } void update_group_floaters(const LLUUID& group_id) { // *HACK: added to do a live update of the groups floater if it is // open. LLFloaterGroups* fg = LLFloaterGroups::getInstance(gAgent.getID()); if(fg) { fg->reset(); } LLFloaterGroupInfo::refreshGroup(group_id); // update avatar info LLFloaterAvatarInfo* fa = LLFloaterAvatarInfo::getInstance(gAgent.getID()); if(fa) { fa->resetGroupList(); } if (gIMView) { // update the talk view gIMView->refresh(); } } // static void LLAgent::processAgentDropGroup(LLMessageSystem *msg, void **) { LLUUID agent_id; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id ); if (agent_id != gAgentID) { llwarns << "processAgentDropGroup for agent other than me" << llendl; return; } LLUUID group_id; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_GroupID, group_id ); // Remove the group if it already exists remove it and add the new data to pick up changes. LLGroupData gd; gd.mID = group_id; S32 index = gAgent.mGroups.find(gd); if (index != -1) { gAgent.mGroups.remove(index); if (gAgent.getGroupID() == group_id) { gAgent.mGroupID.setNull(); gAgent.mGroupPowers = 0; gAgent.mGroupName[0] = '\0'; gAgent.mGroupTitle[0] = '\0'; } // refresh all group information gAgent.sendAgentDataUpdateRequest(); gGroupMgr->clearGroupData(group_id); // close the floater for this group, if any. LLFloaterGroupInfo::closeGroup(group_id); // refresh the group panel of the search window, if necessary. LLFloaterDirectory::refreshGroup(group_id); } else { llwarns << "processAgentDropGroup, agent is not part of group " << group_id << llendl; } } // static void LLAgent::processAgentGroupDataUpdate(LLMessageSystem *msg, void **) { LLUUID agent_id; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id ); if (agent_id != gAgentID) { llwarns << "processAgentGroupDataUpdate for agent other than me" << llendl; return; } S32 count = msg->getNumberOfBlocksFast(_PREHASH_GroupData); LLGroupData group; S32 index = -1; bool need_floater_update = false; char group_name[DB_GROUP_NAME_BUF_SIZE]; /*Flawfinder: ignore*/ for(S32 i = 0; i < count; ++i) { msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupID, group.mID, i); msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupInsigniaID, group.mInsigniaID, i); msg->getU64(_PREHASH_GroupData, "GroupPowers", group.mPowers, i); msg->getBOOL(_PREHASH_GroupData, "AcceptNotices", group.mAcceptNotices, i); msg->getS32(_PREHASH_GroupData, "Contribution", group.mContribution, i); msg->getStringFast(_PREHASH_GroupData, _PREHASH_GroupName, DB_GROUP_NAME_BUF_SIZE, group_name, i); group.mName.assign(group_name); if(group.mID.notNull()) { need_floater_update = true; // Remove the group if it already exists remove it and add the new data to pick up changes. index = gAgent.mGroups.find(group); if (index != -1) { gAgent.mGroups.remove(index); } gAgent.mGroups.put(group); } if (need_floater_update) { update_group_floaters(group.mID); } } } // static void LLAgent::processAgentDataUpdate(LLMessageSystem *msg, void **) { LLUUID agent_id; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id ); if (agent_id != gAgentID) { llwarns << "processAgentDataUpdate for agent other than me" << llendl; return; } msg->getStringFast(_PREHASH_AgentData, _PREHASH_GroupTitle, DB_GROUP_TITLE_BUF_SIZE, gAgent.mGroupTitle); LLUUID active_id; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_ActiveGroupID, active_id); if(active_id.notNull()) { gAgent.mGroupID = active_id; msg->getU64(_PREHASH_AgentData, "GroupPowers", gAgent.mGroupPowers); msg->getString(_PREHASH_AgentData, _PREHASH_GroupName, DB_GROUP_NAME_BUF_SIZE, gAgent.mGroupName); } else { gAgent.mGroupID.setNull(); gAgent.mGroupPowers = 0; gAgent.mGroupName[0] = '\0'; } update_group_floaters(active_id); gAgent.fireEvent(new LLEvent(&gAgent, "new group"), ""); } // static void LLAgent::processScriptControlChange(LLMessageSystem *msg, void **) { S32 block_count = msg->getNumberOfBlocks("Data"); for (S32 block_index = 0; block_index < block_count; block_index++) { BOOL take_controls; U32 controls; BOOL passon; U32 i; msg->getBOOL("Data", "TakeControls", take_controls, block_index); if (take_controls) { // take controls msg->getU32("Data", "Controls", controls, block_index ); msg->getBOOL("Data", "PassToAgent", passon, block_index ); U32 total_count = 0; for (i = 0; i < TOTAL_CONTROLS; i++) { if (controls & ( 1 << i)) { if (passon) { gAgent.mControlsTakenPassedOnCount[i]++; } else { gAgent.mControlsTakenCount[i]++; } total_count++; } } // Any control taken? If so, might be first time. if (total_count > 0) { LLFirstUse::useOverrideKeys(); } } else { // release controls msg->getU32("Data", "Controls", controls, block_index ); msg->getBOOL("Data", "PassToAgent", passon, block_index ); for (i = 0; i < TOTAL_CONTROLS; i++) { if (controls & ( 1 << i)) { if (passon) { gAgent.mControlsTakenPassedOnCount[i]--; if (gAgent.mControlsTakenPassedOnCount[i] < 0) { gAgent.mControlsTakenPassedOnCount[i] = 0; } } else { gAgent.mControlsTakenCount[i]--; if (gAgent.mControlsTakenCount[i] < 0) { gAgent.mControlsTakenCount[i] = 0; } } } } } } } /* // static void LLAgent::processControlTake(LLMessageSystem *msg, void **) { U32 controls; msg->getU32("Data", "Controls", controls ); U32 passon; msg->getBOOL("Data", "PassToAgent", passon ); S32 i; S32 total_count = 0; for (i = 0; i < TOTAL_CONTROLS; i++) { if (controls & ( 1 << i)) { if (passon) { gAgent.mControlsTakenPassedOnCount[i]++; } else { gAgent.mControlsTakenCount[i]++; } total_count++; } } // Any control taken? If so, might be first time. if (total_count > 0) { LLFirstUse::useOverrideKeys(); } } // static void LLAgent::processControlRelease(LLMessageSystem *msg, void **) { U32 controls; msg->getU32("Data", "Controls", controls ); U32 passon; msg->getBOOL("Data", "PassToAgent", passon ); S32 i; for (i = 0; i < TOTAL_CONTROLS; i++) { if (controls & ( 1 << i)) { if (passon) { gAgent.mControlsTakenPassedOnCount[i]--; if (gAgent.mControlsTakenPassedOnCount[i] < 0) { gAgent.mControlsTakenPassedOnCount[i] = 0; } } else { gAgent.mControlsTakenCount[i]--; if (gAgent.mControlsTakenCount[i] < 0) { gAgent.mControlsTakenCount[i] = 0; } } } } } */ //static void LLAgent::processAgentCachedTextureResponse(LLMessageSystem *mesgsys, void **user_data) { gAgent.mNumPendingQueries--; LLVOAvatar* avatarp = gAgent.getAvatarObject(); if (!avatarp || avatarp->isDead()) { llwarns << "No avatar for user in cached texture update!" << llendl; return; } if (gAgent.cameraCustomizeAvatar()) { // ignore baked textures when in customize mode return; } S32 query_id; mesgsys->getS32Fast(_PREHASH_AgentData, _PREHASH_SerialNum, query_id); S32 num_texture_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_WearableData); S32 num_results = 0; for (S32 texture_block = 0; texture_block < num_texture_blocks; texture_block++) { LLUUID texture_id; U8 texture_index; mesgsys->getUUIDFast(_PREHASH_WearableData, _PREHASH_TextureID, texture_id, texture_block); mesgsys->getU8Fast(_PREHASH_WearableData, _PREHASH_TextureIndex, texture_index, texture_block); if (texture_id.notNull() && (S32)texture_index < BAKED_TEXTURE_COUNT && gAgent.mActiveCacheQueries[ texture_index ] == query_id) { //llinfos << "Received cached texture " << (U32)texture_index << ": " << texture_id << llendl; avatarp->setCachedBakedTexture((LLVOAvatar::ETextureIndex)LLVOAvatar::sBakedTextureIndices[texture_index], texture_id); //avatarp->setTETexture( LLVOAvatar::sBakedTextureIndices[texture_index], texture_id ); gAgent.mActiveCacheQueries[ texture_index ] = 0; num_results++; } } llinfos << "Received cached texture response for " << num_results << " textures." << llendl; avatarp->updateMeshTextures(); if (gAgent.mNumPendingQueries == 0) { // RN: not sure why composites are disabled at this point avatarp->setCompositeUpdatesEnabled(TRUE); gAgent.sendAgentSetAppearance(); } } BOOL LLAgent::anyControlGrabbed() const { U32 i; for (i = 0; i < TOTAL_CONTROLS; i++) { if (gAgent.mControlsTakenCount[i] > 0) return TRUE; if (gAgent.mControlsTakenPassedOnCount[i] > 0) return TRUE; } return FALSE; } BOOL LLAgent::isControlGrabbed(S32 control_index) const { return mControlsTakenCount[control_index] > 0; } void LLAgent::forceReleaseControls() { gMessageSystem->newMessage("ForceScriptControlRelease"); gMessageSystem->nextBlock("AgentData"); gMessageSystem->addUUID("AgentID", getID()); gMessageSystem->addUUID("SessionID", getSessionID()); sendReliableMessage(); } void LLAgent::setHomePosRegion( const U64& region_handle, const LLVector3& pos_region) { mHaveHomePosition = TRUE; mHomeRegionHandle = region_handle; mHomePosRegion = pos_region; } BOOL LLAgent::getHomePosGlobal( LLVector3d* pos_global ) { if(!mHaveHomePosition) { return FALSE; } F32 x = 0; F32 y = 0; from_region_handle( mHomeRegionHandle, &x, &y); pos_global->setVec( x + mHomePosRegion.mV[VX], y + mHomePosRegion.mV[VY], mHomePosRegion.mV[VZ] ); return TRUE; } void LLAgent::clearVisualParams(void *data) { LLVOAvatar* avatarp = gAgent.getAvatarObject(); if (avatarp) { avatarp->clearVisualParamWeights(); avatarp->updateVisualParams(); } } //--------------------------------------------------------------------------- // Teleport //--------------------------------------------------------------------------- // teleportCore() - stuff to do on any teleport // protected bool LLAgent::teleportCore(bool is_local) { if(TELEPORT_NONE != mTeleportState) { llwarns << "Attempt to teleport when already teleporting." << llendl; return false; } // Don't call LLFirstUse::useTeleport because we don't know // yet if the teleport will succeed. Look in // process_teleport_location_reply // close the map and find panels so we can see our destination LLFloaterWorldMap::hide(NULL); LLFloaterDirectory::hide(NULL); // Close all pie menus, deselect land, etc. // Don't change the camera until we know teleport succeeded. JC resetView(FALSE); // local logic gViewerStats->incStat(LLViewerStats::ST_TELEPORT_COUNT); if (!is_local) { gTeleportDisplay = TRUE; gAgent.setTeleportState( LLAgent::TELEPORT_START ); } make_ui_sound("UISndTeleportOut"); return true; } void LLAgent::teleportRequest( const U64& region_handle, const LLVector3& pos_local) { LLViewerRegion* regionp = getRegion(); if(regionp && teleportCore()) { llinfos << "TeleportRequest: '" << region_handle << "':" << pos_local << llendl; LLMessageSystem* msg = gMessageSystem; msg->newMessage("TeleportLocationRequest"); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, getID()); msg->addUUIDFast(_PREHASH_SessionID, getSessionID()); msg->nextBlockFast(_PREHASH_Info); msg->addU64("RegionHandle", region_handle); msg->addVector3("Position", pos_local); LLVector3 look_at(0,1,0); msg->addVector3("LookAt", look_at); sendReliableMessage(); } } // Landmark ID = LLUUID::null means teleport home void LLAgent::teleportViaLandmark(const LLUUID& landmark_id) { LLViewerRegion *regionp = getRegion(); if(regionp && teleportCore()) { LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_TeleportLandmarkRequest); msg->nextBlockFast(_PREHASH_Info); msg->addUUIDFast(_PREHASH_AgentID, getID()); msg->addUUIDFast(_PREHASH_SessionID, getSessionID()); msg->addUUIDFast(_PREHASH_LandmarkID, landmark_id); sendReliableMessage(); } } void LLAgent::teleportViaLure(const LLUUID& lure_id, BOOL godlike) { LLViewerRegion* regionp = getRegion(); if(regionp && teleportCore()) { U32 teleport_flags = 0x0; if (godlike) { teleport_flags |= TELEPORT_FLAGS_VIA_GODLIKE_LURE; teleport_flags |= TELEPORT_FLAGS_DISABLE_CANCEL; } else { teleport_flags |= TELEPORT_FLAGS_VIA_LURE; } // send the message LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_TeleportLureRequest); msg->nextBlockFast(_PREHASH_Info); msg->addUUIDFast(_PREHASH_AgentID, getID()); msg->addUUIDFast(_PREHASH_SessionID, getSessionID()); msg->addUUIDFast(_PREHASH_LureID, lure_id); msg->addU32("TeleportFlags", teleport_flags); sendReliableMessage(); } } // James Cook, July 28, 2005 void LLAgent::teleportCancel() { LLViewerRegion* regionp = getRegion(); if(regionp) { // send the message LLMessageSystem* msg = gMessageSystem; msg->newMessage("TeleportCancel"); msg->nextBlockFast(_PREHASH_Info); msg->addUUIDFast(_PREHASH_AgentID, getID()); msg->addUUIDFast(_PREHASH_SessionID, getSessionID()); sendReliableMessage(); } gTeleportDisplay = FALSE; gAgent.setTeleportState( LLAgent::TELEPORT_CANCELLING ); } void LLAgent::teleportViaLocation(const LLVector3d& pos_global) { LLViewerRegion* regionp = getRegion(); LLSimInfo* info = gWorldMap->simInfoFromPosGlobal(pos_global); if(regionp && info) { U32 x_pos; U32 y_pos; from_region_handle(info->mHandle, &x_pos, &y_pos); LLVector3 pos_local( (F32)(pos_global.mdV[VX] - x_pos), (F32)(pos_global.mdV[VY] - y_pos), (F32)(pos_global.mdV[VZ])); teleportRequest(info->mHandle, pos_local); } else if(regionp && teleportCore(regionp->getHandle() == to_region_handle_global((F32)pos_global.mdV[VX], (F32)pos_global.mdV[VY]))) { llwarns << "Using deprecated teleportlocationrequest." << llendl; // send the message LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_TeleportLocationRequest); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, getID()); msg->addUUIDFast(_PREHASH_SessionID, getSessionID()); msg->nextBlockFast(_PREHASH_Info); F32 width = regionp->getWidth(); LLVector3 pos(fmod((F32)pos_global.mdV[VX], width), fmod((F32)pos_global.mdV[VY], width), (F32)pos_global.mdV[VZ]); F32 region_x = (F32)(pos_global.mdV[VX]); F32 region_y = (F32)(pos_global.mdV[VY]); U64 region_handle = to_region_handle_global(region_x, region_y); msg->addU64Fast(_PREHASH_RegionHandle, region_handle); msg->addVector3Fast(_PREHASH_Position, pos); pos.mV[VX] += 1; msg->addVector3Fast(_PREHASH_LookAt, pos); sendReliableMessage(); } } void LLAgent::setTeleportState(ETeleportState state) { mTeleportState = state; if (mTeleportState > TELEPORT_NONE && gSavedSettings.getBOOL("FreezeTime")) { LLFloaterSnapshot::hide(0); } } void LLAgent::fidget() { if (!getAFK()) { F32 curTime = mFidgetTimer.getElapsedTimeF32(); if (curTime > mNextFidgetTime) { // pick a random fidget anim here S32 oldFidget = mCurrentFidget; mCurrentFidget = ll_rand(NUM_AGENT_STAND_ANIMS); if (mCurrentFidget != oldFidget) { LLAgent::stopFidget(); switch(mCurrentFidget) { case 0: mCurrentFidget = 0; break; case 1: sendAnimationRequest(ANIM_AGENT_STAND_1, ANIM_REQUEST_START); mCurrentFidget = 1; break; case 2: sendAnimationRequest(ANIM_AGENT_STAND_2, ANIM_REQUEST_START); mCurrentFidget = 2; break; case 3: sendAnimationRequest(ANIM_AGENT_STAND_3, ANIM_REQUEST_START); mCurrentFidget = 3; break; case 4: sendAnimationRequest(ANIM_AGENT_STAND_4, ANIM_REQUEST_START); mCurrentFidget = 4; break; } } // calculate next fidget time mNextFidgetTime = curTime + ll_frand(MAX_FIDGET_TIME - MIN_FIDGET_TIME) + MIN_FIDGET_TIME; } } } void LLAgent::stopFidget() { LLDynamicArray anims; anims.put(ANIM_AGENT_STAND_1); anims.put(ANIM_AGENT_STAND_2); anims.put(ANIM_AGENT_STAND_3); anims.put(ANIM_AGENT_STAND_4); gAgent.sendAnimationRequests(anims, ANIM_REQUEST_STOP); } void LLAgent::requestEnterGodMode() { LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_RequestGodlikePowers); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->nextBlockFast(_PREHASH_RequestBlock); msg->addBOOLFast(_PREHASH_Godlike, TRUE); msg->addUUIDFast(_PREHASH_Token, LLUUID::null); // simulator and userserver need to know about your request sendReliableMessage(); msg->sendReliable(gUserServer); } void LLAgent::requestLeaveGodMode() { LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_RequestGodlikePowers); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->nextBlockFast(_PREHASH_RequestBlock); msg->addBOOLFast(_PREHASH_Godlike, FALSE); msg->addUUIDFast(_PREHASH_Token, LLUUID::null); // simulator and userserver need to know about your request sendReliableMessage(); msg->sendReliable(gUserServer); } // wearables LLAgent::createStandardWearablesAllDoneCallback::~createStandardWearablesAllDoneCallback() { gAgent.createStandardWearablesAllDone(); } LLAgent::sendAgentWearablesUpdateCallback::~sendAgentWearablesUpdateCallback() { gAgent.sendAgentWearablesUpdate(); } LLAgent::addWearableToAgentInventoryCallback::addWearableToAgentInventoryCallback( LLPointer cb, S32 index, LLWearable* wearable, U32 todo) : mIndex(index), mWearable(wearable), mTodo(todo), mCB(cb) { } void LLAgent::addWearableToAgentInventoryCallback::fire(const LLUUID& inv_item) { if (inv_item.isNull()) return; gAgent.addWearabletoAgentInventoryDone(mIndex, inv_item, mWearable); if (mTodo & CALL_UPDATE) { gAgent.sendAgentWearablesUpdate(); } if (mTodo & CALL_RECOVERDONE) { gAgent.recoverMissingWearableDone(); } /* * Do this for every one in the loop */ if (mTodo & CALL_CREATESTANDARDDONE) { gAgent.createStandardWearablesDone(mIndex); } if (mTodo & CALL_MAKENEWOUTFITDONE) { gAgent.makeNewOutfitDone(mIndex); } } void LLAgent::addWearabletoAgentInventoryDone( S32 index, const LLUUID& item_id, LLWearable* wearable) { if (item_id.isNull()) return; LLUUID old_item_id = mWearableEntry[index].mItemID; mWearableEntry[index].mItemID = item_id; mWearableEntry[index].mWearable = wearable; if (old_item_id.notNull()) gInventory.addChangedMask(LLInventoryObserver::LABEL, old_item_id); gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id); LLViewerInventoryItem* item = gInventory.getItem(item_id); if(item && wearable) { // We're changing the asset id, so we both need to set it // locally via setAssetUUID() and via setTransactionID() which // will be decoded on the server. JC item->setAssetUUID(wearable->getID()); item->setTransactionID(wearable->getTransactionID()); gInventory.addChangedMask(LLInventoryObserver::INTERNAL, item_id); item->updateServer(FALSE); } gInventory.notifyObservers(); } void LLAgent::sendAgentWearablesUpdate() { // First make sure that we have inventory items for each wearable S32 i; for(i=0; i < WT_COUNT; ++i) { LLWearable* wearable = mWearableEntry[ i ].mWearable; if (wearable) { if( mWearableEntry[ i ].mItemID.isNull() ) { LLPointer cb = new addWearableToAgentInventoryCallback( LLPointer(NULL), i, wearable, addWearableToAgentInventoryCallback::CALL_NONE); addWearableToAgentInventory(cb, wearable); } else { gInventory.addChangedMask( LLInventoryObserver::LABEL, mWearableEntry[i].mItemID ); } } } // Then make sure the inventory is in sync with the avatar. gInventory.notifyObservers(); // Send the AgentIsNowWearing gMessageSystem->newMessageFast(_PREHASH_AgentIsNowWearing); gMessageSystem->nextBlockFast(_PREHASH_AgentData); gMessageSystem->addUUIDFast(_PREHASH_AgentID, getID()); gMessageSystem->addUUIDFast(_PREHASH_SessionID, getSessionID()); lldebugs << "sendAgentWearablesUpdate()" << llendl; for(i=0; i < WT_COUNT; ++i) { gMessageSystem->nextBlockFast(_PREHASH_WearableData); U8 type_u8 = (U8)i; gMessageSystem->addU8Fast(_PREHASH_WearableType, type_u8 ); LLWearable* wearable = mWearableEntry[ i ].mWearable; if( wearable ) { //llinfos << "Sending wearable " << wearable->getName() << llendl; gMessageSystem->addUUIDFast(_PREHASH_ItemID, mWearableEntry[ i ].mItemID ); } else { //llinfos << "Not wearing wearable type " << LLWearable::typeToTypeName((EWearableType)i) << llendl; gMessageSystem->addUUIDFast(_PREHASH_ItemID, LLUUID::null ); } lldebugs << " " << LLWearable::typeToTypeLabel((EWearableType)i) << ": " << (wearable ? wearable->getID() : LLUUID::null) << llendl; } gAgent.sendReliableMessage(); } void LLAgent::saveWearable( EWearableType type, BOOL send_update ) { LLWearable* old_wearable = mWearableEntry[(S32)type].mWearable; if( old_wearable && (old_wearable->isDirty() || old_wearable->isOldVersion()) ) { LLWearable* new_wearable = gWearableList.createCopyFromAvatar( old_wearable ); mWearableEntry[(S32)type].mWearable = new_wearable; LLInventoryItem* item = gInventory.getItem(mWearableEntry[(S32)type].mItemID); if( item ) { // Update existing inventory item LLPointer template_item = new LLViewerInventoryItem(item->getUUID(), item->getParentUUID(), item->getPermissions(), new_wearable->getID(), new_wearable->getAssetType(), item->getInventoryType(), item->getName(), item->getDescription(), item->getSaleInfo(), item->getFlags(), item->getCreationDate()); template_item->setTransactionID(new_wearable->getTransactionID()); template_item->updateServer(FALSE); gInventory.updateItem(template_item); } else { // Add a new inventory item (shouldn't ever happen here) U32 todo = addWearableToAgentInventoryCallback::CALL_NONE; if (send_update) { todo |= addWearableToAgentInventoryCallback::CALL_UPDATE; } LLPointer cb = new addWearableToAgentInventoryCallback( LLPointer(NULL), (S32)type, new_wearable, todo); addWearableToAgentInventory(cb, new_wearable); return; } if( send_update ) { sendAgentWearablesUpdate(); } } } void LLAgent::saveWearableAs( EWearableType type, const std::string& new_name, BOOL save_in_lost_and_found) { if(!isWearableCopyable(type)) { llwarns << "LLAgent::saveWearableAs() not copyable." << llendl; return; } LLWearable* old_wearable = getWearable(type); if(!old_wearable) { llwarns << "LLAgent::saveWearableAs() no old wearable." << llendl; return; } LLInventoryItem* item = gInventory.getItem(mWearableEntry[type].mItemID); if(!item) { llwarns << "LLAgent::saveWearableAs() no inventory item." << llendl; return; } std::string trunc_name(new_name); LLString::truncate(trunc_name, DB_INV_ITEM_NAME_STR_LEN); LLWearable* new_wearable = gWearableList.createCopyFromAvatar( old_wearable, trunc_name); LLPointer cb = new addWearableToAgentInventoryCallback( LLPointer(NULL), type, new_wearable, addWearableToAgentInventoryCallback::CALL_UPDATE); LLUUID category_id; if (save_in_lost_and_found) { category_id = gInventory.findCategoryUUIDForType( LLAssetType::AT_LOST_AND_FOUND); } else { // put in same folder as original category_id = item->getParentUUID(); } copy_inventory_item( gAgent.getID(), item->getPermissions().getOwner(), item->getUUID(), category_id, new_name, cb); /* LLWearable* old_wearable = getWearable( type ); if( old_wearable ) { LLString old_name = old_wearable->getName(); old_wearable->setName( new_name ); LLWearable* new_wearable = gWearableList.createCopyFromAvatar( old_wearable ); old_wearable->setName( old_name ); LLUUID category_id; LLInventoryItem* item = gInventory.getItem( mWearableEntry[ type ].mItemID ); if( item ) { new_wearable->setPermissions(item->getPermissions()); if (save_in_lost_and_found) { category_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND); } else { // put in same folder as original category_id = item->getParentUUID(); } LLInventoryView* view = LLInventoryView::getActiveInventory(); if(view) { view->getPanel()->setSelection(item->getUUID(), TAKE_FOCUS_NO); } } mWearableEntry[ type ].mWearable = new_wearable; LLPointer cb = new addWearableToAgentInventoryCallback( LLPointer(NULL), type, addWearableToAgentInventoryCallback::CALL_UPDATE); addWearableToAgentInventory(cb, new_wearable, category_id); } */ } void LLAgent::revertWearable( EWearableType type ) { LLWearable* wearable = mWearableEntry[(S32)type].mWearable; if( wearable ) { wearable->writeToAvatar( TRUE ); } sendAgentSetAppearance(); } void LLAgent::revertAllWearables() { for( S32 i=0; i < WT_COUNT; i++ ) { revertWearable( (EWearableType)i ); } } void LLAgent::saveAllWearables() { //if(!gInventory.isLoaded()) //{ // return; //} for( S32 i=0; i < WT_COUNT; i++ ) { saveWearable( (EWearableType)i, FALSE ); } sendAgentWearablesUpdate(); } // Called when the user changes the name of a wearable inventory item that is currenlty being worn. void LLAgent::setWearableName( const LLUUID& item_id, const std::string& new_name ) { for( S32 i=0; i < WT_COUNT; i++ ) { if( mWearableEntry[i].mItemID == item_id ) { LLWearable* old_wearable = mWearableEntry[i].mWearable; llassert( old_wearable ); LLString old_name = old_wearable->getName(); old_wearable->setName( new_name ); LLWearable* new_wearable = gWearableList.createCopy( old_wearable ); LLInventoryItem* item = gInventory.getItem(item_id); if(item) { new_wearable->setPermissions(item->getPermissions()); } old_wearable->setName( old_name ); mWearableEntry[i].mWearable = new_wearable; sendAgentWearablesUpdate(); break; } } } BOOL LLAgent::isWearableModifiable(EWearableType type) { LLUUID item_id = getWearableItem(type); if(!item_id.isNull()) { LLInventoryItem* item = gInventory.getItem(item_id); if(item && item->getPermissions().allowModifyBy(gAgent.getID(), gAgent.getGroupID())) { return TRUE; } } return FALSE; } BOOL LLAgent::isWearableCopyable(EWearableType type) { LLUUID item_id = getWearableItem(type); if(!item_id.isNull()) { LLInventoryItem* item = gInventory.getItem(item_id); if(item && item->getPermissions().allowCopyBy(gAgent.getID(), gAgent.getGroupID())) { return TRUE; } } return FALSE; } U32 LLAgent::getWearablePermMask(EWearableType type) { LLUUID item_id = getWearableItem(type); if(!item_id.isNull()) { LLInventoryItem* item = gInventory.getItem(item_id); if(item) { return item->getPermissions().getMaskOwner(); } } return PERM_NONE; } LLInventoryItem* LLAgent::getWearableInventoryItem(EWearableType type) { LLUUID item_id = getWearableItem(type); LLInventoryItem* item = NULL; if(item_id.notNull()) { item = gInventory.getItem(item_id); } return item; } LLWearable* LLAgent::getWearableFromWearableItem( const LLUUID& item_id ) { for( S32 i=0; i < WT_COUNT; i++ ) { if( mWearableEntry[i].mItemID == item_id ) { return mWearableEntry[i].mWearable; } } return NULL; } void LLAgent::sendAgentWearablesRequest() { gMessageSystem->newMessageFast(_PREHASH_AgentWearablesRequest); gMessageSystem->nextBlockFast(_PREHASH_AgentData); gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() ); sendReliableMessage(); } // Used to enable/disable menu items. // static BOOL LLAgent::selfHasWearable( void* userdata ) { EWearableType type = (EWearableType)(intptr_t)userdata; return gAgent.getWearable( type ) != NULL; } BOOL LLAgent::isWearingItem( const LLUUID& item_id ) { return (getWearableFromWearableItem( item_id ) != NULL); } // static void LLAgent::processAgentInitialWearablesUpdate( LLMessageSystem* mesgsys, void** user_data ) { // We should only receive this message a single time. Ignore subsequent AgentWearablesUpdates // that may result from AgentWearablesRequest having been sent more than once. static BOOL first = TRUE; if( first ) { first = FALSE; } else { return; } if (gNoRender) { return; } LLUUID agent_id; gMessageSystem->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id ); LLVOAvatar* avatar = gAgent.getAvatarObject(); if( avatar && (agent_id == avatar->getID()) ) { gMessageSystem->getU32Fast(_PREHASH_AgentData, _PREHASH_SerialNum, gAgent.mAgentWearablesUpdateSerialNum ); S32 num_wearables = gMessageSystem->getNumberOfBlocksFast(_PREHASH_WearableData); if( num_wearables < 4 ) { // Transitional state. Avatars should always have at least their body parts (hair, eyes, shape and skin). // The fact that they don't have any here (only a dummy is sent) implies that this account existed // before we had wearables, or that the database has gotten messed up. // Deal with this by creating new body parts. //avatar->createStandardWearables(); // no, deal with it by noting that we need to choose a // gender. gAgent.setGenderChosen(FALSE); return; } //lldebugs << "processAgentInitialWearablesUpdate()" << llendl; // Add wearables LLUUID asset_id_array[ WT_COUNT ]; S32 i; for( i=0; i < num_wearables; i++ ) { U8 type_u8 = 0; gMessageSystem->getU8Fast(_PREHASH_WearableData, _PREHASH_WearableType, type_u8, i ); if( type_u8 >= WT_COUNT ) { continue; } EWearableType type = (EWearableType) type_u8; LLUUID item_id; gMessageSystem->getUUIDFast(_PREHASH_WearableData, _PREHASH_ItemID, item_id, i ); LLUUID asset_id; gMessageSystem->getUUIDFast(_PREHASH_WearableData, _PREHASH_AssetID, asset_id, i ); if( asset_id.isNull() ) { LLWearable::removeFromAvatar( type, FALSE ); } else { LLAssetType::EType asset_type = LLWearable::typeToAssetType( type ); if( asset_type == LLAssetType::AT_NONE ) { continue; } gAgent.mWearableEntry[type].mItemID = item_id; asset_id_array[type] = asset_id; } lldebugs << " " << LLWearable::typeToTypeLabel(type) << llendl; } // now that we have the asset ids...request the wearable assets for( i = 0; i < WT_COUNT; i++ ) { if( !gAgent.mWearableEntry[i].mItemID.isNull() ) { gWearableList.getAsset( asset_id_array[i], LLString::null, LLWearable::typeToAssetType( (EWearableType) i ), LLAgent::onInitialWearableAssetArrived, (void*)(intptr_t)i ); } } } } // A single wearable that the avatar was wearing on start-up has arrived from the database. // static void LLAgent::onInitialWearableAssetArrived( LLWearable* wearable, void* userdata ) { EWearableType type = (EWearableType)(intptr_t)userdata; LLVOAvatar* avatar = gAgent.getAvatarObject(); if( !avatar ) { return; } if( wearable ) { llassert( type == wearable->getType() ); gAgent.mWearableEntry[ type ].mWearable = wearable; // disable composites if initial textures are baked avatar->setupComposites(); gAgent.queryWearableCache(); wearable->writeToAvatar( FALSE ); avatar->setCompositeUpdatesEnabled(TRUE); gInventory.addChangedMask( LLInventoryObserver::LABEL, gAgent.mWearableEntry[type].mItemID ); } else { // Somehow the asset doesn't exist in the database. gAgent.recoverMissingWearable( type ); } gInventory.notifyObservers(); // Have all the wearables that the avatar was wearing at log-in arrived? if( !gAgent.mWearablesLoaded ) { gAgent.mWearablesLoaded = TRUE; for( S32 i = 0; i < WT_COUNT; i++ ) { if( !gAgent.mWearableEntry[i].mItemID.isNull() && !gAgent.mWearableEntry[i].mWearable ) { gAgent.mWearablesLoaded = FALSE; break; } } } if( gAgent.mWearablesLoaded ) { // Make sure that the server's idea of the avatar's wearables actually match the wearables. gAgent.sendAgentSetAppearance(); // Check to see if there are any baked textures that we hadn't uploaded before we logged off last time. // If there are any, schedule them to be uploaded as soon as the layer textures they depend on arrive. if( !gAgent.cameraCustomizeAvatar() ) { avatar->requestLayerSetUploads(); } } } // Normally, all wearables referred to "AgentWearablesUpdate" will correspond to actual assets in the // database. If for some reason, we can't load one of those assets, we can try to reconstruct it so that // the user isn't left without a shape, for example. (We can do that only after the inventory has loaded.) void LLAgent::recoverMissingWearable( EWearableType type ) { // Try to recover by replacing missing wearable with a new one. LLNotifyBox::showXml("ReplacedMissingWearable"); lldebugs << "Wearable " << LLWearable::typeToTypeLabel( type ) << " could not be downloaded. Replaced inventory item with default wearable." << llendl; LLWearable* new_wearable = gWearableList.createNewWearable(type); S32 type_s32 = (S32) type; mWearableEntry[type_s32].mWearable = new_wearable; new_wearable->writeToAvatar( TRUE ); // Add a new one in the lost and found folder. // (We used to overwrite the "not found" one, but that could potentially // destory content.) JC LLUUID lost_and_found_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND); LLPointer cb = new addWearableToAgentInventoryCallback( LLPointer(NULL), type_s32, new_wearable, addWearableToAgentInventoryCallback::CALL_RECOVERDONE); addWearableToAgentInventory( cb, new_wearable, lost_and_found_id, TRUE); } void LLAgent::recoverMissingWearableDone() { // Have all the wearables that the avatar was wearing at log-in arrived or been fabricated? mWearablesLoaded = TRUE; for( S32 i = 0; i < WT_COUNT; i++ ) { if( !mWearableEntry[i].mItemID.isNull() && !mWearableEntry[i].mWearable ) { mWearablesLoaded = FALSE; break; } } if( mWearablesLoaded ) { // Make sure that the server's idea of the avatar's wearables actually match the wearables. sendAgentSetAppearance(); } else { gInventory.addChangedMask( LLInventoryObserver::LABEL, LLUUID::null ); gInventory.notifyObservers(); } } void LLAgent::createStandardWearables(BOOL female) { llwarns << "Creating Standard " << (female ? "female" : "male" ) << " Wearables" << llendl; if (mAvatarObject.isNull()) { return; } if(female) mAvatarObject->setSex(SEX_FEMALE); else mAvatarObject->setSex(SEX_MALE); BOOL create[WT_COUNT] = { TRUE, //WT_SHAPE TRUE, //WT_SKIN TRUE, //WT_HAIR TRUE, //WT_EYES TRUE, //WT_SHIRT TRUE, //WT_PANTS TRUE, //WT_SHOES TRUE, //WT_SOCKS FALSE, //WT_JACKET FALSE, //WT_GLOVES TRUE, //WT_UNDERSHIRT TRUE, //WT_UNDERPANTS FALSE //WT_SKIRT }; for( S32 i=0; i < WT_COUNT; i++ ) { bool once = false; LLPointer donecb = NULL; if( create[i] ) { if (!once) { once = true; donecb = new createStandardWearablesAllDoneCallback; } llassert( mWearableEntry[i].mWearable == NULL ); LLWearable* wearable = gWearableList.createNewWearable((EWearableType)i); mWearableEntry[i].mWearable = wearable; // no need to update here... LLPointer cb = new addWearableToAgentInventoryCallback( donecb, i, wearable, addWearableToAgentInventoryCallback::CALL_CREATESTANDARDDONE); addWearableToAgentInventory(cb, wearable, LLUUID::null, FALSE); } } } void LLAgent::createStandardWearablesDone(S32 index) { LLWearable* wearable = mWearableEntry[index].mWearable; if (wearable) { wearable->writeToAvatar(TRUE); } } void LLAgent::createStandardWearablesAllDone() { // ... because sendAgentWearablesUpdate will notify inventory // observers. mWearablesLoaded = TRUE; sendAgentWearablesUpdate(); sendAgentSetAppearance(); // Treat this as the first texture entry message, if none received yet mAvatarObject->onFirstTEMessageReceived(); } void LLAgent::makeNewOutfit( const std::string& new_folder_name, const LLDynamicArray& wearables_to_include, const LLDynamicArray& attachments_to_include, BOOL rename_clothing) { if (mAvatarObject.isNull()) { return; } // First, make a folder in the Clothes directory. LLUUID folder_id = gInventory.createNewCategory( gInventory.findCategoryUUIDForType(LLAssetType::AT_CLOTHING), LLAssetType::AT_NONE, new_folder_name); bool found_first_item = false; /////////////////// // Wearables if( wearables_to_include.count() ) { // Then, iterate though each of the wearables and save copies of them in the folder. S32 i; S32 count = wearables_to_include.count(); LLDynamicArray delete_items; LLPointer cbdone = NULL; for( i = 0; i < count; ++i ) { S32 index = wearables_to_include[i]; LLWearable* old_wearable = mWearableEntry[ index ].mWearable; if( old_wearable ) { std::string new_name; LLWearable* new_wearable; new_wearable = gWearableList.createCopy(old_wearable); if (rename_clothing) { new_name = new_folder_name; new_name.append(" "); new_name.append(old_wearable->getTypeLabel()); LLString::truncate(new_name, DB_INV_ITEM_NAME_STR_LEN); new_wearable->setName(new_name); } LLViewerInventoryItem* item = gInventory.getItem(mWearableEntry[index].mItemID); S32 todo = addWearableToAgentInventoryCallback::CALL_NONE; if (!found_first_item) { found_first_item = true; /* set the focus to the first item */ todo |= addWearableToAgentInventoryCallback::CALL_MAKENEWOUTFITDONE; /* send the agent wearables update when done */ cbdone = new sendAgentWearablesUpdateCallback; } LLPointer cb = new addWearableToAgentInventoryCallback( cbdone, index, new_wearable, todo); if (isWearableCopyable((EWearableType)index)) { copy_inventory_item( gAgent.getID(), item->getPermissions().getOwner(), item->getUUID(), folder_id, new_name, cb); } else { move_inventory_item( gAgent.getID(), gAgent.getSessionID(), item->getUUID(), folder_id, new_name, cb); } } } gInventory.notifyObservers(); } /////////////////// // Attachments if( attachments_to_include.count() ) { BOOL msg_started = FALSE; LLMessageSystem* msg = gMessageSystem; S32 i; for( i = 0; i < attachments_to_include.count(); i++ ) { S32 attachment_pt = attachments_to_include[i]; LLViewerJointAttachment* attachment = mAvatarObject->mAttachmentPoints.getIfThere( attachment_pt ); if(!attachment) continue; LLViewerObject* attached_object = attachment->getObject(); if(!attached_object) continue; const LLUUID& item_id = attachment->getItemID(); if(item_id.isNull()) continue; LLInventoryItem* item = gInventory.getItem(item_id); if(!item) continue; if(!msg_started) { msg_started = TRUE; msg->newMessage("CreateNewOutfitAttachments"); msg->nextBlock("AgentData"); msg->addUUID("AgentID", getID()); msg->addUUID("SessionID", getSessionID()); msg->nextBlock("HeaderData"); msg->addUUID("NewFolderID", folder_id); } msg->nextBlock("ObjectData"); msg->addUUID("OldItemID", item_id); msg->addUUID("OldFolderID", item->getParentUUID()); } if( msg_started ) { sendReliableMessage(); } } } void LLAgent::makeNewOutfitDone(S32 index) { LLUUID first_item_id = mWearableEntry[index].mItemID; // Open the inventory and select the first item we added. if( first_item_id.notNull() ) { LLInventoryView* view = LLInventoryView::getActiveInventory(); if(view) { view->getPanel()->setSelection(first_item_id, TAKE_FOCUS_NO); } } } void LLAgent::addWearableToAgentInventory( LLPointer cb, LLWearable* wearable, const LLUUID& category_id, BOOL notify) { create_inventory_item( gAgent.getID(), gAgent.getSessionID(), category_id, wearable->getTransactionID(), wearable->getName(), wearable->getDescription(), wearable->getAssetType(), LLInventoryType::IT_WEARABLE, wearable->getType(), wearable->getPermissions().getMaskNextOwner(), cb); } //----------------------------------------------------------------------------- // sendAgentSetAppearance() //----------------------------------------------------------------------------- void LLAgent::sendAgentSetAppearance() { if (mAvatarObject.isNull()) return; if (mNumPendingQueries > 0 && !gAgent.cameraCustomizeAvatar()) { return; } llinfos << "TAT: Sent AgentSetAppearance: " << (( mAvatarObject->getTEImage( LLVOAvatar::TEX_HEAD_BAKED )->getID() != IMG_DEFAULT_AVATAR ) ? "HEAD " : "head " ) << (( mAvatarObject->getTEImage( LLVOAvatar::TEX_UPPER_BAKED )->getID() != IMG_DEFAULT_AVATAR ) ? "UPPER " : "upper " ) << (( mAvatarObject->getTEImage( LLVOAvatar::TEX_LOWER_BAKED )->getID() != IMG_DEFAULT_AVATAR ) ? "LOWER " : "lower " ) << (( mAvatarObject->getTEImage( LLVOAvatar::TEX_EYES_BAKED )->getID() != IMG_DEFAULT_AVATAR ) ? "EYES" : "eyes" ) << llendl; //dumpAvatarTEs( "sendAgentSetAppearance()" ); LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_AgentSetAppearance); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, getID()); msg->addUUIDFast(_PREHASH_SessionID, getSessionID()); // correct for the collisiton tolerance (to make it look like the // agent is actually walking on the ground/object) // NOTE -- when we start correcting all of the other Havok geometry // to compensate for the COLLISION_TOLERANCE ugliness we will have // to tweak this number again LLVector3 body_size = mAvatarObject->mBodySize; msg->addVector3Fast(_PREHASH_Size, body_size); // To guard against out of order packets // Note: always start by sending 1. This resets the server's count. 0 on the server means "uninitialized" mAppearanceSerialNum++; msg->addU32Fast(_PREHASH_SerialNum, mAppearanceSerialNum ); // is texture data current relative to wearables? // KLW - TAT this will probably need to check the local queue. BOOL textures_current = !mAvatarObject->hasPendingBakedUploads() && mWearablesLoaded; S32 baked_texture_index; for( baked_texture_index = 0; baked_texture_index < BAKED_TEXTURE_COUNT; baked_texture_index++ ) { S32 tex_index = LLVOAvatar::sBakedTextureIndices[baked_texture_index]; // if we're not wearing a skirt, we don't need the texture to be baked if (tex_index == LLVOAvatar::TEX_SKIRT_BAKED && !mAvatarObject->isWearingWearableType(WT_SKIRT)) { continue; } // IMG_DEFAULT_AVATAR means not baked if (mAvatarObject->getTEImage( tex_index)->getID() == IMG_DEFAULT_AVATAR) { textures_current = FALSE; break; } } // only update cache entries if we have all our baked textures if (textures_current) { llinfos << "TAT: Sending cached texture data" << llendl; for (baked_texture_index = 0; baked_texture_index < BAKED_TEXTURE_COUNT; baked_texture_index++) { LLUUID hash; for( S32 wearable_num = 0; wearable_num < MAX_WEARABLES_PER_LAYERSET; wearable_num++ ) { EWearableType wearable_type = WEARABLE_BAKE_TEXTURE_MAP[baked_texture_index][wearable_num]; LLWearable* wearable = getWearable( wearable_type ); if (wearable) { hash ^= wearable->getID(); } } if (hash.notNull()) { hash ^= BAKED_TEXTURE_HASH[baked_texture_index]; } S32 tex_index = LLVOAvatar::sBakedTextureIndices[baked_texture_index]; msg->nextBlockFast(_PREHASH_WearableData); msg->addUUIDFast(_PREHASH_CacheID, hash); msg->addU8Fast(_PREHASH_TextureIndex, (U8)tex_index); } } msg->nextBlockFast(_PREHASH_ObjectData); mAvatarObject->packTEMessage( gMessageSystem ); S32 transmitted_params = 0; for (LLViewerVisualParam* param = (LLViewerVisualParam*)mAvatarObject->getFirstVisualParam(); param; param = (LLViewerVisualParam*)mAvatarObject->getNextVisualParam()) { F32 param_value = param->getWeight(); if (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE) { msg->nextBlockFast(_PREHASH_VisualParam ); // We don't send the param ids. Instead, we assume that the receiver has the same params in the same sequence. U8 new_weight = F32_to_U8(param_value, param->getMinWeight(), param->getMaxWeight()); msg->addU8Fast(_PREHASH_ParamValue, new_weight ); transmitted_params++; } } // llinfos << "Avatar XML num VisualParams transmitted = " << transmitted_params << llendl; sendReliableMessage(); } void LLAgent::sendAgentDataUpdateRequest() { gMessageSystem->newMessageFast(_PREHASH_AgentDataUpdateRequest); gMessageSystem->nextBlockFast(_PREHASH_AgentData); gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); sendReliableMessage(); } void LLAgent::removeWearable( EWearableType type ) { LLWearable* old_wearable = mWearableEntry[ type ].mWearable; if ( (gAgent.mAccess < SIM_ACCESS_MATURE) && (type == WT_UNDERSHIRT || type == WT_UNDERPANTS)) { // Can't take off underclothing in simple UI mode or on PG accounts return; } if( old_wearable ) { if( old_wearable->isDirty() ) { // Bring up view-modal dialog: Save changes? Yes, No, Cancel gViewerWindow->alertXml("RemoveWearableSave", LLAgent::onRemoveWearableDialog, (void*)(S32)type ); return; } else { removeWearableFinal( type ); } } } // static void LLAgent::onRemoveWearableDialog( S32 option, void* userdata ) { EWearableType type = (EWearableType)(intptr_t)userdata; switch( option ) { case 0: // "Save" gAgent.saveWearable( type ); gAgent.removeWearableFinal( type ); break; case 1: // "Don't Save" gAgent.removeWearableFinal( type ); break; case 2: // "Cancel" break; default: llassert(0); break; } } // Called by removeWearable() and onRemoveWearableDialog() to actually do the removal. void LLAgent::removeWearableFinal( EWearableType type ) { LLWearable* old_wearable = mWearableEntry[ type ].mWearable; gInventory.addChangedMask( LLInventoryObserver::LABEL, mWearableEntry[type].mItemID ); mWearableEntry[ type ].mWearable = NULL; mWearableEntry[ type ].mItemID.setNull(); queryWearableCache(); if( old_wearable ) { old_wearable->removeFromAvatar( TRUE ); } // Update the server sendAgentWearablesUpdate(); sendAgentSetAppearance(); gInventory.notifyObservers(); } void LLAgent::copyWearableToInventory( EWearableType type ) { LLWearable* wearable = mWearableEntry[ type ].mWearable; if( wearable ) { // Save the old wearable if it has changed. if( wearable->isDirty() ) { wearable = gWearableList.createCopyFromAvatar( wearable ); mWearableEntry[ type ].mWearable = wearable; } // Make a new entry in the inventory. (Put it in the same folder as the original item if possible.) LLUUID category_id; LLInventoryItem* item = gInventory.getItem( mWearableEntry[ type ].mItemID ); if( item ) { category_id = item->getParentUUID(); wearable->setPermissions(item->getPermissions()); } LLPointer cb = new addWearableToAgentInventoryCallback( LLPointer(NULL), type, wearable); addWearableToAgentInventory(cb, wearable, category_id); } } // A little struct to let setWearable() communicate more than one value with onSetWearableDialog(). struct LLSetWearableData { LLSetWearableData( const LLUUID& new_item_id, LLWearable* new_wearable ) : mNewItemID( new_item_id ), mNewWearable( new_wearable ) {} LLUUID mNewItemID; LLWearable* mNewWearable; }; BOOL LLAgent::needsReplacement(EWearableType wearableType, S32 remove) { return TRUE; /*if (remove) return TRUE; return getWearable(wearableType) ? TRUE : FALSE;*/ } // Assumes existing wearables are not dirty. void LLAgent::setWearableOutfit( const LLInventoryItem::item_array_t& items, const LLDynamicArray< LLWearable* >& wearables, BOOL remove ) { lldebugs << "setWearableOutfit() start" << llendl; BOOL wearables_to_remove[WT_COUNT]; wearables_to_remove[WT_SHAPE] = FALSE; wearables_to_remove[WT_SKIN] = FALSE; wearables_to_remove[WT_HAIR] = FALSE; wearables_to_remove[WT_EYES] = FALSE; wearables_to_remove[WT_SHIRT] = remove; wearables_to_remove[WT_PANTS] = remove; wearables_to_remove[WT_SHOES] = remove; wearables_to_remove[WT_SOCKS] = remove; wearables_to_remove[WT_JACKET] = remove; wearables_to_remove[WT_GLOVES] = remove; wearables_to_remove[WT_UNDERSHIRT] = (gAgent.mAccess >= SIM_ACCESS_MATURE) & remove; wearables_to_remove[WT_UNDERPANTS] = (gAgent.mAccess >= SIM_ACCESS_MATURE) & remove; wearables_to_remove[WT_SKIRT] = remove; S32 count = wearables.count(); llassert( items.count() == count ); S32 i; for( i = 0; i < count; i++ ) { LLWearable* new_wearable = wearables[i]; LLPointer new_item = items[i]; EWearableType type = new_wearable->getType(); wearables_to_remove[type] = FALSE; LLWearable* old_wearable = mWearableEntry[ type ].mWearable; if( old_wearable ) { const LLUUID& old_item_id = mWearableEntry[ type ].mItemID; if( (old_wearable->getID() == new_wearable->getID()) && (old_item_id == new_item->getUUID()) ) { lldebugs << "No change to wearable asset and item: " << LLWearable::typeToTypeName( type ) << llendl; continue; } gInventory.addChangedMask(LLInventoryObserver::LABEL, old_item_id); // Assumes existing wearables are not dirty. if( old_wearable->isDirty() ) { llassert(0); continue; } } mWearableEntry[ type ].mItemID = new_item->getUUID(); mWearableEntry[ type ].mWearable = new_wearable; } std::vector wearables_being_removed; for( i = 0; i < WT_COUNT; i++ ) { if( wearables_to_remove[i] ) { wearables_being_removed.push_back(mWearableEntry[ i ].mWearable); mWearableEntry[ i ].mWearable = NULL; gInventory.addChangedMask(LLInventoryObserver::LABEL, mWearableEntry[ i ].mItemID); mWearableEntry[ i ].mItemID.setNull(); } } gInventory.notifyObservers(); queryWearableCache(); std::vector::iterator wearable_iter; for( wearable_iter = wearables_being_removed.begin(); wearable_iter != wearables_being_removed.end(); ++wearable_iter) { LLWearable* wearablep = *wearable_iter; if (wearablep) { wearablep->removeFromAvatar( TRUE ); } } for( i = 0; i < count; i++ ) { wearables[i]->writeToAvatar( TRUE ); } LLFloaterCustomize::setCurrentWearableType( WT_SHAPE ); // Start rendering & update the server mWearablesLoaded = TRUE; sendAgentWearablesUpdate(); sendAgentSetAppearance(); lldebugs << "setWearableOutfit() end" << llendl; } // User has picked "wear on avatar" from a menu. void LLAgent::setWearable( LLInventoryItem* new_item, LLWearable* new_wearable ) { EWearableType type = new_wearable->getType(); LLWearable* old_wearable = mWearableEntry[ type ].mWearable; if( old_wearable ) { const LLUUID& old_item_id = mWearableEntry[ type ].mItemID; if( (old_wearable->getID() == new_wearable->getID()) && (old_item_id == new_item->getUUID()) ) { lldebugs << "No change to wearable asset and item: " << LLWearable::typeToTypeName( type ) << llendl; return; } if( old_wearable->isDirty() ) { // Bring up modal dialog: Save changes? Yes, No, Cancel gViewerWindow->alertXml( "SetWearableSave", LLAgent::onSetWearableDialog, new LLSetWearableData( new_item->getUUID(), new_wearable )); return; } } setWearableFinal( new_item, new_wearable ); } // static void LLAgent::onSetWearableDialog( S32 option, void* userdata ) { LLSetWearableData* data = (LLSetWearableData*)userdata; LLInventoryItem* new_item = gInventory.getItem( data->mNewItemID ); if( !new_item ) { delete data; return; } switch( option ) { case 0: // "Save" gAgent.saveWearable( data->mNewWearable->getType() ); gAgent.setWearableFinal( new_item, data->mNewWearable ); break; case 1: // "Don't Save" gAgent.setWearableFinal( new_item, data->mNewWearable ); break; case 2: // "Cancel" break; default: llassert(0); break; } delete data; } // Called from setWearable() and onSetWearableDialog() to actually set the wearable. void LLAgent::setWearableFinal( LLInventoryItem* new_item, LLWearable* new_wearable ) { EWearableType type = new_wearable->getType(); // Replace the old wearable with a new one. llassert( new_item->getAssetUUID() == new_wearable->getID() ); LLUUID old_item_id = mWearableEntry[ type ].mItemID; mWearableEntry[ type ].mItemID = new_item->getUUID(); mWearableEntry[ type ].mWearable = new_wearable; if (old_item_id.notNull()) { gInventory.addChangedMask( LLInventoryObserver::LABEL, old_item_id ); gInventory.notifyObservers(); } //llinfos << "LLVOAvatar::setWearable()" << llendl; queryWearableCache(); new_wearable->writeToAvatar( TRUE ); // Update the server sendAgentWearablesUpdate(); sendAgentSetAppearance(); } void LLAgent::queryWearableCache() { if (!mWearablesLoaded) { return; } // Look up affected baked textures. // If they exist: // disallow updates for affected layersets (until dataserver responds with cache request.) // If cache miss…turn updates back on and invalidate composite. // If cache hit, modify baked texture entries. // // Cache requests contain list of hashes for each baked texture entry. // Response is list of valid baked texture assets. (same message) gMessageSystem->newMessageFast(_PREHASH_AgentCachedTexture); gMessageSystem->nextBlockFast(_PREHASH_AgentData); gMessageSystem->addUUIDFast(_PREHASH_AgentID, getID()); gMessageSystem->addUUIDFast(_PREHASH_SessionID, getSessionID()); gMessageSystem->addS32Fast(_PREHASH_SerialNum, mTextureCacheQueryID); S32 num_queries = 0; for (S32 baked_texture_index = 0; baked_texture_index < BAKED_TEXTURE_COUNT; baked_texture_index++) { LLUUID hash; for (S32 wearable_num = 0; wearable_num < MAX_WEARABLES_PER_LAYERSET; wearable_num++) { EWearableType wearable_type = WEARABLE_BAKE_TEXTURE_MAP[baked_texture_index][wearable_num]; LLWearable* wearable = getWearable( wearable_type ); if (wearable) { hash ^= wearable->getID(); } } if (hash.notNull()) { hash ^= BAKED_TEXTURE_HASH[baked_texture_index]; num_queries++; // *NOTE: make sure at least one request gets packed //llinfos << "Requesting texture for hash " << hash << " in baked texture slot " << baked_texture_index << llendl; gMessageSystem->nextBlockFast(_PREHASH_WearableData); gMessageSystem->addUUIDFast(_PREHASH_ID, hash); gMessageSystem->addU8Fast(_PREHASH_TextureIndex, (U8)baked_texture_index); } mActiveCacheQueries[ baked_texture_index ] = mTextureCacheQueryID; } llinfos << "Requesting texture cache entry for " << num_queries << " baked textures" << llendl; gMessageSystem->sendReliable(getRegion()->getHost()); mNumPendingQueries++; mTextureCacheQueryID++; } // User has picked "remove from avatar" from a menu. // static void LLAgent::userRemoveWearable( void* userdata ) { EWearableType type = (EWearableType)(intptr_t)userdata; if( !(type==WT_SHAPE || type==WT_SKIN || type==WT_HAIR ) ) //&& //!((gAgent.mAccess >= SIM_ACCESS_MATURE) && ( type==WT_UNDERPANTS || type==WT_UNDERSHIRT )) ) { gAgent.removeWearable( type ); } } void LLAgent::userRemoveAllClothes( void* userdata ) { // We have to do this up front to avoid having to deal with the case of multiple wearables being dirty. if( gFloaterCustomize ) { gFloaterCustomize->askToSaveAllIfDirty( LLAgent::userRemoveAllClothesStep2, NULL ); } else { LLAgent::userRemoveAllClothesStep2( TRUE, NULL ); } } void LLAgent::userRemoveAllClothesStep2( BOOL proceed, void* userdata ) { if( proceed ) { gAgent.removeWearable( WT_SHIRT ); gAgent.removeWearable( WT_PANTS ); gAgent.removeWearable( WT_SHOES ); gAgent.removeWearable( WT_SOCKS ); gAgent.removeWearable( WT_JACKET ); gAgent.removeWearable( WT_GLOVES ); gAgent.removeWearable( WT_UNDERSHIRT ); gAgent.removeWearable( WT_UNDERPANTS ); gAgent.removeWearable( WT_SKIRT ); } } void LLAgent::userRemoveAllAttachments( void* userdata ) { LLVOAvatar* avatarp = gAgent.getAvatarObject(); if(!avatarp) { llwarns << "No avatar found." << llendl; return; } gMessageSystem->newMessage("ObjectDetach"); gMessageSystem->nextBlockFast(_PREHASH_AgentData); gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); LLViewerJointAttachment* attachment; for (attachment = avatarp->mAttachmentPoints.getFirstData(); attachment; attachment = avatarp->mAttachmentPoints.getNextData()) { LLViewerObject* objectp = attachment->getObject(); if (objectp) { gMessageSystem->nextBlockFast(_PREHASH_ObjectData); gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, objectp->getLocalID()); } } gMessageSystem->sendReliable( gAgent.getRegionHost() ); } bool LLAgent::LLHideGroupTitleListener::handleEvent(LLPointer event, const LLSD &userdata) { gAgent.setHideGroupTitle(event->getValue()); return true; } bool LLAgent::LLEffectColorListener::handleEvent(LLPointer event, const LLSD &userdata) { gAgent.setEffectColor(LLColor4(event->getValue())); return true; } void LLAgent::observeFriends() { if(!mFriendObserver) { mFriendObserver = new LLAgentFriendObserver; LLAvatarTracker::instance().addObserver(mFriendObserver); friendsChanged(); } } // EOF