From ba91b3eabbc40e4a0b610ccf7bd8ea2465318f03 Mon Sep 17 00:00:00 2001
From: Kitty Barnett <develop@catznip.com>
Date: Wed, 8 Jun 2016 01:54:40 +0200
Subject: [PATCH] Added @setcam_avdistmin[:<distance>]=n|y,
 @setcam_avdistmax[:<distance>]=n|y, @setcam_focusdistmin[:<distance>]=n|y and
 @setcam_foccusdistmax[:<distance>]=n|y

--HG--
branch : RLVa
---
 indra/newview/llagentcamera.cpp | 99 ++++++++++++++++++++++++++++++++-
 indra/newview/llagentcamera.h   | 12 ++++
 indra/newview/rlvactions.cpp    | 66 +++++++++++++++++-----
 indra/newview/rlvactions.h      | 21 +++++--
 indra/newview/rlvdefines.h      |  8 +++
 indra/newview/rlvhelper.cpp     |  8 +++
 6 files changed, 194 insertions(+), 20 deletions(-)

diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp
index 637326acd0..1392f155b8 100755
--- a/indra/newview/llagentcamera.cpp
+++ b/indra/newview/llagentcamera.cpp
@@ -821,7 +821,13 @@ void LLAgentCamera::setCameraZoomFraction(F32 fraction)
 
 		LLVector3d camera_offset_dir = mCameraFocusOffsetTarget;
 		camera_offset_dir.normalize();
-		mCameraFocusOffsetTarget = camera_offset_dir * rescale(fraction, 0.f, 1.f, max_zoom, min_zoom);
+// [RLVa:KB] - Checked: 2.0.0
+		const LLVector3d focus_offset_target = camera_offset_dir * rescale(fraction, 0.f, 1.f, max_zoom, min_zoom);
+		if ( (RlvActions::isRlvEnabled()) && (!allowFocusOffsetChange(focus_offset_target)) )
+			return;
+		mCameraFocusOffsetTarget = focus_offset_target;
+// [/RLVa:KB]
+//		mCameraFocusOffsetTarget = camera_offset_dir * rescale(fraction, 0.f, 1.f, max_zoom, min_zoom);
 	}
 	startCameraAnimation();
 }
@@ -946,6 +952,11 @@ void LLAgentCamera::cameraZoomIn(const F32 fraction)
 		new_distance = llclamp( new_distance, APPEARANCE_MIN_ZOOM, APPEARANCE_MAX_ZOOM );
 	}
 
+// [RLVa:KB] - Checked: 2.0.0
+	if ( (RlvActions::isRlvEnabled()) && (!allowFocusOffsetChange(new_distance * camera_offset_unit)) )
+		return;
+// [/RLVa:KB]
+
 	mCameraFocusOffsetTarget = new_distance * camera_offset_unit;
 }
 
@@ -1009,6 +1020,11 @@ void LLAgentCamera::cameraOrbitIn(const F32 meters)
 			new_distance = llclamp( new_distance, APPEARANCE_MIN_ZOOM, APPEARANCE_MAX_ZOOM );
 		}
 
+// [RLVa:KB] - Checked: 2.0.0
+		if ( (RlvActions::isRlvEnabled()) && (!allowFocusOffsetChange(new_distance * camera_offset_unit)) )
+			return;
+// [/RLVa:KB]
+
 		// Compute new camera offset
 		mCameraFocusOffsetTarget = new_distance * camera_offset_unit;
 		cameraZoomIn(1.f);
@@ -1931,6 +1947,44 @@ LLVector3d LLAgentCamera::calcCameraPositionTargetGlobal(BOOL *hit_limit)
 //			}
 	}
 
