Files
SingularityViewer/indra/newview/llagentcamera.cpp
2014-03-24 16:56:10 -04: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