Files
SingularityViewer/indra/newview/llagentcamera.cpp
Inusaito Sayori ab55c61dc3 Feature Request: Add option to reset angles/offsets adjustments made to current camera preset.
Adds "Reset Camera Preset Angle to Default" to view menu
Wasn't quite sure where to hook this up, menu seemed the best for the moment, but really it's more of a preferences button function.
2014-01-23 19:35:17 -05:00

2997 lines
92 KiB
C++

/**
* @file llagentcamera.cpp
* @brief LLAgent class implementation
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "llagentcamera.h"
#include "pipeline.h"
#include "llagent.h"
#include "llanimationstates.h"
#include "llfloatercamera.h"
#include "llfloatergroups.h"
#include "llhudmanager.h"
#include "lljoystickbutton.h"
#include "llselectmgr.h"
#include "llsmoothstep.h"
#include "lltoolmgr.h"
#include "llviewercamera.h"
#include "llviewercontrol.h"
#include "llviewerjoystick.h"
#include "llviewermenu.h"
#include "llviewerobjectlist.h"
#include "llviewerregion.h"
#include "llviewerwindow.h"
#include "llvoavatarself.h"
#include "llwindow.h"
#include "llworld.h"
#include "llfloatertools.h" //For gFloaterTools
#include "floaterao.h" //For LLFloaterAO
#include "llfloatercustomize.h" //For gFloaterCustomize
// [RLVa:KB] - Checked: 2010-05-10 (RLVa-1.2.0g)
#include "rlvhandler.h"
// [/RLVa:KB]
using namespace LLAvatarAppearanceDefines;
extern LLMenuBarGL* gMenuBarView;
// 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 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.f;//0.1f;
const F32 MIN_CAMERA_DISTANCE = 0.f;//0.1f;
const F32 AVATAR_ZOOM_MIN_X_FACTOR = 0.f;//0.55f;
const F32 AVATAR_ZOOM_MIN_Y_FACTOR = 0.f;//0.7f;
const F32 AVATAR_ZOOM_MIN_Z_FACTOR = 0.f;//1.15f;
const F32 MAX_CAMERA_DISTANCE_FROM_AGENT = 50.f;
const F32 MAX_CAMERA_SMOOTH_DISTANCE = 50.0f;
const F32 HEAD_BUFFER_SIZE = 0.3f;
const F32 CUSTOMIZE_AVATAR_CAMERA_ANIM_SLOP = 0.1f;
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;
const F32 CUSTOMIZE_AVATAR_CAMERA_DEFAULT_DIST = 3.5f;
const F32 GROUND_TO_AIR_CAMERA_TRANSITION_TIME = 0.5f;
const F32 GROUND_TO_AIR_CAMERA_TRANSITION_START_TIME = 0.5f;
const F32 OBJECT_EXTENTS_PADDING = 0.5f;
// The agent instance.
LLAgentCamera gAgentCamera;
//-----------------------------------------------------------------------------
// LLAgentCamera()
//-----------------------------------------------------------------------------
LLAgentCamera::LLAgentCamera() :
mInitialized(false),
mDrawDistance( DEFAULT_FAR_PLANE ),
mLookAt(NULL),
mPointAt(NULL),
mHUDTargetZoom(1.f),
mHUDCurZoom(1.f),
mForceMouselook(FALSE),
mCameraMode( CAMERA_MODE_THIRD_PERSON ),
mLastCameraMode( CAMERA_MODE_THIRD_PERSON ),
mCameraPreset(CAMERA_PRESET_REAR_VIEW),
mCameraAnimating( FALSE ),
mAnimationCameraStartGlobal(),
mAnimationFocusStartGlobal(),
mAnimationTimer(),
mAnimationDuration(0.33f),
mCameraFOVZoomFactor(0.f),
mCameraCurrentFOVZoomFactor(0.f),
mCameraFocusOffset(),
mCameraFOVDefault(DEFAULT_FIELD_OF_VIEW),
mCameraCollidePlane(),
mCurrentCameraDistance(2.f), // meters, set in init()
mTargetCameraDistance(2.f),
mCameraZoomFraction(1.f), // deprecated
mThirdPersonHeadOffset(0.f, 0.f, 1.f),
mSitCameraEnabled(FALSE),
mCameraSmoothingLastPositionGlobal(),
mCameraSmoothingLastPositionAgent(),
mCameraSmoothingStop(false),
mCameraUpVector(LLVector3::z_axis), // default is straight up
mFocusOnAvatar(TRUE),
mFocusGlobal(),
mFocusTargetGlobal(),
mFocusObject(NULL),
mFocusObjectDist(0.f),
mFocusObjectOffset(),
mFocusDotRadius( 0.1f ), // meters
mUIOffset(0.f),
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.f),
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)
{
mFollowCam.setMaxCameraDistantFromSubject( MAX_CAMERA_DISTANCE_FROM_AGENT );
clearGeneralKeys();
clearOrbitKeys();
clearPanKeys();
}
// Requires gSavedSettings to be initialized.
//-----------------------------------------------------------------------------
// init()
//-----------------------------------------------------------------------------
void LLAgentCamera::init()
{
// *Note: this is where LLViewerCamera::getInstance() used to be constructed.
mDrawDistance = gSavedSettings.getF32("RenderFarClip");
LLViewerCamera::getInstance()->setView(DEFAULT_FIELD_OF_VIEW);
// Leave at 0.1 meters until we have real near clip management
LLViewerCamera::getInstance()->setNear(0.1f);
LLViewerCamera::getInstance()->setFar(mDrawDistance); // if you want to change camera settings, do so in camera.h
LLViewerCamera::getInstance()->setAspect( gViewerWindow->getDisplayAspectRatio() ); // default, overridden in LLViewerWindow::reshape
LLViewerCamera::getInstance()->setViewHeightInPixels(768); // default, overridden in LLViewerWindow::reshape
mCameraFocusOffsetTarget = LLVector4(gSavedSettings.getVector3("CameraOffsetBuild"));
mCameraPreset = (ECameraPreset) gSavedSettings.getU32("CameraPreset");
mCameraOffsetInitial[CAMERA_PRESET_REAR_VIEW] = gSavedSettings.getControl("CameraOffsetRearView");
mCameraOffsetInitial[CAMERA_PRESET_FRONT_VIEW] = gSavedSettings.getControl("CameraOffsetFrontView");
mCameraOffsetInitial[CAMERA_PRESET_GROUP_VIEW] = gSavedSettings.getControl("CameraOffsetGroupView");
mFocusOffsetInitial[CAMERA_PRESET_REAR_VIEW] = gSavedSettings.getControl("FocusOffsetRearView");
mFocusOffsetInitial[CAMERA_PRESET_FRONT_VIEW] = gSavedSettings.getControl("FocusOffsetFrontView");
mFocusOffsetInitial[CAMERA_PRESET_GROUP_VIEW] = gSavedSettings.getControl("FocusOffsetGroupView");
mCameraCollidePlane.clearVec();
mCurrentCameraDistance = getCameraOffsetInitial().magVec() * gSavedSettings.getF32("CameraOffsetScale");
mTargetCameraDistance = mCurrentCameraDistance;
mCameraZoomFraction = 1.f;
mInitialized = true;
}
//-----------------------------------------------------------------------------
// cleanup()
//-----------------------------------------------------------------------------
void LLAgentCamera::cleanup()
{
setSitCamera(LLUUID::null);
if(mLookAt)
{
mLookAt->markDead() ;
mLookAt = NULL;
}
if(mPointAt)
{
mPointAt->markDead() ;
mPointAt = NULL;
}
setFocusObject(NULL);
}
void LLAgentCamera::setAvatarObject(LLVOAvatarSelf* avatar)
{
if (!mLookAt)
{
mLookAt = (LLHUDEffectLookAt *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_LOOKAT);
}
if (!mPointAt)
{
mPointAt = (LLHUDEffectPointAt *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINTAT);
}
if (!mLookAt.isNull())
{
mLookAt->setSourceObject(avatar);
}
if (!mPointAt.isNull())
{
mPointAt->setSourceObject(avatar);
}
}
//-----------------------------------------------------------------------------
// LLAgent()
//-----------------------------------------------------------------------------
LLAgentCamera::~LLAgentCamera()
{
cleanup();
// *Note: this is where LLViewerCamera::getInstance() used to be deleted.
}
// Change camera back to third person, stop the autopilot,
// deselect stuff, etc.
//-----------------------------------------------------------------------------
// resetView()
//-----------------------------------------------------------------------------
void LLAgentCamera::resetView(BOOL reset_camera, BOOL change_camera)
{
if (gAgent.getAutoPilot())
{
gAgent.stopAutoPilot(TRUE);
}
if (!gNoRender)
{
LLSelectMgr::getInstance()->unhighlightAll();
// By popular request, keep land selection while walking around. JC
// LLViewerParcelMgr::getInstance()->deselectLand();
// force deselect when walking and attachment is selected
// this is so people don't wig out when their avatar moves without animating
if (LLSelectMgr::getInstance()->getSelection()->isAttachment())
{
LLSelectMgr::getInstance()->deselectAll();
}
if (gMenuHolder != NULL)
{
// Hide all popup menus
gMenuHolder->hideMenus();
}
}
static const LLCachedControl<bool> freeze_time("FreezeTime",false);
if (change_camera && !freeze_time)
{
changeCameraToDefault();
if (LLViewerJoystick::getInstance()->getOverrideCamera())
{
handle_toggle_flycam();
}
// reset avatar mode from eventual residual motion
if (LLToolMgr::getInstance()->inBuildMode())
{
LLViewerJoystick::getInstance()->moveAvatar(true);
}
//Camera Tool is needed for Free Camera Control Mode
/*if (!LLFloaterCamera::inFreeCameraMode())
{
LLFloaterReg::hideInstance("build");
// Switch back to basic toolset
LLToolMgr::getInstance()->setCurrentToolset(gBasicToolset);
}*/
gFloaterTools->close();
gViewerWindow->showCursor();
// Switch back to basic toolset
LLToolMgr::getInstance()->setCurrentToolset(gBasicToolset);
}
if (reset_camera && !freeze_time)
{
if (!gViewerWindow->getLeftMouseDown() && cameraThirdPerson())
{
// leaving mouse-steer mode
LLVector3 agent_at_axis = gAgent.getAtAxis();
agent_at_axis -= projected_vec(agent_at_axis, gAgent.getReferenceUpVector());
agent_at_axis.normalize();
gAgent.resetAxes(lerp(gAgent.getAtAxis(), agent_at_axis, LLCriticalDamp::getInterpolant(0.3f)));
}
setFocusOnAvatar(TRUE, ANIMATE);
mCameraFOVZoomFactor = 0.f;
}
mHUDTargetZoom = 1.f;
}
// Allow camera to be moved somewhere other than behind avatar.
//-----------------------------------------------------------------------------
// unlockView()
//-----------------------------------------------------------------------------
void LLAgentCamera::unlockView()
{
if (getFocusOnAvatar())
{
if (isAgentAvatarValid())
{
setFocusGlobal(LLVector3d::zero, gAgentAvatarp->mID);
}
setFocusOnAvatar(FALSE, FALSE); // no animation
}
}
//-----------------------------------------------------------------------------
// slamLookAt()
//-----------------------------------------------------------------------------
void LLAgentCamera::slamLookAt(const LLVector3 &look_at)
{
LLVector3 look_at_norm = look_at;
look_at_norm.mV[VZ] = 0.f;
look_at_norm.normalize();
gAgent.resetAxes(look_at_norm);
}
//-----------------------------------------------------------------------------
// calcFocusOffset()
//-----------------------------------------------------------------------------
LLVector3 LLAgentCamera::calcFocusOffset(LLViewerObject *object, LLVector3 original_focus_point, S32 x, S32 y)
{
LLMatrix4 obj_matrix = object->getRenderMatrix();
LLQuaternion obj_rot = object->getRenderRotation();
LLVector3 obj_pos = object->getRenderPosition();
BOOL is_avatar = object->isAvatar();
// if is avatar - don't do any funk heuristics to position the focal point
// see DEV-30589
if (is_avatar)
{
return original_focus_point - obj_pos;
}
LLQuaternion inv_obj_rot = ~obj_rot; // get inverse of rotation
LLVector3 object_extents = object->getScale();
// make sure they object extents are non-zero
object_extents.clamp(0.001f, F32_MAX);
// obj_to_cam_ray is unit vector pointing from object center to camera, in the coordinate frame of the object
LLVector3 obj_to_cam_ray = obj_pos - LLViewerCamera::getInstance()->getOrigin();
obj_to_cam_ray.rotVec(inv_obj_rot);
obj_to_cam_ray.normalize();
// obj_to_cam_ray_proportions are the (positive) ratios of
// the obj_to_cam_ray x,y,z components with the x,y,z object dimensions.
LLVector3 obj_to_cam_ray_proportions;
obj_to_cam_ray_proportions.mV[VX] = llabs(obj_to_cam_ray.mV[VX] / object_extents.mV[VX]);
obj_to_cam_ray_proportions.mV[VY] = llabs(obj_to_cam_ray.mV[VY] / object_extents.mV[VY]);
obj_to_cam_ray_proportions.mV[VZ] = llabs(obj_to_cam_ray.mV[VZ] / object_extents.mV[VZ]);
// find the largest ratio stored in obj_to_cam_ray_proportions
// this corresponds to the object's local axial plane (XY, YZ, XZ) that is *most* facing the camera
LLVector3 longest_object_axis;
// is x-axis longest?
if (obj_to_cam_ray_proportions.mV[VX] > obj_to_cam_ray_proportions.mV[VY]
&& obj_to_cam_ray_proportions.mV[VX] > obj_to_cam_ray_proportions.mV[VZ])
{
// then grab it
longest_object_axis.setVec(obj_matrix.getFwdRow4());
}
// is y-axis longest?
else if (obj_to_cam_ray_proportions.mV[VY] > obj_to_cam_ray_proportions.mV[VZ])
{
// then grab it
longest_object_axis.setVec(obj_matrix.getLeftRow4());
}
// otherwise, use z axis
else
{
longest_object_axis.setVec(obj_matrix.getUpRow4());
}
// Use this axis as the normal to project mouse click on to plane with that normal, at the object center.
// This generates a point behind the mouse cursor that is approximately in the middle of the object in
// terms of depth.
// We do this to allow the camera rotation tool to "tumble" the object by rotating the camera.
// If the focus point were the object surface under the mouse, camera rotation would introduce an undesirable
// eccentricity to the object orientation
LLVector3 focus_plane_normal(longest_object_axis);
focus_plane_normal.normalize();
LLVector3d focus_pt_global;
gViewerWindow->mousePointOnPlaneGlobal(focus_pt_global, x, y, gAgent.getPosGlobalFromAgent(obj_pos), focus_plane_normal);
LLVector3 focus_pt = gAgent.getPosAgentFromGlobal(focus_pt_global);
// find vector from camera to focus point in object space
LLVector3 camera_to_focus_vec = focus_pt - LLViewerCamera::getInstance()->getOrigin();
camera_to_focus_vec.rotVec(inv_obj_rot);
// find vector from object origin to focus point in object coordinates
LLVector3 focus_offset_from_object_center = focus_pt - obj_pos;
// convert to object-local space
focus_offset_from_object_center.rotVec(inv_obj_rot);
// We need to project the focus point back into the bounding box of the focused object.
// Do this by calculating the XYZ scale factors needed to get focus offset back in bounds along the camera_focus axis
LLVector3 clip_fraction;
// for each axis...
for (U32 axis = VX; axis <= VZ; axis++)
{
//...calculate distance that focus offset sits outside of bounding box along that axis...
//NOTE: dist_out_of_bounds keeps the sign of focus_offset_from_object_center
F32 dist_out_of_bounds;
if (focus_offset_from_object_center.mV[axis] > 0.f)
{
dist_out_of_bounds = llmax(0.f, focus_offset_from_object_center.mV[axis] - (object_extents.mV[axis] * 0.5f));
}
else
{
dist_out_of_bounds = llmin(0.f, focus_offset_from_object_center.mV[axis] + (object_extents.mV[axis] * 0.5f));
}
//...then calculate the scale factor needed to push camera_to_focus_vec back in bounds along current axis
if (llabs(camera_to_focus_vec.mV[axis]) < 0.0001f)
{
// don't divide by very small number
clip_fraction.mV[axis] = 0.f;
}
else
{
clip_fraction.mV[axis] = dist_out_of_bounds / camera_to_focus_vec.mV[axis];
}
}
LLVector3 abs_clip_fraction = clip_fraction;
abs_clip_fraction.abs();
// find axis of focus offset that is *most* outside the bounding box and use that to
// 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_offset_from_object_center -= clip_fraction.mV[VX] * camera_to_focus_vec;
}
else if (abs_clip_fraction.mV[VY] > abs_clip_fraction.mV[VZ])
{
focus_offset_from_object_center -= clip_fraction.mV[VY] * camera_to_focus_vec;
}
else
{
focus_offset_from_object_center -= clip_fraction.mV[VZ] * camera_to_focus_vec;
}
// convert back to world space
focus_offset_from_object_center.rotVec(obj_rot);
// now, based on distance of camera from object relative to object size
// push the focus point towards the near surface of the object when (relatively) close to the objcet
// or keep the focus point in the object middle when (relatively) far
// NOTE: leave focus point in middle of avatars, since the behavior you want when alt-zooming on avatars
// is almost always "tumble about middle" and not "spin around surface point"
if (!is_avatar)
{
LLVector3 obj_rel = original_focus_point - object->getRenderPosition();
//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 * LLViewerCamera::getInstance()->getAtAxis());
F32 viewDist = dist_vec(obj_pos + obj_rel, LLViewerCamera::getInstance()->getOrigin());
LLBBox obj_bbox = object->getBoundingBoxAgent();
F32 bias = 0.f;
// virtual_camera_pos is the camera position we are simulating by backing the camera off
// and adjusting the FOV
LLVector3 virtual_camera_pos = gAgent.getPosAgentFromGlobal(mFocusTargetGlobal + (getCameraPositionGlobal() - mFocusTargetGlobal) / (1.f + mCameraFOVZoomFactor));
// if the camera is inside the object (large, hollow objects, for example)
// leave focus point all the way to destination depth, away from object center
if(!obj_bbox.containsPointAgent(virtual_camera_pos))
{
// 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_offset_from_object_center, obj_rel, bias);
}
focus_offset_from_object_center = obj_rel;
}
return focus_offset_from_object_center;
}
//-----------------------------------------------------------------------------
// calcCameraMinDistance()
//-----------------------------------------------------------------------------
BOOL LLAgentCamera::calcCameraMinDistance(F32 &obj_min_distance)
{
BOOL soft_limit = FALSE; // is the bounding box to be treated literally (volumes) or as an approximation (avatars)
static const LLCachedControl<bool> disable_camera_constraints("DisableCameraConstraints");
if (!mFocusObject || mFocusObject->isDead() ||
mFocusObject->isMesh() ||
disable_camera_constraints)
{
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() - gAgent.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;
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.normalize();
// 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.normalize();
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());
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 += LLViewerCamera::getInstance()->getNear() + (soft_limit ? 0.1f : 0.2f);
return TRUE;
}
F32 LLAgentCamera::getCameraZoomFraction()
{
static const LLCachedControl<bool> ascent_disable_min_zoom_dist("AscentDisableMinZoomDist");
// 0.f -> camera zoomed all the way out
// 1.f -> camera zoomed all the way in
LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection();
if (selection->getObjectCount() && selection->getSelectType() == SELECT_TYPE_HUD)
{
// already [0,1]
return mHUDTargetZoom;
}
else if (ascent_disable_min_zoom_dist)
{
return mCameraZoomFraction;
}
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 = 65535.f*4.f;//llmin(mDrawDistance - DIST_FUDGE,
// LLWorld::getInstance()->getRegionWidthInMeters() - DIST_FUDGE,
// 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 LLAgentCamera::setCameraZoomFraction(F32 fraction)
{
static const LLCachedControl<bool> ascent_disable_min_zoom_dist("AscentDisableMinZoomDist");
// 0.f -> camera zoomed all the way out
// 1.f -> camera zoomed all the way in
LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection();
if (selection->getObjectCount() && selection->getSelectType() == SELECT_TYPE_HUD)
{
mHUDTargetZoom = fraction;
}
else if (mFocusOnAvatar && cameraThirdPerson() && !ascent_disable_min_zoom_dist)
{
mCameraZoomFraction = rescale(fraction, 0.f, 1.f, MAX_ZOOM_FRACTION, MIN_ZOOM_FRACTION);
}
else if (cameraCustomizeAvatar())
{
LLVector3d camera_offset_dir = mCameraFocusOffsetTarget;
camera_offset_dir.normalize();
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,
// LLWorld::getInstance()->getRegionWidthInMeters() - DIST_FUDGE,
// MAX_CAMERA_DISTANCE_FROM_AGENT);
if (!ascent_disable_min_zoom_dist)
{
if (mFocusObject.notNull())
{
if (mFocusObject->isAvatar())
{
min_zoom = AVATAR_MIN_ZOOM;
}
else
{
min_zoom = OBJECT_MIN_ZOOM;
}
}
}
else
{
min_zoom = 0.f;
}
LLVector3d camera_offset_dir = mCameraFocusOffsetTarget;
camera_offset_dir.normalize();
//mCameraFocusOffsetTarget = camera_offset_dir * rescale(fraction, 0.f, 1.f, max_zoom, min_zoom);
mCameraFocusOffsetTarget = camera_offset_dir * rescale(fraction, 0.f, 65535.*4., 1.f, min_zoom);
}
startCameraAnimation();
}
//-----------------------------------------------------------------------------
// cameraOrbitAround()
//-----------------------------------------------------------------------------
void LLAgentCamera::cameraOrbitAround(const F32 radians)
{
LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->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))
{
gAgent.yaw(radians);
}
else
{
mCameraFocusOffsetTarget.rotVec(radians, 0.f, 0.f, 1.f);
cameraZoomIn(1.f);
}
}
//-----------------------------------------------------------------------------
// cameraOrbitOver()
//-----------------------------------------------------------------------------
void LLAgentCamera::cameraOrbitOver(const F32 angle)
{
LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection();
if (selection->getObjectCount() && selection->getSelectType() == SELECT_TYPE_HUD)
{
// do nothing for hud selection
}
else if (mFocusOnAvatar && mCameraMode == CAMERA_MODE_THIRD_PERSON)
{
gAgent.pitch(angle);
}
else
{
LLVector3 camera_offset_unit(mCameraFocusOffsetTarget);
camera_offset_unit.normalize();
F32 angle_from_up = acos( camera_offset_unit * gAgent.getReferenceUpVector() );
LLVector3d left_axis;
left_axis.setVec(LLViewerCamera::getInstance()->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 LLAgentCamera::cameraZoomIn(const F32 fraction)
{
if (gDisconnected)
{
return;
}
LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection();
if (selection->getObjectCount() && selection->getSelectType() == SELECT_TYPE_HUD)
{
// just update hud zoom level
mHUDTargetZoom /= fraction;
return;
}
LLVector3d camera_offset_unit(mCameraFocusOffsetTarget);
F32 min_zoom = 0.f;//LAND_MIN_ZOOM;
F32 current_distance = (F32)camera_offset_unit.normalize();
F32 new_distance = current_distance * fraction;
static const LLCachedControl<bool> ascent_disable_min_zoom_dist("AscentDisableMinZoomDist");
if (!ascent_disable_min_zoom_dist)
{
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*/ INT_MAX - DIST_FUDGE//,
/*LLWorld::getInstance()->getRegionWidthInMeters() - DIST_FUDGE )*/;
if (new_distance > max_distance)
{
// <edit> screw cam constraints
//new_distance = max_distance;
// </edit>
/*
// 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 LLAgentCamera::cameraOrbitIn(const F32 meters)
{
if (mFocusOnAvatar && mCameraMode == CAMERA_MODE_THIRD_PERSON)
{
static const LLCachedControl<F32> camera_offset_scale("CameraOffsetScale");
F32 camera_offset_dist = llmax(0.001f, getCameraOffsetInitial().magVec() * camera_offset_scale);
mCameraZoomFraction = (mTargetCameraDistance - meters) / camera_offset_dist;
static const LLCachedControl<bool> freeze_time("FreezeTime",false);
if (!freeze_time && 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_unit(mCameraFocusOffsetTarget);
F32 current_distance = (F32)camera_offset_unit.normalize();
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,
LLWorld::getInstance()->getRegionWidthInMeters() - DIST_FUDGE );
if (new_distance > max_distance)
{
// Unless camera is unlocked
if (!gSavedSettings.getBOOL("DisableCameraConstraints"))
{
return;
}
}
if( CAMERA_MODE_CUSTOMIZE_AVATAR == getCameraMode() )
{
new_distance = llclamp( new_distance, APPEARANCE_MIN_ZOOM, APPEARANCE_MAX_ZOOM );
}
*/
// Compute new camera offset
mCameraFocusOffsetTarget = new_distance * camera_offset_unit;
cameraZoomIn(1.f);
}
}
//-----------------------------------------------------------------------------
// cameraPanIn()
//-----------------------------------------------------------------------------
void LLAgentCamera::cameraPanIn(F32 meters)
{
LLVector3d at_axis;
at_axis.setVec(LLViewerCamera::getInstance()->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();
// NOTE: panning movements expect the camera to move exactly with the focus target, not animated behind -Nyx
mCameraSmoothingLastPositionGlobal = calcCameraPositionTargetGlobal();
}
//-----------------------------------------------------------------------------
// cameraPanLeft()
//-----------------------------------------------------------------------------
void LLAgentCamera::cameraPanLeft(F32 meters)
{
LLVector3d left_axis;
left_axis.setVec(LLViewerCamera::getInstance()->getLeftAxis());
mFocusTargetGlobal += meters * left_axis;
mFocusGlobal = mFocusTargetGlobal;
// disable smoothing for camera pan, which causes some residents unhappiness
mCameraSmoothingStop = true;
cameraZoomIn(1.f);
updateFocusOffset();
// NOTE: panning movements expect the camera to move exactly with the focus target, not animated behind - Nyx
mCameraSmoothingLastPositionGlobal = calcCameraPositionTargetGlobal();
}
//-----------------------------------------------------------------------------
// cameraPanUp()
//-----------------------------------------------------------------------------
void LLAgentCamera::cameraPanUp(F32 meters)
{
LLVector3d up_axis;
up_axis.setVec(LLViewerCamera::getInstance()->getUpAxis());
mFocusTargetGlobal += meters * up_axis;
mFocusGlobal = mFocusTargetGlobal;
// disable smoothing for camera pan, which causes some residents unhappiness
mCameraSmoothingStop = true;
cameraZoomIn(1.f);
updateFocusOffset();
// NOTE: panning movements expect the camera to move exactly with the focus target, not animated behind -Nyx
mCameraSmoothingLastPositionGlobal = calcCameraPositionTargetGlobal();
}
//-----------------------------------------------------------------------------
// updateLookAt()
//-----------------------------------------------------------------------------
void LLAgentCamera::updateLookAt(const S32 mouse_x, const S32 mouse_y)
{
static LLVector3 last_at_axis;
if (!isAgentAvatarValid()) return;
LLQuaternion av_inv_rot = ~gAgentAvatarp->mRoot->getWorldRotation();
LLVector3 root_at = LLVector3::x_axis * gAgentAvatarp->mRoot->getWorldRotation();
if ((gViewerWindow->getMouseVelocityStat()->getCurrent() < 0.01f) &&
(root_at * last_at_axis > 0.95f))
{
LLVector3 vel = gAgentAvatarp->getVelocity();
if (vel.magVecSquared() > 4.f)
{
setLookAt(LOOKAT_TARGET_IDLE, gAgentAvatarp, vel * av_inv_rot);
}
else
{
// *FIX: rotate mframeagent by sit object's rotation?
LLQuaternion look_rotation = gAgentAvatarp->isSitting() ? gAgentAvatarp->getRenderRotation() : gAgent.getFrameAgent().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, gAgentAvatarp, look_offset);
}
last_at_axis = root_at;
return;
}
last_at_axis = root_at;
if (CAMERA_MODE_CUSTOMIZE_AVATAR == getCameraMode())
{
setLookAt(LOOKAT_TARGET_NONE, gAgentAvatarp, LLVector3(-2.f, 0.f, 0.f));
}
else
{
// Move head based on cursor position
ELookAtType lookAtType = LOOKAT_TARGET_NONE;
LLVector3 headLookAxis;
LLCoordFrame frameCamera = *((LLCoordFrame*)LLViewerCamera::getInstance());
if (cameraMouselook())
{
lookAtType = LOOKAT_TARGET_MOUSELOOK;
}
else if (cameraThirdPerson())
{
// range from -.5 to .5
F32 x_from_center =
((F32) mouse_x / (F32) gViewerWindow->getWorldViewWidthScaled() ) - 0.5f;
F32 y_from_center =
((F32) mouse_y / (F32) gViewerWindow->getWorldViewHeightScaled() ) - 0.5f;
static const LLCachedControl<F32> yaw_from_mouse_position("YawFromMousePosition");
static const LLCachedControl<F32> pitch_from_mouse_position("PitchFromMousePosition");
frameCamera.yaw( - x_from_center * yaw_from_mouse_position * DEG_TO_RAD);
frameCamera.pitch( - y_from_center * pitch_from_mouse_position * 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, gAgentAvatarp, headLookAxis);
}
}
//-----------------------------------------------------------------------------
// updateCamera()
//-----------------------------------------------------------------------------
void LLAgentCamera::updateCamera()
{
//static LLFastTimer::DeclareTimer ftm("Camera");
//LLFastTimer t(ftm);
// - changed camera_skyward to the new global "mCameraUpVector"
mCameraUpVector = LLVector3::z_axis;
//LLVector3 camera_skyward(0.f, 0.f, 1.f);
U32 camera_mode = mCameraAnimating ? mLastCameraMode : mCameraMode;
validateFocusObject();
if (isAgentAvatarValid() &&
gAgentAvatarp->isSitting() &&
camera_mode == CAMERA_MODE_MOUSELOOK)
{
//changed camera_skyward to the new global "mCameraUpVector"
mCameraUpVector = mCameraUpVector * gAgentAvatarp->getRenderRotation();
}
if (cameraThirdPerson() && mFocusOnAvatar && LLFollowCamMgr::getActiveFollowCamParams())
{
changeCameraToFollow();
}
//NOTE - this needs to be integrated into a general upVector system here within llAgent.
if ( camera_mode == CAMERA_MODE_FOLLOW && mFocusOnAvatar )
{
mCameraUpVector = mFollowCam.getUpVector();
}
if (mSitCameraEnabled)
{
if (mSitCameraReferenceObject->isDead())
{
setSitCamera(LLUUID::null);
}
}
// Update UI with our camera inputs
LLFloaterCamera* camera_floater = LLFloaterCamera::getInstance();//LLFloaterReg::findTypedInstance<LLFloaterCamera>("camera");
if (camera_floater)
{
camera_floater->mRotate->setToggleState(gAgentCamera.getOrbitRightKey() > 0.f, // left
gAgentCamera.getOrbitUpKey() > 0.f, // top
gAgentCamera.getOrbitLeftKey() > 0.f, // right
gAgentCamera.getOrbitDownKey() > 0.f); // bottom
camera_floater->mTrack->setToggleState(gAgentCamera.getPanLeftKey() > 0.f, // left
gAgentCamera.getPanUpKey() > 0.f, // top
gAgentCamera.getPanRightKey() > 0.f, // right
gAgentCamera.getPanDownKey() > 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 (gAgentCamera.getOrbitUpKey() || gAgentCamera.getOrbitDownKey())
{
F32 input_rate = gAgentCamera.getOrbitUpKey() - gAgentCamera.getOrbitDownKey();
cameraOrbitOver( input_rate * ORBIT_OVER_RATE / gFPSClamped );
}
if (gAgentCamera.getOrbitLeftKey() || gAgentCamera.getOrbitRightKey())
{
F32 input_rate = gAgentCamera.getOrbitLeftKey() - gAgentCamera.getOrbitRightKey();
cameraOrbitAround(input_rate * ORBIT_AROUND_RATE / gFPSClamped);
}
if (gAgentCamera.getOrbitInKey() || gAgentCamera.getOrbitOutKey())
{
F32 input_rate = gAgentCamera.getOrbitInKey() - gAgentCamera.getOrbitOutKey();
LLVector3d to_focus = gAgent.getPosGlobalFromAgent(LLViewerCamera::getInstance()->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 (gAgentCamera.getPanInKey() || gAgentCamera.getPanOutKey())
{
F32 input_rate = gAgentCamera.getPanInKey() - gAgentCamera.getPanOutKey();
cameraPanIn(input_rate * PAN_RATE / gFPSClamped);
}
if (gAgentCamera.getPanRightKey() || gAgentCamera.getPanLeftKey())
{
F32 input_rate = gAgentCamera.getPanRightKey() - gAgentCamera.getPanLeftKey();
cameraPanLeft(input_rate * -PAN_RATE / gFPSClamped );
}
if (gAgentCamera.getPanUpKey() || gAgentCamera.getPanDownKey())
{
F32 input_rate = gAgentCamera.getPanUpKey() - gAgentCamera.getPanDownKey();
cameraPanUp(input_rate * PAN_RATE / gFPSClamped );
}
// Clear camera keyboard keys.
gAgentCamera.clearOrbitKeys();
gAgentCamera.clearPanKeys();
// lerp camera focus offset
mCameraFocusOffset = lerp(mCameraFocusOffset, mCameraFocusOffsetTarget, LLCriticalDamp::getInterpolant(CAMERA_FOCUS_HALF_LIFE));
if ( mCameraMode == CAMERA_MODE_FOLLOW )
{
if (isAgentAvatarValid())
{
//--------------------------------------------------------------------------------
// 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 = gAgentAvatarp->isSitting() ? gAgentAvatarp->getRenderRotation() : gAgent.getFrameAgent().getQuaternion();
LLFollowCamParams* current_cam = LLFollowCamMgr::getActiveFollowCamParams();
if (current_cam)
{
mFollowCam.copyParams(*current_cam);
mFollowCam.setSubjectPositionAndRotation( gAgentAvatarp->getRenderPosition(), avatarRotationForFollowCam );
mFollowCam.update();
LLViewerJoystick::getInstance()->setCameraNeedsUpdate(true);
}
else
{
changeCameraToThirdPerson(TRUE);
}
}
}
BOOL hit_limit;
LLVector3d camera_pos_global;
LLVector3d camera_target_global = calcCameraPositionTargetGlobal(&hit_limit);
mCameraVirtualPositionAgent = gAgent.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);
gAgent.setShowAvatar(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 )
{
gAgent.setShowAvatar(FALSE);
}
// ...adjust position for animation
F32 smooth_fraction_of_animation = llsmoothstep(0.0f, 1.0f, fraction_of_animation);
camera_pos_global = lerp(mAnimationCameraStartGlobal, camera_target_global, smooth_fraction_of_animation);
mFocusGlobal = lerp(mAnimationFocusStartGlobal, focus_target_global, smooth_fraction_of_animation);
}
else
{
// ...animation complete
mCameraAnimating = FALSE;
camera_pos_global = camera_target_global;
mFocusGlobal = focus_target_global;
gAgent.endAnimationUpdateUI();
gAgent.setShowAvatar(TRUE);
}
if (isAgentAvatarValid() && (mCameraMode != CAMERA_MODE_MOUSELOOK))
{
gAgentAvatarp->updateAttachmentVisibility(mCameraMode);
}
}
else
{
camera_pos_global = camera_target_global;
mFocusGlobal = focus_target_global;
gAgent.setShowAvatar(TRUE);
}
// smoothing
if (TRUE)
{
LLVector3d agent_pos = gAgent.getPositionGlobal();
LLVector3d camera_pos_agent = camera_pos_global - agent_pos;
// Sitting on what you're manipulating can cause camera jitter with smoothing.
// This turns off smoothing while editing. -MG
bool in_build_mode = LLToolMgr::getInstance()->inBuildMode();
mCameraSmoothingStop = mCameraSmoothingStop || in_build_mode;
if (cameraThirdPerson() && !mCameraSmoothingStop)
{
const F32 SMOOTHING_HALF_LIFE = 0.02f;
static const LLCachedControl<F32> camera_position_smoothing("CameraPositionSmoothing");
F32 smoothing = LLCriticalDamp::getInterpolant(camera_position_smoothing * SMOOTHING_HALF_LIFE, FALSE);
if (!mFocusObject) // we differentiate on avatar mode
{
// for avatar-relative focus, we smooth in avatar space -
// the avatar moves too jerkily w/r/t global space to smooth there.
LLVector3d delta = camera_pos_agent - mCameraSmoothingLastPositionAgent;
if (delta.magVec() < MAX_CAMERA_SMOOTH_DISTANCE) // only smooth over short distances please
{
camera_pos_agent = lerp(mCameraSmoothingLastPositionAgent, camera_pos_agent, smoothing);
camera_pos_global = camera_pos_agent + agent_pos;
}
}
else
{
LLVector3d delta = camera_pos_global - mCameraSmoothingLastPositionGlobal;
if (delta.magVec() < MAX_CAMERA_SMOOTH_DISTANCE) // only smooth over short distances please
{
camera_pos_global = lerp(mCameraSmoothingLastPositionGlobal, camera_pos_global, smoothing);
}
}
}
mCameraSmoothingLastPositionGlobal = camera_pos_global;
mCameraSmoothingLastPositionAgent = camera_pos_agent;
mCameraSmoothingStop = false;
}
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 = gAgent.getPosAgentFromGlobal(mFocusGlobal);
mCameraPositionAgent = gAgent.getPosAgentFromGlobal(camera_pos_global);
// Move the camera
LLViewerCamera::getInstance()->updateCameraLocation(mCameraPositionAgent, mCameraUpVector, focus_agent);
//LLViewerCamera::getInstance()->updateCameraLocation(mCameraPositionAgent, camera_skyward, focus_agent);
//RN: translate UI offset after camera is oriented properly
LLViewerCamera::getInstance()->translate(LLViewerCamera::getInstance()->getLeftAxis() * ui_offset);
// Change FOV
LLViewerCamera::getInstance()->setView(LLViewerCamera::getInstance()->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 = gAgent.getPositionGlobal();
if (!gAgent.getLastPositionGlobal().isExactlyZero())
{
LLVector3d delta = global_pos - gAgent.getLastPositionGlobal();
gAgent.setDistanceTraveled(gAgent.getDistanceTraveled() + delta.magVec());
}
gAgent.setLastPositionGlobal(global_pos);
if (LLVOAvatar::sVisibleInFirstPerson && isAgentAvatarValid() && !gAgentAvatarp->isSitting() && cameraMouselook())
{
LLVector3 head_pos = gAgentAvatarp->mHeadp->getWorldPosition() +
LLVector3(0.08f, 0.f, 0.05f) * gAgentAvatarp->mHeadp->getWorldRotation() +
LLVector3(0.1f, 0.f, 0.f) * gAgentAvatarp->mPelvisp->getWorldRotation();
LLVector3 diff = mCameraPositionAgent - head_pos;
diff = diff * ~gAgentAvatarp->mRoot->getWorldRotation();
LLJoint* torso_joint = gAgentAvatarp->mTorsop;
LLJoint* chest_joint = gAgentAvatarp->mChestp;
LLVector3 torso_scale = torso_joint->getScale();
LLVector3 chest_scale = chest_joint->getScale();
/*// shorten avatar skeleton to avoid foot interpenetration
if (!gAgentAvatarp->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 = gAgentAvatarp->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;
}*/
gAgentAvatarp->mPelvisp->setPosition(gAgentAvatarp->mPelvisp->getPosition() + diff);
gAgentAvatarp->mRoot->updateWorldMatrixChildren();
/*for (LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin(); //Can be an array.
iter != gAgentAvatarp->mAttachmentPoints.end(); )
{
LLVOAvatar::attachment_map_t::iterator curiter = iter++;
LLViewerJointAttachment* attachment = curiter->second;
for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin();
attachment_iter != attachment->mAttachedObjects.end();
++attachment_iter)
{
LLViewerObject *attached_object = (*attachment_iter);*/
std::vector<std::pair<LLViewerObject*,LLViewerJointAttachment*> >::iterator attachment_iter = gAgentAvatarp->mAttachedObjectsVector.begin();
for(;attachment_iter!=gAgentAvatarp->mAttachedObjectsVector.end();++attachment_iter)
{{
LLViewerObject* attached_object = attachment_iter->first;
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 LLAgentCamera::updateLastCamera()
{
mLastCameraMode = mCameraMode;
}
void LLAgentCamera::updateFocusOffset()
{
validateFocusObject();
if (mFocusObject.notNull())
{
LLVector3d obj_pos = gAgent.getPosGlobalFromAgent(mFocusObject->getRenderPosition());
mFocusObjectOffset.setVec(mFocusTargetGlobal - obj_pos);
}
}
void LLAgentCamera::validateFocusObject()
{
if (mFocusObject.notNull() &&
mFocusObject->isDead())
{
mFocusObjectOffset.clearVec();
clearFocusObject();
mCameraFOVZoomFactor = 0.f;
}
}
//-----------------------------------------------------------------------------
// calcCustomizeAvatarUIOffset()
//-----------------------------------------------------------------------------
F32 LLAgentCamera::calcCustomizeAvatarUIOffset( const LLVector3d& camera_pos_global )
{
F32 ui_offset = 0.f;
if( LLFloaterCustomize::instanceExists() && CAMERA_MODE_CUSTOMIZE_AVATAR == mCameraMode )
{
const LLRect& rect = LLFloaterCustomize::getInstance()->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 * LLViewerCamera::getInstance()->getView() * LLViewerCamera::getInstance()->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, gAgentCamera.getFocusGlobal());
mUIOffset = lerp(mUIOffset, ui_offset, LLCriticalDamp::getInterpolant(0.05f));
return mUIOffset * range;
}
//-----------------------------------------------------------------------------
// calcFocusPositionTargetGlobal()
//-----------------------------------------------------------------------------
LLVector3d LLAgentCamera::calcFocusPositionTargetGlobal()
{
if (mFocusObject.notNull() && mFocusObject->isDead())
{
clearFocusObject();
}
if (mCameraMode == CAMERA_MODE_FOLLOW && mFocusOnAvatar)
{
mFocusTargetGlobal = gAgent.getPosGlobalFromAgent(mFollowCam.getSimulatedFocus());
return mFocusTargetGlobal;
}
else if (mCameraMode == CAMERA_MODE_MOUSELOOK)
{
LLVector3d at_axis(1.0, 0.0, 0.0);
LLQuaternion agent_rot = gAgent.getFrameAgent().getQuaternion();
if (isAgentAvatarValid() && gAgentAvatarp->getParent())
{
LLViewerObject* root_object = (LLViewerObject*)gAgentAvatarp->getRoot();
if (!root_object->flagCameraDecoupled())
{
agent_rot *= ((LLViewerObject*)(gAgentAvatarp->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;
static const LLCachedControl<bool> track_focus_object("TrackFocusObject");
if (track_focus_object && drawablep)
{
if (drawablep->isActive() && !mFocusObject->isAvatar())
{
if (mFocusObject->isSelected())
{
gPipeline.updateMoveNormalAsync(drawablep);
}
else if(!drawablep->isState(LLDrawable::ON_MOVE_LIST))
{
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(gAgent.getPosGlobalFromAgent(focus_agent));
}
return mFocusTargetGlobal;
}
else if (mSitCameraEnabled && isAgentAvatarValid() && gAgentAvatarp->isSitting() && mSitCameraReferenceObject.notNull())
{
// sit camera
LLVector3 object_pos = mSitCameraReferenceObject->getRenderPosition();
LLQuaternion object_rot = mSitCameraReferenceObject->getRenderRotation();
LLVector3 target_pos = object_pos + (mSitCameraFocus * object_rot);
return gAgent.getPosGlobalFromAgent(target_pos);
}
else
{
return gAgent.getPositionGlobal() + calcThirdPersonFocusOffset();
}
}
LLVector3d LLAgentCamera::calcThirdPersonFocusOffset()
{
// ...offset from avatar
LLVector3d focus_offset;
LLQuaternion agent_rot = gAgent.getFrameAgent().getQuaternion();
if (isAgentAvatarValid() && gAgentAvatarp->getParent())
{
agent_rot *= ((LLViewerObject*)(gAgentAvatarp->getParent()))->getRenderRotation();
}
focus_offset = convert_from_llsd<LLVector3d>(mFocusOffsetInitial[mCameraPreset]->get(), TYPE_VEC3D, "");
return focus_offset * agent_rot;
}
void LLAgentCamera::setupSitCamera()
{
// agent frame entering this function is in world coordinates
if (isAgentAvatarValid() && gAgentAvatarp->getParent())
{
LLQuaternion parent_rot = ((LLViewerObject*)gAgentAvatarp->getParent())->getRenderRotation();
// slam agent coordinate frame to proper parent local version
LLVector3 at_axis = gAgent.getFrameAgent().getAtAxis();
at_axis.mV[VZ] = 0.f;
at_axis.normalize();
gAgent.resetAxes(at_axis * ~parent_rot);
}
}
//-----------------------------------------------------------------------------
// getCameraPositionAgent()
//-----------------------------------------------------------------------------
const LLVector3 &LLAgentCamera::getCameraPositionAgent() const
{
return LLViewerCamera::getInstance()->getOrigin();
}
//-----------------------------------------------------------------------------
// getCameraPositionGlobal()
//-----------------------------------------------------------------------------
LLVector3d LLAgentCamera::getCameraPositionGlobal() const
{
return gAgent.getPosGlobalFromAgent(LLViewerCamera::getInstance()->getOrigin());
}
//-----------------------------------------------------------------------------
// calcCameraFOVZoomFactor()
//-----------------------------------------------------------------------------
F32 LLAgentCamera::calcCameraFOVZoomFactor()
{
LLVector3 camera_offset_dir;
camera_offset_dir.setVec(mCameraFocusOffset);
if (mCameraMode == CAMERA_MODE_MOUSELOOK)
{
return 0.f;
}
else if (mFocusObject.notNull() && !mFocusObject->isAvatar() && !mFocusOnAvatar)
{
// don't FOV zoom on mostly transparent objects
F32 obj_min_dist = 0.f;
static const LLCachedControl<bool> ascent_disable_min_zoom_dist("AscentDisableMinZoomDist");
if (!ascent_disable_min_zoom_dist)
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 LLAgentCamera::calcCameraPositionTargetGlobal(BOOL *hit_limit)
{
// Compute base camera position and look-at points.
F32 camera_land_height;
LLVector3d frame_center_global = !isAgentAvatarValid() ?
gAgent.getPositionGlobal() :
gAgent.getPosGlobalFromAgent(gAgentAvatarp->mRoot->getWorldPosition());
BOOL isConstrained = FALSE;
LLVector3d head_offset;
head_offset.setVec(mThirdPersonHeadOffset);
LLVector3d camera_position_global;
if (mCameraMode == CAMERA_MODE_FOLLOW && mFocusOnAvatar)
{
camera_position_global = gAgent.getPosGlobalFromAgent(mFollowCam.getSimulatedPosition());
}
else if (mCameraMode == CAMERA_MODE_MOUSELOOK)
{
if (!isAgentAvatarValid() || gAgentAvatarp->mDrawable.isNull())
{
llwarns << "Null avatar drawable!" << llendl;
return LLVector3d::zero;
}
head_offset.clearVec();
if (gAgentAvatarp->isSitting() && gAgentAvatarp->getParent())
{
gAgentAvatarp->updateHeadOffset();
head_offset.mdV[VX] = gAgentAvatarp->mHeadOffset.mV[VX];
head_offset.mdV[VY] = gAgentAvatarp->mHeadOffset.mV[VY];
head_offset.mdV[VZ] = gAgentAvatarp->mHeadOffset.mV[VZ] + 0.1f;
const LLMatrix4& mat = ((LLViewerObject*) gAgentAvatarp->getParent())->getRenderMatrix();
camera_position_global = gAgent.getPosGlobalFromAgent
((gAgentAvatarp->getPosition()+
LLVector3(head_offset)*gAgentAvatarp->getRotation()) * mat);
}
else
{
head_offset.mdV[VZ] = gAgentAvatarp->mHeadOffset.mV[VZ];
if (gAgentAvatarp->isSitting())
{
head_offset.mdV[VZ] += 0.1;
}
camera_position_global = gAgent.getPosGlobalFromAgent(gAgentAvatarp->getRenderPosition());//frame_center_global;
head_offset = head_offset * gAgentAvatarp->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
&& isAgentAvatarValid()
&& gAgentAvatarp->isSitting()
&& 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 = gAgent.getPosGlobalFromAgent(target_pos);
}
else
{
static const LLCachedControl<F32> camera_offset_scale("CameraOffsetScale");
local_camera_offset = mCameraZoomFraction * getCameraOffsetInitial() * camera_offset_scale;
// are we sitting down?
if (isAgentAvatarValid() && gAgentAvatarp->getParent())
{
LLQuaternion parent_rot = ((LLViewerObject*)gAgentAvatarp->getParent())->getRenderRotation();
// slam agent coordinate frame to proper parent local version
LLVector3 at_axis = gAgent.getFrameAgent().getAtAxis() * parent_rot;
at_axis.mV[VZ] = 0.f;
at_axis.normalize();
gAgent.resetAxes(at_axis * ~parent_rot);
local_camera_offset = local_camera_offset * gAgent.getFrameAgent().getQuaternion() * parent_rot;
}
else
{
local_camera_offset = gAgent.getFrameAgent().rotateToAbsolute( local_camera_offset );
}
static const LLCachedControl<bool> sg_ignore_sim_cam_consts("SGIgnoreSimulatorCameraConstraints",false);
if ( !mCameraCollidePlane.isExactlyZero()
&& !sg_ignore_sim_cam_consts
&& isAgentAvatarValid()
&& !gAgentAvatarp->isSitting())
{
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.normalize();
F32 pos_dot_norm = gAgent.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.normalize();
}
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;
camera_offset.setVec( local_camera_offset );
camera_position_global = frame_center_global + head_offset + camera_offset;
if (isAgentAvatarValid())
{
LLVector3d camera_lag_d;
F32 lag_interp = LLCriticalDamp::getInterpolant(CAMERA_LAG_HALF_LIFE);
LLVector3 target_lag;
LLVector3 vel = gAgent.getVelocity();
// lag by appropriate amount for flying
F32 time_in_air = gAgentAvatarp->mTimeInAir.getElapsedTimeF32();
if(!mCameraAnimating && gAgentAvatarp->mInAir && time_in_air > GROUND_TO_AIR_CAMERA_TRANSITION_START_TIME)
{
LLVector3 frame_at_axis = gAgent.getFrameAgent().getAtAxis();
frame_at_axis -= projected_vec(frame_at_axis, gAgent.getReferenceUpVector());
frame_at_axis.normalize();
//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() && gViewerWindow->getLastPick().mObjectID == gAgentAvatarp->getID())
{
// disable camera lag when using mouse-directed steering
target_lag.clearVec();
}
else
{
static const LLCachedControl<F32> dynamic_camera_strength("DynamicCameraStrength");
target_lag = vel * dynamic_camera_strength / 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;
}
static const LLCachedControl<bool> disable_camera_constraints("DisableCameraConstraints");
if (!disable_camera_constraints && !gAgent.isGodlike())
{
LLViewerRegion* regionp = LLWorld::getInstance()->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 : mDrawDistance;
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 = LLWorld::getInstance()->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();
camera_land_height = LLWorld::getInstance()->resolveLandHeightGlobal(camera_position_global);
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;
}
LLVector3 LLAgentCamera::getCameraOffsetInitial()
{
return convert_from_llsd<LLVector3>(mCameraOffsetInitial[mCameraPreset]->get(), TYPE_VEC3, "");
}
// Adds change to vector CachedControl, vec, at idx
template <typename Vec, typename T>
void change_vec(const T& change, LLCachedControl<Vec>& vec, const U32& idx = VZ)
{
Vec changed(vec);
changed[idx] += change;
vec = changed;
}
// Same as above, but for ControlVariables
template <typename Vec, typename T>
void change_vec(const T& change, LLPointer<LLControlVariable>& vec, const U32& idx = VZ)
{
Vec changed(vec->get());
changed[idx] += change;
vec->set(changed.getValue());
}
//-----------------------------------------------------------------------------
// handleScrollWheel()
//-----------------------------------------------------------------------------
void LLAgentCamera::handleScrollWheel(S32 clicks)
{
if (mCameraMode == CAMERA_MODE_FOLLOW && getFocusOnAvatar())
{
if (!mFollowCam.getPositionLocked()) // not if the followCam position is locked in place
{
mFollowCam.zoom(clicks);
if (mFollowCam.isZoomedToMinimumDistance())
{
changeCameraToMouselook(FALSE);
}
}
}
else
{
LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->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))
{
if (MASK mask = gKeyboard->currentMask(true)) // Singu Note: Conveniently set view offsets while modifier keys are held during scroll
{
static const LLCachedControl<bool> enableCameraOffsetScroll("SinguOffsetScrollKeys");
if (mask & MASK_CONTROL|MASK_SHIFT && enableCameraOffsetScroll)
{
const F32 change(static_cast<F32>(clicks) * 0.1f);
if (mask & MASK_SHIFT)
{
change_vec<LLVector3d>(change, mFocusOffsetInitial[mCameraPreset]);
}
if (mask & MASK_CONTROL)
{
change_vec<LLVector3>(change, mCameraOffsetInitial[mCameraPreset]);
}
return;
}
}
F32 camera_offset_initial_mag = getCameraOffsetInitial().magVec();
static const LLCachedControl<F32> camera_offset_scale("CameraOffsetScale");
F32 current_zoom_fraction = mTargetCameraDistance / (camera_offset_initial_mag * camera_offset_scale);
current_zoom_fraction *= 1.f - pow(ROOT_ROOT_TWO, clicks);
cameraOrbitIn(current_zoom_fraction * camera_offset_initial_mag * camera_offset_scale);
}
else
{
F32 current_zoom_fraction = (F32)mCameraFocusOffsetTarget.magVec();
cameraOrbitIn(current_zoom_fraction * (1.f - pow(ROOT_ROOT_TWO, clicks)));
}
}
}
//-----------------------------------------------------------------------------
// resetPresetOffsets() - Sets the current preset back to its default state
//-----------------------------------------------------------------------------
void LLAgentCamera::resetPresetOffsets()
{
mFocusOffsetInitial[mCameraPreset]->resetToDefault();
mCameraOffsetInitial[mCameraPreset]->resetToDefault();
}
//-----------------------------------------------------------------------------
// getCameraMinOffGround()
//-----------------------------------------------------------------------------
F32 LLAgentCamera::getCameraMinOffGround()
{
if (mCameraMode == CAMERA_MODE_MOUSELOOK)
{
return 0.f;
}
else
{
static const LLCachedControl<bool> disable_camera_constraints("DisableCameraConstraints");
if (disable_camera_constraints)
{
return -1000.f;
}
else
{
return 0.5f;
}
}
}
//-----------------------------------------------------------------------------
// resetCamera()
//-----------------------------------------------------------------------------
void LLAgentCamera::resetCamera()
{
// Remove any pitch from the avatar
LLVector3 at = gAgent.getFrameAgent().getAtAxis();
at.mV[VZ] = 0.f;
at.normalize();
gAgent.resetAxes(at);
// have to explicitly clear field of view zoom now
mCameraFOVZoomFactor = 0.f;
updateCamera();
}
//-----------------------------------------------------------------------------
// changeCameraToMouselook()
//-----------------------------------------------------------------------------
void LLAgentCamera::changeCameraToMouselook(BOOL animate)
{
if (!gSavedSettings.getBOOL("EnableMouselook")
|| LLViewerJoystick::getInstance()->getOverrideCamera())
{
return;
}
// visibility changes at end of animation
gViewerWindow->getWindow()->resetBusyCount();
// Menus should not remain open on switching to mouselook...
LLMenuGL::sMenuContainer->hideMenus();
//LLUI::clearPopups();
// unpause avatar animation
gAgent.unpauseAnimation();
LLToolMgr::getInstance()->setCurrentToolset(gMouselookToolset);
gSavedSettings.setBOOL("FirstPersonBtnState", FALSE);
gSavedSettings.setBOOL("MouselookBtnState", TRUE);
gSavedSettings.setBOOL("ThirdPersonBtnState", FALSE);
gSavedSettings.setBOOL("BuildBtnState", FALSE);
if (isAgentAvatarValid())
{
gAgentAvatarp->stopMotion(ANIM_AGENT_BODY_NOISE);
gAgentAvatarp->stopMotion(ANIM_AGENT_BREATHE_ROT);
}
//gViewerWindow->stopGrab();
LLSelectMgr::getInstance()->deselectAll();
gViewerWindow->hideCursor();
gViewerWindow->moveCursorToCenter();
if (mCameraMode != CAMERA_MODE_MOUSELOOK)
{
mMouselookTimer.reset();
gFocusMgr.setKeyboardFocus( NULL );
if (gSavedSettings.getBOOL("AONoStandsInMouselook")) LLFloaterAO::stopMotion(LLFloaterAO::getCurrentStandId(), FALSE,TRUE);
updateLastCamera();
mCameraMode = CAMERA_MODE_MOUSELOOK;
const U32 old_flags = gAgent.getControlFlags();
gAgent.setControlFlags(AGENT_CONTROL_MOUSELOOK);
if (old_flags != gAgent.getControlFlags())
{
gAgent.setFlagsDirty();
}
if (animate)
{
startCameraAnimation();
}
else
{
mCameraAnimating = FALSE;
gAgent.endAnimationUpdateUI();
}
}
}
//-----------------------------------------------------------------------------
// changeCameraToDefault()
//-----------------------------------------------------------------------------
void LLAgentCamera::changeCameraToDefault()
{
if (LLViewerJoystick::getInstance()->getOverrideCamera())
{
return;
}
if (LLFollowCamMgr::getActiveFollowCamParams())
{
changeCameraToFollow();
}
else
{
changeCameraToThirdPerson();
}
}
//-----------------------------------------------------------------------------
// changeCameraToFollow()
//-----------------------------------------------------------------------------
void LLAgentCamera::changeCameraToFollow(BOOL animate)
{
if (LLViewerJoystick::getInstance()->getOverrideCamera())
{
return;
}
if(mCameraMode != CAMERA_MODE_FOLLOW)
{
if (mCameraMode == CAMERA_MODE_MOUSELOOK)
{
animate = FALSE;
}
startCameraAnimation();
updateLastCamera();
mCameraMode = CAMERA_MODE_FOLLOW;
// bang-in the current focus, position, and up vector of the follow cam
mFollowCam.reset(mCameraPositionAgent, LLViewerCamera::getInstance()->getPointOfInterest(), LLVector3::z_axis);
if (gBasicToolset)
{
LLToolMgr::getInstance()->setCurrentToolset(gBasicToolset);
}
if (isAgentAvatarValid())
{
gAgentAvatarp->mPelvisp->setPosition(LLVector3::zero);
gAgentAvatarp->startMotion( ANIM_AGENT_BODY_NOISE );
gAgentAvatarp->startMotion( ANIM_AGENT_BREATHE_ROT );
}
gSavedSettings.setBOOL("FirstPersonBtnState", FALSE);
gSavedSettings.setBOOL("MouselookBtnState", FALSE);
gSavedSettings.setBOOL("ThirdPersonBtnState", TRUE);
gSavedSettings.setBOOL("BuildBtnState", FALSE);
// unpause avatar animation
gAgent.unpauseAnimation();
gAgent.clearControlFlags(AGENT_CONTROL_MOUSELOOK);
if (animate)
{
startCameraAnimation();
}
else
{
mCameraAnimating = FALSE;
gAgent.endAnimationUpdateUI();
}
}
}
//-----------------------------------------------------------------------------
// changeCameraToThirdPerson()
//-----------------------------------------------------------------------------
void LLAgentCamera::changeCameraToThirdPerson(BOOL animate)
{
if (LLViewerJoystick::getInstance()->getOverrideCamera())
{
return;
}
gViewerWindow->getWindow()->resetBusyCount();
mCameraZoomFraction = INITIAL_ZOOM_FRACTION;
if (isAgentAvatarValid())
{
if (!gAgentAvatarp->isSitting())
{
gAgentAvatarp->mPelvisp->setPosition(LLVector3::zero);
}
gAgentAvatarp->startMotion(ANIM_AGENT_BODY_NOISE);
gAgentAvatarp->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
gAgent.unpauseAnimation();
if (mCameraMode != CAMERA_MODE_THIRD_PERSON)
{
if (gBasicToolset)
{
LLToolMgr::getInstance()->setCurrentToolset(gBasicToolset);
}
mCameraLag.clearVec();
if (mCameraMode == CAMERA_MODE_MOUSELOOK)
{
mCurrentCameraDistance = MIN_CAMERA_DISTANCE;
mTargetCameraDistance = MIN_CAMERA_DISTANCE;
animate = FALSE;
}
updateLastCamera();
mCameraMode = CAMERA_MODE_THIRD_PERSON;
gAgent.clearControlFlags(AGENT_CONTROL_MOUSELOOK);
}
// Remove any pitch from the avatar
if (isAgentAvatarValid() && gAgentAvatarp->getParent())
{
LLQuaternion obj_rot = ((LLViewerObject*)gAgentAvatarp->getParent())->getRenderRotation();
at_axis = LLViewerCamera::getInstance()->getAtAxis();
at_axis.mV[VZ] = 0.f;
at_axis.normalize();
gAgent.resetAxes(at_axis * ~obj_rot);
}
else
{
at_axis = gAgent.getFrameAgent().getAtAxis();
at_axis.mV[VZ] = 0.f;
at_axis.normalize();
gAgent.resetAxes(at_axis);
}
if (animate)
{
startCameraAnimation();
}
else
{
mCameraAnimating = FALSE;
gAgent.endAnimationUpdateUI();
}
}
//-----------------------------------------------------------------------------
// changeCameraToCustomizeAvatar()
//-----------------------------------------------------------------------------
void LLAgentCamera::changeCameraToCustomizeAvatar()
{
if (LLViewerJoystick::getInstance()->getOverrideCamera() || !isAgentAvatarValid())
{
return;
}
// <edit>
//gAgent.standUp(); // force stand up
// </edit>
gViewerWindow->getWindow()->resetBusyCount();
if (gFaceEditToolset)
{
LLToolMgr::getInstance()->setCurrentToolset(gFaceEditToolset);
}
gSavedSettings.setBOOL("FirstPersonBtnState", FALSE);
gSavedSettings.setBOOL("MouselookBtnState", FALSE);
gSavedSettings.setBOOL("ThirdPersonBtnState", FALSE);
gSavedSettings.setBOOL("BuildBtnState", FALSE);
// <edit>
if(gSavedSettings.getBOOL("AppearanceCameraMovement"))
// </edit>
startCameraAnimation();
if (mCameraMode != CAMERA_MODE_CUSTOMIZE_AVATAR)
{
updateLastCamera();
mCameraMode = CAMERA_MODE_CUSTOMIZE_AVATAR;
U32 old_flags = gAgent.getControlFlags();
gAgent.clearControlFlags(AGENT_CONTROL_MOUSELOOK);
if (old_flags != gAgent.getControlFlags())
{
gAgent.setFlagsDirty();
}
gFocusMgr.setKeyboardFocus( NULL );
gFocusMgr.setMouseCapture( NULL );
// Remove any pitch or rotation from the avatar
LLVector3 at = gAgent.getAtAxis();
at.mV[VZ] = 0.f;
at.normalize();
gAgent.resetAxes(at);
gAgent.sendAnimationRequest(ANIM_AGENT_CUSTOMIZE, ANIM_REQUEST_START);
gAgent.setCustomAnim(TRUE);
gAgentAvatarp->startMotion(ANIM_AGENT_CUSTOMIZE);
LLMotion* turn_motion = gAgentAvatarp->findMotion(ANIM_AGENT_CUSTOMIZE);
if (turn_motion)
{
// delay camera animation long enough to play through turn animation
setAnimationDuration(turn_motion->getDuration() + CUSTOMIZE_AVATAR_CAMERA_ANIM_SLOP);
}
}
// <edit>
if(!gSavedSettings.getBOOL("AppearanceCameraMovement"))
{
//hmm
mCameraAnimating = FALSE;
gAgent.endAnimationUpdateUI();
return;
}
// </edit>
LLVector3 agent_at = gAgent.getAtAxis();
agent_at.mV[VZ] = 0.f;
agent_at.normalize();
// default focus point for customize avatar
LLVector3 focus_target = isAgentAvatarValid()
? gAgentAvatarp->mHeadp->getWorldPosition()
: gAgent.getPositionAgent();
LLVector3d camera_offset(agent_at * -1.0);
// push camera up and out from avatar
camera_offset.mdV[VZ] = 0.1f;
camera_offset *= CUSTOMIZE_AVATAR_CAMERA_DEFAULT_DIST;
LLVector3d focus_target_global = gAgent.getPosGlobalFromAgent(focus_target);
setAnimationDuration(gSavedSettings.getF32("ZoomTime"));
setCameraPosAndFocusGlobal(focus_target_global + camera_offset, focus_target_global, gAgent.getID());
}
void LLAgentCamera::switchCameraPreset(ECameraPreset preset)
{
//zoom is supposed to be reset for the front and group views
mCameraZoomFraction = 1.f;
//focusing on avatar in that case means following him on movements
mFocusOnAvatar = TRUE;
mCameraPreset = preset;
gSavedSettings.setU32("CameraPreset", mCameraPreset);
}
//
// Focus point management
//
void LLAgentCamera::setAnimationDuration(F32 duration)
{
if (mCameraAnimating)
{
// do not cut any existing camera animation short
F32 animation_left = llmax(0.f, mAnimationDuration - mAnimationTimer.getElapsedTimeF32());
mAnimationDuration = llmax(duration, animation_left);
}
else
{
mAnimationDuration = duration;
}
}
//-----------------------------------------------------------------------------
// startCameraAnimation()
//-----------------------------------------------------------------------------
void LLAgentCamera::startCameraAnimation()
{
mAnimationCameraStartGlobal = getCameraPositionGlobal();
mAnimationFocusStartGlobal = mFocusGlobal;
setAnimationDuration(gSavedSettings.getF32("ZoomTime"));
mAnimationTimer.reset();
mCameraAnimating = TRUE;
}
//-----------------------------------------------------------------------------
// stopCameraAnimation()
//-----------------------------------------------------------------------------
void LLAgentCamera::stopCameraAnimation()
{
mCameraAnimating = FALSE;
}
void LLAgentCamera::clearFocusObject()
{
if (mFocusObject.notNull())
{
startCameraAnimation();
setFocusObject(NULL);
mFocusObjectOffset.clearVec();
}
}
void LLAgentCamera::setFocusObject(LLViewerObject* object)
{
mFocusObject = object;
}
// Focus on a point, but try to keep camera position stable.
//-----------------------------------------------------------------------------
// setFocusGlobal()
//-----------------------------------------------------------------------------
void LLAgentCamera::setFocusGlobal(const LLPickInfo& pick)
{
static const LLCachedControl<bool> freeze_time("FreezeTime",false);
LLViewerObject* objectp = gObjectList.findObject(pick.mObjectID);
if (objectp && !freeze_time)
{
// focus on object plus designated offset
// which may or may not be same as pick.mPosGlobal
setFocusGlobal(objectp->getPositionGlobal() + LLVector3d(pick.mObjectOffset), pick.mObjectID);
}
else
{
// focus directly on point where user clicked
setFocusGlobal(pick.mPosGlobal, pick.mObjectID);
}
}
void LLAgentCamera::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 (isAgentAvatarValid())
{
mFocusTargetGlobal = gAgent.getPosGlobalFromAgent(gAgentAvatarp->mHeadp->getWorldPosition());
}
else
{
mFocusTargetGlobal = gAgent.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, (gAgent.getPosAgentFromGlobal(focus) - focus_obj->getRenderPosition()) * ~focus_obj->getRenderRotation());
}
}
else
{
setLookAt(LOOKAT_TARGET_FOCUS, NULL, gAgent.getPosAgentFromGlobal(mFocusTargetGlobal));
}
}
}
else // focus == mFocusTargetGlobal
{
if (focus.isExactlyZero())
{
if (isAgentAvatarValid())
{
mFocusTargetGlobal = gAgent.getPosGlobalFromAgent(gAgentAvatarp->mHeadp->getWorldPosition());
}
else
{
mFocusTargetGlobal = gAgent.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.notNull() && !mFocusObject->isAvatar())
{
mFocusObject = (LLViewerObject*) mFocusObject->getParent();
}
setFocusObject((LLViewerObject*)mFocusObject);
}
updateFocusOffset();
}
}
// Used for avatar customization
//-----------------------------------------------------------------------------
// setCameraPosAndFocusGlobal()
//-----------------------------------------------------------------------------
void LLAgentCamera::setCameraPosAndFocusGlobal(const LLVector3d& camera_pos, const LLVector3d& focus, const LLUUID &object_id)
{
LLVector3d old_focus = mFocusTargetGlobal.isExactlyZero() ? 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(LLViewerCamera::getInstance()->getLeftAxis() * calcCustomizeAvatarUIOffset( mAnimationCameraStartGlobal ));
}
}
//LLViewerCamera::getInstance()->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, (gAgent.getPosAgentFromGlobal(focus) - mFocusObject->getRenderPosition()) * ~mFocusObject->getRenderRotation());
}
}
else
{
setLookAt(LOOKAT_TARGET_FOCUS, NULL, gAgent.getPosAgentFromGlobal(mFocusTargetGlobal));
}
if (mCameraAnimating)
{
const F64 ANIM_METERS_PER_SECOND = 10.0;
const F64 MIN_ANIM_SECONDS = 0.5;
const F64 MAX_ANIM_SECONDS = 10.0;
F64 anim_duration = llmax( MIN_ANIM_SECONDS, sqrt(focus_delta_squared) / ANIM_METERS_PER_SECOND );
anim_duration = llmin( anim_duration, MAX_ANIM_SECONDS );
setAnimationDuration( (F32)anim_duration );
}
updateFocusOffset();
}
//-----------------------------------------------------------------------------
// setSitCamera()
//-----------------------------------------------------------------------------
void LLAgentCamera::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 LLAgentCamera::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 (!mFocusOnAvatar && focus_on_avatar)
{
setFocusGlobal(LLVector3d::zero);
mCameraFOVZoomFactor = 0.f;
if (mCameraMode == CAMERA_MODE_THIRD_PERSON)
{
LLVector3 at_axis;
if (isAgentAvatarValid() && gAgentAvatarp->getParent())
{
LLQuaternion obj_rot = ((LLViewerObject*)gAgentAvatarp->getParent())->getRenderRotation();
at_axis = LLViewerCamera::getInstance()->getAtAxis();
at_axis.mV[VZ] = 0.f;
at_axis.normalize();
gAgent.resetAxes(at_axis * ~obj_rot);
}
else
{
at_axis = LLViewerCamera::getInstance()->getAtAxis();
at_axis.mV[VZ] = 0.f;
at_axis.normalize();
gAgent.resetAxes(at_axis);
}
}
}
// unlocking camera from avatar
else if (mFocusOnAvatar && !focus_on_avatar)
{
// keep camera focus point consistent, even though it is now unlocked
setFocusGlobal(gAgent.getPositionGlobal() + calcThirdPersonFocusOffset(), gAgent.getID());
}
mFocusOnAvatar = focus_on_avatar;
}
BOOL LLAgentCamera::setLookAt(ELookAtType target_type, LLViewerObject *object, LLVector3 position)
{
if(gSavedSettings.getBOOL("PrivateLookAt"))
{
if(!mLookAt || mLookAt->isDead())
return FALSE;
position.clearVec();
return mLookAt->setLookAt(LOOKAT_TARGET_NONE, gAgentAvatarp, position);
}
if(object && object->isAttachment())
{
LLViewerObject* parent = object;
while(parent)
{
if (parent == gAgentAvatarp)
{
// looking at an attachment on ourselves, which we don't want to do
object = gAgentAvatarp;
position.clearVec();
}
parent = (LLViewerObject*)parent->getParent();
}
}
if(!mLookAt || mLookAt->isDead())
{
mLookAt = (LLHUDEffectLookAt *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_LOOKAT);
mLookAt->setSourceObject(gAgentAvatarp);
}
return mLookAt->setLookAt(target_type, object, position);
}
//-----------------------------------------------------------------------------
// lookAtLastChat()
//-----------------------------------------------------------------------------
void LLAgentCamera::lookAtLastChat()
{
lookAtObject(gAgent.getLastChatter());
}
//Pulled implementation out of lookAtLastChat and adapted to work for for general objects
bool LLAgentCamera::lookAtObject(const LLUUID &object_id, bool self)
{
// Block if camera is animating or not in normal third person camera mode
if (mCameraAnimating || !cameraThirdPerson())
{
return false;
}
LLViewerObject *chatter = gObjectList.findObject(object_id);
if (!chatter)
{
return false;
}
LLVector3 delta_pos;
F32 radius = chatter->getVObjRadius();
if (chatter->isAvatar())
{
LLVOAvatar *chatter_av = (LLVOAvatar*)chatter;
if (isAgentAvatarValid() && chatter_av->mHeadp)
{
delta_pos = chatter_av->mHeadp->getWorldPosition() - gAgentAvatarp->mHeadp->getWorldPosition();
}
else
{
delta_pos = chatter->getPositionAgent() - gAgent.getPositionAgent();
}
delta_pos.normalize();
gAgent.setControlFlags(AGENT_CONTROL_STOP);
changeCameraToThirdPerson();
LLVector3 new_camera_pos = gAgentAvatarp->mHeadp->getWorldPosition();
LLVector3 left = delta_pos % LLVector3::z_axis;
left.normalize();
LLVector3 up = left % delta_pos;
up.normalize();
new_camera_pos -= delta_pos * 0.4f;
new_camera_pos += left * 0.3f;
new_camera_pos += up * 0.2f;
setFocusOnAvatar(FALSE, FALSE);
if (chatter_av->mHeadp)
{
setFocusGlobal(gAgent.getPosGlobalFromAgent(chatter_av->mHeadp->getWorldPosition()), object_id);
if(self)
mCameraFocusOffsetTarget = gAgent.getPosGlobalFromAgent(new_camera_pos) - gAgent.getPosGlobalFromAgent(chatter_av->mHeadp->getWorldPosition());
else
mCameraFocusOffsetTarget.setVec(radius, radius, 0.f);
}
else
{
setFocusGlobal(chatter->getPositionGlobal(), object_id);
if(self)
mCameraFocusOffsetTarget = gAgent.getPosGlobalFromAgent(new_camera_pos) - chatter->getPositionGlobal();
else
mCameraFocusOffsetTarget.setVec(radius, radius, 0.f);
}
}
else
{
delta_pos = chatter->getRenderPosition() - gAgent.getPositionAgent();
delta_pos.normalize();
gAgent.setControlFlags(AGENT_CONTROL_STOP);
changeCameraToThirdPerson();
LLVector3 new_camera_pos = gAgentAvatarp->mHeadp->getWorldPosition();
LLVector3 left = delta_pos % LLVector3::z_axis;
left.normalize();
LLVector3 up = left % delta_pos;
up.normalize();
new_camera_pos -= delta_pos * 0.4f;
new_camera_pos += left * 0.3f;
new_camera_pos += up * 0.2f;
setFocusOnAvatar(FALSE, FALSE);
setFocusGlobal(chatter->getPositionGlobal(), object_id);
if(self)
mCameraFocusOffsetTarget = gAgent.getPosGlobalFromAgent(new_camera_pos) - chatter->getPositionGlobal();
else
mCameraFocusOffsetTarget.setVec(radius, radius, 0.f);
}
return true;
}
BOOL LLAgentCamera::setPointAt(EPointAtType target_type, LLViewerObject *object, LLVector3 position)
{
// disallow pointing at attachments and avatars
if ((object && (object->isAttachment() || object->isAvatar())) || gSavedSettings.getBOOL("DisablePointAtAndBeam"))
{
return FALSE;
}
if (!mPointAt || mPointAt->isDead())
{
mPointAt = (LLHUDEffectPointAt *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINTAT);
mPointAt->setSourceObject(gAgentAvatarp);
}
return mPointAt->setPointAt(target_type, object, position);
}
ELookAtType LLAgentCamera::getLookAtType()
{
if (mLookAt)
{
return mLookAt->getLookAtType();
}
return LOOKAT_TARGET_NONE;
}
EPointAtType LLAgentCamera::getPointAtType()
{
if (mPointAt)
{
return mPointAt->getPointAtType();
}
return POINTAT_TARGET_NONE;
}
void LLAgentCamera::clearGeneralKeys()
{
mAtKey = 0;
mWalkKey = 0;
mLeftKey = 0;
mUpKey = 0;
mYawKey = 0.f;
mPitchKey = 0.f;
}
void LLAgentCamera::clearOrbitKeys()
{
mOrbitLeftKey = 0.f;
mOrbitRightKey = 0.f;
mOrbitUpKey = 0.f;
mOrbitDownKey = 0.f;
mOrbitInKey = 0.f;
mOrbitOutKey = 0.f;
}
void LLAgentCamera::clearPanKeys()
{
mPanRightKey = 0.f;
mPanLeftKey = 0.f;
mPanUpKey = 0.f;
mPanDownKey = 0.f;
mPanInKey = 0.f;
mPanOutKey = 0.f;
}
// static
S32 LLAgentCamera::directionToKey(S32 direction)
{
if (direction > 0) return 1;
if (direction < 0) return -1;
return 0;
}
// EOF