Newer
Older
/**
* @file llagent.cpp
* @brief LLAgent class implementation
*
* $LicenseInfo:firstyear=2001&license=viewergpl$
*
* Copyright (c) 2001-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "llagent.h"
#include "llagentcamera.h"
brad kittenbrink
committed
#include "llagentlistener.h"
#include "llanimationstates.h"
#include "llfirstuse.h"
#include "llgroupactions.h"
Andrew Meadows
committed
#include "llhomelocationresponder.h"
#include "llhudmanager.h"
#include "lljoystickbutton.h"
#include "llmorphview.h"
#include "llmoveview.h"
#include "llnavigationbar.h" // to show/hide navigation bar when changing mouse look state
#include "llnearbychatbar.h"
#include "llnotificationsutil.h"
Andrew Polunin
committed
#include "llpaneltopinfobar.h"
#include "llparcel.h"
#include "llrendersphere.h"
Andrew Meadows
committed
#include "llsdutil.h"
#include "llsmoothstep.h"
brad kittenbrink
committed
#include "llteleportflags.h"
#include "llviewerdisplay.h"
#include "llviewerobjectlist.h"
#include "llviewerparcelmgr.h"
#include "llviewerstats.h"
#include "llvoavatarself.h"
#include "llwindow.h"
using namespace LLVOAvatarDefines;
const BOOL ANIMATE = TRUE;
const U8 AGENT_STATE_TYPING = 0x04;
const U8 AGENT_STATE_EDITING = 0x10;
// Autopilot constants
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
const F32 MAX_VELOCITY_AUTO_LAND_SQUARED = 4.f * 4.f;
const F64 CHAT_AGE_FAST_RATE = 3.0;
// fidget constants
const F32 MIN_FIDGET_TIME = 8.f; // seconds
const F32 MAX_FIDGET_TIME = 20.f; // seconds
Steven Bennetts
committed
// The agent instance.
LLAgent gAgent;
//--------------------------------------------------------------------
// Statics
//
const F32 LLAgent::TYPING_TIMEOUT_SECS = 5.f;
std::map<std::string, std::string> LLAgent::sTeleportErrorMessages;
std::map<std::string, std::string> LLAgent::sTeleportProgressMessages;
Don Kjer
committed
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();
}
}
bool handleSlowMotionAnimation(const LLSD& newvalue)
{
if (newvalue.asBoolean())
{
gAgentAvatarp->setAnimTimeFactor(0.2f);
}
else
{
gAgentAvatarp->setAnimTimeFactor(1.0f);
}
return true;
}
// ************************************************************
// 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()
//-----------------------------------------------------------------------------
brad kittenbrink
committed
mListener(),
mDoubleTapRunTimer(),
mDoubleTapRunMode(DOUBLETAP_NONE),
mbAlwaysRun(false),
mbRunning(false),
Dave Simmons
committed
mAgentAccess(gSavedSettings),
mTeleportState( TELEPORT_NONE ),
mRegionp(NULL),
mAgentOriginGlobal(),
mPositionGlobal(),
mLastPositionGlobal(LLVector3d::zero),
mRenderState(0),
mTypingTimer(),
mViewsPushed(FALSE),
mShowAvatar(TRUE),
mFrameAgent(),
mIsBusy(FALSE),
mControlFlags(0x00000000),
mbFlagsDirty(FALSE),
mbFlagsNeedReset(FALSE),
mbJump(FALSE),
mAutoPilot(FALSE),
mAutoPilotFlyOnStop(FALSE),
mAutoPilotTargetGlobal(),
mAutoPilotStopDistance(1.f),
mAutoPilotUseRotation(FALSE),
mAutoPilotTargetFacing(LLVector3::zero),
mAutoPilotTargetDist(0.f),
mAutoPilotNoProgressFrameCount(0),
mAutoPilotRotationThreshold(0.f),
mAutoPilotFinishedCallback(NULL),
mAutoPilotCallbackData(NULL),
James Cook
committed
mEffectColor(LLColor4(0.f, 1.f, 1.f, 1.f)),
mHaveHomePosition(FALSE),
mHomeRegionHandle( 0 ),
mNearChatRadius(CHAT_NORMAL_RADIUS / 2.f),
mNextFidgetTime(0.f),
mCurrentFidget(0),
mFirstLogin(FALSE),
mGenderChosen(FALSE),
for (U32 i = 0; i < TOTAL_CONTROLS; i++)
{
mControlsTakenCount[i] = 0;
mControlsTakenPassedOnCount[i] = 0;
}
brad kittenbrink
committed
mListener.reset(new LLAgentListener(*this));
}
// Requires gSavedSettings to be initialized.
//-----------------------------------------------------------------------------
// init()
//-----------------------------------------------------------------------------
void LLAgent::init()
{
gSavedSettings.declareBOOL("SlowMotionAnimation", FALSE, "Declared in code", FALSE);
gSavedSettings.getControl("SlowMotionAnimation")->getSignal()->connect(boost::bind(&handleSlowMotionAnimation, _2));
// *Note: this is where LLViewerCamera::getInstance() used to be constructed.
setFlying( gSavedSettings.getBOOL("FlyingAtExit") );
James Cook
committed
mEffectColor = LLUIColorTable::instance().getColor("EffectColor");
gSavedSettings.getControl("PreferredMaturity")->getValidateSignal()->connect(boost::bind(&LLAgent::validateMaturity, this, _2));
gSavedSettings.getControl("PreferredMaturity")->getSignal()->connect(boost::bind(&LLAgent::handleMaturity, this, _2));
mInitialized = TRUE;
}
//-----------------------------------------------------------------------------
// cleanup()
//-----------------------------------------------------------------------------
void LLAgent::cleanup()
{
mRegionp = NULL;
}
//-----------------------------------------------------------------------------
// LLAgent()
//-----------------------------------------------------------------------------
LLAgent::~LLAgent()
{
cleanup();
// *Note: this is where LLViewerCamera::getInstance() used to be deleted.
// Handle any actions that need to be performed when the main app gains focus
// (such as through alt-tab).
//-----------------------------------------------------------------------------
// onAppFocusGained()
//-----------------------------------------------------------------------------
void LLAgent::onAppFocusGained()
{
if (CAMERA_MODE_MOUSELOOK == gAgentCamera.getCameraMode())
gAgentCamera.changeCameraToDefault();
LLToolMgr::getInstance()->clearSavedTool();
F64 elapsed_time = (F64)gAgentAvatarp->mChatTimer.getElapsedTimeF32();
gAgentAvatarp->mChatTimer.setAge(elapsed_time + (F64)gFrameDTClamped * (CHAT_AGE_FAST_RATE - 1.0));
}
}
//-----------------------------------------------------------------------------
// moveAt()
//-----------------------------------------------------------------------------
mMoveTimer.reset();
LLFirstUse::notMoving(false);
// age chat timer so it fades more quickly when you are intentionally moving
ageChat();
Loren Shih
committed
gAgentCamera.setAtKey(LLAgentCamera::directionToKey(direction));
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);
}
gAgentCamera.resetView();
}
//-----------------------------------------------------------------------------
// moveAtNudge()
//-----------------------------------------------------------------------------
void LLAgent::moveAtNudge(S32 direction)
{
mMoveTimer.reset();
LLFirstUse::notMoving(false);
// age chat timer so it fades more quickly when you are intentionally moving
ageChat();
Loren Shih
committed
gAgentCamera.setWalkKey(LLAgentCamera::directionToKey(direction));
if (direction > 0)
{
setControlFlags(AGENT_CONTROL_NUDGE_AT_POS);
}
else if (direction < 0)
{
setControlFlags(AGENT_CONTROL_NUDGE_AT_NEG);
}
gAgentCamera.resetView();
}
//-----------------------------------------------------------------------------
// moveLeft()
//-----------------------------------------------------------------------------
void LLAgent::moveLeft(S32 direction)
{
mMoveTimer.reset();
LLFirstUse::notMoving(false);
// age chat timer so it fades more quickly when you are intentionally moving
ageChat();
Loren Shih
committed
gAgentCamera.setLeftKey(LLAgentCamera::directionToKey(direction));
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);
}
gAgentCamera.resetView();
}
//-----------------------------------------------------------------------------
// moveLeftNudge()
//-----------------------------------------------------------------------------
void LLAgent::moveLeftNudge(S32 direction)
{
mMoveTimer.reset();
LLFirstUse::notMoving(false);
// age chat timer so it fades more quickly when you are intentionally moving
ageChat();
Loren Shih
committed
gAgentCamera.setLeftKey(LLAgentCamera::directionToKey(direction));
if (direction > 0)
{
setControlFlags(AGENT_CONTROL_NUDGE_LEFT_POS);
}
else if (direction < 0)
{
setControlFlags(AGENT_CONTROL_NUDGE_LEFT_NEG);
}
gAgentCamera.resetView();
}
//-----------------------------------------------------------------------------
// moveUp()
//-----------------------------------------------------------------------------
void LLAgent::moveUp(S32 direction)
{
mMoveTimer.reset();
LLFirstUse::notMoving(false);
// age chat timer so it fades more quickly when you are intentionally moving
ageChat();
Loren Shih
committed
gAgentCamera.setUpKey(LLAgentCamera::directionToKey(direction));
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);
}
gAgentCamera.resetView();
}
//-----------------------------------------------------------------------------
// moveYaw()
//-----------------------------------------------------------------------------
Loren Shih
committed
gAgentCamera.setYawKey(mag);
if (mag > 0)
{
setControlFlags(AGENT_CONTROL_YAW_POS);
}
else if (mag < 0)
{
setControlFlags(AGENT_CONTROL_YAW_NEG);
}
gAgentCamera.resetView();
}
//-----------------------------------------------------------------------------
// movePitch()
//-----------------------------------------------------------------------------
void LLAgent::movePitch(F32 mag)
Loren Shih
committed
gAgentCamera.setPitchKey(mag);
setControlFlags(AGENT_CONTROL_PITCH_POS);
{
setControlFlags(AGENT_CONTROL_PITCH_NEG);
}
}
// Does this parcel allow you to fly?
BOOL LLAgent::canFly()
{
if (isGodlike()) return TRUE;
LLViewerRegion* regionp = getRegion();
if (regionp && regionp->getBlockFly()) return FALSE;
LLParcel* parcel = LLViewerParcelMgr::getInstance()->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();
}
BOOL LLAgent::getFlying() const
{
return mControlFlags & AGENT_CONTROL_FLY;
}
//-----------------------------------------------------------------------------
// setFlying()
//-----------------------------------------------------------------------------
void LLAgent::setFlying(BOOL fly)
{
Sergei Litovchuk
committed
// *HACK: Don't allow to start the flying mode if we got ANIM_AGENT_STANDUP signal
// because in this case we won't get a signal to start avatar flying animation and
// it will be walking with flying mode "ON" indication. However we allow to switch
// the flying mode off if we get ANIM_AGENT_STANDUP signal. See process_avatar_animation().
if(fly && gAgentAvatarp->mSignaledAnimations.find(ANIM_AGENT_STANDUP) != gAgentAvatarp->mSignaledAnimations.end())
{
return;
}
// don't allow taking off while sitting
if (fly && gAgentAvatarp->isSitting())
{
return;
}
}
if (fly)
{
BOOL was_flying = getFlying();
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;
LLViewerStats::getInstance()->incStat(LLViewerStats::ST_FLY_COUNT);
}
setControlFlags(AGENT_CONTROL_FLY);
}
else
{
clearControlFlags(AGENT_CONTROL_FLY);
}
// Update Movement Controls according to Fly mode
LLFloaterMove::setFlyingMode(fly);
mbFlagsDirty = TRUE;
}
// UI based mechanism of setting fly state
//-----------------------------------------------------------------------------
// toggleFlying()
//-----------------------------------------------------------------------------
BOOL fly = !gAgent.getFlying();
gAgent.setFlying( fly );
gAgentCamera.resetView();
}
// static
bool LLAgent::enableFlying()
{
BOOL sitting = FALSE;
sitting = gAgentAvatarp->isSitting();
}
return !sitting;
void LLAgent::standUp()
{
setControlFlags(AGENT_CONTROL_STAND_UP);
}
//-----------------------------------------------------------------------------
// setRegion()
//-----------------------------------------------------------------------------
void LLAgent::setRegion(LLViewerRegion *regionp)
{
llassert(regionp);
if (mRegionp != regionp)
{
// std::string host_name;
// host_name = regionp->getHost().getHostName();
std::string ip = regionp->getHost().getString();
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 = LLViewerCamera::getInstance()->getOrigin();
LLViewerCamera::getInstance()->setOrigin(camera_position_agent - delta);
LLWorld::getInstance()->updateAgentOffset(agent_offset_global);
// Hack to keep sky in the agent's region, otherwise it may get deleted - DJS 08/02/02
// *TODO: possibly refactor into gSky->setAgentRegion(regionp)? -Brad
if (gSky.mVOSkyp)
{
gSky.mVOSkyp->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 = LLViewerCamera::getInstance()->getOrigin();
LLViewerCamera::getInstance()->setOrigin(camera_position_agent - delta);
LLWorld::getInstance()->updateAgentOffset(mAgentOriginGlobal);
}
}
mRegionp = regionp;
// Must shift hole-covering water object locations because local
// coordinate frame changed.
LLWorld::getInstance()->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);
Josh Bell
committed
LLSelectMgr::getInstance()->updateSelectionCenter();
}
//-----------------------------------------------------------------------------
// getRegion()
//-----------------------------------------------------------------------------
LLViewerRegion *LLAgent::getRegion() const
{
return mRegionp;
}
LLHost LLAgent::getRegionHost() const
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
{
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();
}
Josh Bell
committed
//-----------------------------------------------------------------------------
// 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)
{
Steven Bennetts
committed
lldebugs << "Trying to send message when disconnected!" << llendl;
Steven Bennetts
committed
lldebugs << "LLAgent::sendReliableMessage No region for agent yet, not sending message!" << llendl;
return;
}
gMessageSystem->sendReliable(mRegionp->getHost());
}
//-----------------------------------------------------------------------------
// getVelocity()
//-----------------------------------------------------------------------------
LLVector3 LLAgent::getVelocity() const
{
return gAgentAvatarp->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 (isAgentAvatarValid() && gAgentAvatarp->getParent())
{
LLVector3 pos_agent_sitting;
LLVector3d pos_agent_d;
LLViewerObject *parent = (LLViewerObject*)gAgentAvatarp->getParent();
pos_agent_sitting = gAgentAvatarp->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;
}
}
//-----------------------------------------------------------------------------
// getPositionGlobal()
//-----------------------------------------------------------------------------
const LLVector3d &LLAgent::getPositionGlobal() const
if (isAgentAvatarValid() && !gAgentAvatarp->mDrawable.isNull())
mPositionGlobal = getPosGlobalFromAgent(gAgentAvatarp->getRenderPosition());
}
else
{
mPositionGlobal = getPosGlobalFromAgent(mFrameAgent.getOrigin());
}
return mPositionGlobal;
}
//-----------------------------------------------------------------------------
// getPositionAgent()
//-----------------------------------------------------------------------------
const LLVector3 &LLAgent::getPositionAgent()
{
if (isAgentAvatarValid() && !gAgentAvatarp->mDrawable.isNull())
mFrameAgent.setOrigin(gAgentAvatarp->getRenderPosition());
}
return mFrameAgent.getOrigin();
}
//-----------------------------------------------------------------------------
// getRegionsVisited()
//-----------------------------------------------------------------------------
Don Kjer
committed
S32 LLAgent::getRegionsVisited() const
{
return mRegionsVisited.size();
}
//-----------------------------------------------------------------------------
// getDistanceTraveled()
//-----------------------------------------------------------------------------
Don Kjer
committed
F64 LLAgent::getDistanceTraveled() const
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
{
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;
gAgentAvatarp->getParent() &&
gAgentAvatarp->mDrawable.notNull())
U32 camera_mode = gAgentCamera.getCameraAnimating() ? gAgentCamera.getLastCameraMode() : gAgentCamera.getCameraMode();
// 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*)gAgentAvatarp->getParent())->getRenderRotation();
}
else if (camera_mode == CAMERA_MODE_MOUSELOOK)
{
// make the up vector point to the avatar's +z axis
up_vector = up_vector * gAgentAvatarp->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
mFrameAgent.pitch(clampPitchToLimits(angle));
}
// Radians, positive is forward into ground
//-----------------------------------------------------------------------------
// clampPitchToLimits()
//-----------------------------------------------------------------------------
F32 LLAgent::clampPitchToLimits(F32 angle)
{
// 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 (isAgentAvatarValid() && gAgentAvatarp->isSitting())
{
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;
}