+// [RLVa:KB] - Checked: RLVa-2.0.0
+	if ( (CAMERA_MODE_THIRD_PERSON == mCameraMode) && (RlvActions::isRlvEnabled()) && (RlvActions::isCameraDistanceClamped()) )
+	{
+		m_fRlvMinDist = m_fRlvMaxDist = false;
+
+		// Av-locked | Focus-locked | Result
+		// ===================================================
+		//     T     |      T       | skip focus => slam av
+		//     T     |      F       | skip focus => slam av
+		//     F     |      T       | skip av    => slam focus
+		//     F     |      F       | clamp focus then av
+		bool fCamAvDistClamped, fCamAvDistLocked = false; float nCamAvDistLimitMin, nCamAvDistLimitMax;
+		if (fCamAvDistClamped = RlvActions::getCameraAvatarDistanceLimits(nCamAvDistLimitMin, nCamAvDistLimitMax))
+			fCamAvDistLocked = nCamAvDistLimitMin == nCamAvDistLimitMax;
+		bool fCamFocusDistClamped, fCamFocusDistLocked = false; float nCamFocusDistLimitMin, nCamFocusDistLimitMax;
+		if (fCamFocusDistClamped = RlvActions::getCameraFocusDistanceLimits(nCamFocusDistLimitMin, nCamFocusDistLimitMax))
+			fCamFocusDistLocked = nCamFocusDistLimitMin == nCamFocusDistLimitMax;
+
+		// Check focus distance limits
+		if ( (fCamFocusDistClamped) && (!fCamAvDistLocked) )
+		{
+			const LLVector3 offsetCameraLocal = mCameraZoomFraction * getCameraOffsetInitial() * gSavedSettings.getF32("CameraOffsetScale");
+			const LLVector3d offsetCamera(gAgent.getFrameAgent().rotateToAbsolute(offsetCameraLocal));
+			const LLVector3d posFocusCam = frame_center_global + head_offset + offsetCamera;
+			if (clampCameraPosition(camera_position_global, posFocusCam, nCamFocusDistLimitMin, nCamFocusDistLimitMax))
+				isConstrained = TRUE;
+		}
+
+		// Check avatar distance limits
+		if ( (fCamAvDistClamped) && (fCamAvDistLocked || !fCamFocusDistClamped) )
+		{
+			const LLVector3d posAvatarCam = gAgent.getPosGlobalFromAgent( (isAgentAvatarValid()) ? gAgentAvatarp->mHeadp->getWorldPosition() : gAgent.getPositionAgent() );
+			if (clampCameraPosition(camera_position_global, posAvatarCam, nCamAvDistLimitMin, nCamAvDistLimitMax))
+				isConstrained = TRUE;
+		}
+	}
+// [/RLVa:KB]
+
 	// Don't let camera go underground
 	F32 camera_min_off_ground = getCameraMinOffGround();
 
@@ -1951,6 +2005,49 @@ LLVector3d LLAgentCamera::calcCameraPositionTargetGlobal(BOOL *hit_limit)
 	return camera_position_global;
 }
 
