908 lines
31 KiB
C++
908 lines
31 KiB
C++
/**
|
|
* @file llfollowcam.cpp
|
|
* @author Jeffrey Ventrella
|
|
* @brief LLFollowCam class implementation
|
|
*
|
|
* $LicenseInfo:firstyear=2005&license=viewergpl$
|
|
*
|
|
* Copyright (c) 2005-2009, Linden Research, Inc.
|
|
*
|
|
* Second Life Viewer Source Code
|
|
* The source code in this file ("Source Code") is provided by Linden Lab
|
|
* to you under the terms of the GNU General Public License, version 2.0
|
|
* ("GPL"), unless you have obtained a separate licensing agreement
|
|
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
|
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
|
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
|
*
|
|
* There are special exceptions to the terms and conditions of the GPL as
|
|
* it is applied to this Source Code. View the full text of the exception
|
|
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
|
* online at
|
|
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
*
|
|
* By copying, modifying or distributing this software, you acknowledge
|
|
* that you have read and understood your obligations described above,
|
|
* and agree to abide by those obligations.
|
|
*
|
|
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
|
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
|
* COMPLETENESS OR PERFORMANCE.
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
#include "llviewerprecompiledheaders.h"
|
|
#include "llfollowcam.h"
|
|
#include "llagent.h"
|
|
|
|
//-------------------------------------------------------
|
|
// class statics
|
|
//-------------------------------------------------------
|
|
std::map<LLUUID, LLFollowCamParams*> LLFollowCamMgr::sParamMap;
|
|
std::vector<LLFollowCamParams*> LLFollowCamMgr::sParamStack;
|
|
|
|
//-------------------------------------------------------
|
|
// constants
|
|
//-------------------------------------------------------
|
|
const F32 ONE_HALF = 0.5;
|
|
const F32 FOLLOW_CAM_ZOOM_FACTOR = 0.1f;
|
|
const F32 FOLLOW_CAM_MIN_ZOOM_AMOUNT = 0.1f;
|
|
const F32 DISTANCE_EPSILON = 0.0001f;
|
|
const F32 DEFAULT_MAX_DISTANCE_FROM_SUBJECT = 1000.0; // this will be correctly set on me by my caller
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
// This is how slowly the camera position moves to its ideal position
|
|
//----------------------------------------------------------------------------------------
|
|
const F32 FOLLOW_CAM_MIN_POSITION_LAG = 0.0f;
|
|
const F32 FOLLOW_CAM_DEFAULT_POSITION_LAG = 0.1f;
|
|
const F32 FOLLOW_CAM_MAX_POSITION_LAG = 3.0f;
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
// This is how slowly the camera focus moves to its subject
|
|
//----------------------------------------------------------------------------------------
|
|
const F32 FOLLOW_CAM_MIN_FOCUS_LAG = 0.0f;
|
|
const F32 FOLLOW_CAM_DEFAULT_FOCUS_LAG = 0.1f;
|
|
const F32 FOLLOW_CAM_MAX_FOCUS_LAG = 3.0f;
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
// This is far the position can get from its IDEAL POSITION before it starts getting pulled
|
|
//----------------------------------------------------------------------------------------
|
|
const F32 FOLLOW_CAM_MIN_POSITION_THRESHOLD = 0.0f;
|
|
const F32 FOLLOW_CAM_DEFAULT_POSITION_THRESHOLD = 1.0f;
|
|
const F32 FOLLOW_CAM_MAX_POSITION_THRESHOLD = 4.0f;
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
// This is far the focus can get from the subject before it starts getting pulled
|
|
//----------------------------------------------------------------------------------------
|
|
const F32 FOLLOW_CAM_MIN_FOCUS_THRESHOLD = 0.0f;
|
|
const F32 FOLLOW_CAM_DEFAULT_FOCUS_THRESHOLD = 1.0f;
|
|
const F32 FOLLOW_CAM_MAX_FOCUS_THRESHOLD = 4.0f;
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
// This is the distance the camera wants to be from the subject
|
|
//----------------------------------------------------------------------------------------
|
|
const F32 FOLLOW_CAM_MIN_DISTANCE = 0.5f;
|
|
const F32 FOLLOW_CAM_DEFAULT_DISTANCE = 3.0f;
|
|
//const F32 FOLLOW_CAM_MAX_DISTANCE = 10.0f; // from now on I am using mMaxCameraDistantFromSubject
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
// this is an angluar value
|
|
// It affects the angle that the camera rises (pitches) in relation
|
|
// to the horizontal plane
|
|
//----------------------------------------------------------------------------------------
|
|
const F32 FOLLOW_CAM_MIN_PITCH = -45.0f;
|
|
const F32 FOLLOW_CAM_DEFAULT_PITCH = 0.0f;
|
|
const F32 FOLLOW_CAM_MAX_PITCH = 80.0f; // keep under 90 degrees - avoid gimble lock!
|
|
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
// how high or low the camera considers its ideal focus to be (relative to its subject)
|
|
//----------------------------------------------------------------------------------------
|
|
const F32 FOLLOW_CAM_MIN_FOCUS_OFFSET = -10.0f;
|
|
const LLVector3 FOLLOW_CAM_DEFAULT_FOCUS_OFFSET = LLVector3(1.0f, 0.f, 0.f);
|
|
const F32 FOLLOW_CAM_MAX_FOCUS_OFFSET = 10.0f;
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
// This affects the rate at which the camera adjusts to stay behind the subject
|
|
//----------------------------------------------------------------------------------------
|
|
const F32 FOLLOW_CAM_MIN_BEHINDNESS_LAG = 0.0f;
|
|
const F32 FOLLOW_CAM_DEFAULT_BEHINDNESS_LAG = 0.f;
|
|
const F32 FOLLOW_CAM_MAX_BEHINDNESS_LAG = 3.0f;
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
// in degrees: This is the size of the pie slice behind the subject matter within which the camera is free to move
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
const F32 FOLLOW_CAM_MIN_BEHINDNESS_ANGLE = 0.0f;
|
|
const F32 FOLLOW_CAM_DEFAULT_BEHINDNESS_ANGLE = 10.0f;
|
|
const F32 FOLLOW_CAM_MAX_BEHINDNESS_ANGLE = 180.0f;
|
|
const F32 FOLLOW_CAM_BEHINDNESS_EPSILON = 1.0f;
|
|
|
|
//------------------------------------
|
|
// Constructor
|
|
//------------------------------------
|
|
LLFollowCamParams::LLFollowCamParams()
|
|
{
|
|
mMaxCameraDistantFromSubject = DEFAULT_MAX_DISTANCE_FROM_SUBJECT;
|
|
mPositionLocked = false;
|
|
mFocusLocked = false;
|
|
mUsePosition = false;
|
|
mUseFocus = false;
|
|
|
|
//------------------------------------------------------
|
|
// setting the attributes to their defaults
|
|
//------------------------------------------------------
|
|
setPositionLag ( FOLLOW_CAM_DEFAULT_POSITION_LAG );
|
|
setFocusLag ( FOLLOW_CAM_DEFAULT_FOCUS_LAG );
|
|
setPositionThreshold( FOLLOW_CAM_DEFAULT_POSITION_THRESHOLD );
|
|
setFocusThreshold ( FOLLOW_CAM_DEFAULT_FOCUS_THRESHOLD );
|
|
setBehindnessLag ( FOLLOW_CAM_DEFAULT_BEHINDNESS_LAG );
|
|
setDistance ( FOLLOW_CAM_DEFAULT_DISTANCE );
|
|
setPitch ( FOLLOW_CAM_DEFAULT_PITCH );
|
|
setFocusOffset ( FOLLOW_CAM_DEFAULT_FOCUS_OFFSET );
|
|
setBehindnessAngle ( FOLLOW_CAM_DEFAULT_BEHINDNESS_ANGLE );
|
|
setPositionThreshold( FOLLOW_CAM_DEFAULT_POSITION_THRESHOLD );
|
|
setFocusThreshold ( FOLLOW_CAM_DEFAULT_FOCUS_THRESHOLD );
|
|
|
|
}
|
|
|
|
LLFollowCamParams::~LLFollowCamParams() { }
|
|
|
|
//---------------------------------------------------------
|
|
// buncho set methods
|
|
//---------------------------------------------------------
|
|
|
|
//---------------------------------------------------------
|
|
void LLFollowCamParams::setPositionLag( F32 p )
|
|
{
|
|
mPositionLag = llclamp(p, FOLLOW_CAM_MIN_POSITION_LAG, FOLLOW_CAM_MAX_POSITION_LAG);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------
|
|
void LLFollowCamParams::setFocusLag( F32 f )
|
|
{
|
|
mFocusLag = llclamp(f, FOLLOW_CAM_MIN_FOCUS_LAG, FOLLOW_CAM_MAX_FOCUS_LAG);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------
|
|
void LLFollowCamParams::setPositionThreshold( F32 p )
|
|
{
|
|
mPositionThreshold = llclamp(p, FOLLOW_CAM_MIN_POSITION_THRESHOLD, FOLLOW_CAM_MAX_POSITION_THRESHOLD);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------
|
|
void LLFollowCamParams::setFocusThreshold( F32 f )
|
|
{
|
|
mFocusThreshold = llclamp(f, FOLLOW_CAM_MIN_FOCUS_THRESHOLD, FOLLOW_CAM_MAX_FOCUS_THRESHOLD);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------
|
|
void LLFollowCamParams::setPitch( F32 p )
|
|
{
|
|
mPitch = llclamp(p, FOLLOW_CAM_MIN_PITCH, FOLLOW_CAM_MAX_PITCH);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------
|
|
void LLFollowCamParams::setBehindnessLag( F32 b )
|
|
{
|
|
mBehindnessLag = llclamp(b, FOLLOW_CAM_MIN_BEHINDNESS_LAG, FOLLOW_CAM_MAX_BEHINDNESS_LAG);
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
void LLFollowCamParams::setBehindnessAngle( F32 b )
|
|
{
|
|
mBehindnessMaxAngle = llclamp(b, FOLLOW_CAM_MIN_BEHINDNESS_ANGLE, FOLLOW_CAM_MAX_BEHINDNESS_ANGLE);
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
void LLFollowCamParams::setDistance( F32 d )
|
|
{
|
|
mDistance = llclamp(d, FOLLOW_CAM_MIN_DISTANCE, mMaxCameraDistantFromSubject);
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
void LLFollowCamParams::setPositionLocked( bool l )
|
|
{
|
|
mPositionLocked = l;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
void LLFollowCamParams::setFocusLocked( bool l )
|
|
{
|
|
mFocusLocked = l;
|
|
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
void LLFollowCamParams::setFocusOffset( const LLVector3& v )
|
|
{
|
|
mFocusOffset = v;
|
|
mFocusOffset.clamp(FOLLOW_CAM_MIN_FOCUS_OFFSET, FOLLOW_CAM_MAX_FOCUS_OFFSET);
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
void LLFollowCamParams::setPosition( const LLVector3& p )
|
|
{
|
|
mUsePosition = true;
|
|
mPosition = p;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
void LLFollowCamParams::setFocus( const LLVector3& f )
|
|
{
|
|
mUseFocus = true;
|
|
mFocus = f;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// buncho get methods
|
|
//---------------------------------------------------------
|
|
F32 LLFollowCamParams::getPositionLag () const { return mPositionLag; }
|
|
F32 LLFollowCamParams::getFocusLag () const { return mFocusLag; }
|
|
F32 LLFollowCamParams::getPositionThreshold () const { return mPositionThreshold; }
|
|
F32 LLFollowCamParams::getFocusThreshold () const { return mFocusThreshold; }
|
|
F32 LLFollowCamParams::getDistance () const { return mDistance; }
|
|
F32 LLFollowCamParams::getPitch () const { return mPitch; }
|
|
LLVector3 LLFollowCamParams::getFocusOffset () const { return mFocusOffset; }
|
|
F32 LLFollowCamParams::getBehindnessAngle () const { return mBehindnessMaxAngle; }
|
|
F32 LLFollowCamParams::getBehindnessLag () const { return mBehindnessLag; }
|
|
LLVector3 LLFollowCamParams::getPosition () const { return mPosition; }
|
|
LLVector3 LLFollowCamParams::getFocus () const { return mFocus; }
|
|
bool LLFollowCamParams::getPositionLocked () const { return mPositionLocked; }
|
|
bool LLFollowCamParams::getFocusLocked () const { return mFocusLocked; }
|
|
|
|
//------------------------------------
|
|
// Constructor
|
|
//------------------------------------
|
|
LLFollowCam::LLFollowCam() : LLFollowCamParams()
|
|
{
|
|
mUpVector = LLVector3::z_axis;
|
|
mSubjectPosition = LLVector3::zero;
|
|
mSubjectRotation = LLQuaternion::DEFAULT;
|
|
|
|
mZoomedToMinimumDistance = false;
|
|
mPitchCos = mPitchSin = 0.f;
|
|
mPitchSineAndCosineNeedToBeUpdated = true;
|
|
|
|
mSimulatedDistance = mDistance;
|
|
}
|
|
|
|
void LLFollowCam::copyParams(LLFollowCamParams& params)
|
|
{
|
|
setPositionLag(params.getPositionLag());
|
|
setFocusLag(params.getFocusLag());
|
|
setFocusThreshold( params.getFocusThreshold());
|
|
setPositionThreshold(params.getPositionThreshold());
|
|
setPitch(params.getPitch());
|
|
setFocusOffset(params.getFocusOffset());
|
|
setBehindnessAngle(params.getBehindnessAngle());
|
|
setBehindnessLag(params.getBehindnessLag());
|
|
|
|
setPositionLocked(params.getPositionLocked());
|
|
setFocusLocked(params.getFocusLocked());
|
|
|
|
setDistance(params.getDistance());
|
|
if (params.getUsePosition())
|
|
{
|
|
setPosition(params.getPosition());
|
|
}
|
|
if (params.getUseFocus())
|
|
{
|
|
setFocus(params.getFocus());
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------
|
|
void LLFollowCam::update()
|
|
{
|
|
//####################################################################################
|
|
// update Focus
|
|
//####################################################################################
|
|
LLVector3 offsetSubjectPosition = mSubjectPosition + (mFocusOffset * mSubjectRotation);
|
|
|
|
LLVector3 simulated_pos_agent = gAgent.getPosAgentFromGlobal(mSimulatedPositionGlobal);
|
|
LLVector3 vectorFromCameraToSubject = offsetSubjectPosition - simulated_pos_agent;
|
|
F32 distanceFromCameraToSubject = vectorFromCameraToSubject.magVec();
|
|
|
|
LLVector3 whereFocusWantsToBe = mFocus;
|
|
LLVector3 focus_pt_agent = gAgent.getPosAgentFromGlobal(mSimulatedFocusGlobal);
|
|
if ( mFocusLocked ) // if focus is locked, only relative focus needs to be updated
|
|
{
|
|
mRelativeFocus = (focus_pt_agent - mSubjectPosition) * ~mSubjectRotation;
|
|
}
|
|
else
|
|
{
|
|
LLVector3 focusOffset = offsetSubjectPosition - focus_pt_agent;
|
|
F32 focusOffsetDistance = focusOffset.magVec();
|
|
|
|
LLVector3 focusOffsetDirection = focusOffset / focusOffsetDistance;
|
|
whereFocusWantsToBe = focus_pt_agent +
|
|
(focusOffsetDirection * (focusOffsetDistance - mFocusThreshold));
|
|
if ( focusOffsetDistance > mFocusThreshold )
|
|
{
|
|
// this version normalizes focus threshold by distance
|
|
// so that the effect is not changed with distance
|
|
/*
|
|
F32 focusThresholdNormalizedByDistance = distanceFromCameraToSubject * mFocusThreshold;
|
|
if ( focusOffsetDistance > focusThresholdNormalizedByDistance )
|
|
{
|
|
LLVector3 focusOffsetDirection = focusOffset / focusOffsetDistance;
|
|
F32 force = focusOffsetDistance - focusThresholdNormalizedByDistance;
|
|
*/
|
|
|
|
F32 focusLagLerp = LLCriticalDamp::getInterpolant( mFocusLag );
|
|
focus_pt_agent = lerp( focus_pt_agent, whereFocusWantsToBe, focusLagLerp );
|
|
mSimulatedFocusGlobal = gAgent.getPosGlobalFromAgent(focus_pt_agent);
|
|
}
|
|
mRelativeFocus = lerp(mRelativeFocus, (focus_pt_agent - mSubjectPosition) * ~mSubjectRotation, LLCriticalDamp::getInterpolant(0.05f));
|
|
}// if focus is not locked ---------------------------------------------
|
|
|
|
|
|
LLVector3 whereCameraPositionWantsToBe = gAgent.getPosAgentFromGlobal(mSimulatedPositionGlobal);
|
|
if ( mPositionLocked )
|
|
{
|
|
mRelativePos = (whereCameraPositionWantsToBe - mSubjectPosition) * ~mSubjectRotation;
|
|
}
|
|
else
|
|
{
|
|
//####################################################################################
|
|
// update Position
|
|
//####################################################################################
|
|
//-------------------------------------------------------------------------
|
|
// I determine the horizontal vector from the camera to the subject
|
|
//-------------------------------------------------------------------------
|
|
LLVector3 horizontalVectorFromCameraToSubject = vectorFromCameraToSubject;
|
|
horizontalVectorFromCameraToSubject.mV[VZ] = 0.0f;
|
|
|
|
//---------------------------------------------------------
|
|
// Now I determine the horizontal distance
|
|
//---------------------------------------------------------
|
|
F32 horizontalDistanceFromCameraToSubject = horizontalVectorFromCameraToSubject.magVec();
|
|
|
|
//---------------------------------------------------------
|
|
// Then I get the (normalized) horizontal direction...
|
|
//---------------------------------------------------------
|
|
LLVector3 horizontalDirectionFromCameraToSubject;
|
|
if ( horizontalDistanceFromCameraToSubject < DISTANCE_EPSILON )
|
|
{
|
|
// make sure we still have a normalized vector if distance is really small
|
|
// (this case is rare and fleeting)
|
|
horizontalDirectionFromCameraToSubject = LLVector3::z_axis;
|
|
}
|
|
else
|
|
{
|
|
// I'm not using the "normalize" method, because I can just divide by horizontalDistanceFromCameraToSubject
|
|
horizontalDirectionFromCameraToSubject = horizontalVectorFromCameraToSubject / horizontalDistanceFromCameraToSubject;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
// Here is where I determine an offset relative to subject position in oder to set the ideal position.
|
|
//------------------------------------------------------------------------------------------------------------
|
|
if ( mPitchSineAndCosineNeedToBeUpdated )
|
|
{
|
|
calculatePitchSineAndCosine();
|
|
mPitchSineAndCosineNeedToBeUpdated = false;
|
|
}
|
|
|
|
LLVector3 positionOffsetFromSubject;
|
|
positionOffsetFromSubject.setVec
|
|
(
|
|
horizontalDirectionFromCameraToSubject.mV[ VX ] * mPitchCos,
|
|
horizontalDirectionFromCameraToSubject.mV[ VY ] * mPitchCos,
|
|
-mPitchSin
|
|
);
|
|
|
|
positionOffsetFromSubject *= mSimulatedDistance;
|
|
|
|
//----------------------------------------------------------------------
|
|
// Finally, ideal position is set by taking the subject position and
|
|
// extending the positionOffsetFromSubject from that
|
|
//----------------------------------------------------------------------
|
|
LLVector3 idealCameraPosition = offsetSubjectPosition - positionOffsetFromSubject;
|
|
|
|
//--------------------------------------------------------------------------------
|
|
// Now I prepare to move the current camera position towards its ideal position...
|
|
//--------------------------------------------------------------------------------
|
|
LLVector3 vectorFromPositionToIdealPosition = idealCameraPosition - simulated_pos_agent;
|
|
F32 distanceFromPositionToIdealPosition = vectorFromPositionToIdealPosition.magVec();
|
|
|
|
//put this inside of the block?
|
|
LLVector3 normalFromPositionToIdealPosition = vectorFromPositionToIdealPosition / distanceFromPositionToIdealPosition;
|
|
|
|
whereCameraPositionWantsToBe = simulated_pos_agent +
|
|
(normalFromPositionToIdealPosition * (distanceFromPositionToIdealPosition - mPositionThreshold));
|
|
//-------------------------------------------------------------------------------------------------
|
|
// The following method takes the target camera position and resets it so that it stays "behind" the subject,
|
|
// using behindness angle and behindness force as parameters affecting the exact behavior
|
|
//-------------------------------------------------------------------------------------------------
|
|
if ( distanceFromPositionToIdealPosition > mPositionThreshold )
|
|
{
|
|
F32 positionPullLerp = LLCriticalDamp::getInterpolant( mPositionLag );
|
|
simulated_pos_agent = lerp( simulated_pos_agent, whereCameraPositionWantsToBe, positionPullLerp );
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// don't let the camera get farther than its official max distance
|
|
//--------------------------------------------------------------------
|
|
if ( distanceFromCameraToSubject > mMaxCameraDistantFromSubject )
|
|
{
|
|
LLVector3 directionFromCameraToSubject = vectorFromCameraToSubject / distanceFromCameraToSubject;
|
|
simulated_pos_agent = offsetSubjectPosition - directionFromCameraToSubject * mMaxCameraDistantFromSubject;
|
|
}
|
|
|
|
////-------------------------------------------------------------------------------------------------
|
|
//// The following method takes mSimulatedPositionGlobal and resets it so that it stays "behind" the subject,
|
|
//// using behindness angle and behindness force as parameters affecting the exact behavior
|
|
////-------------------------------------------------------------------------------------------------
|
|
updateBehindnessConstraint(gAgent.getPosAgentFromGlobal(mSimulatedFocusGlobal), simulated_pos_agent);
|
|
mSimulatedPositionGlobal = gAgent.getPosGlobalFromAgent(simulated_pos_agent);
|
|
|
|
mRelativePos = lerp(mRelativePos, (simulated_pos_agent - mSubjectPosition) * ~mSubjectRotation, LLCriticalDamp::getInterpolant(0.05f));
|
|
} // if position is not locked -----------------------------------------------------------
|
|
|
|
|
|
//####################################################################################
|
|
// update UpVector
|
|
//####################################################################################
|
|
// this just points upward for now, but I anticipate future effects requiring
|
|
// some rolling ("banking" effects for fun, swoopy vehicles, etc.)
|
|
mUpVector = LLVector3::z_axis;
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
BOOL LLFollowCam::updateBehindnessConstraint(LLVector3 focus, LLVector3& cam_position)
|
|
{
|
|
BOOL constraint_active = FALSE;
|
|
// only apply this stuff if the behindness angle is something other than opened up all the way
|
|
if ( mBehindnessMaxAngle < FOLLOW_CAM_MAX_BEHINDNESS_ANGLE - FOLLOW_CAM_BEHINDNESS_EPSILON )
|
|
{
|
|
//--------------------------------------------------------------
|
|
// horizontalized vector from focus to camera
|
|
//--------------------------------------------------------------
|
|
LLVector3 horizontalVectorFromFocusToCamera;
|
|
horizontalVectorFromFocusToCamera.setVec(cam_position - focus);
|
|
horizontalVectorFromFocusToCamera.mV[ VZ ] = 0.0f;
|
|
F32 cameraZ = cam_position.mV[ VZ ];
|
|
|
|
//--------------------------------------------------------------
|
|
// distance of horizontalized vector
|
|
//--------------------------------------------------------------
|
|
F32 horizontalDistance = horizontalVectorFromFocusToCamera.magVec();
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
// calculate horizontalized back vector of the subject and scale by horizontalDistance
|
|
//--------------------------------------------------------------------------------------------------
|
|
LLVector3 horizontalSubjectBack( -1.0f, 0.0f, 0.0f );
|
|
horizontalSubjectBack *= mSubjectRotation;
|
|
horizontalSubjectBack.mV[ VZ ] = 0.0f;
|
|
horizontalSubjectBack.normVec(); // because horizontalizing might make it shorter than 1
|
|
horizontalSubjectBack *= horizontalDistance;
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
// find the angle (in degrees) between these vectors
|
|
//--------------------------------------------------------------------------------------------------
|
|
F32 cameraOffsetAngle = 0.f;
|
|
LLVector3 cameraOffsetRotationAxis;
|
|
LLQuaternion camera_offset_rotation;
|
|
camera_offset_rotation.shortestArc(horizontalSubjectBack, horizontalVectorFromFocusToCamera);
|
|
camera_offset_rotation.getAngleAxis(&cameraOffsetAngle, cameraOffsetRotationAxis);
|
|
cameraOffsetAngle *= RAD_TO_DEG;
|
|
|
|
if ( cameraOffsetAngle > mBehindnessMaxAngle )
|
|
{
|
|
F32 fraction = ((cameraOffsetAngle - mBehindnessMaxAngle) / cameraOffsetAngle) * LLCriticalDamp::getInterpolant(mBehindnessLag);
|
|
cam_position = focus + horizontalSubjectBack * (slerp(fraction, camera_offset_rotation, LLQuaternion::DEFAULT));
|
|
cam_position.mV[VZ] = cameraZ; // clamp z value back to what it was before we started messing with it
|
|
constraint_active = TRUE;
|
|
}
|
|
}
|
|
return constraint_active;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------
|
|
void LLFollowCam::calculatePitchSineAndCosine()
|
|
{
|
|
F32 radian = mPitch * DEG_TO_RAD;
|
|
mPitchCos = cos( radian );
|
|
mPitchSin = sin( radian );
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
void LLFollowCam::setSubjectPositionAndRotation( const LLVector3 p, const LLQuaternion r )
|
|
{
|
|
mSubjectPosition = p;
|
|
mSubjectRotation = r;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------
|
|
void LLFollowCam::zoom( S32 z )
|
|
{
|
|
F32 zoomAmount = z * mSimulatedDistance * FOLLOW_CAM_ZOOM_FACTOR;
|
|
|
|
if (( zoomAmount < FOLLOW_CAM_MIN_ZOOM_AMOUNT )
|
|
&& ( zoomAmount > -FOLLOW_CAM_MIN_ZOOM_AMOUNT ))
|
|
{
|
|
if ( zoomAmount < 0.0f )
|
|
{
|
|
zoomAmount = -FOLLOW_CAM_MIN_ZOOM_AMOUNT;
|
|
}
|
|
else
|
|
{
|
|
zoomAmount = FOLLOW_CAM_MIN_ZOOM_AMOUNT;
|
|
}
|
|
}
|
|
|
|
mSimulatedDistance += zoomAmount;
|
|
|
|
mZoomedToMinimumDistance = false;
|
|
if ( mSimulatedDistance < FOLLOW_CAM_MIN_DISTANCE )
|
|
{
|
|
mSimulatedDistance = FOLLOW_CAM_MIN_DISTANCE;
|
|
|
|
// if zoomAmount is negative (i.e., getting closer), then
|
|
// this signifies having hit the minimum:
|
|
if ( zoomAmount < 0.0f )
|
|
{
|
|
mZoomedToMinimumDistance = true;
|
|
}
|
|
}
|
|
else if ( mSimulatedDistance > mMaxCameraDistantFromSubject )
|
|
{
|
|
mSimulatedDistance = mMaxCameraDistantFromSubject;
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------
|
|
bool LLFollowCam::isZoomedToMinimumDistance()
|
|
{
|
|
return mZoomedToMinimumDistance;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------
|
|
void LLFollowCam::reset( const LLVector3 p, const LLVector3 f , const LLVector3 u )
|
|
{
|
|
setPosition(p);
|
|
setFocus(f);
|
|
mUpVector = u;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
void LLFollowCam::setMaxCameraDistantFromSubject( F32 m )
|
|
{
|
|
mMaxCameraDistantFromSubject = m;
|
|
}
|
|
|
|
|
|
void LLFollowCam::setPitch( F32 p )
|
|
{
|
|
LLFollowCamParams::setPitch(p);
|
|
mPitchSineAndCosineNeedToBeUpdated = true; // important
|
|
}
|
|
|
|
void LLFollowCam::setDistance( F32 d )
|
|
{
|
|
if (d != mDistance)
|
|
{
|
|
LLFollowCamParams::setDistance(d);
|
|
mSimulatedDistance = d;
|
|
mZoomedToMinimumDistance = false;
|
|
}
|
|
}
|
|
|
|
void LLFollowCam::setPosition( const LLVector3& p )
|
|
{
|
|
if (p != mPosition)
|
|
{
|
|
LLFollowCamParams::setPosition(p);
|
|
mSimulatedPositionGlobal = gAgent.getPosGlobalFromAgent(mPosition);
|
|
if (mPositionLocked)
|
|
{
|
|
mRelativePos = (mPosition - mSubjectPosition) * ~mSubjectRotation;
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLFollowCam::setFocus( const LLVector3& f )
|
|
{
|
|
if (f != mFocus)
|
|
{
|
|
LLFollowCamParams::setFocus(f);
|
|
mSimulatedFocusGlobal = gAgent.getPosGlobalFromAgent(f);
|
|
if (mFocusLocked)
|
|
{
|
|
mRelativeFocus = (mFocus - mSubjectPosition) * ~mSubjectRotation;
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLFollowCam::setPositionLocked( bool locked )
|
|
{
|
|
LLFollowCamParams::setPositionLocked(locked);
|
|
if (locked)
|
|
{
|
|
// propagate set position to relative position
|
|
mRelativePos = (gAgent.getPosAgentFromGlobal(mSimulatedPositionGlobal) - mSubjectPosition) * ~mSubjectRotation;
|
|
}
|
|
}
|
|
|
|
void LLFollowCam::setFocusLocked( bool locked )
|
|
{
|
|
LLFollowCamParams::setFocusLocked(locked);
|
|
if (locked)
|
|
{
|
|
// propagate set position to relative position
|
|
mRelativeFocus = (gAgent.getPosAgentFromGlobal(mSimulatedFocusGlobal) - mSubjectPosition) * ~mSubjectRotation;
|
|
}
|
|
}
|
|
|
|
|
|
LLVector3 LLFollowCam::getSimulatedPosition() const
|
|
{
|
|
// return simulated position
|
|
return mSubjectPosition + (mRelativePos * mSubjectRotation);
|
|
}
|
|
|
|
LLVector3 LLFollowCam::getSimulatedFocus() const
|
|
{
|
|
// return simulated focus point
|
|
return mSubjectPosition + (mRelativeFocus * mSubjectRotation);
|
|
}
|
|
|
|
LLVector3 LLFollowCam::getUpVector()
|
|
{
|
|
return mUpVector;
|
|
}
|
|
|
|
|
|
//------------------------------------
|
|
// Destructor
|
|
//------------------------------------
|
|
LLFollowCam::~LLFollowCam()
|
|
{
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------
|
|
// LLFollowCamMgr
|
|
//-------------------------------------------------------
|
|
//static
|
|
void LLFollowCamMgr::cleanupClass()
|
|
{
|
|
for (param_map_t::iterator iter = sParamMap.begin(); iter != sParamMap.end(); ++iter)
|
|
{
|
|
LLFollowCamParams* params = iter->second;
|
|
delete params;
|
|
}
|
|
sParamMap.clear();
|
|
}
|
|
|
|
//static
|
|
void LLFollowCamMgr::setPositionLag( const LLUUID& source, F32 lag)
|
|
{
|
|
LLFollowCamParams* paramsp = getParamsForID(source);
|
|
if (paramsp)
|
|
{
|
|
paramsp->setPositionLag(lag);
|
|
}
|
|
}
|
|
|
|
//static
|
|
void LLFollowCamMgr::setFocusLag( const LLUUID& source, F32 lag)
|
|
{
|
|
LLFollowCamParams* paramsp = getParamsForID(source);
|
|
if (paramsp)
|
|
{
|
|
paramsp->setFocusLag(lag);
|
|
}
|
|
}
|
|
|
|
//static
|
|
void LLFollowCamMgr::setFocusThreshold( const LLUUID& source, F32 threshold)
|
|
{
|
|
LLFollowCamParams* paramsp = getParamsForID(source);
|
|
if (paramsp)
|
|
{
|
|
paramsp->setFocusThreshold(threshold);
|
|
}
|
|
|
|
}
|
|
|
|
//static
|
|
void LLFollowCamMgr::setPositionThreshold( const LLUUID& source, F32 threshold)
|
|
{
|
|
LLFollowCamParams* paramsp = getParamsForID(source);
|
|
if (paramsp)
|
|
{
|
|
paramsp->setPositionThreshold(threshold);
|
|
}
|
|
}
|
|
|
|
//static
|
|
void LLFollowCamMgr::setDistance( const LLUUID& source, F32 distance)
|
|
{
|
|
LLFollowCamParams* paramsp = getParamsForID(source);
|
|
if (paramsp)
|
|
{
|
|
paramsp->setDistance(distance);
|
|
}
|
|
}
|
|
|
|
//static
|
|
void LLFollowCamMgr::setPitch( const LLUUID& source, F32 pitch)
|
|
{
|
|
LLFollowCamParams* paramsp = getParamsForID(source);
|
|
if (paramsp)
|
|
{
|
|
paramsp->setPitch(pitch);
|
|
}
|
|
}
|
|
|
|
//static
|
|
void LLFollowCamMgr::setFocusOffset( const LLUUID& source, const LLVector3& offset)
|
|
{
|
|
LLFollowCamParams* paramsp = getParamsForID(source);
|
|
if (paramsp)
|
|
{
|
|
paramsp->setFocusOffset(offset);
|
|
}
|
|
}
|
|
|
|
//static
|
|
void LLFollowCamMgr::setBehindnessAngle( const LLUUID& source, F32 angle)
|
|
{
|
|
LLFollowCamParams* paramsp = getParamsForID(source);
|
|
if (paramsp)
|
|
{
|
|
paramsp->setBehindnessAngle(angle);
|
|
}
|
|
}
|
|
|
|
//static
|
|
void LLFollowCamMgr::setBehindnessLag( const LLUUID& source, F32 force)
|
|
{
|
|
LLFollowCamParams* paramsp = getParamsForID(source);
|
|
if (paramsp)
|
|
{
|
|
paramsp->setBehindnessLag(force);
|
|
}
|
|
}
|
|
|
|
//static
|
|
void LLFollowCamMgr::setPosition( const LLUUID& source, const LLVector3 position)
|
|
{
|
|
LLFollowCamParams* paramsp = getParamsForID(source);
|
|
if (paramsp)
|
|
{
|
|
paramsp->setPosition(position);
|
|
}
|
|
}
|
|
|
|
//static
|
|
void LLFollowCamMgr::setFocus( const LLUUID& source, const LLVector3 focus)
|
|
{
|
|
LLFollowCamParams* paramsp = getParamsForID(source);
|
|
if (paramsp)
|
|
{
|
|
paramsp->setFocus(focus);
|
|
}
|
|
}
|
|
|
|
//static
|
|
void LLFollowCamMgr::setPositionLocked( const LLUUID& source, bool locked)
|
|
{
|
|
LLFollowCamParams* paramsp = getParamsForID(source);
|
|
if (paramsp)
|
|
{
|
|
paramsp->setPositionLocked(locked);
|
|
}
|
|
}
|
|
|
|
//static
|
|
void LLFollowCamMgr::setFocusLocked( const LLUUID& source, bool locked )
|
|
{
|
|
LLFollowCamParams* paramsp = getParamsForID(source);
|
|
if (paramsp)
|
|
{
|
|
paramsp->setFocusLocked(locked);
|
|
}
|
|
}
|
|
|
|
//static
|
|
LLFollowCamParams* LLFollowCamMgr::getParamsForID(const LLUUID& source)
|
|
{
|
|
LLFollowCamParams* params = NULL;
|
|
|
|
param_map_t::iterator found_it = sParamMap.find(source);
|
|
if (found_it == sParamMap.end()) // didn't find it?
|
|
{
|
|
params = new LLFollowCamParams();
|
|
sParamMap[source] = params;
|
|
}
|
|
else
|
|
{
|
|
params = found_it->second;
|
|
}
|
|
|
|
return params;
|
|
}
|
|
|
|
//static
|
|
LLFollowCamParams* LLFollowCamMgr::getActiveFollowCamParams()
|
|
{
|
|
if (sParamStack.empty())
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return sParamStack.back();
|
|
}
|
|
|
|
//static
|
|
void LLFollowCamMgr::setCameraActive( const LLUUID& source, bool active )
|
|
{
|
|
LLFollowCamParams* params = getParamsForID(source);
|
|
param_stack_t::iterator found_it = std::find(sParamStack.begin(), sParamStack.end(), params);
|
|
if (found_it != sParamStack.end())
|
|
{
|
|
sParamStack.erase(found_it);
|
|
}
|
|
// put on top of stack
|
|
if(active)
|
|
{
|
|
sParamStack.push_back(params);
|
|
}
|
|
}
|
|
|
|
//static
|
|
void LLFollowCamMgr::removeFollowCamParams(const LLUUID& source)
|
|
{
|
|
setCameraActive(source, FALSE);
|
|
LLFollowCamParams* params = getParamsForID(source);
|
|
sParamMap.erase(source);
|
|
delete params;
|
|
}
|
|
|
|
//static
|
|
bool LLFollowCamMgr::isScriptedCameraSource(const LLUUID& source)
|
|
{
|
|
param_map_t::iterator found_it = sParamMap.find(source);
|
|
return (found_it != sParamMap.end());
|
|
}
|
|
|
|
//static
|
|
void LLFollowCamMgr::dump()
|
|
{
|
|
S32 param_count = 0;
|
|
llinfos << "Scripted camera active stack" << llendl;
|
|
for (param_stack_t::iterator param_it = sParamStack.begin();
|
|
param_it != sParamStack.end();
|
|
++param_it)
|
|
{
|
|
llinfos << param_count++ <<
|
|
" rot_limit: " << (*param_it)->getBehindnessAngle() <<
|
|
" rot_lag: " << (*param_it)->getBehindnessLag() <<
|
|
" distance: " << (*param_it)->getDistance() <<
|
|
" focus: " << (*param_it)->getFocus() <<
|
|
" foc_lag: " << (*param_it)->getFocusLag() <<
|
|
" foc_lock: " << ((*param_it)->getFocusLocked() ? "Y" : "N") <<
|
|
" foc_offset: " << (*param_it)->getFocusOffset() <<
|
|
" foc_thresh: " << (*param_it)->getFocusThreshold() <<
|
|
" pitch: " << (*param_it)->getPitch() <<
|
|
" pos: " << (*param_it)->getPosition() <<
|
|
" pos_lag: " << (*param_it)->getPositionLag() <<
|
|
" pos_lock: " << ((*param_it)->getPositionLocked() ? "Y" : "N") <<
|
|
" pos_thresh: " << (*param_it)->getPositionThreshold() << llendl;
|
|
}
|
|
}
|
|
|