+// [RLVa:KB] - Checked: RLVa-2.0.0
+bool LLAgentCamera::allowFocusOffsetChange(const LLVector3d& offsetFocus)
+{
+	if (RlvActions::isCameraDistanceClamped())
+	{
+		if ( (CAMERA_MODE_THIRD_PERSON == getCameraMode()) && ((m_fRlvMinDist) || (m_fRlvMaxDist)) )
+		{
+			const LLVector3d posFocusGlobal = calcFocusPositionTargetGlobal();
+			// Don't allow moving the focus offset if at minimum and moving closer (or if at maximum and moving further) to prevent camera warping
+			F32 nCurDist = llabs((posFocusGlobal + mCameraFocusOffsetTarget - m_posRlvRefGlobal).magVec());
+			F32 nNewDist = llabs((posFocusGlobal + offsetFocus - m_posRlvRefGlobal).magVec());
+			if ( ((m_fRlvMaxDist) && (nNewDist > nCurDist)) || ((m_fRlvMinDist) && (nNewDist < nCurDist)) )
+				return false;
+		}
+	}
+	return true;
+}
+
+bool LLAgentCamera::clampCameraPosition(LLVector3d& posCamGlobal, const LLVector3d posCamRefGlobal, float nDistMin, float nDistMax)
+{
+	const LLVector3d offsetCamera = posCamGlobal - posCamRefGlobal;
+
+	F32 nCamAvDist = llabs(offsetCamera.magVec()), nDistMult = NAN;
+	if (nCamAvDist > nDistMax)
+	{
+		nDistMult = nDistMax / nCamAvDist;
+		m_fRlvMaxDist = true;
+	}
+	else if (nCamAvDist < nDistMin)
+	{
+		nDistMult = nDistMin / nCamAvDist;
+		m_fRlvMinDist = true;
+	}
+
+	if (!isnan(nDistMult))
+	{
+		posCamGlobal = posCamRefGlobal + nDistMult * offsetCamera;
+		m_posRlvRefGlobal = posCamRefGlobal;
+		return true;
+	}
+	return false;
+}
+// [/RLVa:KB]
 
 LLVector3 LLAgentCamera::getCameraOffsetInitial()
 {
diff --git a/indra/newview/llagentcamera.h b/indra/newview/llagentcamera.h
index 3a448de783..969a86ab55 100755
--- a/indra/newview/llagentcamera.h
+++ b/indra/newview/llagentcamera.h
@@ -305,6 +305,18 @@ public:
 	F32				mHUDTargetZoom;	// Target zoom level for HUD objects (used when editing)
 	F32				mHUDCurZoom; 	// Current animated zoom level for HUD objects
 
+// [RLVa:KB] - Checked: RLVa-2.0.0
+	//--------------------------------------------------------------------
+	// RLVa
+	//--------------------------------------------------------------------
+protected:
+	bool allowFocusOffsetChange(const LLVector3d& offsetFocus);
+	bool clampCameraPosition(LLVector3d& posCamGlobal, const LLVector3d posCamRefGlobal, float nDistMin, float nDistMax);
+
+	bool m_fRlvMaxDist;				// True if the camera is at max distance
+	bool m_fRlvMinDist;				// True if the camera is at min distance
+	LLVector3d m_posRlvRefGlobal;		// Current reference point for distance calculations
+// [/RLVa:KB]
 
 /********************************************************************************
  **                                                                            **
diff --git a/indra/newview/rlvactions.cpp b/indra/newview/rlvactions.cpp
index 89bba202fd..f1c9ec27ab 100644
--- a/indra/newview/rlvactions.cpp
+++ b/indra/newview/rlvactions.cpp
@@ -27,6 +27,12 @@
 // Camera
 //
 
+bool RlvActions::canChangeCameraFOV(const LLUUID& idRlvObject)
+{
+	// NOTE: if an object has exclusive camera controls then all other objects are locked out
+	return (!gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM)) || (gRlvHandler.hasBehaviour(idRlvObject, RLV_BHVR_SETCAM));
+}
+
 bool RlvActions::canChangeCameraPreset(const LLUUID& idRlvObject)
 {
 	// NOTE: if an object has exclusive camera controls then all other objects are locked out
@@ -35,10 +41,16 @@ bool RlvActions::canChangeCameraPreset(const LLUUID& idRlvObject)
 		(!gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_EYEOFFSET)) && (!gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_FOCUSOFFSET));
 }
 
-bool RlvActions::canChangeCameraFOV(const LLUUID& idRlvObject)
+bool RlvActions::isCameraDistanceClamped()
 {
-	// NOTE: if an object has exclusive camera controls then all other objects are locked out
-	return (!gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM)) || (gRlvHandler.hasBehaviour(idRlvObject, RLV_BHVR_SETCAM));
+	return
+		(gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_AVDISTMIN)) || (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_AVDISTMAX)) ||
+		(gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_FOCUSDISTMIN)) || (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_FOCUSDISTMAX));
+}
+
+bool RlvActions::isCameraFOVClamped()
+{
+	return (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_FOVMIN)) || (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_FOVMAX));
 }
 
 bool RlvActions::isCameraPresetLocked()
@@ -46,23 +58,49 @@ bool RlvActions::isCameraPresetLocked()
 	return (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM)) || (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_EYEOFFSET)) || (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_FOCUSOFFSET));
 }
 
-bool RlvActions::isCameraFOVClamped()
+bool RlvActions::getCameraAvatarDistanceLimits(float& nDistMin, float& nDistMax)
 {
-	return (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_FOVMIN)) || (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_FOVMAX));
+	bool fDistMin = gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_AVDISTMIN), fDistMax = gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_AVDISTMAX);
+	if ( (fDistMin) || (fDistMax) )
+	{
+		static RlvCachedBehaviourModifier<float> sCamDistMin(RLV_MODIFIER_SETCAM_AVDISTMIN);
+		static RlvCachedBehaviourModifier<float> sCamDistMax(RLV_MODIFIER_SETCAM_AVDISTMAX);
+
+		nDistMax = (fDistMax) ? sCamDistMax : F32_MAX;
+		nDistMin = (fDistMin) ? sCamDistMin : 0.0;
+		return true;
+	}
+	return false;
 }
 
-bool RlvActions::getCameraFOVLimits(F32& nFOVMin, F32& nFOVMax)
+bool RlvActions::getCameraFocusDistanceLimits(float& nDistMin, float& nDistMax)
 {
-	static RlvCachedBehaviourModifier<float> sCamFovMin(RLV_MODIFIER_SETCAM_FOVMIN);
-	static RlvCachedBehaviourModifier<float> sCamFovMax(RLV_MODIFIER_SETCAM_FOVMAX);
-
-	bool fClampMax = gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_FOVMAX);
-	nFOVMax = (fClampMax) ? sCamFovMax : LLViewerCamera::getInstance()->getMaxView();
+	bool fDistMin = gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_FOCUSDISTMIN), fDistMax = gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_FOCUSDISTMAX);
+	if ( (fDistMin) || (fDistMax) )
+	{
+		static RlvCachedBehaviourModifier<float> sCamDistMin(RLV_MODIFIER_SETCAM_FOCUSDISTMIN);
+		static RlvCachedBehaviourModifier<float> sCamDistMax(RLV_MODIFIER_SETCAM_FOCUSDISTMAX);
 
-	bool fClampMin = gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_FOVMIN);
-	nFOVMin = (fClampMin) ? sCamFovMin : LLViewerCamera::getInstance()->getMinView();
+		nDistMax = (fDistMax) ? sCamDistMax : F32_MAX;
+		nDistMin = (fDistMin) ? sCamDistMin : 0.0;
+		return true;
+	}
+	return false;
+}
 
-	return (fClampMin) || (fClampMax);
+bool RlvActions::getCameraFOVLimits(F32& nFOVMin, F32& nFOVMax)
+{
+	bool fClampMin = gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_FOVMIN), fClampMax = gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_FOVMAX);
+	if ( (fClampMin) || (fClampMax) )
+	{
+		static RlvCachedBehaviourModifier<float> sCamFovMin(RLV_MODIFIER_SETCAM_FOVMIN);
+		static RlvCachedBehaviourModifier<float> sCamFovMax(RLV_MODIFIER_SETCAM_FOVMAX);
+
+		nFOVMin = (fClampMin) ? sCamFovMin : LLViewerCamera::getInstance()->getMinView();
+		nFOVMax = (fClampMax) ? sCamFovMax : LLViewerCamera::getInstance()->getMaxView();
+		return true;
+	}
+	return false;
 }
 
 // ============================================================================
diff --git a/indra/newview/rlvactions.h b/indra/newview/rlvactions.h
index e93060f384..2fbe9e0aca 100644
--- a/indra/newview/rlvactions.h
+++ b/indra/newview/rlvactions.h
@@ -29,15 +29,25 @@ class RlvActions
 	// Camera
 	// ======
 public:
+	/*
+	 * Returns true if the specified object cannot manipulate the camera FOV
+	 */
+	static bool canChangeCameraFOV(const LLUUID& idRlvObject);
+
 	/*
 	 * Returns true if the specified object can manipulate the camera offset and/or focus offset values
 	 */
 	static bool canChangeCameraPreset(const LLUUID& idRlvObject);
 
 	/*
-	 * Returns true if the specified object cannot manipulate the camera FOV
+	 * Returns true if the camera's distance (from either the avatar of the focus) is currently restricted/clamped
 	 */
-	static bool canChangeCameraFOV(const LLUUID& idRlvObject);
+	static bool isCameraDistanceClamped();
+
+	/*
+	 * Returns true if the camera's FOV is currently restricted/clamped
+	 */
+	static bool isCameraFOVClamped();
 
 	/*
 	 * Returns true if the camera offset and focus offset are locked (prevents changing the current camera preset)
@@ -45,14 +55,15 @@ public:
 	static bool isCameraPresetLocked();
 
 	/*
-	 * Returns true if the camera's FOV is currently restricted/clamped
+	 * Retrieves the current (avatar or focus) camera distance limits
 	 */
-	static bool isCameraFOVClamped();
+	static bool getCameraAvatarDistanceLimits(float& nDistMin, float& nDistMax);
+	static bool getCameraFocusDistanceLimits(float& nDistMin, float& nDistMax);
 
 	/*
 	 * Retrieves the current camera FOV limits - returns isCameraFOVClamped()
 	 */
-	static bool getCameraFOVLimits(F32& nFOVMin, F32& nFOVMax);
+	static bool getCameraFOVLimits(float& nFOVMin, float& nFOVMax);
 
 	// ================================
 	// Communication/Avatar interaction
diff --git a/indra/newview/rlvdefines.h b/indra/newview/rlvdefines.h
index 7814545bd0..847b86d743 100644
--- a/indra/newview/rlvdefines.h
+++ b/indra/newview/rlvdefines.h
@@ -190,6 +190,10 @@ enum ERlvBehaviour {
 
 	// Camera
 	RLV_BHVR_SETCAM,                // Gives an object exclusive control of the user's camera
+	RLV_BHVR_SETCAM_AVDISTMIN,		// Enforces a minimum distance from the avatar
+	RLV_BHVR_SETCAM_AVDISTMAX,		// Enforces a maximum distance from the avatar
+	RLV_BHVR_SETCAM_FOCUSDISTMIN,	// Enforces a minimum distance from the camera focus
+	RLV_BHVR_SETCAM_FOCUSDISTMAX,	// Enforces a maximum distance from the camera focus
 	RLV_BHVR_SETCAM_EYEOFFSET,      // Changes the default camera offset
 	RLV_BHVR_SETCAM_FOCUSOFFSET,    // Changes the default camera focus offset
 	RLV_BHVR_SETCAM_FOCUS,			// Forces the camera focus and/or position to a specific object, avatar or position
@@ -204,6 +208,10 @@ enum ERlvBehaviour {
 
 enum ERlvBehaviourModifier
 {
+	RLV_MODIFIER_SETCAM_AVDISTMIN,
+	RLV_MODIFIER_SETCAM_AVDISTMAX,
+	RLV_MODIFIER_SETCAM_FOCUSDISTMIN,
+	RLV_MODIFIER_SETCAM_FOCUSDISTMAX,
 	RLV_MODIFIER_SETCAM_EYEOFFSET,
 	RLV_MODIFIER_SETCAM_FOCUSOFFSET,
 	RLV_MODIFIER_SETCAM_FOVMIN,
diff --git a/indra/newview/rlvhelper.cpp b/indra/newview/rlvhelper.cpp
index 6237653cbe..ffc95be74b 100644
--- a/indra/newview/rlvhelper.cpp
+++ b/indra/newview/rlvhelper.cpp
@@ -161,6 +161,14 @@ RlvBehaviourDictionary::RlvBehaviourDictionary()
 	addEntry(new RlvBehaviourGenericProcessor<RLV_OPTION_NONE>("viewtexture", RLV_BHVR_VIEWTEXTURE));
 	// Camera
 	addEntry(new RlvBehaviourToggleProcessor<RLV_BHVR_SETCAM, RLV_OPTION_NONE>("setcam"));
+	addEntry(new RlvBehaviourGenericProcessor<RLV_OPTION_MODIFIER>("setcam_avdistmin", RLV_BHVR_SETCAM_AVDISTMIN));
+	addModifier(RLV_BHVR_SETCAM_AVDISTMIN, RLV_MODIFIER_SETCAM_AVDISTMIN, new RlvBehaviourModifier(0.0f, true, new RlvBehaviourModifier_CompMax()));
+	addEntry(new RlvBehaviourGenericProcessor<RLV_OPTION_MODIFIER>("setcam_avdistmax", RLV_BHVR_SETCAM_AVDISTMAX));
+	addModifier(RLV_BHVR_SETCAM_AVDISTMAX, RLV_MODIFIER_SETCAM_AVDISTMAX, new RlvBehaviourModifier(F32_MAX, true, new RlvBehaviourModifier_CompMin()));
+	addEntry(new RlvBehaviourGenericProcessor<RLV_OPTION_MODIFIER>("setcam_focusdistmin", RLV_BHVR_SETCAM_FOCUSDISTMIN));
+	addModifier(RLV_BHVR_SETCAM_FOCUSDISTMIN, RLV_MODIFIER_SETCAM_FOCUSDISTMIN, new RlvBehaviourModifier(0.0f, true, new RlvBehaviourModifier_CompMax()));
+	addEntry(new RlvBehaviourGenericProcessor<RLV_OPTION_MODIFIER>("setcam_focusdistmax", RLV_BHVR_SETCAM_FOCUSDISTMAX));
+	addModifier(RLV_BHVR_SETCAM_FOCUSDISTMAX, RLV_MODIFIER_SETCAM_FOCUSDISTMAX, new RlvBehaviourModifier(F32_MAX, true, new RlvBehaviourModifier_CompMin()));
 	addEntry(new RlvBehaviourToggleProcessor<RLV_BHVR_SETCAM_EYEOFFSET, RLV_OPTION_MODIFIER, RlvBehaviourCamEyeFocusOffsetHandler>("setcam_eyeoffset", RlvBehaviourInfo::BHVR_EXPERIMENTAL));
 	addModifier(RLV_BHVR_SETCAM_EYEOFFSET, RLV_MODIFIER_SETCAM_EYEOFFSET, new RlvBehaviourModifierHandler<RLV_MODIFIER_SETCAM_EYEOFFSET>(LLVector3::zero, true, nullptr));
 	addEntry(new RlvBehaviourToggleProcessor<RLV_BHVR_SETCAM_FOCUSOFFSET, RLV_OPTION_MODIFIER, RlvBehaviourCamEyeFocusOffsetHandler>("setcam_focusoffset", RlvBehaviourInfo::BHVR_EXPERIMENTAL));
-- 
GitLab