diff --git a/.gitattributes b/.gitattributes
index 9fe54bf3d886f7405c63311d3655e4729f22654c..218c75152b5ba56295f5ce903164e3157ddda784 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,4 +1,4 @@
-*          text eol=lf
+*          text=auto
 
 # Sources
 *.cpp      text diff=cpp
@@ -13,6 +13,8 @@
 # Documents
 *.txt      text
 *.xml      text
+indra/tools/vstool/README.txt -text
+indra/newview/installers/windows/FILES_ARE_UNICODE_UTF-16LE.txt -text
 
 # Graphics
 *.png      binary diff=exif
diff --git a/.gitignore b/.gitignore
old mode 100755
new mode 100644
index 7d4a125a8a6195fdc5f063b8ffb5f0ba20f8ace3..aae4c00959025ca184891f252fd15900232a8954
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@
 *.bak
 *.diff
 *.orig
+*.patch
 *.pyc
 *.rej
 *.swp
diff --git a/.hgpatchinfo/.RLVa.dep b/.hgpatchinfo/.RLVa.dep
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/.hgpatchinfo/Appearance-Misc.desc b/.hgpatchinfo/Appearance-Misc.desc
deleted file mode 100644
index 551bffc46ca2e6049c89f872b651c4d19391a9ec..0000000000000000000000000000000000000000
--- a/.hgpatchinfo/Appearance-Misc.desc
+++ /dev/null
@@ -1,23 +0,0 @@
-[Appearance/Misc]
-- fixed   : LLAppearanceMgr::filterWearableItems() doesn't properly filter body parts
-- fixed   : LLWearableList::processGetAssetReply() creates multiple LLWearable instances for the same asset UUID
-    -> fix for http://jira.secondlife.com/browse/VWR-20608
-- fixed   : attachments sometimes detach only to instantly get reattached after logon
-- fixed   : Add to/Replace Outfit removes newly worn attachments on completion
-    -> fix for http://jira.secondlife.com/browse/VWR-18512
-- fixed   : "Replace Outfit" isn't available for non-outfit folders that don't contain a full set of body parts (eyes, hair base, skin and shape)
-    -> fix for http://jira.secondlife.com/browse/VWR-23972
-- fixed   : attachments that attach and then instantly detach don't have their COF link removed
-- fixed   : multiple LLWearableHoldingPattern instances lead to "COF corruption"
-- fixed   : get_is_item_worn() shouldn't make the assumption that items in COFs are always worn
-- fixed   : drag-and-drop wear behaviour of an attachment onto self isn't consistant with the drag-and-drop behaviour of wearables
-    -> normal-drop : replace wear
-    -> Ctrl-drop   : add wear
-- fixed   : LLAppearanceMgr::registerAttachment() fails to (re)add a link for worn attachments that aren't linked to in COF at log-on
-- fixed   : LLViewerObject::getAttachmentItemID() sometimes returns the NULL UUID for the avatar's own attachments
-- fixed   : LLAppearanceMgr::updateAppearanceFromCOF() doesn't properly filter items collected from folder links
-    -> create an outfit with a folder link + "Replace Outfit" == wearables that exist in both COF and the linked folder will end up worn multiple times
-- changed : enable "Replace Current Outfit" on the base outfit if it's marked dirty
-- changed : "RenderUnloadedAvatar" no longer affects the user's own avatar
-    -> side-effect of the fix above due to the change to LLVOAvatar::isFullyLoaded()
-- added   : InitialWearablesLoadedSignal signal which is emitted *once* when the initial wearables are loaded
diff --git a/autobuild.xml b/autobuild.xml
index 976d7952505a850b96a5f9f7fdb77f506b1cb4ae..8bfe66a468d8302d599bd189ef76fdbb3318539a 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -2694,9 +2694,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>b35adcf74d22c128045aa87aade74736</string>
+              <string>545954e46a316e469f6b68ecbcb76573</string>
               <key>url</key>
-              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/openjpeg_3p-update-openjpeg/rev/297018/arch/CYGWIN/installer/openjpeg-1.4.297018-windows-297018.tar.bz2</string>
+              <string>http://viewer.catznip.com/downloads/packages/openjpeg-1.5.2.171271050-windows-171271050.tar.bz2</string>
             </map>
             <key>name</key>
             <string>windows</string>
@@ -2706,9 +2706,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>47c09d96ae84fa4518b639cd93ba45b3</string>
+              <string>a68103651741c8b66356153ee5668d6b</string>
               <key>url</key>
-              <string>https://bitbucket.org/NiranV/fmodex/downloads/openjpeg-1.4.201412051021-windows-x64-201412051021.tar.bz2</string>
+              <string>http://viewer.catznip.com/downloads/packages/openjpeg-1.5.2.171271046-windows64-171271046.tar.bz2</string>
             </map>
             <key>name</key>
             <string>windows64</string>
diff --git a/doc/contributions.txt b/doc/contributions.txt
index 76f3056695b7a95f37ecdb40a0fbfed423bf28a9..f07724d363ddac825ad04c7992c0eb2c0f1d834a 100755
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -1519,6 +1519,7 @@ Whirly Fizzle
 	STORM-1930
 	BUG-6659
 	STORM-2078
+	BUG-17349
 Whoops Babii
 	VWR-631
 	VWR-1640
diff --git a/indra/cmake/DirectX.cmake b/indra/cmake/DirectX.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..083a5a3b03369d9ec4162a7cab8135655a84652e
--- /dev/null
+++ b/indra/cmake/DirectX.cmake
@@ -0,0 +1,32 @@
+# -*- cmake -*-
+
+if (WINDOWS)
+  include(FindWindowsSDK)
+
+  get_windowssdk_include_dirs(${WINDOWSSDK_PREFERRED_DIR} WINDOWSSDK_INCLUDE_DIRS)
+  find_path(DIRECTX_INCLUDE_DIR
+      NAMES dxdiag.h
+      PATHS ${WINDOWSSDK_INCLUDE_DIRS})
+  if (DIRECTX_INCLUDE_DIR)
+    include_directories(${DIRECTX_INCLUDE_DIR})
+    if (DIRECTX_FIND_QUIETLY)
+      message(STATUS "Found DirectX include: ${DIRECTX_INCLUDE_DIR}")
+    endif (DIRECTX_FIND_QUIETLY)
+  else (DIRECTX_INCLUDE_DIR)
+    message(FATAL_ERROR "Could not find DirectX SDK Include")
+  endif (DIRECTX_INCLUDE_DIR)
+
+
+  get_windowssdk_library_dirs(${WINDOWSSDK_PREFERRED_DIR} WINDOWSSDK_LIBRARY_DIRS)
+  find_path(DIRECTX_LIBRARY_DIR
+      NAMES dxguid.lib
+      PATHS ${WINDOWSSDK_LIBRARY_DIRS})
+  if (DIRECTX_LIBRARY_DIR)
+    if (DIRECTX_FIND_QUIETLY)
+      message(STATUS "Found DirectX include: ${DIRECTX_LIBRARY_DIR}")
+    endif (DIRECTX_FIND_QUIETLY)
+  else (DIRECTX_LIBRARY_DIR)
+    message(FATAL_ERROR "Could not find DirectX SDK Libraries")
+  endif (DIRECTX_LIBRARY_DIR)
+
+endif (WINDOWS)
diff --git a/indra/llaudio/llaudioengine.cpp b/indra/llaudio/llaudioengine.cpp
index 493a0744652c3792c70b32261ae132d0fcd18497..7d41226b497f5542e1ec3038c5ad2ab39abb2756 100644
--- a/indra/llaudio/llaudioengine.cpp
+++ b/indra/llaudio/llaudioengine.cpp
@@ -112,7 +112,7 @@ void LLAudioEngine::setDefaults()
 }
 
 
-bool LLAudioEngine::init(const S32 num_channels, void* userdata)
+bool LLAudioEngine::init(const S32 num_channels, void* userdata, const std::string &app_title)
 {
 	setDefaults();
 
diff --git a/indra/llaudio/llaudioengine.h b/indra/llaudio/llaudioengine.h
index f1e1b4e308c79a440115011ed64a53c2dfe0343e..97674f15f7a37494baf8da9ef8030a1d389acfb0 100644
--- a/indra/llaudio/llaudioengine.h
+++ b/indra/llaudio/llaudioengine.h
@@ -99,7 +99,7 @@ class LLAudioEngine
 	virtual ~LLAudioEngine();
 
 	// initialization/startup/shutdown
-	virtual bool init(const S32 num_channels, void *userdata);
+	virtual bool init(const S32 num_channels, void *userdata, const std::string &app_title);
 	virtual std::string getDriverName(bool verbose) = 0;
 	virtual void shutdown();
 
diff --git a/indra/llaudio/llaudioengine_fmodstudio.h b/indra/llaudio/llaudioengine_fmodstudio.h
index 16e287cd92db3f7bbe634802ba44d9b4d8ac894c..350a27b4e2a3e91f0232407ff9b0c7d4fe0c6540 100644
--- a/indra/llaudio/llaudioengine_fmodstudio.h
+++ b/indra/llaudio/llaudioengine_fmodstudio.h
@@ -96,8 +96,8 @@ class LLAudioEngine_FMODSTUDIO final : public LLAudioEngine
 class LLAudioChannelFMODSTUDIO final : public LLAudioChannel
 {
 public:
-	LLAudioChannelFMODSTUDIO(FMOD::System *audioengine);
-	virtual ~LLAudioChannelFMODSTUDIO();
+    LLAudioChannelFMODSTUDIO(FMOD::System *audioengine);
+    virtual ~LLAudioChannelFMODSTUDIO();
 
 protected:
 	/*virtual*/ void play() final override;
@@ -121,8 +121,8 @@ class LLAudioChannelFMODSTUDIO final : public LLAudioChannel
 class LLAudioBufferFMODSTUDIO final : public LLAudioBuffer
 {
 public:
-	LLAudioBufferFMODSTUDIO(FMOD::System *audioengine);
-	virtual ~LLAudioBufferFMODSTUDIO();
+    LLAudioBufferFMODSTUDIO(FMOD::System *audioengine);
+    virtual ~LLAudioBufferFMODSTUDIO();
 
 	/*virtual*/ bool loadWAV(const std::string& filename) final override;
 	/*virtual*/ U32 getLength() final override;
diff --git a/indra/llaudio/llaudioengine_openal.cpp b/indra/llaudio/llaudioengine_openal.cpp
index 40600f9be0a9d34eaeefbbce8f5ff20c00fa7f1f..44dc34c7d03ea3db01288f0bdaefbf7cf5fdf12a 100644
--- a/indra/llaudio/llaudioengine_openal.cpp
+++ b/indra/llaudio/llaudioengine_openal.cpp
@@ -48,6 +48,12 @@ LLAudioEngine_OpenAL::LLAudioEngine_OpenAL()
 
 // virtual
 bool LLAudioEngine_OpenAL::init(const S32 num_channels, void* userdata)
+LLAudioEngine_OpenAL::~LLAudioEngine_OpenAL()
+{
+}
+
+// virtual
+bool LLAudioEngine_OpenAL::init(const S32 num_channels, void* userdata, const std::string &app_title)
 {
 	mWindGen = NULL;
 	LLAudioEngine::init(num_channels, userdata);
@@ -234,6 +240,13 @@ bool LLAudioChannelOpenAL::isPlaying()
 
 bool LLAudioChannelOpenAL::updateBuffer()
 {
+    if (!mCurrentSourcep)
+    {
+        // This channel isn't associated with any source, nothing
+        // to be updated
+        return false;
+    }
+
 	if (LLAudioChannel::updateBuffer())
 	{
 		// Base class update returned true, which means that we need to actually
diff --git a/indra/llaudio/llaudioengine_openal.h b/indra/llaudio/llaudioengine_openal.h
index 32b652ffe1be9c9e434363f44355d15796b563b8..b6bd4b16d6cb092cce72e25562ecd5c4615f787e 100644
--- a/indra/llaudio/llaudioengine_openal.h
+++ b/indra/llaudio/llaudioengine_openal.h
@@ -40,8 +40,8 @@ class LLAudioEngine_OpenAL : public LLAudioEngine
 		LLAudioEngine_OpenAL();
 		virtual ~LLAudioEngine_OpenAL() = default;
 
-		virtual bool init(const S32 num_channels, void *user_data);
-        	virtual std::string getDriverName(bool verbose);
+        virtual bool init(const S32 num_channels, void *user_data, const std::string &app_title);
+        virtual std::string getDriverName(bool verbose);
 		virtual void allocateListener();
 
 		virtual void shutdown();
diff --git a/indra/llaudio/lllistener_fmodstudio.h b/indra/llaudio/lllistener_fmodstudio.h
index ebc9f68d2237225347ab4f26e907fea03ec3cc3a..5f183e975e3d32c820b0ffd2d7711b3a7729c0da 100644
--- a/indra/llaudio/lllistener_fmodstudio.h
+++ b/indra/llaudio/lllistener_fmodstudio.h
@@ -3,9 +3,9 @@
  * @brief Description of LISTENER class abstracting the audio support
  * as an FMOD Studio implementation (windows and Linux)
  *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * $LicenseInfo:firstyear=2020&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
+ * Copyright (C) 2020, 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
@@ -39,8 +39,8 @@ namespace FMOD
 //Interfaces
 class LLListener_FMODSTUDIO final : public LLListener
 {
- public:  
-	LLListener_FMODSTUDIO(FMOD::System *system);
+public:
+    LLListener_FMODSTUDIO(FMOD::System *system);
 	virtual ~LLListener_FMODSTUDIO() = default;
 
 	void translate(const LLVector3& offset) final override;
@@ -53,10 +53,10 @@ class LLListener_FMODSTUDIO final : public LLListener
 	F32 getDopplerFactor() final override;
 	void setRolloffFactor(F32 factor) final override;
 	F32 getRolloffFactor() final override;
- protected:
-	 FMOD::System *mSystem;
-	 F32 mDopplerFactor;
-	 F32 mRolloffFactor;
+protected:
+    FMOD::System *mSystem;
+    F32 mDopplerFactor;
+    F32 mRolloffFactor;
 };
 
 #endif
diff --git a/indra/llinventory/llsettingssky.cpp b/indra/llinventory/llsettingssky.cpp
index 306c73292057c05f4da4fe5b2e481d6a8413cdb2..02dcd47a47c95de49ea6e3099d1059e513891828 100644
--- a/indra/llinventory/llsettingssky.cpp
+++ b/indra/llinventory/llsettingssky.cpp
@@ -40,7 +40,7 @@ namespace
     const LLUUID IMG_HALO("12149143-f599-91a7-77ac-b52a3c0f59cd");
 }
 
-namespace {
+//namespace {
     LLQuaternion convert_azimuth_and_altitude_to_quat(F32 azimuth, F32 altitude)
     {
         F32 sinTheta = sin(azimuth);
@@ -64,7 +64,7 @@ namespace {
 
         return quat;
     }
-}
+//}
 
 static LLTrace::BlockTimerStatHandle FTM_BLEND_SKYVALUES("Blending Sky Environment");
 static LLTrace::BlockTimerStatHandle FTM_RECALCULATE_SKYVALUES("Recalculate Sky");
diff --git a/indra/llmath/v3dmath.h b/indra/llmath/v3dmath.h
index 4938273d5bccf66af385b6dde824478da5e9d4b8..61feecc3eee821006c4be14dc59ef4ac3ee51086 100644
--- a/indra/llmath/v3dmath.h
+++ b/indra/llmath/v3dmath.h
@@ -115,6 +115,9 @@ class LLVector3d
 		friend LLVector3d operator*(const F64 k, const LLVector3d& a);				// Return a times scaler k
 		friend bool operator==(const LLVector3d& a, const LLVector3d& b);		// Return a == b
 		friend bool operator!=(const LLVector3d& a, const LLVector3d& b);		// Return a != b
+// [RLVa:KB] - RlvBehaviourModifierCompMin/Max
+		friend bool operator<(const LLVector3 &a, const LLVector3 &b);		// Return a < b
+// [/RLVa:KB]
 
 		friend const LLVector3d& operator+=(LLVector3d& a, const LLVector3d& b);	// Return vector a + b
 		friend const LLVector3d& operator-=(LLVector3d& a, const LLVector3d& b);	// Return vector a minus b
@@ -395,6 +398,17 @@ inline bool operator!=(const LLVector3d& a, const LLVector3d& b)
 			||(a.mdV[2] != b.mdV[2]));
 }
 
+// [RLVa:KB] - RlvBehaviourModifierCompMin/Max
+inline bool operator<(const LLVector3d& lhs, const LLVector3d& rhs)
+{
+	return (lhs.mdV[0] < rhs.mdV[0]
+			|| (lhs.mdV[0] == rhs.mdV[0]
+				&& (lhs.mdV[1] < rhs.mdV[1]
+					|| ((lhs.mdV[1] == rhs.mdV[1])
+						&& lhs.mdV[2] < rhs.mdV[2]))));
+}
+// [/RLVa:KB]
+
 inline const LLVector3d& operator+=(LLVector3d& a, const LLVector3d& b)
 {
 	a.mdV[0] += b.mdV[0];
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp
index a282376e88fbb5456bb7b13b148f8213dccf4ebf..e448eab3a5d9cb49c69683ed35262e2b29d5612f 100644
--- a/indra/llui/llfloater.cpp
+++ b/indra/llui/llfloater.cpp
@@ -1,5 +1,4 @@
 /** 
-
  * @file llfloater.cpp
  * @brief LLFloater base class
  *
diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h
index 63baed679345181f875abebe22f2f5419bc5b78b..7360bd76598799dc17e90fc46f2539c694ee8fe2 100644
--- a/indra/llui/lluictrl.h
+++ b/indra/llui/lluictrl.h
@@ -213,7 +213,8 @@ class LLUICtrl
 
 	virtual void	setColor(const LLColor4& color);
 
-	F32 			getCurrentTransparency();
+	// Ansariel: Changed to virtual. We might want to change the transparency ourself!
+	virtual F32	getCurrentTransparency();
 
 	void				setTransparencyType(ETypeTransparency type);
 	ETypeTransparency	getTransparencyType() const {return mTransparencyType;}
diff --git a/indra/llxml/llcontrol.cpp b/indra/llxml/llcontrol.cpp
index fe5003fbc65e363eb8896826c9e2c0170f64615c..6b9eaa5be5f7bed0cf65c2c854b36986f3a943a3 100644
--- a/indra/llxml/llcontrol.cpp
+++ b/indra/llxml/llcontrol.cpp
@@ -40,6 +40,7 @@
 #include "v4coloru.h"
 #include "v4color.h"
 #include "v3color.h"
+#include "llquaternion.h"
 #include "llrect.h"
 #include "llxmltree.h"
 #include "llsdserialize.h"
@@ -145,6 +146,9 @@ bool LLControlVariable::llsd_compare(const LLSD& a, const LLSD & b)
 	case TYPE_VEC3D:
 		result = LLVector3d(a) == LLVector3d(b);
 		break;
+	case TYPE_QUAT:
+		result = LLQuaternion(a) == LLQuaternion(b);
+		break;
 	case TYPE_RECT:
 		result = LLRect(a) == LLRect(b);
 		break;
@@ -402,6 +406,7 @@ const std::string LLControlGroup::mTypeString[TYPE_COUNT] = { "U32"
                                                              ,"String"
                                                              ,"Vector3"
                                                              ,"Vector3D"
+                                                             ,"Quaternion"
                                                              ,"Rect"
                                                              ,"Color4"
                                                              ,"Color3"
@@ -581,6 +586,11 @@ LLControlVariable* LLControlGroup::declareVec3d(const std::string& name, const L
 	return declareControl(name, TYPE_VEC3D, initial_val.getValue(), comment, persist);
 }
 
+LLControlVariable* LLControlGroup::declareQuat(const std::string& name, const LLQuaternion &initial_val, const std::string& comment, LLControlVariable::ePersist persist)
+{
+	return declareControl(name, TYPE_QUAT, initial_val.getValue(), comment, persist);
+}
+
 LLControlVariable* LLControlGroup::declareRect(const std::string& name, const LLRect &initial_val, const std::string& comment, LLControlVariable::ePersist persist)
 {
 	return declareControl(name, TYPE_RECT, initial_val.getValue(), comment, persist);
@@ -670,6 +680,11 @@ LLVector3d LLControlGroup::getVector3d(const std::string& name)
 	return get<LLVector3d>(name);
 }
 
+LLQuaternion LLControlGroup::getQuaternion(const std::string& name)
+{
+	return get<LLQuaternion>(name);
+}
+
 LLRect LLControlGroup::getRect(const std::string& name)
 {
 	return get<LLRect>(name);
@@ -759,6 +774,11 @@ void LLControlGroup::setVector3d(const std::string& name, const LLVector3d &val)
 	set(name, val);
 }
 
+void LLControlGroup::setQuaternion(const std::string& name, const LLQuaternion &val)
+{
+	set(name, val);
+}
+
 void LLControlGroup::setRect(const std::string& name, const LLRect &val)
 {
 	set(name, val);
@@ -953,6 +973,16 @@ U32 LLControlGroup::loadFromFileLegacy(const std::string& filename, BOOL require
 				validitems++;
 			}
 			break;
+		case TYPE_QUAT:
+			{
+				LLQuaternion quat;
+
+				child_nodep->getAttributeQuat("value", quat);
+
+				control->set(quat.getValue());
+				validitems++;
+			}
+			break;
 		case TYPE_RECT:
 			{
 				//RN: hack to support reading rectangles from a string
@@ -1512,6 +1542,11 @@ template <> eControlType get_control_type<LLVector3d>()
 	return TYPE_VEC3D; 
 }
 
+template <> eControlType get_control_type<LLQuaternion>()
+{
+	return TYPE_QUAT;
+}
+
 template <> eControlType get_control_type<LLRect>() 
 { 
 	return TYPE_RECT; 
@@ -1559,6 +1594,10 @@ template <> LLSD convert_to_llsd<LLVector3d>(const LLVector3d& in)
 { 
 	return in.getValue(); 
 }
+template <> LLSD convert_to_llsd<LLQuaternion>(const LLQuaternion& in)
+{
+	return in.getValue();
+}
 
 template <> LLSD convert_to_llsd<LLRect>(const LLRect& in) 
 { 
@@ -1683,6 +1722,18 @@ LLVector3d convert_from_llsd<LLVector3d>(const LLSD& sd, eControlType type, cons
 	}
 }
 
+template<>
+LLQuaternion convert_from_llsd<LLQuaternion>(const LLSD& sd, eControlType type, const std::string& control_name)
+{
+	if (type == TYPE_QUAT)
+		return (LLQuaternion)sd;
+	else
+	{
+		CONTROL_ERRS << "Invalid LLQuaternion value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL;
+		return LLQuaternion();
+	}
+}
+
 template<>
 LLRect convert_from_llsd<LLRect>(const LLSD& sd, eControlType type, const std::string& control_name)
 {
diff --git a/indra/llxml/llcontrol.h b/indra/llxml/llcontrol.h
index 75f6765d31db18771c480abe63e62b9a29a98bee..dd938939a31f974f7caaea2506ebe23f0cfdb6ad 100644
--- a/indra/llxml/llcontrol.h
+++ b/indra/llxml/llcontrol.h
@@ -67,6 +67,7 @@
 
 class LLVector3;
 class LLVector3d;
+class LLQuaternion;
 class LLColor4;
 class LLColor3;
 //BD - Vector2
@@ -84,6 +85,7 @@ typedef enum e_control_type
 	TYPE_STRING,
 	TYPE_VEC3,
 	TYPE_VEC3D,
+	TYPE_QUAT,
 	TYPE_RECT,
 	TYPE_COL4,
 	TYPE_COL3,
@@ -272,6 +274,7 @@ class LLControlGroup : public LLInstanceTracker<LLControlGroup, std::string>
 	LLControlVariable* declareString(const std::string& name, const std::string &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
 	LLControlVariable* declareVec3(const std::string& name, const LLVector3 &initial_val,const std::string& comment,  LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
 	LLControlVariable* declareVec3d(const std::string& name, const LLVector3d &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
+	LLControlVariable* declareQuat(const std::string& name, const LLQuaternion &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
 	LLControlVariable* declareRect(const std::string& name, const LLRect &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
 	LLControlVariable* declareColor4(const std::string& name, const LLColor4 &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
 	LLControlVariable* declareColor3(const std::string& name, const LLColor3 &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
@@ -292,10 +295,10 @@ class LLControlGroup : public LLInstanceTracker<LLControlGroup, std::string>
 	
 	LLWString	getWString(const std::string& name);
 	LLVector3	getVector3(const std::string& name);
-	LLVector3d	getVector3d(const std::string& name);
+	LLVector3d	getVector3d(const std::string& name);	
 	LLRect		getRect(const std::string& name);
 	LLSD        getLLSD(const std::string& name);
-
+	LLQuaternion	getQuaternion(const std::string& name);
 
 	LLColor4	getColor(const std::string& name);
 	LLColor4	getColor4(const std::string& name);
@@ -333,6 +336,7 @@ class LLControlGroup : public LLInstanceTracker<LLControlGroup, std::string>
 	void	setString(const std::string&  name, const std::string& val);
 	void	setVector3(const std::string& name, const LLVector3 &val);
 	void	setVector3d(const std::string& name, const LLVector3d &val);
+	void	setQuaternion(const std::string& name, const LLQuaternion &val);
 	void	setRect(const std::string& name, const LLRect &val);
 	void	setColor4(const std::string& name, const LLColor4 &val);
 	void    setLLSD(const std::string& name, const LLSD& val);
@@ -511,7 +515,8 @@ template <> eControlType get_control_type<bool>();
 //template <> eControlType get_control_type<BOOL> () 
 template <> eControlType get_control_type<std::string>();
 template <> eControlType get_control_type<LLVector3>();
-template <> eControlType get_control_type<LLVector3d>(); 
+template <> eControlType get_control_type<LLVector3d>();
+template <> eControlType get_control_type<LLQuaternion>();
 template <> eControlType get_control_type<LLRect>();
 template <> eControlType get_control_type<LLColor4>();
 template <> eControlType get_control_type<LLColor3>();
@@ -524,7 +529,8 @@ template <> eControlType get_control_type<LLVector4>();
 
 template <> LLSD convert_to_llsd<U32>(const U32& in);
 template <> LLSD convert_to_llsd<LLVector3>(const LLVector3& in);
-template <> LLSD convert_to_llsd<LLVector3d>(const LLVector3d& in); 
+template <> LLSD convert_to_llsd<LLVector3d>(const LLVector3d& in);
+template <> LLSD convert_to_llsd<LLQuaternion>(const LLQuaternion& in);
 template <> LLSD convert_to_llsd<LLRect>(const LLRect& in);
 template <> LLSD convert_to_llsd<LLColor4>(const LLColor4& in);
 template <> LLSD convert_to_llsd<LLColor3>(const LLColor3& in);
@@ -538,6 +544,7 @@ template<> std::string convert_from_llsd<std::string>(const LLSD& sd, eControlTy
 template<> LLWString convert_from_llsd<LLWString>(const LLSD& sd, eControlType type, const std::string& control_name);
 template<> LLVector3 convert_from_llsd<LLVector3>(const LLSD& sd, eControlType type, const std::string& control_name);
 template<> LLVector3d convert_from_llsd<LLVector3d>(const LLSD& sd, eControlType type, const std::string& control_name);
+template<> LLQuaternion convert_from_llsd<LLQuaternion>(const LLSD& sd, eControlType type, const std::string& control_name);
 template<> LLRect convert_from_llsd<LLRect>(const LLSD& sd, eControlType type, const std::string& control_name);
 template<> bool convert_from_llsd<bool>(const LLSD& sd, eControlType type, const std::string& control_name);
 template<> S32 convert_from_llsd<S32>(const LLSD& sd, eControlType type, const std::string& control_name);
diff --git a/indra/llxml/llcontrolgroupreader.h b/indra/llxml/llcontrolgroupreader.h
index 95bbcb671a61ded2cc6bc3c507060aaee15f1fda..195a6962d7cfebbefca1fd900b50f65a34a38bfe 100644
--- a/indra/llxml/llcontrolgroupreader.h
+++ b/indra/llxml/llcontrolgroupreader.h
@@ -74,6 +74,7 @@ class LLControlGroupReader
 	virtual void		setString(const std::string&  name, const std::string& val) {}
 	virtual void		setVector3(const std::string& name, const LLVector3 &val) {}
 	virtual void		setVector3d(const std::string& name, const LLVector3d &val) {}
+	virtual void		setQuaternion(const std::string& name, const LLQuaternion &val) {}
 	virtual void		setRect(const std::string& name, const LLRect &val) {}
 	virtual void		setColor4(const std::string& name, const LLColor4 &val) {}
 	virtual void    	setLLSD(const std::string& name, const LLSD& val) {}
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index b7534b5df77c15c34eb17dfb90008edee438f331..e3d7d2bc91a01bbfcd54a878ce2e9ce9c4096cf2 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -744,6 +744,7 @@ set(viewer_SOURCE_FILES
     noise.cpp
     pipeline.cpp
     rlvactions.cpp
+    rlvenvironment.cpp
     rlvhandler.cpp
     rlvhelper.cpp
     rlvcommon.cpp
@@ -1392,6 +1393,7 @@ set(viewer_HEADER_FILES
     noise.h
     pipeline.h
     rlvactions.h
+    rlvenvironment.h
     rlvdefines.h
     rlvhandler.h
     rlvhelper.h
diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt
index a4c853ea2ea69dbd548168a6823f26d772f27f2c..133cad286fdf619d1cbbb26931c01f4e180b36a4 100644
--- a/indra/newview/VIEWER_VERSION.txt
+++ b/indra/newview/VIEWER_VERSION.txt
@@ -1 +1 @@
-6.4.2
+6.4.3
diff --git a/indra/newview/app_settings/camera/Front.xml b/indra/newview/app_settings/camera/Front.xml
new file mode 100644
index 0000000000000000000000000000000000000000..39f44e11a841a8c420096924cfaf5529ea8e109a
--- /dev/null
+++ b/indra/newview/app_settings/camera/Front.xml
@@ -0,0 +1,142 @@
+<llsd>
+    <map>
+    <key>AppearanceCameraMovement</key>
+        <map>
+        <key>Comment</key>
+            <string>When entering appearance editing mode, camera zooms in on currently selected portion of avatar</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>Boolean</string>
+        <key>Value</key>
+            <integer>1</integer>
+        </map>
+    <key>AvatarSitRotation</key>
+    <map>
+        <key>Comment</key>
+        <string>Avatar real sitting rotation used in preset</string>
+        <key>Persist</key>
+        <integer>1</integer>
+        <key>Type</key>
+        <string>LLSD</string>
+        <key>Value</key>
+        <array>
+          <real>0</real>
+          <real>0</real>
+          <real>0</real>
+          <real>1</real>
+        </array>
+    </map>
+    <key>CameraAngle</key>
+        <map>
+        <key>Comment</key>
+            <string>Camera field of view angle (Radians)</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>F32</string>
+        <key>Value</key>
+            <real>1.047197551</real>
+        </map>
+    <key>CameraOffsetBuild</key>
+        <map>
+        <key>Comment</key>
+            <string>Default camera position relative to focus point when entering build mode</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>Vector3</string>
+        <key>Value</key>
+            <array>
+                <real>-6</real>
+                <real>0</real>
+                <real>6</real>
+            </array>
+        </map>
+    <key>CameraOffsetRearView</key>
+        <map>
+        <key>Comment</key>
+            <string>Initial camera offset from avatar in Front View</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>Vector3</string>
+        <key>Value</key>
+            <array>
+                <real>2.2</real>
+                <real>0.0</real>
+                <real>0.0</real>
+            </array>
+        </map>
+    <key>CameraOffsetScale</key>
+        <map>
+        <key>Comment</key>
+            <string>Scales the default offset</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>F32</string>
+        <key>Value</key>
+            <real>1</real>
+        </map>
+    <key>CameraZoomFraction</key>
+        <map>
+        <key>Comment</key>
+            <string>Mousewheel driven fraction of zoom</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>F32</string>
+        <key>Value</key>
+            <real>0.90322577953338623</real>
+        </map>
+    <key>EditCameraMovement</key>
+        <map>
+        <key>Comment</key>
+            <string>When entering build mode, camera moves up above avatar</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>Boolean</string>
+        <key>Value</key>
+            <integer>0</integer>
+        </map>
+    <key>FocusOffsetRearView</key>
+        <map>
+        <key>Comment</key>
+            <string>Initial focus point offset relative to avatar for the camera preset Front View (x-axis is forward)</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>Vector3D</string>
+        <key>Value</key>
+            <array>
+                <real>0.0</real>
+                <real>0.0</real>
+                <real>0.0</real>
+            </array>
+        </map>
+    <key>PresetCameraActive</key>
+        <map>
+        <key>Comment</key>
+            <string>Name of currently selected preference</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>String</string>
+        <key>Value</key>
+            <string>Default</string>
+        </map>
+    <key>TrackFocusObject</key>
+        <map>
+        <key>Comment</key>
+            <string>Camera tracks last object zoomed on</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>Boolean</string>
+        <key>Value</key>
+            <integer>1</integer>
+        </map>
+    </map>
+</llsd>
diff --git a/indra/newview/app_settings/camera/Rear.xml b/indra/newview/app_settings/camera/Rear.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8dc36353ce20a3ea3eaa6405c45c07b043f33956
--- /dev/null
+++ b/indra/newview/app_settings/camera/Rear.xml
@@ -0,0 +1,142 @@
+<llsd>
+    <map>
+    <key>AppearanceCameraMovement</key>
+        <map>
+        <key>Comment</key>
+            <string>When entering appearance editing mode, camera zooms in on currently selected portion of avatar</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>Boolean</string>
+        <key>Value</key>
+            <integer>1</integer>
+        </map>
+    <key>AvatarSitRotation</key>
+    <map>
+        <key>Comment</key>
+        <string>Avatar real sitting rotation used in preset</string>
+        <key>Persist</key>
+        <integer>1</integer>
+        <key>Type</key>
+        <string>LLSD</string>
+        <key>Value</key>
+        <array>
+          <real>0</real>
+          <real>0</real>
+          <real>0</real>
+          <real>1</real>
+        </array>
+    </map>
+    <key>CameraAngle</key>
+        <map>
+        <key>Comment</key>
+            <string>Camera field of view angle (Radians)</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>F32</string>
+        <key>Value</key>
+            <real>1.047197551</real>
+        </map>
+    <key>CameraOffsetBuild</key>
+        <map>
+        <key>Comment</key>
+            <string>Default camera position relative to focus point when entering build mode</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>Vector3</string>
+        <key>Value</key>
+            <array>
+                <real>-6</real>
+                <real>0</real>
+                <real>6</real>
+            </array>
+        </map>
+    <key>CameraOffsetRearView</key>
+        <map>
+        <key>Comment</key>
+            <string>Initial camera offset from avatar in Rear View</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>Vector3</string>
+        <key>Value</key>
+            <array>
+                <real>-3</real>
+                <real>0</real>
+                <real>0.75</real>
+            </array>
+        </map>
+    <key>CameraOffsetScale</key>
+        <map>
+        <key>Comment</key>
+            <string>Scales the default offset</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>F32</string>
+        <key>Value</key>
+            <real>1</real>
+        </map>
+    <key>CameraZoomFraction</key>
+        <map>
+        <key>Comment</key>
+            <string>Mousewheel driven fraction of zoom</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>F32</string>
+        <key>Value</key>
+            <real>0.90322577953338623</real>
+        </map>
+    <key>EditCameraMovement</key>
+        <map>
+        <key>Comment</key>
+            <string>When entering build mode, camera moves up above avatar</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>Boolean</string>
+        <key>Value</key>
+            <integer>0</integer>
+        </map>
+    <key>FocusOffsetRearView</key>
+        <map>
+        <key>Comment</key>
+            <string>Initial focus point offset relative to avatar for the camera preset Rear View (x-axis is forward)</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>Vector3D</string>
+        <key>Value</key>
+            <array>
+                <real>1.0</real>
+                <real>0.0</real>
+                <real>1.0</real>
+            </array>
+        </map>
+    <key>PresetCameraActive</key>
+        <map>
+        <key>Comment</key>
+            <string>Name of currently selected preference</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>String</string>
+        <key>Value</key>
+            <string>Default</string>
+        </map>
+    <key>TrackFocusObject</key>
+        <map>
+        <key>Comment</key>
+            <string>Camera tracks last object zoomed on</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>Boolean</string>
+        <key>Value</key>
+            <integer>1</integer>
+        </map>
+    </map>
+</llsd>
diff --git a/indra/newview/app_settings/camera/Side.xml b/indra/newview/app_settings/camera/Side.xml
new file mode 100644
index 0000000000000000000000000000000000000000..089ab93a8f67d3033b5a83586cf22d5441d4dbf9
--- /dev/null
+++ b/indra/newview/app_settings/camera/Side.xml
@@ -0,0 +1,142 @@
+<llsd>
+    <map>
+    <key>AppearanceCameraMovement</key>
+        <map>
+        <key>Comment</key>
+            <string>When entering appearance editing mode, camera zooms in on currently selected portion of avatar</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>Boolean</string>
+        <key>Value</key>
+            <integer>1</integer>
+        </map>
+    <key>AvatarSitRotation</key>
+    <map>
+        <key>Comment</key>
+        <string>Avatar real sitting rotation used in preset</string>
+        <key>Persist</key>
+        <integer>1</integer>
+        <key>Type</key>
+        <string>LLSD</string>
+        <key>Value</key>
+        <array>
+          <real>0</real>
+          <real>0</real>
+          <real>0</real>
+          <real>1</real>
+        </array>
+    </map>
+    <key>CameraAngle</key>
+        <map>
+        <key>Comment</key>
+            <string>Camera field of view angle (Radians)</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>F32</string>
+        <key>Value</key>
+            <real>1.047197551</real>
+        </map>
+    <key>CameraOffsetBuild</key>
+        <map>
+        <key>Comment</key>
+            <string>Default camera position relative to focus point when entering build mode</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>Vector3</string>
+        <key>Value</key>
+            <array>
+                <real>-6</real>
+                <real>0</real>
+                <real>6</real>
+            </array>
+        </map>
+    <key>CameraOffsetRearView</key>
+        <map>
+        <key>Comment</key>
+            <string>Initial camera offset from avatar in Side View</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>Vector3</string>
+        <key>Value</key>
+            <array>
+                <real>-1.0</real>
+                <real>0.7</real>
+                <real>0.5</real>
+            </array>
+        </map>
+    <key>CameraOffsetScale</key>
+        <map>
+        <key>Comment</key>
+            <string>Scales the default offset</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>F32</string>
+        <key>Value</key>
+            <real>1</real>
+        </map>
+    <key>CameraZoomFraction</key>
+        <map>
+        <key>Comment</key>
+            <string>Mousewheel driven fraction of zoom</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>F32</string>
+        <key>Value</key>
+            <real>0.90322577953338623</real>
+        </map>
+    <key>EditCameraMovement</key>
+        <map>
+        <key>Comment</key>
+            <string>When entering build mode, camera moves up above avatar</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>Boolean</string>
+        <key>Value</key>
+            <integer>0</integer>
+        </map>
+    <key>FocusOffsetRearView</key>
+        <map>
+        <key>Comment</key>
+            <string>Initial focus point offset relative to avatar for the camera preset Side View (x-axis is forward)</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>Vector3D</string>
+        <key>Value</key>
+            <array>
+                <real>1.5</real>
+                <real>0.7</real>
+                <real>1.0</real>
+            </array>
+        </map>
+    <key>PresetCameraActive</key>
+        <map>
+        <key>Comment</key>
+            <string>Name of currently selected preference</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>String</string>
+        <key>Value</key>
+            <string>Default</string>
+        </map>
+    <key>TrackFocusObject</key>
+        <map>
+        <key>Comment</key>
+            <string>Camera tracks last object zoomed on</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>Boolean</string>
+        <key>Value</key>
+            <integer>1</integer>
+        </map>
+    </map>
+</llsd>
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 2768f5bc3880b13b70216cb04ecbfe0a0ea4900e..818a3502970233915c3d86fb80d56388da357a66 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -1564,6 +1564,17 @@
       <key>Value</key>
       <real>1.0</real>
     </map>
+    <key>CameraZoomFraction</key>
+    <map>
+      <key>Comment</key>
+      <string>Mousewheel driven fraction of zoom</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>F32</string>
+      <key>Value</key>
+      <real>0.9</real>
+    </map>
     <key>CameraPosOnLogout</key>
     <map>
       <key>Comment</key>
@@ -16433,7 +16444,7 @@
   <key>FMODProfilerEnable</key>
   <map>
     <key>Comment</key>
-    <string>Enable profiler tool if using FMOD Ex</string>
+    <string>Enable profiler tool if using FMOD Ex or FMOD Studio</string>
     <key>Persist</key>
     <integer>1</integer>
     <key>Type</key>
@@ -16444,7 +16455,7 @@
   <key>FMODDecodeBufferSize</key>
   <map>
     <key>Comment</key>
-    <string>Sets the streaming decode buffer size (in milliseconds)</string>
+    <string>Sets the streaming decode buffer size (in milliseconds) for FMOD Ex or FMOD Studio</string>
     <key>Persist</key>
     <integer>1</integer>
     <key>Type</key>
@@ -16466,7 +16477,7 @@
   <key>FMODStreamBufferSize</key>
   <map>
     <key>Comment</key>
-    <string>Sets the streaming buffer size (in milliseconds)</string>
+    <string>Sets the streaming buffer size (in milliseconds) for FMOD Ex or FMOD Studio</string>
     <key>Persist</key>
     <integer>1</integer>
     <key>Type</key>
@@ -16609,6 +16620,17 @@
       <key>Value</key>
       <integer>0</integer>
     </map>
+    <key>HoverHeightAffectsCamera</key>
+    <map>
+      <key>Comment</key>
+      <string>Camera view is affected by Hover Height setting</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <integer>1</integer>
+     </map>
     <key>IndirectMaxComplexity</key>
     <map>
       <key>Comment</key>
diff --git a/indra/newview/app_settings/settings_rlva.xml b/indra/newview/app_settings/settings_rlva.xml
index b462fb6c99719dadb4d1d101a35f0dabf893754d..6c55efaba07771bea7d89589603cd3c3bedddc80 100644
--- a/indra/newview/app_settings/settings_rlva.xml
+++ b/indra/newview/app_settings/settings_rlva.xml
@@ -4,93 +4,93 @@
 <map>
   <!-- RLVa Debugs -->
 	<key>RestrainedLove</key>
-	<map>
-		<key>Comment</key>
-		<string>Toggles RLVa features (requires restart)</string>
-		<key>Persist</key>
-		<integer>1</integer>
-		<key>Type</key>
-		<string>Boolean</string>
-		<key>Value</key>
-		<boolean>1</boolean>
-	</map>
-	<key>RestrainedLoveDebug</key>
-	<map>
-		<key>Comment</key>
-		<string>Toggles RLVa debug mode (displays the commands when in debug mode)</string>
-		<key>Persist</key>
-		<integer>1</integer>
-		<key>Type</key>
-		<string>Boolean</string>
-		<key>Value</key>
-		<boolean>0</boolean>
-	</map>
-	<key>RestrainedLoveCanOOC</key>
-	<map>
-		<key>Comment</key>
-		<string>Allows sending OOC chat when send chat restricted, or seeing OOC chat when receive chat restricted</string>
-		<key>Persist</key>
-		<integer>1</integer>
-		<key>Type</key>
-		<string>Boolean</string>
-		<key>Value</key>
-		<boolean>1</boolean>
-	</map>
-	<key>RestrainedLoveForbidGiveToRLV</key>
-	<map>
-		<key>Comment</key>
-		<string>When TRUE, forbids to give sub-folders to the #RLV folder</string>
-		<key>Persist</key>
-		<integer>1</integer>
-		<key>Type</key>
-		<string>Boolean</string>
-		<key>Value</key>
-		<boolean>0</boolean>
-	</map>
-	<key>RestrainedLoveNoSetEnv</key>
-	<map>
-		<key>Comment</key>
-		<string>When TRUE, forbids to set the environment (time of day and Windlight settings) via RLVa. (requires restart)</string>
-		<key>Persist</key>
-		<integer>1</integer>
-		<key>Type</key>
-		<string>Boolean</string>
-		<key>Value</key>
-		<boolean>0</boolean>
-	</map>
-	<key>RestrainedLoveReplaceWhenFolderBeginsWith</key>
-	<map>
-		<key>Comment</key>
-		<string>If a folder name begins with this string, its attach behavior will always be "replace", never "stack". Default is blank (disabled).</string>
-		<key>Persist</key>
-		<integer>1</integer>
-		<key>Type</key>
-		<string>String</string>
-		<key>Value</key>
-		<string></string>
-	</map>
-	<key>RestrainedLoveShowEllipsis</key>
-	<map>
-		<key>Comment</key>
-		<string>When TRUE, show "..." when someone speaks, while the avatar is prevented from hearing. When FALSE, don't show anything.</string>
-		<key>Persist</key>
-		<integer>1</integer>
-		<key>Type</key>
-		<string>Boolean</string>
-		<key>Value</key>
-		<boolean>1</boolean>
-	</map>
-	<key>RestrainedLoveStackWhenFolderBeginsWith</key>
-	<map>
-		<key>Comment</key>
-		<string>If a folder name begins with this string, its attach behavior will always be "stack", never "replace". Default is "+".</string>
-		<key>Persist</key>
-		<integer>1</integer>
-		<key>Type</key>
-		<string>String</string>
-		<key>Value</key>
-		<string>+</string>
-	</map>
+    <map>
+      <key>Comment</key>
+      <string>Toggles RLVa features (requires restart)</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <boolean>1</boolean>
+    </map>
+    <key>RestrainedLoveDebug</key>
+    <map>
+      <key>Comment</key>
+      <string>Toggles RLVa debug mode (displays the commands when in debug mode)</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <boolean>0</boolean>
+    </map>
+    <key>RestrainedLoveCanOOC</key>
+    <map>
+      <key>Comment</key>
+      <string>Allows sending OOC chat when send chat restricted, or seeing OOC chat when receive chat restricted</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <boolean>1</boolean>
+    </map>
+    <key>RestrainedLoveForbidGiveToRLV</key>
+    <map>
+      <key>Comment</key>
+      <string>When TRUE, forbids to give sub-folders to the #RLV folder</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <boolean>0</boolean>
+    </map>
+    <key>RestrainedLoveNoSetEnv</key>
+    <map>
+      <key>Comment</key>
+      <string>When TRUE, forbids to set the environment (time of day and Windlight settings) via RLVa. (requires restart)</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <boolean>0</boolean>
+    </map>
+    <key>RestrainedLoveReplaceWhenFolderBeginsWith</key>
+    <map>
+      <key>Comment</key>
+      <string>If a folder name begins with this string, its attach behavior will always be "replace", never "stack". Default is blank (disabled).</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>String</string>
+      <key>Value</key>
+      <string></string>
+    </map>
+    <key>RestrainedLoveShowEllipsis</key>
+    <map>
+      <key>Comment</key>
+      <string>When TRUE, show "..." when someone speaks, while the avatar is prevented from hearing. When FALSE, don't show anything.</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <boolean>1</boolean>
+    </map>
+    <key>RestrainedLoveStackWhenFolderBeginsWith</key>
+    <map>
+      <key>Comment</key>
+      <string>If a folder name begins with this string, its attach behavior will always be "stack", never "replace". Default is "+".</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>String</string>
+      <key>Value</key>
+      <string>+</string>
+    </map>
     <key>RLVaBlockedExperiences</key>
     <map>
       <key>Comment</key>
@@ -102,105 +102,105 @@
       <key>Value</key>
       <string>bfe25fb4-222c-11e5-85a2-fa4c4ccaa202</string>
     </map>
-	<key>RLVaCompatibilityModeList</key>
-	<map>
-		<key>Comment</key>
-		<string>Contains a list of creators or partial items names that require compatibility mode handling (see wiki for more information and syntax)</string>
-		<key>Persist</key>
-		<integer>1</integer>
-		<key>Type</key>
-		<string>String</string>
-		<key>Value</key>
-		<string></string>
-	</map>
-	<key>RLVaDebugDeprecateExplicitPoint</key>
-	<map>
-		<key>Comment</key>
-		<string>Ignore attachment point names on inventory items and categories (incomplete)</string>
-		<key>Persist</key>
-		<integer>1</integer>
-		<key>Type</key>
-		<string>Boolean</string>
-		<key>Value</key>
-		<boolean>0</boolean>
-	</map>
-	<key>RLVaDebugHideUnsetDuplicate</key>
-	<map>
-		<key>Comment</key>
-		<string>Suppresses reporting "unset" or "duplicate" command restrictions when RestrainedLoveDebug is TRUE</string>
-		<key>Persist</key>
-		<integer>1</integer>
-		<key>Type</key>
-		<string>Boolean</string>
-		<key>Value</key>
-		<boolean>0</boolean>
-	</map>
-	<key>RLVaEnableCompositeFolders</key>
-	<map>
-		<key>Comment</key>
-		<string>Enables composite folders for shared inventory</string>
-		<key>Persist</key>
-		<integer>1</integer>
-		<key>Type</key>
-		<string>Boolean</string>
-		<key>Value</key>
-		<boolean>0</boolean>
-	</map>
-	<key>RLVaEnableIMQuery</key>
-	<map>
-		<key>Comment</key>
-		<string>Enables a limited number of configuration queries via IM (e.g. @version)</string>
-		<key>Persist</key>
-		<integer>1</integer>
-		<key>Type</key>
-		<string>Boolean</string>
-		<key>Value</key>
-		<boolean>1</boolean>
-	</map>
-	<key>RLVaEnableLegacyNaming</key>
-	<map>
-		<key>Comment</key>
-		<string>Enables legacy naming convention for folders</string>
-		<key>Persist</key>
-		<integer>1</integer>
-		<key>Type</key>
-		<string>Boolean</string>
-		<key>Value</key>
-		<boolean>1</boolean>
-	</map>
-	<key>RLVaEnableSharedWear</key>
-	<map>
-		<key>Comment</key>
-		<string>Attachments in the shared #RLV folder can be force-attached without needing to specify an attachment point</string>
-		<key>Persist</key>
-		<integer>1</integer>
-		<key>Type</key>
-		<string>Boolean</string>
-		<key>Value</key>
-		<boolean>1</boolean>
-	</map>
-	<key>RLVaEnableTemporaryAttachments</key>
-	<map>
-		<key>Comment</key>
-		<string>Allows temporary attachments (regardless of origin) to issue RLV commands</string>
-		<key>Persist</key>
-		<integer>1</integer>
-		<key>Type</key>
-		<string>Boolean</string>
-		<key>Value</key>
-		<boolean>1</boolean>
-	</map>
-	<key>RLVaExperimentalCommands</key>
-	<map>
-		<key>Comment</key>
-		<string>Enables the experimental command set</string>
-		<key>Persist</key>
-		<integer>1</integer>
-		<key>Type</key>
-		<string>Boolean</string>
-		<key>Value</key>
-		<boolean>1</boolean>
-	</map>
+    <key>RLVaCompatibilityModeList</key>
+    <map>
+      <key>Comment</key>
+      <string>Contains a list of creators or partial items names that require compatibility mode handling (see wiki for more information and syntax)</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>String</string>
+      <key>Value</key>
+      <string></string>
+    </map>
+    <key>RLVaDebugDeprecateExplicitPoint</key>
+    <map>
+      <key>Comment</key>
+      <string>Ignore attachment point names on inventory items and categories (incomplete)</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <boolean>0</boolean>
+    </map>
+    <key>RLVaDebugHideUnsetDuplicate</key>
+    <map>
+      <key>Comment</key>
+      <string>Suppresses reporting "unset" or "duplicate" command restrictions when RestrainedLoveDebug is TRUE</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <boolean>0</boolean>
+    </map>
+    <key>RLVaEnableCompositeFolders</key>
+    <map>
+      <key>Comment</key>
+      <string>Enables composite folders for shared inventory</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <boolean>0</boolean>
+    </map>
+    <key>RLVaEnableIMQuery</key>
+    <map>
+      <key>Comment</key>
+      <string>Enables a limited number of configuration queries via IM (e.g. @version)</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <boolean>1</boolean>
+    </map>
+    <key>RLVaEnableLegacyNaming</key>
+    <map>
+      <key>Comment</key>
+      <string>Enables legacy naming convention for folders</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <boolean>1</boolean>
+    </map>
+    <key>RLVaEnableSharedWear</key>
+    <map>
+      <key>Comment</key>
+      <string>Attachments in the shared #RLV folder can be force-attached without needing to specify an attachment point</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <boolean>1</boolean>
+    </map>
+    <key>RLVaEnableTemporaryAttachments</key>
+    <map>
+      <key>Comment</key>
+      <string>Allows temporary attachments (regardless of origin) to issue RLV commands</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <boolean>1</boolean>
+    </map>
+    <key>RLVaExperimentalCommands</key>
+    <map>
+      <key>Comment</key>
+      <string>Enables the experimental command set</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <boolean>1</boolean>
+    </map>
     <key>RLVaExperienceMaturityThreshold</key>
     <map>
       <key>Comment</key>
@@ -212,83 +212,83 @@
       <key>Value</key>
       <integer>2</integer>
     </map>
-	<key>RLVaHideLockedLayers</key>
-	<map>
-		<key>Comment</key>
-		<string>Hides "remove outfit" restricted worn clothing layers from @getoufit</string>
-		<key>Persist</key>
-		<integer>1</integer>
-		<key>Type</key>
-		<string>Boolean</string>
-		<key>Value</key>
-		<boolean>0</boolean>
-	</map>
-	<key>RLVaHideLockedAttachments</key>
-	<map>
-		<key>Comment</key>
-		<string>Hides non-detachable worn attachments from @getattach</string>
-		<key>Persist</key>
-		<integer>1</integer>
-		<key>Type</key>
-		<string>Boolean</string>
-		<key>Value</key>
-		<boolean>0</boolean>
-	</map>
-	<key>RLVaSharedInvAutoRename</key>
-	<map>
-		<key>Comment</key>
-		<string>Automatically renames shared inventory items when worn</string>
-		<key>Persist</key>
-		<integer>1</integer>
-		<key>Type</key>
-		<string>Boolean</string>
-		<key>Value</key>
-		<boolean>1</boolean>
-	</map>
-	<key>RLVaShowAssertionFailures</key>
-	<map>
-		<key>Comment</key>
-		<string>Notify the user when an assertion fails</string>
-		<key>Persist</key>
-		<integer>1</integer>
-		<key>Type</key>
-		<string>Boolean</string>
-		<key>Value</key>
-		<boolean>1</boolean>
-	</map>
+    <key>RLVaHideLockedLayers</key>
+    <map>
+      <key>Comment</key>
+      <string>Hides "remove outfit" restricted worn clothing layers from @getoufit</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <boolean>0</boolean>
+    </map>
+    <key>RLVaHideLockedAttachments</key>
+    <map>
+      <key>Comment</key>
+      <string>Hides non-detachable worn attachments from @getattach</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <boolean>0</boolean>
+    </map>
+    <key>RLVaSharedInvAutoRename</key>
+    <map>
+      <key>Comment</key>
+      <string>Automatically renames shared inventory items when worn</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <boolean>1</boolean>
+    </map>
+    <key>RLVaShowAssertionFailures</key>
+    <map>
+      <key>Comment</key>
+      <string>Notify the user when an assertion fails</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <boolean>1</boolean>
+    </map>
     <key>RLVaSplitRedirectChat</key>
-	<map>
-		<key>Comment</key>
+    <map>
+      <key>Comment</key>
       <string>Splits long nearby chat lines across multiple messages when @redir* restricted.</string>
-		<key>Persist</key>
-		<integer>1</integer>
-		<key>Type</key>
-		<string>Boolean</string>
-		<key>Value</key>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
       <boolean>0</boolean>
-	</map>
+    </map>
     <key>RLVaTopLevelMenu</key>
-	<map>
-		<key>Comment</key>
+    <map>
+      <key>Comment</key>
       <string>Show the RLVa specific menu as a top level menu</string>
-		<key>Persist</key>
-		<integer>1</integer>
-		<key>Type</key>
-		<string>Boolean</string>
-		<key>Value</key>
-		<boolean>1</boolean>
-	</map>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <boolean>1</boolean>
+    </map>
     <key>RLVaWearReplaceUnlocked</key>
-	<map>
-		<key>Comment</key>
+    <map>
+      <key>Comment</key>
       <string>Don't block wear replace when at least one attachment on the target attachment point is non-detachable</string>
-		<key>Persist</key>
-		<integer>1</integer>
-		<key>Type</key>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
       <string>Boolean</string>
-		<key>Value</key>
+      <key>Value</key>
       <boolean>1</boolean>
-	</map>
+    </map>
 </map>
 </llsd>
 
diff --git a/indra/newview/linux_tools/client-readme.txt b/indra/newview/linux_tools/client-readme.txt
index e01b9e4bc63548a7d7c8c765ffa73a28a6f675b0..cb8d1af53582be2fd3fb7c464596e730cef883f7 100644
--- a/indra/newview/linux_tools/client-readme.txt
+++ b/indra/newview/linux_tools/client-readme.txt
@@ -187,8 +187,8 @@ The 'secondlife' script which launches Second Life contains some
 configuration options for advanced troubleshooters.
 
 * AUDIO - Edit the 'secondlife' script and you will see these audio
-  options: LL_BAD_OPENAL_DRIVER, LL_BAD_FMOD_ESD, LL_BAD_FMOD_OSS, and
-  LL_BAD_FMOD_ALSA.  Second Life tries to use OpenAL, ESD, OSS, then ALSA
+  options: LL_BAD_OPENAL_DRIVER, LL_BAD_FMODSTUDIO_DRIVER.
+  Second Life tries to use OpenAL, FMODSTUDIO (PULSEAUDIO, ALSA)
   audio drivers in this order; you may uncomment the corresponding LL_BAD_*
   option to skip an audio driver which you believe may be causing you trouble.
 
diff --git a/indra/newview/linux_tools/wrapper.sh b/indra/newview/linux_tools/wrapper.sh
index c23401d5a661fd954536fadf9c9b8fa0c7da3779..eb3ead433b742f687384b6a1da1469317a633909 100755
--- a/indra/newview/linux_tools/wrapper.sh
+++ b/indra/newview/linux_tools/wrapper.sh
@@ -4,17 +4,15 @@
 ## These options are for self-assisted troubleshooting during this beta
 ## testing phase; you should not usually need to touch them.
 
-## - Avoids using any FMOD Ex audio driver.
-#export LL_BAD_FMODEX_DRIVER=x
+## - Avoids using any FMOD STUDIO audio driver.
+#export LL_BAD_FMODSTUDIO_DRIVER=x
 ## - Avoids using any OpenAL audio driver.
 #export LL_BAD_OPENAL_DRIVER=x
 
-## - Avoids using the FMOD Ex PulseAudio audio driver.
+## - Avoids using the FMOD Studio or FMOD Ex PulseAudio audio driver.
 #export LL_BAD_FMOD_PULSEAUDIO=x
-## - Avoids using the FMOD or FMOD Ex ALSA audio driver.
+## - Avoids using the FMOD Studio or FMOD Ex ALSA audio driver.
 #export LL_BAD_FMOD_ALSA=x
-## - Avoids using the FMOD or FMOD Ex OSS audio driver.
-#export LL_BAD_FMOD_OSS=x
 
 ## - Avoids the optional OpenGL extensions which have proven most problematic
 ##   on some hardware.  Disabling this option may cause BETTER PERFORMANCE but
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index a3dbc70479dc8500a9ef4e2a37dc38d7d1ace0ea..16656ced90e839c0e0e0e42f1f9864bf215dcb08 100644
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -692,6 +692,13 @@ void LLAgent::moveLeftNudge(S32 direction)
 //-----------------------------------------------------------------------------
 void LLAgent::moveUp(S32 direction, bool reset)
 {
+// [RLVa:KB] - Checked: RLVa-2.2 (@jump)
+	if ( (!RlvActions::canJump()) && (direction > 0) && (!getFlying()) )
+	{
+		return;
+	}
+// [/Sl:KB]
+
 	mMoveTimer.reset();
 	LLFirstUse::notMoving(false);
 
@@ -760,8 +767,11 @@ void LLAgent::movePitch(F32 mag)
 // Does this parcel allow you to fly?
 BOOL LLAgent::canFly()
 {
-// [RLVa:KB] - Checked: 2010-03-02 (RLVa-1.2.0d) | Modified: RLVa-1.0.0c
-	if (gRlvHandler.hasBehaviour(RLV_BHVR_FLY)) return FALSE;
+// [RLVa:KB] - Checked: RLVa-1.0
+	if (!RlvActions::canFly())
+	{
+		return FALSE;
+	}
 // [/RLVa:KB]
 	if (isGodlike()) return TRUE;
 
@@ -811,8 +821,8 @@ void LLAgent::setFlying(BOOL fly, BOOL fail_sound)
 
 	if (fly)
 	{
-// [RLVa:KB] - Checked: 2010-03-02 (RLVa-1.2.0d) | Modified: RLVa-1.0.0c
-		if (gRlvHandler.hasBehaviour(RLV_BHVR_FLY))
+// [RLVa:KB] - Checked: RLVa-1.0
+		if (!RlvActions::canFly())
 		{
 			return;
 		}
diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp
index 42d38c5bcc4d852e475ec0d713ea8794d5146af6..7862ea910553967a651a6e875335aa5a65c2ff8a 100644
--- a/indra/newview/llagentcamera.cpp
+++ b/indra/newview/llagentcamera.cpp
@@ -220,6 +220,9 @@ LLAgentCamera::LLAgentCamera() :
 	clearGeneralKeys();
 	clearOrbitKeys();
 	clearPanKeys();
+
+	resetPanDiff();
+	resetOrbitDiff();
 }
 
 // Requires gSavedSettings to be initialized.
@@ -245,6 +248,24 @@ void LLAgentCamera::init()
 	//BD - Unlimited Camera Presets
 	mCameraPresetName = gSavedSettings.getString("CameraPresetName");
 	initializeCameraPresets();
+	
+	/*mCameraPreset = (ECameraPreset) gSavedSettings.getU32("CameraPresetType");
+
+//	mCameraOffsetInitial = gSavedSettings.getControl("CameraOffsetRearView");
+//	mFocusOffsetInitial = gSavedSettings.getControl("FocusOffsetRearView");
+// [RLVa:KB] - @setcam_eyeoffset, @setcam_focusoffset and @setcam_eyeoffsetscale
+	mCameraOffsetInitialControl = gSavedSettings.getControl("CameraOffsetRearView");
+	mFocusOffsetInitialControl = gSavedSettings.getControl("FocusOffsetRearView");
+	if (RlvActions::isRlvEnabled())
+	{
+		mRlvCameraOffsetInitialControl = gSavedSettings.declareVec3("CameraOffsetRLVaView", LLVector3::zero, "Declared in code", LLControlVariable::PERSIST_NO);
+		mRlvCameraOffsetInitialControl->setHiddenFromSettingsEditor(true);
+		mRlvCameraOffsetScaleControl = gSavedSettings.declareF32("CameraOffsetScaleRLVa", 0.0f, "Declared in code", LLControlVariable::PERSIST_NO);
+		mRlvCameraOffsetScaleControl->setHiddenFromSettingsEditor(true);
+		mRlvFocusOffsetInitialControl = gSavedSettings.declareVec3d("FocusOffsetRLVaView", LLVector3d::zero, "Declared in code", LLControlVariable::PERSIST_NO);
+		mRlvFocusOffsetInitialControl->setHiddenFromSettingsEditor(true);
+	}*/
+// [/RLVa:KB]
 
 	mCameraCollidePlane.clearVec();
 	mCurrentCameraDistance = getCameraOffsetInitial().magVec() * gSavedSettings.getF32("CameraOffsetScale");
@@ -399,7 +420,8 @@ void LLAgentCamera::resetView(BOOL reset_camera, BOOL change_camera)
 
 		mCameraFOVZoomFactor = 0.f;
 	}
-
+	resetPanDiff();
+	resetOrbitDiff();
 	mHUDTargetZoom = 1.f;
 }
 
@@ -769,7 +791,7 @@ BOOL LLAgentCamera::calcCameraMinDistance(F32 &obj_min_distance)
 	return TRUE;
 }
 
-F32 LLAgentCamera::getCameraZoomFraction()
+F32 LLAgentCamera::getCameraZoomFraction(bool get_third_person)
 {
 	// 0.f -> camera zoomed all the way out
 	// 1.f -> camera zoomed all the way in
@@ -779,7 +801,7 @@ F32 LLAgentCamera::getCameraZoomFraction()
 		// already [0,1]
 		return mHUDTargetZoom;
 	}
-	else if (mFocusOnAvatar && cameraThirdPerson())
+	else if (get_third_person || (mFocusOnAvatar && cameraThirdPerson()))
 	{
 		return clamp_rescale(mCameraZoomFraction, MIN_ZOOM_FRACTION, MAX_ZOOM_FRACTION, 1.f, 0.f);
 	}
@@ -841,17 +863,17 @@ void LLAgentCamera::setCameraZoomFraction(F32 fraction)
 		//BD - Allow infinite zoom.
 		F32 max_zoom = mDrawDistance;
 
-		if (mFocusObject.notNull())
-		{
-			if (mFocusObject->isAvatar())
+			if (mFocusObject.notNull())
 			{
-				min_zoom = AVATAR_MIN_ZOOM;
-			}
-			else
-			{
-				min_zoom = OBJECT_MIN_ZOOM;
+				if (mFocusObject->isAvatar())
+				{
+					min_zoom = AVATAR_MIN_ZOOM;
+				}
+				else
+				{
+					min_zoom = OBJECT_MIN_ZOOM;
+				}
 			}
-		}
 
 		LLVector3d camera_offset_dir = mCameraFocusOffsetTarget;
 		camera_offset_dir.normalize();
@@ -890,6 +912,7 @@ void LLAgentCamera::cameraOrbitAround(const F32 radians)
 	}
 	else
 	{
+		mOrbitAroundRadians += radians;
 		mCameraFocusOffsetTarget.rotVec(radians, 0.f, 0.f, 1.f);
 		
 		cameraZoomIn(1.f);
@@ -921,12 +944,14 @@ void LLAgentCamera::cameraOrbitOver(const F32 angle)
 		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);
+		mOrbitOverAngle += angle_from_up - new_angle;
 		mCameraFocusOffsetTarget.rotVec(angle_from_up - new_angle, left_axis);
 
 		cameraZoomIn(1.f);
 	}
 }
 
+<<<<<<< HEAD
 //BD - Camera Rolling
 //-----------------------------------------------------------------------------
 // cameraRollOver()
@@ -936,6 +961,27 @@ void LLAgentCamera::cameraRollOver(const F32 angle)
 	mCameraRollAngle += fmod(angle, F_TWO_PI);
 }
 
+void LLAgentCamera::resetCameraOrbit()
+{
+	LLVector3 camera_offset_unit(mCameraFocusOffsetTarget);
+	camera_offset_unit.normalize();
+
+	LLVector3d left_axis;
+	left_axis.setVec(LLViewerCamera::getInstance()->getLeftAxis());
+	mCameraFocusOffsetTarget.rotVec(-mOrbitOverAngle, left_axis);
+	
+	mCameraFocusOffsetTarget.rotVec(-mOrbitAroundRadians, 0.f, 0.f, 1.f);
+
+	cameraZoomIn(1.f);
+	resetOrbitDiff();
+}
+
+void LLAgentCamera::resetOrbitDiff()
+{
+	mOrbitAroundRadians = 0;
+	mOrbitOverAngle = 0;
+}
+
 //-----------------------------------------------------------------------------
 // cameraZoomIn()
 //-----------------------------------------------------------------------------
@@ -1015,7 +1061,10 @@ void LLAgentCamera::cameraOrbitIn(const F32 meters)
 {
 	if (mFocusOnAvatar && mCameraMode == CAMERA_MODE_THIRD_PERSON)
 	{
-		F32 camera_offset_dist = llmax(0.001f, getCameraOffsetInitial().magVec() * gSavedSettings.getF32("CameraOffsetScale"));
+// [RLVa:KB] - @setcam_eyeoffsetscale
+		F32 camera_offset_dist = llmax(0.001f, getCameraOffsetInitial().magVec() * getCameraOffsetScale());
+// [/RLVa:KB]
+//		F32 camera_offset_dist = llmax(0.001f, getCameraOffsetInitial().magVec() * gSavedSettings.getF32("CameraOffsetScale"));
 		
 		mCameraZoomFraction = (mTargetCameraDistance - meters) / camera_offset_dist;
 
@@ -1088,6 +1137,8 @@ void LLAgentCamera::cameraPanIn(F32 meters)
 	LLVector3d at_axis;
 	at_axis.setVec(LLViewerCamera::getInstance()->getAtAxis());
 
+	mPanFocusDiff += meters * at_axis;
+
 	mFocusTargetGlobal += meters * at_axis;
 	mFocusGlobal = mFocusTargetGlobal;
 	// don't enforce zoom constraints as this is the only way for users to get past them easily
@@ -1104,6 +1155,8 @@ void LLAgentCamera::cameraPanLeft(F32 meters)
 	LLVector3d left_axis;
 	left_axis.setVec(LLViewerCamera::getInstance()->getLeftAxis());
 
+	mPanFocusDiff += meters * left_axis;
+
 	mFocusTargetGlobal += meters * left_axis;
 	mFocusGlobal = mFocusTargetGlobal;
 
@@ -1124,6 +1177,8 @@ void LLAgentCamera::cameraPanUp(F32 meters)
 	LLVector3d up_axis;
 	up_axis.setVec(LLViewerCamera::getInstance()->getUpAxis());
 
+	mPanFocusDiff += meters * up_axis;
+
 	mFocusTargetGlobal += meters * up_axis;
 	mFocusGlobal = mFocusTargetGlobal;
 
@@ -1136,6 +1191,26 @@ void LLAgentCamera::cameraPanUp(F32 meters)
 	mCameraSmoothingLastPositionGlobal = calcCameraPositionTargetGlobal();
 }
 
+void LLAgentCamera::resetCameraPan()
+{
+	mFocusTargetGlobal -= mPanFocusDiff;
+
+	mFocusGlobal = mFocusTargetGlobal;
+	mCameraSmoothingStop = true;
+
+	cameraZoomIn(1.f);
+	updateFocusOffset();
+
+	mCameraSmoothingLastPositionGlobal = calcCameraPositionTargetGlobal();
+
+	resetPanDiff();
+}
+
+void LLAgentCamera::resetPanDiff()
+{
+	mPanFocusDiff.clear();
+}
+
 //-----------------------------------------------------------------------------
 // updateLookAt()
 //-----------------------------------------------------------------------------
@@ -1215,7 +1290,7 @@ void LLAgentCamera::updateCamera()
 
 // [RLVa:KB] - Checked: RLVa-2.0.0
 	// Set focus back on our avie if something changed it
-	if ( (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_UNLOCK)) && (cameraThirdPerson()) && (!getFocusOnAvatar()) )
+	if ( (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_UNLOCK)) && ((cameraThirdPerson()) || (cameraFollow())) && (!getFocusOnAvatar()) )
 	{
 		setFocusOnAvatar(TRUE, FALSE);
 	}
@@ -1764,6 +1839,10 @@ LLVector3d LLAgentCamera::calcThirdPersonFocusOffset()
 	}
 
 	focus_offset = mFocusOffsetInitial[mCameraPresetName];
+//	focus_offset = convert_from_llsd<LLVector3d>(mFocusOffsetInitial->get(), TYPE_VEC3D, "");
+// [RLVa:KB] - @setcam_focusoffset
+	//focus_offset = getFocusOffsetInitial();
+// [/RLVa:KB]
 	return focus_offset * agent_rot;
 }
 
@@ -1838,7 +1917,7 @@ LLVector3d LLAgentCamera::calcCameraPositionTargetGlobal(BOOL *hit_limit)
 	F32			camera_land_height;
 	LLVector3d	frame_center_global = !isAgentAvatarValid() ? 
 		gAgent.getPositionGlobal() :
-		gAgent.getPosGlobalFromAgent(gAgentAvatarp->mRoot->getWorldPosition());
+		gAgent.getPosGlobalFromAgent(getAvatarRootPosition());
 	
 	BOOL		isConstrained = FALSE;
 	LLVector3d	head_offset;
@@ -1901,7 +1980,10 @@ LLVector3d LLAgentCamera::calcCameraPositionTargetGlobal(BOOL *hit_limit)
 		}
 		else
 		{
-			local_camera_offset = mCameraZoomFraction * getCameraOffsetInitial() * gSavedSettings.getF32("CameraOffsetScale");
+// [RLVa:KB] - @setcam_eyeoffsetscale
+			local_camera_offset = mCameraZoomFraction * getCameraOffsetInitial() * getCameraOffsetScale();
+// [/RLVa:KB]
+//			local_camera_offset = mCameraZoomFraction * getCameraOffsetInitial() * gSavedSettings.getF32("CameraOffsetScale");
 			
 			// are we sitting down?
 			if (isAgentAvatarValid() && gAgentAvatarp->getParent())
@@ -1912,7 +1994,7 @@ LLVector3d LLAgentCamera::calcCameraPositionTargetGlobal(BOOL *hit_limit)
 				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
@@ -2073,7 +2155,7 @@ LLVector3d LLAgentCamera::calcCameraPositionTargetGlobal(BOOL *hit_limit)
 	}
 
 // [RLVa:KB] - Checked: RLVa-2.0.0
-	if ( (CAMERA_MODE_THIRD_PERSON == mCameraMode) && (RlvActions::isRlvEnabled()) && (RlvActions::isCameraDistanceClamped()) )
+	if ( (RlvActions::isRlvEnabled()) && ((CAMERA_MODE_THIRD_PERSON == mCameraMode) || (CAMERA_MODE_FOLLOW == mCameraMode)) && (RlvActions::isCameraDistanceClamped()) )
 	{
 		m_fRlvMinDist = m_fRlvMaxDist = false;
 
@@ -2093,7 +2175,10 @@ LLVector3d LLAgentCamera::calcCameraPositionTargetGlobal(BOOL *hit_limit)
 		// Check focus distance limits
 		if ( (fCamOriginDistClamped) && (!fCamAvDistLocked) )
 		{
-			const LLVector3 offsetCameraLocal = mCameraZoomFraction * getCameraOffsetInitial() * gSavedSettings.getF32("CameraOffsetScale");
+//			const LLVector3 offsetCameraLocal = mCameraZoomFraction * getCameraOffsetInitial() * gSavedSettings.getF32("CameraOffsetScale");
+// [RLVa:KB] - @setcam_eyeoffsetscale
+			const LLVector3 offsetCameraLocal = mCameraZoomFraction * getCameraOffsetInitial() * getCameraOffsetScale();
+// [/RLVa:KB]
 			const LLVector3d offsetCamera(gAgent.getFrameAgent().rotateToAbsolute(offsetCameraLocal));
 			const LLVector3d posFocusCam = frame_center_global + head_offset + offsetCamera;
 			if (clampCameraPosition(camera_position_global, posFocusCam, nCamOriginDistLimitMin, nCamOriginDistLimitMax))
@@ -2135,7 +2220,7 @@ bool LLAgentCamera::allowFocusOffsetChange(const LLVector3d& offsetFocus)
 {
 	if (RlvActions::isCameraDistanceClamped())
 	{
-		if ( (CAMERA_MODE_THIRD_PERSON == getCameraMode()) && ((m_fRlvMinDist) || (m_fRlvMaxDist)) )
+		if ( ((CAMERA_MODE_THIRD_PERSON == getCameraMode()) || (CAMERA_MODE_FOLLOW == 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
@@ -2174,16 +2259,55 @@ bool LLAgentCamera::clampCameraPosition(LLVector3d& posCamGlobal, const LLVector
 }
 // [/RLVa:KB]
 
+LLVector3 LLAgentCamera::getCurrentCameraOffset()
+{
+	return (LLViewerCamera::getInstance()->getOrigin() - getAvatarRootPosition() - mThirdPersonHeadOffset) * ~getCurrentAvatarRotation();
+}
+
+LLVector3d LLAgentCamera::getCurrentFocusOffset()
+{
+	return (mFocusTargetGlobal - gAgent.getPositionGlobal()) * ~getCurrentAvatarRotation();
+}
+
+LLQuaternion LLAgentCamera::getCurrentAvatarRotation()
+{
+	LLViewerObject* sit_object = (LLViewerObject*)gAgentAvatarp->getParent();
+	
+	LLQuaternion av_rot = gAgent.getFrameAgent().getQuaternion();
+	LLQuaternion obj_rot = sit_object ? sit_object->getRenderRotation() : LLQuaternion::DEFAULT;
+	return av_rot * obj_rot;
+}
+
+bool LLAgentCamera::isJoystickCameraUsed()
+{
+	return ((mOrbitAroundRadians != 0) || (mOrbitOverAngle != 0) || !mPanFocusDiff.isNull());
+}
+
 LLVector3 LLAgentCamera::getCameraOffsetInitial()
 {
 	return mCameraOffsetInitial[mCameraPresetName];
+// [RLVa:KB] - @setcam_eyeoffset
+	//return convert_from_llsd<LLVector3>( (ECameraPreset::CAMERA_RLV_SETCAM_VIEW != mCameraPreset) ? mCameraOffsetInitialControl->get() : mRlvCameraOffsetInitialControl->get(), TYPE_VEC3, "");
+// [/RLVa:KB]
+//	return convert_from_llsd<LLVector3>(mCameraOffsetInitial->get(), TYPE_VEC3, "");
 }
 
 LLVector3d LLAgentCamera::getFocusOffsetInitial()
 {
 	return mFocusOffsetInitial[mCameraPresetName];
+// [RLVa:KB] - @setcam_focusoffset
+	//return convert_from_llsd<LLVector3d>( (ECameraPreset::CAMERA_RLV_SETCAM_VIEW != mCameraPreset) ? mFocusOffsetInitialControl->get() : mRlvFocusOffsetInitialControl->get(), TYPE_VEC3D, "");
+// [/RLVa:KB]
+//	return convert_from_llsd<LLVector3d>(mFocusOffsetInitial->get(), TYPE_VEC3D, "");
 }
 
+// [RLVa:KB] - @setcam_eyeoffsetscale
+F32 LLAgentCamera::getCameraOffsetScale() const
+{
+	return gSavedSettings.getF32( (ECameraPreset::CAMERA_RLV_SETCAM_VIEW != mCameraPreset) ? "CameraOffsetScale" : "CameraOffsetScaleRLVa");
+}
+// [/RLVa:KB]
+
 F32 LLAgentCamera::getCameraMaxZoomDistance()
 {
     // Ignore "DisableCameraConstraints", we don't want to be out of draw range when we focus onto objects or avatars
@@ -2192,6 +2316,12 @@ F32 LLAgentCamera::getCameraMaxZoomDistance()
                  LLWorld::getInstance()->getRegionWidthInMeters() - CAMERA_FUDGE_FROM_OBJECT);
 }
 
+LLVector3 LLAgentCamera::getAvatarRootPosition()
+{
+    static LLCachedControl<bool> use_hover_height(gSavedSettings, "HoverHeightAffectsCamera");
+    return use_hover_height ? gAgentAvatarp->mRoot->getWorldPosition() : gAgentAvatarp->mRoot->getWorldPosition() - gAgentAvatarp->getHoverOffset();
+
+}
 //-----------------------------------------------------------------------------
 // handleScrollWheel()
 //-----------------------------------------------------------------------------
@@ -2228,10 +2358,16 @@ void LLAgentCamera::handleScrollWheel(S32 clicks)
 		{
 			F32 camera_offset_initial_mag = getCameraOffsetInitial().magVec();
 			
-			F32 current_zoom_fraction = mTargetCameraDistance / (camera_offset_initial_mag * gSavedSettings.getF32("CameraOffsetScale"));
+//			F32 current_zoom_fraction = mTargetCameraDistance / (camera_offset_initial_mag * gSavedSettings.getF32("CameraOffsetScale"));
+// [RLVa:KB] - @setcam_eyeoffsetscale
+			F32 current_zoom_fraction = mTargetCameraDistance / (camera_offset_initial_mag * getCameraOffsetScale());
+// [/RLVa:KB]
 			current_zoom_fraction *= 1.f - pow(ROOT_ROOT_TWO, clicks);
 			
-			cameraOrbitIn(current_zoom_fraction * camera_offset_initial_mag * gSavedSettings.getF32("CameraOffsetScale"));
+// [RLVa:KB] - @setcam_eyeoffsetscale
+			cameraOrbitIn(current_zoom_fraction * camera_offset_initial_mag * getCameraOffsetScale());
+// [/RLVa:KB]
+//			cameraOrbitIn(current_zoom_fraction * camera_offset_initial_mag * gSavedSettings.getF32("CameraOffsetScale"));
 		}
 		else
 		{
@@ -2466,15 +2602,7 @@ void LLAgentCamera::changeCameraToThirdPerson(BOOL animate)
 	}
 
 	// 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
+	if (!isAgentAvatarValid() || !gAgentAvatarp->getParent())
 	{
 		at_axis = gAgent.getFrameAgent().getAtAxis();
 		at_axis.mV[VZ] = 0.f;
@@ -2575,9 +2703,10 @@ void LLAgentCamera::switchCameraPreset(std::string preset_name)
 	initializeCameraPresets();
 
 // [RLVa:KB] - Checked: RLVa-2.0.0
+// [RLVa:KB] - @setcam family
 	if (RlvActions::isRlvEnabled())
 	{
-		// Don't allow changing away from the our view if an object is restricting it
+		// Don't allow changing away from our view if an object is restricting it
 		if (RlvActions::isCameraPresetLocked())
 			preset_name = "RLVa View";
 
@@ -2593,6 +2722,21 @@ void LLAgentCamera::switchCameraPreset(std::string preset_name)
 
 			//BD - Reinitialize our presets, this should reload the RLVa preset values.
 			initializeCameraPresets();
+			/*if (CAMERA_RLV_SETCAM_VIEW == preset)
+			{
+				if (CAMERA_RLV_SETCAM_VIEW == mCameraPreset)
+				{
+					// Don't reset anything if our view is already current
+					return;
+				}
+				else
+				{
+					// When switching to our view, copy the current values
+					mRlvCameraOffsetInitialControl->setDefaultValue(convert_to_llsd(getCameraOffsetInitial()));
+					mRlvFocusOffsetInitialControl->setDefaultValue(convert_to_llsd(getFocusOffsetInitial()));
+					mRlvCameraOffsetScaleControl->setDefaultValue(getCameraOffsetScale());
+				}
+			}*/
 		}
 	}
 // [/RLVa:KB]
@@ -2678,6 +2822,10 @@ void LLAgentCamera::initializeCameraPresets()
 		}
 		infile.close();
 	}
+	resetPanDiff();
+	resetOrbitDiff();
+
+	//gSavedSettings.setU32("CameraPresetType", mCameraPreset);
 }
 
 
@@ -2922,7 +3070,7 @@ void LLAgentCamera::setSitCamera(const LLUUID &object_id, const LLVector3 &camer
 //-----------------------------------------------------------------------------
 // setFocusOnAvatar()
 //-----------------------------------------------------------------------------
-void LLAgentCamera::setFocusOnAvatar(BOOL focus_on_avatar, BOOL animate)
+void LLAgentCamera::setFocusOnAvatar(BOOL focus_on_avatar, BOOL animate, BOOL reset_axes)
 {
 	if (focus_on_avatar != mFocusOnAvatar)
 	{
@@ -2939,22 +3087,14 @@ void LLAgentCamera::setFocusOnAvatar(BOOL focus_on_avatar, BOOL animate)
 	//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)
+	if (!mFocusOnAvatar && focus_on_avatar && reset_axes)
 	{
 		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
+			if (!isAgentAvatarValid() || !gAgentAvatarp->getParent())
 			{
 				at_axis = LLViewerCamera::getInstance()->getAtAxis();
 				at_axis.mV[VZ] = 0.f;
@@ -3104,6 +3244,17 @@ BOOL LLAgentCamera::setPointAt(EPointAtType target_type, LLViewerObject *object,
 	return mPointAt->setPointAt(target_type, object, position);
 }
 
+void LLAgentCamera::rotateToInitSitRot()
+{
+	gAgent.rotate(~gAgent.getFrameAgent().getQuaternion());
+	gAgent.rotate(mInitSitRot);
+}
+
+void LLAgentCamera::resetCameraZoomFraction()
+{ 
+	mCameraZoomFraction = INITIAL_ZOOM_FRACTION; 
+}
+
 ELookAtType LLAgentCamera::getLookAtType()
 {
 	if (mLookAt) 
diff --git a/indra/newview/llagentcamera.h b/indra/newview/llagentcamera.h
index 2287c6d095f956951a9176bf9242fc27224d7e5f..5b44ad3ee606ae641d9aac186f3184d54a6b70cd 100644
--- a/indra/newview/llagentcamera.h
+++ b/indra/newview/llagentcamera.h
@@ -117,6 +117,54 @@ class LLAgentCamera
 	/** Determines maximum camera distance from target for mouselook, opposite to LAND_MIN_ZOOM */
 	F32 getCameraMaxZoomDistance();
 
+// [RLVa:KB] - @setcam family
+	/** Determines default camera offset scale depending on the current camera preset */
+	ECameraPreset getCameraPreset() const { return mCameraPreset; }
+// [/RLVa:KB]
+	void switchCameraPreset(ECameraPreset preset);
+	/** Determines default camera offset depending on the current camera preset */
+	LLVector3 getCameraOffsetInitial();
+// [RLVa:KB] - @setcam_eyeoffsetscale
+	/** Determines default camera offset scale depending on the current camera preset */
+	F32 getCameraOffsetScale() const;
+// [/RLVa:KB]
+	/** Determines default focus offset depending on the current camera preset */
+	LLVector3d getFocusOffsetInitial();
+
+	LLVector3 getCurrentCameraOffset();
+	LLVector3d getCurrentFocusOffset();
+	LLQuaternion getCurrentAvatarRotation();
+	bool isJoystickCameraUsed();
+	void setInitSitRot(LLQuaternion sit_rot) { mInitSitRot = sit_rot; };
+	void rotateToInitSitRot();
+
+private:
+	/** Determines maximum camera distance from target for mouselook, opposite to LAND_MIN_ZOOM */
+	F32 getCameraMaxZoomDistance();
+
+
+	/** Initial camera offset */
+//	LLPointer<LLControlVariable> mCameraOffsetInitial;
+// [RLVa:KB] - @setcam_eyeoffset
+	// Renamed to catch their uses
+	LLPointer<LLControlVariable> mCameraOffsetInitialControl;
+	LLPointer<LLControlVariable> mRlvCameraOffsetInitialControl;
+// [/RLVa:KB]
+
+// [RLVa:KB] - @setcam_eyeoffsetscale
+	LLPointer<LLControlVariable> mRlvCameraOffsetScaleControl;
+// [/RLVa:KB]
+
+	/** Initial focus offset */
+//	LLPointer<LLControlVariable> mFocusOffsetInitial;
+// [RLVa:KB] - @setcam_focusoffset
+	// Renamed to catch their uses
+	LLPointer<LLControlVariable> mFocusOffsetInitialControl;
+	LLPointer<LLControlVariable> mRlvFocusOffsetInitialControl;
+// [/RLVa:KB]
+
+	LLQuaternion mInitSitRot;
+
 	//--------------------------------------------------------------------
 	// Position
 	//--------------------------------------------------------------------
@@ -131,6 +179,8 @@ class LLAgentCamera
 	void			clearCameraLag() { mCameraLag.clearVec(); }
 
 private:
+	LLVector3		getAvatarRootPosition();
+
 	F32				mCurrentCameraDistance;	 		// Current camera offset from avatar
 	F32				mTargetCameraDistance;			// Target camera offset from avatar
 	F32				mCameraFOVZoomFactor;			// Amount of fov zoom applied to camera when zeroing in on an object
@@ -198,7 +248,7 @@ class LLAgentCamera
 	void			validateFocusObject();
 	void			setFocusGlobal(const LLPickInfo& pick);
 	void			setFocusGlobal(const LLVector3d &focus, const LLUUID &object_id = LLUUID::null);
-	void			setFocusOnAvatar(BOOL focus, BOOL animate);
+	void			setFocusOnAvatar(BOOL focus, BOOL animate, BOOL reset_axes = TRUE);
 	void			setCameraPosAndFocusGlobal(const LLVector3d& pos, const LLVector3d& focus, const LLUUID &object_id);
 	void			clearFocusObject();
 	void			setFocusObject(LLViewerObject* object);
@@ -272,25 +322,31 @@ class LLAgentCamera
 
 	void			cameraRollOver(const F32 radians);		// Roll the camera
 
+	void			resetCameraOrbit();
+	void			resetOrbitDiff();
 	//--------------------------------------------------------------------
 	// Zoom
 	//--------------------------------------------------------------------
 public:
-	void			handleScrollWheel(S32 clicks); 			// Mousewheel driven zoom
-	void			cameraZoomIn(const F32 factor);			// Zoom in by fraction of current distance
-	F32				getCameraZoomFraction();				// Get camera zoom as fraction of minimum and maximum zoom
-	void			setCameraZoomFraction(F32 fraction);	// Set camera zoom as fraction of minimum and maximum zoom
+	void			handleScrollWheel(S32 clicks); 							// Mousewheel driven zoom
+	void			cameraZoomIn(const F32 factor);							// Zoom in by fraction of current distance
+	F32				getCameraZoomFraction(bool get_third_person = false);	// Get camera zoom as fraction of minimum and maximum zoom
+	void			setCameraZoomFraction(F32 fraction);					// Set camera zoom as fraction of minimum and maximum zoom
 	F32				calcCameraFOVZoomFactor();
 	F32				getAgentHUDTargetZoom();
 
+	void			resetCameraZoomFraction();
+	F32				getCurrentCameraZoomFraction() { return mCameraZoomFraction; }
+
 	//--------------------------------------------------------------------
 	// Pan
 	//--------------------------------------------------------------------
 public:
 	void			cameraPanIn(const F32 meters);
 	void			cameraPanLeft(const F32 meters);
-	void			cameraPanUp(const F32 meters);
-	
+	void			cameraPanUp(const F32 meters);	
+	void			resetCameraPan();
+	void			resetPanDiff();
 	//--------------------------------------------------------------------
 	// View
 	//--------------------------------------------------------------------
@@ -383,12 +439,11 @@ class LLAgentCamera
 	void			setOrbitDownKey(F32 mag)	{ mOrbitDownKey = mag; }
 	void			setOrbitInKey(F32 mag)		{ mOrbitInKey = mag; }
 	void			setOrbitOutKey(F32 mag)		{ mOrbitOutKey = mag; }
+	void			clearOrbitKeys();
 
 	//BD - Camera Roll
 	void			setRollLeftKey(F32 mag)		{ mRollLeftKey = mag; }
 	void			setRollRightKey(F32 mag)	{ mRollRightKey = mag; }
-
-	void			clearOrbitKeys();
 private:
 	F32				mOrbitLeftKey;
 	F32				mOrbitRightKey;
@@ -396,6 +451,8 @@ class LLAgentCamera
 	F32				mOrbitDownKey;
 	F32				mOrbitInKey;
 	F32				mOrbitOutKey;
+	F32				mOrbitAroundRadians;
+	F32				mOrbitOverAngle;
 
 	//BD - Camera Roll
 	F32				mRollLeftKey;
@@ -428,6 +485,8 @@ class LLAgentCamera
 	F32				mPanInKey;
 	F32				mPanOutKey;
 
+	LLVector3d		mPanFocusDiff;
+
 /**                    Keys
  **                                                                            **
  *******************************************************************************/
diff --git a/indra/newview/llaisapi.cpp b/indra/newview/llaisapi.cpp
index d8588528c03a1816aaf2c50eb317da8779a07a1a..0adc7a703a3d30bd40bd6e99bd1306f530a3f2b6 100644
--- a/indra/newview/llaisapi.cpp
+++ b/indra/newview/llaisapi.cpp
@@ -452,6 +452,11 @@ void AISAPI::InvokeAISCommandCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t ht
 					AISUpdate::parseUUIDArray(result, "_created_categories", ids);
 				}
 				break;
+			case UPDATECATEGORY:
+				{
+					AISUpdate::parseUUIDArray(result, "_updated_categories", ids);
+				}
+				break;
 			default:
 				break;
 		}
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 4a3fcc0284da82888ebe54b16f90443f0b056f9e..6da96867ebbc7d7b420c99aaa6639a9bde28b81b 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -5542,6 +5542,10 @@ void LLAppViewer::disconnectViewer()
 	LLAppearanceMgr::instance().setAttachmentInvLinkEnable(false);
 // [/SL:KB]
 
+// [RLVa:KB] - Checked: RLVa-2.3 (Housekeeping)
+	SUBSYSTEM_CLEANUP(RlvHandler);
+// [/RLVa:KB]
+
 	gAgentWearables.cleanup();
 	gAgentCamera.cleanup();
 	// Also writes cached agent settings to gSavedSettings
diff --git a/indra/newview/llenvironment.cpp b/indra/newview/llenvironment.cpp
index d4284eb34243b2d3bc8136ecf73d0b9bb8f01988..466c9789f3b4e78227a7c75adbd53ffbfb904a88 100644
--- a/indra/newview/llenvironment.cpp
+++ b/indra/newview/llenvironment.cpp
@@ -65,6 +65,10 @@
 #include "llviewergenericmessage.h"
 #include "llexperiencelog.h"
 
+// [RLVa:KB] - Checked: RLVa-2.4 (@setenv)
+#include "rlvactions.h"
+// [/RLVa:KB]
+
 //=========================================================================
 namespace
 {
@@ -1060,6 +1064,13 @@ bool LLEnvironment::getIsMoonUp() const
 //-------------------------------------------------------------------------
 void LLEnvironment::setSelectedEnvironment(LLEnvironment::EnvSelection_t env, LLSettingsBase::Seconds transition, bool forced)
 {
+// [RLVa:KB] - Checked: RLVa-2.4 (@setenv)
+    if ( (!RlvActions::canChangeEnvironment()) && (LLEnvironment::ENV_EDIT != env) )
+    {
+        return;
+    }
+// [/RLVa:KB]
+
     mSelectedEnvironment = env;
     updateEnvironment(transition, forced);
 }
diff --git a/indra/newview/llfloatercamera.cpp b/indra/newview/llfloatercamera.cpp
index bdd6991e53bf28dbc741a1a4c1d04fc1de741d33..5c2c608f139cd561b8b10256ac75a60da6b1c51b 100644
--- a/indra/newview/llfloatercamera.cpp
+++ b/indra/newview/llfloatercamera.cpp
@@ -42,6 +42,11 @@
 #include "llslider.h"
 #include "llfirstuse.h"
 #include "llhints.h"
+#include "lltabcontainer.h"
+#include "llvoavatarself.h"
+// [RLVa:KB] - @setcam
+#include "rlvactions.h"
+// [/RLVa:KB]
 
 //BD - Bone Camera
 #include "llvoavatarself.h"
@@ -686,7 +691,7 @@ void LLFloaterCamera::onClickCameraItem(const LLSD& param)
 	{
 		LLFloaterCamera* camera_floater = LLFloaterCamera::findInstance();
 		if (camera_floater)
-		camera_floater->switchMode(CAMERA_CTRL_MODE_FREE_CAMERA);
+			camera_floater->switchMode(CAMERA_CTRL_MODE_FREE_CAMERA);
 	}
 }
 
@@ -695,6 +700,13 @@ void LLFloaterCamera::switchToPreset()
 	LLScrollListItem* item = mPresetsScroll->getFirstSelected();
 	if (!item) return;
 
+// [RLVa:KB] - @setcam family
+	if (RlvActions::isCameraPresetLocked())
+	{
+		return;
+	}
+// [/RLVa:KB]
+
 	sFreeCamera = false;
 	clear_camera_tool();
 
diff --git a/indra/newview/llfloatercamerapresets.cpp b/indra/newview/llfloatercamerapresets.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..300c945a859ca845696350d935b1236a0b5085c0
--- /dev/null
+++ b/indra/newview/llfloatercamerapresets.cpp
@@ -0,0 +1,145 @@
+/** 
+* @file llfloatercamerapresets.cpp
+*
+* $LicenseInfo:firstyear=2019&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2019, 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 "llfloatercamerapresets.h"
+#include "llfloaterreg.h"
+#include "llnotificationsutil.h"
+#include "llpresetsmanager.h"
+#include "llviewercontrol.h"
+
+LLFloaterCameraPresets::LLFloaterCameraPresets(const LLSD& key)
+:	LLFloater(key)
+{}
+
+LLFloaterCameraPresets::~LLFloaterCameraPresets()
+{}
+
+BOOL LLFloaterCameraPresets::postBuild()
+{
+    mPresetList = getChild<LLFlatListView>("preset_list");
+
+    LLPresetsManager::getInstance()->setPresetListChangeCameraCallback(boost::bind(&LLFloaterCameraPresets::populateList, this));
+
+    return TRUE;
+}
+void LLFloaterCameraPresets::onOpen(const LLSD& key)
+{
+    populateList();
+}
+
+void LLFloaterCameraPresets::populateList()
+{
+    mPresetList->clear();
+
+    LLPresetsManager* presetsMgr = LLPresetsManager::getInstance();
+    std::list<std::string> preset_names;
+
+	presetsMgr->loadPresetNamesFromDir(PRESETS_CAMERA, preset_names, DEFAULT_BOTTOM);
+
+    for (std::list<std::string>::const_iterator it = preset_names.begin(); it != preset_names.end(); ++it)
+    {
+        const std::string& name = *it;
+        bool is_default = presetsMgr->isDefaultCameraPreset(name);
+        LLCameraPresetFlatItem* item = new LLCameraPresetFlatItem(name, is_default);
+        item->postBuild();
+        mPresetList->addItem(item);
+    }
+}
+
+LLCameraPresetFlatItem::LLCameraPresetFlatItem(const std::string &preset_name, bool is_default)
+    : LLPanel(),
+    mPresetName(preset_name),
+    mIsDefaultPrest(is_default)
+{
+    mCommitCallbackRegistrar.add("CameraPresets.Delete", boost::bind(&LLCameraPresetFlatItem::onDeleteBtnClick, this));
+    mCommitCallbackRegistrar.add("CameraPresets.Reset", boost::bind(&LLCameraPresetFlatItem::onResetBtnClick, this));
+    buildFromFile("panel_camera_preset_item.xml");
+}
+
+LLCameraPresetFlatItem::~LLCameraPresetFlatItem()
+{
+}
+
+BOOL LLCameraPresetFlatItem::postBuild()
+{
+    mDeleteBtn = getChild<LLButton>("delete_btn");
+    mDeleteBtn->setVisible(false);
+
+    mResetBtn = getChild<LLButton>("reset_btn");
+    mResetBtn->setVisible(false);
+
+    LLStyle::Params style;
+    LLTextBox* name_text = getChild<LLTextBox>("preset_name");
+    LLFontDescriptor new_desc(name_text->getFont()->getFontDesc());
+    new_desc.setStyle(mIsDefaultPrest ? LLFontGL::ITALIC : LLFontGL::NORMAL);
+    LLFontGL* new_font = LLFontGL::getFont(new_desc);
+    style.font = new_font;
+    name_text->setText(mPresetName, style);
+
+    return true;
+}
+
+void LLCameraPresetFlatItem::onMouseEnter(S32 x, S32 y, MASK mask)
+{
+    mDeleteBtn->setVisible(!mIsDefaultPrest);
+    mResetBtn->setVisible(mIsDefaultPrest);
+    getChildView("hovered_icon")->setVisible(true);
+    LLPanel::onMouseEnter(x, y, mask);
+}
+
+void LLCameraPresetFlatItem::onMouseLeave(S32 x, S32 y, MASK mask)
+{
+    mDeleteBtn->setVisible(false);
+    mResetBtn->setVisible(false);
+    getChildView("hovered_icon")->setVisible(false);
+    LLPanel::onMouseLeave(x, y, mask);
+}
+
+void LLCameraPresetFlatItem::setValue(const LLSD& value)
+{
+    if (!value.isMap()) return;;
+    if (!value.has("selected")) return;
+    getChildView("selected_icon")->setVisible(value["selected"]);
+}
+
+void LLCameraPresetFlatItem::onDeleteBtnClick()
+{
+    if (!LLPresetsManager::getInstance()->deletePreset(PRESETS_CAMERA, mPresetName))
+    {
+        LLSD args;
+        args["NAME"] = mPresetName;
+        LLNotificationsUtil::add("PresetNotDeleted", args);
+    }
+	else if (gSavedSettings.getString("PresetCameraActive") == mPresetName)
+    {
+        gSavedSettings.setString("PresetCameraActive", "");
+    }
+}
+
+void LLCameraPresetFlatItem::onResetBtnClick()
+{
+    LLPresetsManager::getInstance()->resetCameraPreset(mPresetName);
+}
diff --git a/indra/newview/llfloatercamerapresets.h b/indra/newview/llfloatercamerapresets.h
new file mode 100644
index 0000000000000000000000000000000000000000..66430fa399441f67a52aae5944e0ed915b5d8689
--- /dev/null
+++ b/indra/newview/llfloatercamerapresets.h
@@ -0,0 +1,73 @@
+/** 
+* @file llfloatercamerapresets.h
+*
+* $LicenseInfo:firstyear=2019&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2019, 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$
+*/
+#ifndef LLFLOATERCAMERAPRESETS_H
+#define LLFLOATERCAMERAPRESETS_H
+
+#include "llfloater.h"
+#include "llflatlistview.h"
+
+class LLFloaterReg;
+
+class LLFloaterCameraPresets : public LLFloater
+{
+    friend class LLFloaterReg;
+
+    virtual BOOL postBuild();
+    virtual void onOpen(const LLSD& key);
+
+    void populateList();
+
+private:
+    LLFloaterCameraPresets(const LLSD& key);
+    ~LLFloaterCameraPresets();
+
+    LLFlatListView* mPresetList;
+};
+
+class LLCameraPresetFlatItem : public LLPanel
+{
+public:
+    LLCameraPresetFlatItem(const std::string &preset_name, bool is_default);
+    virtual ~LLCameraPresetFlatItem();
+
+    void setValue(const LLSD& value);
+
+    virtual BOOL postBuild();
+    virtual void onMouseEnter(S32 x, S32 y, MASK mask);
+    virtual void onMouseLeave(S32 x, S32 y, MASK mask);
+
+private:
+    void onDeleteBtnClick();
+    void onResetBtnClick();
+
+    LLButton* mDeleteBtn;
+    LLButton* mResetBtn;
+
+    std::string mPresetName;
+    bool mIsDefaultPrest;
+
+};
+
+#endif
diff --git a/indra/newview/llfloaterdeleteprefpreset.cpp b/indra/newview/llfloaterdeleteprefpreset.cpp
index 7dedbbf98430169016ec275228f84c7bcdfae203..0765756b433b86cd260c4165f4cf99ee519779d6 100644
--- a/indra/newview/llfloaterdeleteprefpreset.cpp
+++ b/indra/newview/llfloaterdeleteprefpreset.cpp
@@ -60,13 +60,15 @@ void LLFloaterDeletePrefPreset::onOpen(const LLSD& key)
 {
 	mSubdirectory = key.asString();
 	std::string floater_title = getString(std::string("title_") + mSubdirectory);
-
 	setTitle(floater_title);
 
 	LLComboBox* combo = getChild<LLComboBox>("preset_combo");
-
 	EDefaultOptions option = DEFAULT_HIDE;
-	LLPresetsManager::getInstance()->setPresetNamesInComboBox(mSubdirectory, combo, option);
+	bool action;
+	action = LLPresetsManager::getInstance()->setPresetNamesInComboBox(mSubdirectory, combo, option);
+
+	LLButton* delete_btn = getChild<LLButton>("delete");
+	delete_btn->setEnabled(action);
 }
 
 void LLFloaterDeletePrefPreset::onBtnDelete()
@@ -80,6 +82,13 @@ void LLFloaterDeletePrefPreset::onBtnDelete()
 		args["NAME"] = name;
 		LLNotificationsUtil::add("PresetNotDeleted", args);
 	}
+	else if (mSubdirectory == PRESETS_CAMERA)
+	{
+		if (gSavedSettings.getString("PresetCameraActive") == name)
+		{
+			gSavedSettings.setString("PresetCameraActive", "");
+		}
+	}
 
 	closeFloater();
 }
@@ -87,12 +96,10 @@ void LLFloaterDeletePrefPreset::onBtnDelete()
 void LLFloaterDeletePrefPreset::onPresetsListChange()
 {
 	LLComboBox* combo = getChild<LLComboBox>("preset_combo");
-	LLButton* delete_btn = getChild<LLButton>("delete");
 
 	EDefaultOptions option = DEFAULT_HIDE;
-	LLPresetsManager::getInstance()->setPresetNamesInComboBox(mSubdirectory, combo, option);
 
-	delete_btn->setEnabled(0 != combo->getItemCount());
+	LLPresetsManager::getInstance()->setPresetNamesInComboBox(mSubdirectory, combo, option);
 }
 
 void LLFloaterDeletePrefPreset::onBtnCancel()
diff --git a/indra/newview/llfloaterimnearbychat.cpp b/indra/newview/llfloaterimnearbychat.cpp
index 1962e8158dd3dcf3e974d5e24d7926227833378d..7aa9a666003de7b5ab164fc3e279f78762cfc61e 100644
--- a/indra/newview/llfloaterimnearbychat.cpp
+++ b/indra/newview/llfloaterimnearbychat.cpp
@@ -483,7 +483,10 @@ void LLFloaterIMNearbyChat::onChatBoxKeystroke()
 
 	S32 length = raw_text.length();
 
-	if( (length > 0) && (raw_text[0] != '/') )  // forward slash is used for escape (eg. emote) sequences
+//	if( (length > 0) && (raw_text[0] != '/') )  // forward slash is used for escape (eg. emote) sequences
+// [RLVa:KB] - Checked: 2010-03-26 (RLVa-1.2.0b) | Modified: RLVa-1.0.0d
+	if ( (length > 0) && (raw_text[0] != '/') && (!RlvActions::hasBehaviour(RLV_BHVR_REDIRCHAT)) )
+// [/RLVa:KB]
 	{
 		gAgent.startTyping();
 	}
@@ -729,6 +732,15 @@ void LLFloaterIMNearbyChat::sendChatFromViewer(const LLWString &wtext, EChatType
 		utf8_text = utf8str_truncate(utf8_text, MAX_STRING - 1);
 	}
 
+// [RLVa:KB] - Checked: 2010-03-27 (RLVa-1.2.0b) | Modified: RLVa-1.2.0b
+	if ( (0 == channel) && (RlvActions::isRlvEnabled()) )
+	{
+		// Adjust the (public) chat "volume" on chat and gestures (also takes care of playing the proper animation)
+		type = RlvActions::checkChatVolume(type);
+		animate &= !RlvActions::hasBehaviour( (!RlvUtil::isEmote(utf8_text)) ? RLV_BHVR_REDIRCHAT : RLV_BHVR_REDIREMOTE );
+	}
+// [/RLVa:KB]
+
 	// Don't animate for chats people can't hear (chat to scripts)
 	if (animate && (channel == 0))
 	{
diff --git a/indra/newview/llfloaterloadprefpreset.cpp b/indra/newview/llfloaterloadprefpreset.cpp
index 403db35cc020c57c93c0df43a12fa5c6c15753f1..fa17a9d40e3d08e0e37b99b1044deaa6328d295c 100644
--- a/indra/newview/llfloaterloadprefpreset.cpp
+++ b/indra/newview/llfloaterloadprefpreset.cpp
@@ -42,7 +42,8 @@ LLFloaterLoadPrefPreset::LLFloaterLoadPrefPreset(const LLSD &key)
 
 // virtual
 BOOL LLFloaterLoadPrefPreset::postBuild()
-{	LLFloaterPreference* preferences = LLFloaterReg::getTypedInstance<LLFloaterPreference>("preferences");
+{
+	LLFloaterPreference* preferences = LLFloaterReg::getTypedInstance<LLFloaterPreference>("preferences");
 	if (preferences)
 	{
 		preferences->addDependentFloater(this);
diff --git a/indra/newview/llfloaterpreferenceviewadvanced.cpp b/indra/newview/llfloaterpreferenceviewadvanced.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f8db738923359072443744297795191971ee1e2b
--- /dev/null
+++ b/indra/newview/llfloaterpreferenceviewadvanced.cpp
@@ -0,0 +1,82 @@
+/** 
+ * @file llfloaterpreferenceviewadvanced.cpp
+ * @brief floater for adjusting camera position
+ *
+ * $LicenseInfo:firstyear=2018&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2018, 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 "llfloaterpreferenceviewadvanced.h"
+#include "llfloater.h"
+#include "llfloaterreg.h"
+#include "lluictrlfactory.h"
+#include "llspinctrl.h"
+#include "llviewercontrol.h"
+
+
+LLFloaterPreferenceViewAdvanced::LLFloaterPreferenceViewAdvanced(const LLSD& key) 
+:	LLFloater(key)
+{
+	mCommitCallbackRegistrar.add("CommitSettings",	boost::bind(&LLFloaterPreferenceViewAdvanced::onCommitSettings, this));
+}
+
+LLFloaterPreferenceViewAdvanced::~LLFloaterPreferenceViewAdvanced()
+{}
+
+void LLFloaterPreferenceViewAdvanced::updateCameraControl(const LLVector3& vector)
+{
+	getChild<LLSpinCtrl>("camera_x")->setValue(vector[VX]);
+	getChild<LLSpinCtrl>("camera_y")->setValue(vector[VY]);
+	getChild<LLSpinCtrl>("camera_z")->setValue(vector[VZ]);
+}
+
+void LLFloaterPreferenceViewAdvanced::updateFocusControl(const LLVector3d& vector3d)
+{
+	getChild<LLSpinCtrl>("focus_x")->setValue(vector3d[VX]);
+	getChild<LLSpinCtrl>("focus_y")->setValue(vector3d[VY]);
+	getChild<LLSpinCtrl>("focus_z")->setValue(vector3d[VZ]);
+}
+
+ void LLFloaterPreferenceViewAdvanced::draw()
+{
+	updateCameraControl(gAgentCamera.getCameraOffsetInitial());
+	updateFocusControl(gAgentCamera.getFocusOffsetInitial());
+
+	LLFloater::draw();
+}
+
+void LLFloaterPreferenceViewAdvanced::onCommitSettings()
+{
+	LLVector3 vector;
+	LLVector3d vector3d;
+
+	vector.mV[VX] = (F32)getChild<LLUICtrl>("camera_x")->getValue().asReal();
+	vector.mV[VY] = (F32)getChild<LLUICtrl>("camera_y")->getValue().asReal();
+	vector.mV[VZ] = (F32)getChild<LLUICtrl>("camera_z")->getValue().asReal();
+	gSavedSettings.setVector3("CameraOffsetRearView", vector);
+
+	vector3d.mdV[VX] = (F32)getChild<LLUICtrl>("focus_x")->getValue().asReal();
+	vector3d.mdV[VY] = (F32)getChild<LLUICtrl>("focus_y")->getValue().asReal();
+	vector3d.mdV[VZ] = (F32)getChild<LLUICtrl>("focus_z")->getValue().asReal();
+	gSavedSettings.setVector3d("FocusOffsetRearView", vector3d);
+}
diff --git a/indra/newview/llfloaterpreferenceviewadvanced.h b/indra/newview/llfloaterpreferenceviewadvanced.h
new file mode 100644
index 0000000000000000000000000000000000000000..4619fdaab16b9f79aaea2071b04c5c5252556adb
--- /dev/null
+++ b/indra/newview/llfloaterpreferenceviewadvanced.h
@@ -0,0 +1,51 @@
+/** 
+ * @file llfloaterpreferenceviewadvanced.h
+ * @brief floater for adjusting camera position
+ *
+ * $LicenseInfo:firstyear=2018&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2018, 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$
+ */
+
+#ifndef LLFLOATERPREFERENCEVIEWADVANCED_H
+#define LLFLOATERPREFERENCEVIEWADVANCED_H
+
+#include "llcontrol.h"
+#include "llfloater.h"
+
+class LLFloaterPreferenceViewAdvanced
+:	public LLFloater
+{
+	friend class LLFloaterReg;
+
+public:
+	LLFloaterPreferenceViewAdvanced(const LLSD& key);
+	virtual void draw();
+
+	void onCommitSettings();
+	void updateCameraControl(const LLVector3& vector);
+	void updateFocusControl(const LLVector3d& vector3d);
+
+private:
+	virtual ~LLFloaterPreferenceViewAdvanced();
+};
+
+#endif //LLFLOATERPREFERENCEVIEWADVANCED_H
+
diff --git a/indra/newview/llfloatersavecamerapreset.cpp b/indra/newview/llfloatersavecamerapreset.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..11809f9c824369c5793b7027bede4b48f441a1d4
--- /dev/null
+++ b/indra/newview/llfloatersavecamerapreset.cpp
@@ -0,0 +1,172 @@
+/** 
+ * @file llfloatersavecamerapreset.cpp
+ * @brief Floater to save a camera preset
+ *
+ * $LicenseInfo:firstyear=2020&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2020, 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 "llfloatersavecamerapreset.h"
+
+#include "llagent.h"
+#include "llagentcamera.h"
+#include "llbutton.h"
+#include "llcombobox.h"
+#include "llfloaterpreference.h"
+#include "llfloaterreg.h"
+#include "lllineeditor.h"
+#include "llnotificationsutil.h"
+#include "llpresetsmanager.h"
+#include "llradiogroup.h"
+#include "lltrans.h"
+#include "llvoavatarself.h"
+
+LLFloaterSaveCameraPreset::LLFloaterSaveCameraPreset(const LLSD &key)
+	: LLModalDialog(key)
+{
+}
+
+// virtual
+BOOL LLFloaterSaveCameraPreset::postBuild()
+{
+	mPresetCombo = getChild<LLComboBox>("preset_combo");
+
+	mNameEditor = getChild<LLLineEditor>("preset_txt_editor");
+	mNameEditor->setKeystrokeCallback(boost::bind(&LLFloaterSaveCameraPreset::onPresetNameEdited, this), NULL);
+
+	mSaveButton = getChild<LLButton>("save");
+	mSaveButton->setCommitCallback(boost::bind(&LLFloaterSaveCameraPreset::onBtnSave, this));
+	
+	mSaveRadioGroup = getChild<LLRadioGroup>("radio_save_preset");
+	mSaveRadioGroup->setCommitCallback(boost::bind(&LLFloaterSaveCameraPreset::onSwitchSaveReplace, this));
+	
+	getChild<LLButton>("cancel")->setCommitCallback(boost::bind(&LLFloaterSaveCameraPreset::onBtnCancel, this));
+
+	LLPresetsManager::instance().setPresetListChangeCallback(boost::bind(&LLFloaterSaveCameraPreset::onPresetsListChange, this));
+
+	return TRUE;
+}
+
+void LLFloaterSaveCameraPreset::onPresetNameEdited()
+{
+	if (mSaveRadioGroup->getSelectedIndex() == 0)
+	{
+		// Disable saving a preset having empty name.
+		std::string name = mNameEditor->getValue();
+		mSaveButton->setEnabled(!name.empty());
+	}
+}
+
+void LLFloaterSaveCameraPreset::onOpen(const LLSD& key)
+{
+	LLModalDialog::onOpen(key);
+	S32 index = 0;
+	if (key.has("index"))
+	{
+		index = key["index"].asInteger();
+	}
+
+	LLPresetsManager::getInstance()->setPresetNamesInComboBox(PRESETS_CAMERA, mPresetCombo, DEFAULT_BOTTOM);
+
+	mSaveRadioGroup->setSelectedIndex(index);
+	onPresetNameEdited();
+	onSwitchSaveReplace();
+}
+
+void LLFloaterSaveCameraPreset::onBtnSave()
+{
+	bool is_saving_new = mSaveRadioGroup->getSelectedIndex() == 0;
+	std::string name = is_saving_new ? mNameEditor->getText() : mPresetCombo->getSimple();
+
+	if ((name == LLTrans::getString(PRESETS_DEFAULT)) || (name == PRESETS_DEFAULT))
+	{
+		LLNotificationsUtil::add("DefaultPresetNotSaved");
+	}
+	else 
+	{
+		if (isAgentAvatarValid() && gAgentAvatarp->getParent())
+		{
+			gSavedSettings.setLLSD("AvatarSitRotation", gAgent.getFrameAgent().getQuaternion().getValue());
+		}
+		if (gAgentCamera.isJoystickCameraUsed())
+		{
+			gSavedSettings.setVector3("CameraOffsetRearView", gAgentCamera.getCurrentCameraOffset());
+			gSavedSettings.setVector3d("FocusOffsetRearView", gAgentCamera.getCurrentFocusOffset());
+			gAgentCamera.resetCameraZoomFraction();
+			gAgentCamera.setFocusOnAvatar(TRUE, TRUE, FALSE);
+		}
+		else
+		{
+			LLVector3 camera_offset = gSavedSettings.getVector3("CameraOffsetRearView") * gAgentCamera.getCurrentCameraZoomFraction();
+			gSavedSettings.setVector3("CameraOffsetRearView", camera_offset);
+			gAgentCamera.resetCameraZoomFraction();
+		}
+		if (is_saving_new)
+		{
+			std::list<std::string> preset_names;
+			LLPresetsManager::getInstance()->loadPresetNamesFromDir(PRESETS_CAMERA, preset_names, DEFAULT_HIDE);
+			if (std::find(preset_names.begin(), preset_names.end(), name) != preset_names.end())
+			{
+				LLSD args;
+				args["NAME"] = name;
+				LLNotificationsUtil::add("PresetAlreadyExists", args);
+				return;
+			}
+		}
+		if (!LLPresetsManager::getInstance()->savePreset(PRESETS_CAMERA, name))
+		{
+			LLSD args;
+			args["NAME"] = name;
+			LLNotificationsUtil::add("PresetNotSaved", args);
+		}
+	}
+
+	closeFloater();
+}
+
+void LLFloaterSaveCameraPreset::onPresetsListChange()
+{
+	LLPresetsManager::getInstance()->setPresetNamesInComboBox(PRESETS_CAMERA, mPresetCombo, DEFAULT_BOTTOM);
+}
+
+void LLFloaterSaveCameraPreset::onBtnCancel()
+{
+	closeFloater();
+}
+
+void LLFloaterSaveCameraPreset::onSwitchSaveReplace()
+{
+	bool is_saving_new = mSaveRadioGroup->getSelectedIndex() == 0;
+	std::string label = is_saving_new ? getString("btn_label_save") : getString("btn_label_replace");
+	mSaveButton->setLabel(label);
+	mNameEditor->setEnabled(is_saving_new);
+	mPresetCombo->setEnabled(!is_saving_new);
+	if (is_saving_new)
+	{
+		onPresetNameEdited();
+	}
+	else
+	{
+		mSaveButton->setEnabled(true);
+	}
+}
diff --git a/indra/newview/llfloatersavecamerapreset.h b/indra/newview/llfloatersavecamerapreset.h
new file mode 100644
index 0000000000000000000000000000000000000000..282f213438c7e0b91940941bafa1015d07864a02
--- /dev/null
+++ b/indra/newview/llfloatersavecamerapreset.h
@@ -0,0 +1,60 @@
+/** 
+ * @file llfloatersavecamerapreset.h
+ * @brief Floater to save a camera preset
+
+ *
+ * $LicenseInfo:firstyear=2020&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2020, 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$
+ */
+
+#ifndef LL_LLFLOATERSAVECAMERAPRESET_H
+#define LL_LLFLOATERSAVECAMERAPRESET_H
+
+#include "llmodaldialog.h"
+
+class LLComboBox;
+class LLRadioGroup;
+class LLLineEditor;
+
+class LLFloaterSaveCameraPreset : public LLModalDialog
+{
+
+public:
+	LLFloaterSaveCameraPreset(const LLSD &key);
+
+	/*virtual*/	BOOL	postBuild();
+	/*virtual*/ void	onOpen(const LLSD& key);
+
+	void onBtnSave();
+	void onBtnCancel();
+	void onSwitchSaveReplace();
+
+private:
+	LLRadioGroup*	mSaveRadioGroup;
+	LLLineEditor*	mNameEditor;
+	LLComboBox*		mPresetCombo;
+	LLButton*		mSaveButton;
+
+	void onPresetsListChange();
+	void onPresetNameEdited();
+};
+
+#endif // LL_LLFLOATERSAVECAMERAPRESET_H
diff --git a/indra/newview/llfloatersaveprefpreset.cpp b/indra/newview/llfloatersaveprefpreset.cpp
index 684778c93ac8c1f31389eeaf9478aea19e1c2fef..5f3cf9d95bc195e53c0a066a59a20e9657a3d281 100644
--- a/indra/newview/llfloatersaveprefpreset.cpp
+++ b/indra/newview/llfloatersaveprefpreset.cpp
@@ -1,6 +1,6 @@
 /** 
  * @file llfloatersaveprefpreset.cpp
- * @brief Floater to save a graphics / camera preset
+ * @brief Floater to save a graphics preset
  *
  * $LicenseInfo:firstyear=2014&license=viewerlgpl$
  * Second Life Viewer Source Code
@@ -37,24 +37,27 @@
 #include "lltrans.h"
 
 LLFloaterSavePrefPreset::LLFloaterSavePrefPreset(const LLSD &key)
-:	LLFloater(key)
+	: LLFloater(key)
 {
 }
 
 // virtual
 BOOL LLFloaterSavePrefPreset::postBuild()
-{	LLFloaterPreference* preferences = LLFloaterReg::getTypedInstance<LLFloaterPreference>("preferences");
+{
+	LLFloaterPreference* preferences = LLFloaterReg::getTypedInstance<LLFloaterPreference>("preferences");
 	if (preferences)
 	{
 		preferences->addDependentFloater(this);
 	}
+	
 	getChild<LLComboBox>("preset_combo")->setTextEntryCallback(boost::bind(&LLFloaterSavePrefPreset::onPresetNameEdited, this));
 	getChild<LLComboBox>("preset_combo")->setCommitCallback(boost::bind(&LLFloaterSavePrefPreset::onPresetNameEdited, this));
 	getChild<LLButton>("save")->setCommitCallback(boost::bind(&LLFloaterSavePrefPreset::onBtnSave, this));
+
 	getChild<LLButton>("cancel")->setCommitCallback(boost::bind(&LLFloaterSavePrefPreset::onBtnCancel, this));
 
 	LLPresetsManager::instance().setPresetListChangeCallback(boost::bind(&LLFloaterSavePrefPreset::onPresetsListChange, this));
-
+	
 	mSaveButton = getChild<LLButton>("save");
 	mPresetCombo = getChild<LLComboBox>("preset_combo");
 
@@ -73,10 +76,6 @@ void LLFloaterSavePrefPreset::onOpen(const LLSD& key)
 {
 	mSubdirectory = key.asString();
 
-	std::string floater_title = getString(std::string("title_") + mSubdirectory);
-
-	setTitle(floater_title);
-
 	EDefaultOptions option = DEFAULT_HIDE;
 	LLPresetsManager::getInstance()->setPresetNamesInComboBox(mSubdirectory, mPresetCombo, option);
 
diff --git a/indra/newview/llfloatersaveprefpreset.h b/indra/newview/llfloatersaveprefpreset.h
index 09a87b8c6296b7cb39fdec8b4953736eb19e03db..ae58180e7f46dbc1cc0a8c8f93f45b39ca911120 100644
--- a/indra/newview/llfloatersaveprefpreset.h
+++ b/indra/newview/llfloatersaveprefpreset.h
@@ -1,6 +1,6 @@
 /** 
  * @file llfloatersaveprefpreset.h
- * @brief Floater to save a graphics / camera preset
+ * @brief Floater to save a graphics preset
 
  *
  * $LicenseInfo:firstyear=2014&license=viewerlgpl$
@@ -45,6 +45,7 @@ class LLFloaterSavePrefPreset : public LLFloater
 	void onBtnCancel();
 
 private:
+
 	LLComboBox*		mPresetCombo;
 	LLButton*		mSaveButton;
 
diff --git a/indra/newview/llfloatersettingsdebug.cpp b/indra/newview/llfloatersettingsdebug.cpp
index c27723db1da74e25c4e7edf1ac897de3b1c54cbf..77d54c557611f3f05cc291b0bc8aa1f11372543b 100644
--- a/indra/newview/llfloatersettingsdebug.cpp
+++ b/indra/newview/llfloatersettingsdebug.cpp
@@ -30,6 +30,9 @@
 #include "lluictrlfactory.h"
 //#include "llfirstuse.h"
 #include "llcombobox.h"
+// [RLVa:KB] - Patch: RLVa-2.1.0
+#include "llsdserialize.h"
+// [/RLVa:KB]
 #include "llspinctrl.h"
 #include "llcolorswatch.h"
 #include "llviewercontrol.h"
@@ -116,6 +119,7 @@ void LLFloaterSettingsDebug::onCommitSettings()
 
 	LLVector3 vector;
 	LLVector3d vectord;
+	LLQuaternion quat;
 	LLRect rect;
 	LLColor4 col4;
 	LLColor3 col3;
@@ -160,6 +164,18 @@ void LLFloaterSettingsDebug::onCommitSettings()
 		rect.mRight = mValY->getValue().asInteger();
 		rect.mBottom = mValZ->getValue().asInteger();
 		rect.mTop = mValW->getValue().asInteger();
+	  case TYPE_QUAT:
+		quat.mQ[VX] = getChild<LLUICtrl>("val_spinner_1")->getValue().asReal();
+		quat.mQ[VY] = getChild<LLUICtrl>("val_spinner_2")->getValue().asReal();
+		quat.mQ[VZ] = getChild<LLUICtrl>("val_spinner_3")->getValue().asReal();
+		quat.mQ[VS] = getChild<LLUICtrl>("val_spinner_4")->getValue().asReal();;
+		controlp->set(quat.getValue());
+		break;
+	  case TYPE_RECT:
+		rect.mLeft = getChild<LLUICtrl>("val_spinner_1")->getValue().asInteger();
+		rect.mRight = getChild<LLUICtrl>("val_spinner_2")->getValue().asInteger();
+		rect.mBottom = getChild<LLUICtrl>("val_spinner_3")->getValue().asInteger();
+		rect.mTop = getChild<LLUICtrl>("val_spinner_4")->getValue().asInteger();
 		controlp->set(rect.getValue());
 		break;
 	case TYPE_COL4:
@@ -385,6 +401,40 @@ void LLFloaterSettingsDebug::updateControl(LLControlVariable* controlp)
 			}
 			break;
 		  }
+		  case TYPE_QUAT:
+		  {
+			  LLQuaternion q;
+			  q.setValue(sd);
+			  spinner1->setVisible(TRUE);
+			  spinner1->setLabel(std::string("X"));
+			  spinner2->setVisible(TRUE);
+			  spinner2->setLabel(std::string("Y"));
+			  spinner3->setVisible(TRUE);
+			  spinner3->setLabel(std::string("Z"));
+			  spinner4->setVisible(TRUE);
+			  spinner4->setLabel(std::string("S"));
+			  if (!spinner1->hasFocus())
+			  {
+				  spinner1->setPrecision(4);
+				  spinner1->setValue(q.mQ[VX]);
+			  }
+			  if (!spinner2->hasFocus())
+			  {
+				  spinner2->setPrecision(4);
+				  spinner2->setValue(q.mQ[VY]);
+			  }
+			  if (!spinner3->hasFocus())
+			  {
+				  spinner3->setPrecision(4);
+				  spinner3->setValue(q.mQ[VZ]);
+			  }
+			  if (!spinner4->hasFocus())
+			  {
+				  spinner4->setPrecision(4);
+				  spinner4->setValue(q.mQ[VS]);
+			  }
+			  break;
+		  }
 		  case TYPE_RECT:
 		  {
 			LLRect r;
@@ -528,6 +578,15 @@ void LLFloaterSettingsDebug::updateControl(LLControlVariable* controlp)
 			  }
 			  break;
 		  }
+// [RLVa:KB] - Patch: RLVa-2.1.0
+		  case TYPE_LLSD:
+			  {
+				  std::ostringstream strLLSD;
+				  LLSDSerialize::toPrettyNotation(sd, strLLSD);
+				  mComment->setText(strLLSD.str());
+			  }
+			  break;
+// [/RLVa:KB]
 		  default:
 			mComment->setText(std::string("unknown"));
 			break;
diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp
index 231e4dedcc97a6b9a47cf0e7d3b73a048cf6b4bd..32ac3db8af352f04f3ea6ce607d976670bb3f59c 100644
--- a/indra/newview/llinventoryfunctions.cpp
+++ b/indra/newview/llinventoryfunctions.cpp
@@ -381,7 +381,10 @@ void update_all_marketplace_count()
     return;
 }
 
-void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name)
+//void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name)
+// [RLVa:KB] - Checked: RLVa-2.3 (Give-to-#RLV)
+void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name, LLPointer<LLInventoryCallback> cb)
+// [/RLVa:KB]
 {
 	LLViewerInventoryCategory* cat;
 
@@ -395,7 +398,10 @@ void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::s
 
 	LLSD updates;
 	updates["name"] = new_name;
-	update_inventory_category(cat_id, updates, NULL);
+// [RLVa:KB] - Checked: RLVa-2.3 (Give-to-#RLV)
+	update_inventory_category(cat_id, updates, cb);
+// [/RLVa:KB]
+//	update_inventory_category(cat_id, updates, NULL);
 }
 
 void copy_inventory_category(LLInventoryModel* model,
diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h
index fd106bc2d855b3730b34b22159673ea0a4968087..cb44d0966de86c0d1fb65fb6bbe19298ca20ccf8 100644
--- a/indra/newview/llinventoryfunctions.h
+++ b/indra/newview/llinventoryfunctions.h
@@ -68,7 +68,10 @@ void update_marketplace_category(const LLUUID& cat_id, bool perform_consistency_
 // Nudge all listing categories to signal that their marketplace status changed
 void update_all_marketplace_count();
 
-void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name);
+// [RLVa:KB] - Checked: RLVa-2.3 (Give-to-#RLV)
+void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name, LLPointer<LLInventoryCallback> cb = nullptr);
+// [/RLVa:KB]
+//void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name);
 
 void copy_inventory_category(LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& parent_id, const LLUUID& root_copy_id = LLUUID::null, bool move_no_copy_items = false);
 
diff --git a/indra/newview/lljoystickbutton.cpp b/indra/newview/lljoystickbutton.cpp
index 7fcd6f4361c83f4eeabe918df98ab3ce807931ff..79fafade2d2489cefd87d6ebb70644315824ac11 100644
--- a/indra/newview/lljoystickbutton.cpp
+++ b/indra/newview/lljoystickbutton.cpp
@@ -37,6 +37,7 @@
 #include "llui.h"
 #include "llagent.h"
 #include "llagentcamera.h"
+#include "llviewercamera.h"
 #include "llviewertexture.h"
 #include "llviewertexturelist.h"
 #include "llviewerwindow.h"
@@ -54,6 +55,8 @@ static LLDefaultChildRegistry::Register<LLJoystickQuaternion> r6("joystick_quat"
 const F32 NUDGE_TIME = 0.25f;		// in seconds
 const F32 ORBIT_NUDGE_RATE = 0.05f; // fraction of normal speed
 
+const S32 CENTER_DOT_RADIUS = 7;
+
 //
 // Public Methods
 //
@@ -138,9 +141,25 @@ bool LLJoystick::pointInCircle(S32 x, S32 y) const
 	//center is x and y coordinates of center of joystick circle, and also its radius
 	int center = this->getLocalRect().getHeight()/2;
 	bool in_circle = (x - center) * (x - center) + (y - center) * (y - center) <= center * center;
+	
 	return in_circle;
 }
 
+bool LLJoystick::pointInCenterDot(S32 x, S32 y, S32 radius) const
+{
+	if (this->getLocalRect().getHeight() != this->getLocalRect().getWidth())
+	{
+		LL_WARNS() << "Joystick shape is not square" << LL_ENDL;
+		return true;
+	}
+
+	S32 center = this->getLocalRect().getHeight() / 2;
+
+	bool in_center_circle = (x - center) * (x - center) + (y - center) * (y - center) <= radius * radius;
+
+	return in_center_circle;
+}
+
 BOOL LLJoystick::handleMouseDown(S32 x, S32 y, MASK mask)
 {
 	//LL_INFOS() << "joystick mouse down " << x << ", " << y << LL_ENDL;
@@ -403,8 +422,11 @@ LLJoystickCameraRotate::LLJoystickCameraRotate(const LLJoystickCameraRotate::Par
 	mInLeft( FALSE ),
 	mInTop( FALSE ),
 	mInRight( FALSE ),
-	mInBottom( FALSE )
-{ }
+	mInBottom( FALSE ),
+	mInCenter( FALSE )
+{ 
+	mCenterImageName = "Cam_Rotate_Center";
+}
 
 
 void LLJoystickCameraRotate::updateSlop()
@@ -434,7 +456,16 @@ BOOL LLJoystickCameraRotate::handleMouseDown(S32 x, S32 y, MASK mask)
 	S32 dx = x - horiz_center;
 	S32 dy = y - vert_center;
 
-	if (dy > dx && dy > -dx)
+	if (pointInCenterDot(x, y, CENTER_DOT_RADIUS))
+	{
+		mInitialOffset.mX = 0;
+		mInitialOffset.mY = 0;
+		mInitialQuadrant = JQ_ORIGIN;
+		mInCenter = TRUE;
+
+		resetJoystickCamera();
+	}
+	else if (dy > dx && dy > -dx)
 	{
 		// top
 		mInitialOffset.mX = 0;
@@ -469,9 +500,20 @@ BOOL LLJoystickCameraRotate::handleMouseDown(S32 x, S32 y, MASK mask)
 BOOL LLJoystickCameraRotate::handleMouseUp(S32 x, S32 y, MASK mask)
 {
 	gAgent.setMovementLocked(FALSE);
+	mInCenter = FALSE;
 	return LLJoystick::handleMouseUp(x, y, mask);
 }
 
+BOOL LLJoystickCameraRotate::handleHover(S32 x, S32 y, MASK mask)
+{
+	if (!pointInCenterDot(x, y, CENTER_DOT_RADIUS))
+	{
+		mInCenter = FALSE;
+	}
+
+	return LLJoystick::handleHover(x, y, mask);
+}
+
 void LLJoystickCameraRotate::onHeldDown()
 {
 	updateSlop();
@@ -504,6 +546,11 @@ void LLJoystickCameraRotate::onHeldDown()
 	}
 }
 
+void LLJoystickCameraRotate::resetJoystickCamera()
+{
+	gAgentCamera.resetCameraOrbit();
+}
+
 F32 LLJoystickCameraRotate::getOrbitRate()
 {
 	F32 time = getElapsedHeldDownTime();
@@ -536,24 +583,31 @@ void LLJoystickCameraRotate::draw()
 	getImageUnselected()->draw( 0, 0 );
 	LLPointer<LLUIImage> image = getImageSelected();
 
-	if( mInTop )
+	if (mInCenter)
 	{
-		drawRotatedImage( getImageSelected(), 0 );
+		drawRotatedImage(LLUI::getUIImage(mCenterImageName), 0);
 	}
-
-	if( mInRight )
+	else
 	{
-		drawRotatedImage( getImageSelected(), 1 );
-	}
+		if (mInTop)
+		{
+			drawRotatedImage(getImageSelected(), 0);
+		}
 
-	if( mInBottom )
-	{
-		drawRotatedImage( getImageSelected(), 2 );
-	}
+		if (mInRight)
+		{
+			drawRotatedImage(getImageSelected(), 1);
+		}
 
-	if( mInLeft )
-	{
-		drawRotatedImage( getImageSelected(), 3 );
+		if (mInBottom)
+		{
+			drawRotatedImage(getImageSelected(), 2);
+		}
+
+		if (mInLeft)
+		{
+			drawRotatedImage(getImageSelected(), 3);
+		}
 	}
 }
 
@@ -613,7 +667,9 @@ LLJoystickCameraTrack::Params::Params()
 
 LLJoystickCameraTrack::LLJoystickCameraTrack(const LLJoystickCameraTrack::Params& p)
 :	LLJoystickCameraRotate(p)
-{}
+{
+	mCenterImageName = "Cam_Tracking_Center";
+}
 
 
 void LLJoystickCameraTrack::onHeldDown()
@@ -647,6 +703,11 @@ void LLJoystickCameraTrack::onHeldDown()
 	}
 }
 
+void LLJoystickCameraTrack::resetJoystickCamera()
+{
+	gAgentCamera.resetCameraPan();
+}
+
 //-------------------------------------------------------------------------------
 // LLJoystickQuaternion
 //-------------------------------------------------------------------------------
diff --git a/indra/newview/lljoystickbutton.h b/indra/newview/lljoystickbutton.h
index ee66088b560e77aa1275438263a7f545f5b669e5..b7fdf63e583b4f61e525c5e2b79146053bc134d5 100644
--- a/indra/newview/lljoystickbutton.h
+++ b/indra/newview/lljoystickbutton.h
@@ -80,7 +80,8 @@ class LLJoystick
 	 * Image containing circle is square and this square has adherent points with joystick
 	 * circle. Make sure to change method according to shape other than square. 
 	 */
-	bool			pointInCircle(S32 x, S32 y) const;
+	bool	pointInCircle(S32 x, S32 y) const;
+	bool	pointInCenterDot(S32 x, S32 y, S32 radius) const;
 	
 	static std::string nameFromQuadrant(const EJoystickQuadrant quadrant);
 	static EJoystickQuadrant quadrantFromName(const std::string& name);
@@ -148,7 +149,9 @@ class LLJoystickCameraRotate
 
 	virtual BOOL	handleMouseDown(S32 x, S32 y, MASK mask);
 	virtual BOOL	handleMouseUp(S32 x, S32 y, MASK mask);
+	virtual BOOL	handleHover(S32 x, S32 y, MASK mask);
 	virtual void	onHeldDown();
+	virtual void	resetJoystickCamera();
 	virtual void	draw();
 
 protected:
@@ -161,6 +164,9 @@ class LLJoystickCameraRotate
 	BOOL			mInTop;
 	BOOL			mInRight;
 	BOOL			mInBottom;
+	BOOL			mInCenter;
+
+	std::string		mCenterImageName;
 };
 
 
@@ -177,6 +183,7 @@ class LLJoystickCameraTrack
 
 	LLJoystickCameraTrack(const LLJoystickCameraTrack::Params&);
 	virtual void	onHeldDown();
+	virtual void	resetJoystickCamera();
 };
 
 // 
diff --git a/indra/newview/llpanelnearbymedia.cpp b/indra/newview/llpanelnearbymedia.cpp
index b654e928e2effb2ae4cd04f0c10a71d648bab450..02911313ed41784d3fd020f08a552c560498ea23 100644
--- a/indra/newview/llpanelnearbymedia.cpp
+++ b/indra/newview/llpanelnearbymedia.cpp
@@ -66,9 +66,6 @@ extern LLControlGroup gSavedSettings;
 static const LLUUID PARCEL_MEDIA_LIST_ITEM_UUID = LLUUID("CAB5920F-E484-4233-8621-384CF373A321");
 static const LLUUID PARCEL_AUDIO_LIST_ITEM_UUID = LLUUID("DF4B020D-8A24-4B95-AB5D-CA970D694822");
 
-const F32 AUTO_CLOSE_FADE_TIME_START= 2.0f;
-const F32 AUTO_CLOSE_FADE_TIME_END = 3.0f;
-
 //
 // LLPanelNearByMedia
 //
@@ -82,8 +79,6 @@ LLPanelNearByMedia::LLPanelNearByMedia()
 	  mParcelMediaItem(NULL),
 	  mParcelAudioItem(NULL)
 {
-	mHoverTimer.stop();
-
     // This is just an initial value, mParcelAudioAutoStart does not affect ParcelMediaAutoPlayEnable
     mParcelAudioAutoStart = gSavedSettings.getS32("ParcelMediaAutoPlayEnable") != 0
                             && gSavedSettings.getBOOL("MediaTentativeAutoPlay");
@@ -111,7 +106,7 @@ LLPanelNearByMedia::~LLPanelNearByMedia()
 
 BOOL LLPanelNearByMedia::postBuild()
 {
-	LLPanel::postBuild();
+	LLPanelPulldown::postBuild();
 
 	const S32 RESIZE_BAR_THICKNESS = 6;
 	LLResizeBar::Params p;
@@ -193,45 +188,10 @@ void LLPanelNearByMedia::handleMediaAutoPlayChanged(const LLSD& newvalue)
     inst->cancelNotification();
 }
 
-/*virtual*/
-void LLPanelNearByMedia::onMouseEnter(S32 x, S32 y, MASK mask)
-{
-	mHoverTimer.stop();
-	LLPanel::onMouseEnter(x,y,mask);
-}
-
-
-/*virtual*/
-void LLPanelNearByMedia::onMouseLeave(S32 x, S32 y, MASK mask)
-{
-	mHoverTimer.start();
-	LLPanel::onMouseLeave(x,y,mask);
-}
-
-/*virtual*/ 
-void LLPanelNearByMedia::onTopLost()
-{
-	setVisible(FALSE);
-}
-
-
-/*virtual*/ 
-void LLPanelNearByMedia::onVisibilityChange ( BOOL new_visibility )
-{
-	if (new_visibility)	
-	{
-		mHoverTimer.start(); // timer will be stopped when mouse hovers over panel
-	}
-	else
-	{
-		mHoverTimer.stop();
-	}
-}
-
 /*virtual*/
 void LLPanelNearByMedia::reshape(S32 width, S32 height, BOOL called_from_parent)
 {
-	LLPanel::reshape(width, height, called_from_parent);
+	LLPanelPulldown::reshape(width, height, called_from_parent);
 
 	LLButton* more_btn = findChild<LLButton>("more_btn");
 	if (more_btn && more_btn->getValue().asBoolean())
@@ -255,24 +215,14 @@ void LLPanelNearByMedia::draw()
 
 	refreshList();
 	updateControls();
-	
-	F32 alpha = mHoverTimer.getStarted() 
-		? clamp_rescale(mHoverTimer.getElapsedTimeF32(), AUTO_CLOSE_FADE_TIME_START, AUTO_CLOSE_FADE_TIME_END, 1.f, 0.f)
-		: 1.0f;
-	LLViewDrawContext context(alpha);
 
-	LLPanel::draw();
-
-	if (alpha == 0.f)
-	{
-		setVisible(false);
-	}
+	LLPanelPulldown::draw();
 }
 
 /*virtual*/
 BOOL LLPanelNearByMedia::handleHover(S32 x, S32 y, MASK mask)
 {
-	LLPanel::handleHover(x, y, mask);
+	LLPanelPulldown::handleHover(x, y, mask);
 	
 	// If we are hovering over this panel, make sure to clear any hovered media
 	// ID.  Note that the more general solution would be to clear this ID when
diff --git a/indra/newview/llpanelnearbymedia.h b/indra/newview/llpanelnearbymedia.h
index a9c1b190cfb1ae0c6f7cde07c0d7648bd087ad30..2d898d0aa1e08f8a8a788b03dbac336fb3d761bf 100644
--- a/indra/newview/llpanelnearbymedia.h
+++ b/indra/newview/llpanelnearbymedia.h
@@ -27,7 +27,7 @@
 #ifndef LL_LLPANELNEARBYMEDIA_H
 #define LL_LLPANELNEARBYMEDIA_H
 
-#include "llpanel.h"
+#include "llpanelpulldown.h"
 
 class LLPanelNearbyMedia;
 class LLButton;
@@ -39,16 +39,12 @@ class LLTextBox;
 class LLComboBox;
 class LLViewerMediaImpl;
 
-class LLPanelNearByMedia : public LLPanel
+class LLPanelNearByMedia : public LLPanelPulldown
 {
 public:
 	
 	/*virtual*/ BOOL postBuild();
 	/*virtual*/ void draw();
-	/*virtual*/ void onMouseEnter(S32 x, S32 y, MASK mask);
-	/*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask);
-	/*virtual*/ void onTopLost();
-	/*virtual*/ void onVisibilityChange ( BOOL new_visibility );
 	/*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent);
 	/*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
 
@@ -173,7 +169,6 @@ class LLPanelNearByMedia : public LLPanel
 	
 	LLRect				mMoreRect;
 	LLRect				mLessRect;
-	LLFrameTimer		mHoverTimer;
 	LLScrollListItem*	mParcelMediaItem;
 	LLScrollListItem*	mParcelAudioItem;
 };
diff --git a/indra/newview/llpanelpermissions.cpp b/indra/newview/llpanelpermissions.cpp
index 7efc8c3ec9f68037af26c502e8a8eb97312ee0f6..fecd57b545e15f60dfc223cd37e517d55cdbe74c 100644
--- a/indra/newview/llpanelpermissions.cpp
+++ b/indra/newview/llpanelpermissions.cpp
@@ -425,35 +425,37 @@ void LLPanelPermissions::refresh()
 	// Update creator text field
 	mCreator->setEnabled(TRUE);
 	std::string creator_app_link;
-	//LLSelectMgr::getInstance()->selectGetCreator(mCreatorID, creator_app_link);
-
-// [RLVa:KB] - Checked: 2010-11-02 (RLVa-1.2.2a) | Modified: RLVa-1.2.2a
-	BOOL creators_identical = FALSE;
-// [/RLVa:KB]
 // [RLVa:KB] - Checked: 2010-11-02 (RLVa-1.2.2a) | Modified: RLVa-1.2.2a
-	creators_identical = LLSelectMgr::getInstance()->selectGetCreator(mCreatorID, creator_app_link);
+	const bool creators_identical = LLSelectMgr::getInstance()->selectGetCreator(mCreatorID, creator_app_link);
+	std::string owner_app_link;
+	const bool owners_identical = LLSelectMgr::getInstance()->selectGetOwner(mOwnerID, owner_app_link);
 // [/RLVa:KB]
-//	LLSelectMgr::getInstance()->selectGetCreator(mCreatorID, creator_name);
 
+	LLAvatarName av_name;
+// [RLVa:KB] - Checked: RLVa-2.0.1
+	// Only anonymize the creator if all of the selection was created by the same avie who's also the owner or they're a nearby avie
+	if ( (RlvActions::isRlvEnabled()) && (creators_identical) && (!RlvActions::canShowName(RlvActions::SNC_DEFAULT, mCreatorID)) && ( (mCreatorID == mOwnerID) || (RlvUtil::isNearbyAgent(mCreatorID))) )
+	{
+		creator_app_link = LLSLURL("agent", mCreatorID, "rlvanonym").getSLURLString();
+	}
+	mLabelCreatorName->setText(creator_app_link);
+// [/RLVa:KB]
 	mLabelCreatorName->setEnabled(TRUE);
 
 	// Update owner text field
 	mOwner->setEnabled(TRUE);
 
-	std::string owner_app_link;
-	const BOOL owners_identical = select_mgr->selectGetOwner(mOwnerID, owner_app_link);
+//	std::string owner_app_link;
+//	const BOOL owners_identical = LLSelectMgr::getInstance()->selectGetOwner(mOwnerID, owner_app_link);
+
 
 	LLUUID owner_id = mOwnerID;
 	if (select_mgr->selectIsGroupOwned())
 	{
 		// Group owned already displayed by selectGetOwner
-		LLGroupMgr* group_mgr = LLGroupMgr::getInstance();
-		LLGroupMgrGroupData* group_data = group_mgr->getGroupData(mOwnerID);
-		if (!group_data || group_data && !group_data->isGroupPropertiesDataComplete())
-		{
-			// Triggers refresh
-			group_mgr->sendGroupPropertiesRequest(mOwnerID);
-		}
+// [RLVa:KB] - Checked: RLVa-2.0.1
+		mLabelOwnerName->setValue(owner_app_link);
+// [/RLVa:KB]
 	}
 	else
 	{
@@ -492,6 +494,14 @@ void LLPanelPermissions::refresh()
 	mLabelOwnerName->setValue(owner_slurl);
 // [/RLVa:KB]
 
+//	style_params.link_href = owner_app_link;
+// [RLVa:KB] - Checked: RLVa-2.0.1
+	if ( (RlvActions::isRlvEnabled()) && (owners_identical) && (!RlvActions::canShowName(RlvActions::SNC_DEFAULT, mOwnerID)) )
+	{
+		owner_app_link = LLSLURL("agent", mOwnerID, "rlvanonym").getSLURLString();
+	}
+	mLabelOwnerName->setText(owner_app_link);
+
 	// update group text field
 	mGroup->setEnabled(TRUE);
 	LLUUID group_id;
diff --git a/indra/newview/llpanelpresetscamerapulldown.cpp b/indra/newview/llpanelpresetscamerapulldown.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..183123e534e8a333472d94117ae9475e3d203c12
--- /dev/null
+++ b/indra/newview/llpanelpresetscamerapulldown.cpp
@@ -0,0 +1,149 @@
+/** 
+ * @file llpanelpresetscamerapulldown.cpp
+ * @brief A panel showing a quick way to pick camera presets
+ *
+ * $LicenseInfo:firstyear=2017&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2017, 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 "llpanelpresetscamerapulldown.h"
+
+#include "llviewercontrol.h"
+#include "llstatusbar.h"
+
+#include "llbutton.h"
+#include "lltabcontainer.h"
+#include "llfloatercamera.h"
+#include "llfloaterreg.h"
+#include "llfloaterpreference.h"
+#include "llpresetsmanager.h"
+#include "llsliderctrl.h"
+#include "llscrolllistctrl.h"
+#include "lltrans.h"
+
+///----------------------------------------------------------------------------
+/// Class LLPanelPresetsCameraPulldown
+///----------------------------------------------------------------------------
+
+// Default constructor
+LLPanelPresetsCameraPulldown::LLPanelPresetsCameraPulldown()
+{
+	mCommitCallbackRegistrar.add("Presets.toggleCameraFloater", boost::bind(&LLPanelPresetsCameraPulldown::onViewButtonClick, this, _2));
+	mCommitCallbackRegistrar.add("PresetsCamera.RowClick", boost::bind(&LLPanelPresetsCameraPulldown::onRowClick, this, _2));
+
+	buildFromFile( "panel_presets_camera_pulldown.xml");
+}
+
+BOOL LLPanelPresetsCameraPulldown::postBuild()
+{
+	LLPresetsManager* presetsMgr = LLPresetsManager::getInstance();
+	if (presetsMgr)
+	{
+		// Make sure there is a default preference file
+		presetsMgr->createMissingDefault(PRESETS_CAMERA);
+
+		presetsMgr->startWatching(PRESETS_CAMERA);
+
+		presetsMgr->setPresetListChangeCameraCallback(boost::bind(&LLPanelPresetsCameraPulldown::populatePanel, this));
+	}
+
+	populatePanel();
+
+	return LLPanelPulldown::postBuild();
+}
+
+void LLPanelPresetsCameraPulldown::populatePanel()
+{
+	LLPresetsManager::getInstance()->loadPresetNamesFromDir(PRESETS_CAMERA, mPresetNames, DEFAULT_BOTTOM);
+
+	LLScrollListCtrl* scroll = getChild<LLScrollListCtrl>("preset_camera_list");
+
+	if (scroll && mPresetNames.begin() != mPresetNames.end())
+	{
+		scroll->clearRows();
+
+		std::string active_preset = gSavedSettings.getString("PresetCameraActive");
+		if (active_preset == PRESETS_DEFAULT)
+		{
+			active_preset = LLTrans::getString(PRESETS_DEFAULT);
+		}
+
+		for (std::list<std::string>::const_iterator it = mPresetNames.begin(); it != mPresetNames.end(); ++it)
+		{
+			const std::string& name = *it;
+            LL_DEBUGS() << "adding '" << name << "'" << LL_ENDL;
+            
+			LLSD row;
+			row["columns"][0]["column"] = "preset_name";
+			row["columns"][0]["value"] = name;
+
+			bool is_selected_preset = false;
+			if (name == active_preset)
+			{
+				row["columns"][1]["column"] = "icon";
+				row["columns"][1]["type"] = "icon";
+				row["columns"][1]["value"] = "Check_Mark";
+
+				is_selected_preset = true;
+			}
+
+			LLScrollListItem* new_item = scroll->addElement(row);
+			new_item->setSelected(is_selected_preset);
+		}
+	}
+}
+
+void LLPanelPresetsCameraPulldown::onRowClick(const LLSD& user_data)
+{
+	LLScrollListCtrl* scroll = getChild<LLScrollListCtrl>("preset_camera_list");
+
+	if (scroll)
+	{
+		LLScrollListItem* item = scroll->getFirstSelected();
+		if (item)
+		{
+			std::string name = item->getColumn(1)->getValue().asString();
+
+            LL_DEBUGS() << "selected '" << name << "'" << LL_ENDL;
+			LLFloaterCamera::switchToPreset(name);
+
+			setVisible(FALSE);
+		}
+        else
+        {
+            LL_DEBUGS() << "none selected" << LL_ENDL;
+        }
+	}
+    else
+    {
+        LL_DEBUGS() << "no scroll" << LL_ENDL;
+    }
+}
+
+void LLPanelPresetsCameraPulldown::onViewButtonClick(const LLSD& user_data)
+{
+	// close the minicontrol, we're bringing up the big one
+	setVisible(FALSE);
+
+	LLFloaterReg::toggleInstanceOrBringToFront("camera");
+}
diff --git a/indra/newview/llpanelpresetscamerapulldown.h b/indra/newview/llpanelpresetscamerapulldown.h
new file mode 100644
index 0000000000000000000000000000000000000000..c49bab042e22d8ba89871471865c7b7a53d10b0d
--- /dev/null
+++ b/indra/newview/llpanelpresetscamerapulldown.h
@@ -0,0 +1,49 @@
+/** 
+ * @file llpanelpresetscamerapulldown.h
+ * @brief A panel showing a quick way to pick camera presets
+ *
+ * $LicenseInfo:firstyear=2017&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2017, 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$
+ */
+
+#ifndef LL_LLPANELPRESETSCAMERAPULLDOWN_H
+#define LL_LLPANELPRESETSCAMERAPULLDOWN_H
+
+#include "linden_common.h"
+
+#include "llpanelpulldown.h"
+
+class LLPanelPresetsCameraPulldown : public LLPanelPulldown
+{
+ public:
+	LLPanelPresetsCameraPulldown();
+	/*virtual*/ BOOL postBuild();
+	void populatePanel();
+	
+ private:
+	void onViewButtonClick(const LLSD& user_data);
+	void onRowClick(const LLSD& user_data);
+
+	std::list<std::string> mPresetNames;
+    LOG_CLASS(LLPanelPresetsCameraPulldown);
+};
+
+#endif // LL_LLPANELPRESETSCAMERAPULLDOWN_H
diff --git a/indra/newview/llpanelpresetspulldown.cpp b/indra/newview/llpanelpresetspulldown.cpp
index 377cf0d7e140975974c74f9fe875cd26ac3f36e7..1ebbd09270744c9b81f1ab945900ed6251731288 100644
--- a/indra/newview/llpanelpresetspulldown.cpp
+++ b/indra/newview/llpanelpresetspulldown.cpp
@@ -66,13 +66,20 @@ LLPanelPresetsPulldown::LLPanelPresetsPulldown()
 
 BOOL LLPanelPresetsPulldown::postBuild()
 {
+	LLPresetsManager* presetsMgr = LLPresetsManager::getInstance();
+    presetsMgr->setPresetListChangeCallback(boost::bind(&LLPanelPresetsPulldown::populatePanel, this));
+	// Make sure there is a default preference file
+    presetsMgr->createMissingDefault(PRESETS_GRAPHIC);
+
 	populatePanel();
 
-	return LLPanel::postBuild();
+	return LLPanelPulldown::postBuild();
 }
 
 void LLPanelPresetsPulldown::populatePanel()
 {
+	LLPresetsManager::getInstance()->loadPresetNamesFromDir(PRESETS_GRAPHIC, mPresetNames, DEFAULT_TOP);
+
 	LLScrollListCtrl* scroll = getChild<LLScrollListCtrl>("preset_list");
 	scroll->clearRows();
 
@@ -226,19 +233,3 @@ void LLPanelPresetsPulldown::onGraphicsButtonClick()
 	// bring up the prefs floater
 	gDragonLibrary.openPreferences("display");
 }
-
-//virtual
-void LLPanelPresetsPulldown::draw()
-{
-	F32 alpha = mHoverTimer.getStarted() 
-		? clamp_rescale(mHoverTimer.getElapsedTimeF32(), sAutoCloseFadeStartTimeSec, sAutoCloseTotalTimeSec, 1.f, 0.f)
-		: 1.0f;
-	LLViewDrawContext context(alpha);
-
-	LLPanel::draw();
-
-	if (alpha == 0.f)
-	{
-		setVisible(FALSE);
-	}
-}
diff --git a/indra/newview/llpanelpresetspulldown.h b/indra/newview/llpanelpresetspulldown.h
index b4a860bcf78d471bfba36d2d1c631b93a8140373..f9de7c118a556a0085e0d91926710770e45176f4 100644
--- a/indra/newview/llpanelpresetspulldown.h
+++ b/indra/newview/llpanelpresetspulldown.h
@@ -29,22 +29,13 @@
 
 #include "linden_common.h"
 
-#include "llpanel.h"
+#include "llpanelpulldown.h"
 
-class LLFrameTimer;
 
-class LLPanelPresetsPulldown : public LLPanel
+class LLPanelPresetsPulldown : public LLPanelPulldown
 {
  public:
 	LLPanelPresetsPulldown();
-	/*virtual*/ void draw();
-	/*virtual*/ void onMouseEnter(S32 x, S32 y, MASK mask);
-	/*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask);
-    /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
-    /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
-    /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
-	/*virtual*/ void onTopLost();
-	/*virtual*/ void onVisibilityChange ( BOOL new_visibility );
 	/*virtual*/ BOOL postBuild();
 	void populatePanel();
 	
@@ -53,9 +44,6 @@ class LLPanelPresetsPulldown : public LLPanel
 	void onRowClick(const LLSD& user_data);
 
 	std::list<std::string> mPresetNames;
-	LLFrameTimer mHoverTimer;
-	static const F32 sAutoCloseFadeStartTimeSec;
-	static const F32 sAutoCloseTotalTimeSec;
     LOG_CLASS(LLPanelPresetsPulldown);
 };
 
diff --git a/indra/newview/llpanelpulldown.cpp b/indra/newview/llpanelpulldown.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4de6ee81829eadd0910a13d049d02035a70bbbba
--- /dev/null
+++ b/indra/newview/llpanelpulldown.cpp
@@ -0,0 +1,118 @@
+/**
+* @file llpanelpulldown.cpp
+* @brief A panel that serves as a basis for multiple toolbar pulldown panels
+*
+* $LicenseInfo:firstyear=2020&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2020, 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 "llpanelpulldown.h"
+
+const F32 AUTO_CLOSE_FADE_TIME_START_SEC = 2.0f;
+const F32 AUTO_CLOSE_FADE_TIME_END_SEC = 3.0f;
+
+///----------------------------------------------------------------------------
+/// Class LLPanelPresetsCameraPulldown
+///----------------------------------------------------------------------------
+
+// Default constructor
+LLPanelPulldown::LLPanelPulldown()
+{
+    mHoverTimer.stop();
+}
+
+/*virtual*/
+void LLPanelPulldown::onMouseEnter(S32 x, S32 y, MASK mask)
+{
+    mHoverTimer.stop();
+    LLPanel::onMouseEnter(x, y, mask);
+}
+
+/*virtual*/
+void LLPanelPulldown::onTopLost()
+{
+    setVisible(FALSE);
+}
+
+/*virtual*/
+BOOL LLPanelPulldown::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+    LLPanel::handleMouseDown(x, y, mask);
+    return TRUE;
+}
+
+/*virtual*/
+BOOL LLPanelPulldown::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+    LLPanel::handleRightMouseDown(x, y, mask);
+    return TRUE;
+}
+
+/*virtual*/
+BOOL LLPanelPulldown::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+    LLPanel::handleDoubleClick(x, y, mask);
+    return TRUE;
+}
+
+BOOL LLPanelPulldown::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+    LLPanel::handleScrollWheel(x, y, clicks);
+    return TRUE; //If we got here, then we are in Pulldown's rect, consume the event.
+}
+
+/*virtual*/
+void LLPanelPulldown::onMouseLeave(S32 x, S32 y, MASK mask)
+{
+    mHoverTimer.start();
+    LLPanel::onMouseLeave(x, y, mask);
+}
+
+/*virtual*/
+void LLPanelPulldown::onVisibilityChange(BOOL new_visibility)
+{
+    if (new_visibility)
+    {
+        mHoverTimer.start(); // timer will be stopped when mouse hovers over panel
+    }
+    else
+    {
+        mHoverTimer.stop();
+    }
+}
+
+//virtual
+void LLPanelPulldown::draw()
+{
+    F32 alpha = mHoverTimer.getStarted()
+        ? clamp_rescale(mHoverTimer.getElapsedTimeF32(), AUTO_CLOSE_FADE_TIME_START_SEC, AUTO_CLOSE_FADE_TIME_END_SEC, 1.f, 0.f)
+        : 1.0f;
+    LLViewDrawContext context(alpha);
+
+    LLPanel::draw();
+
+    if (alpha == 0.f)
+    {
+        setVisible(FALSE);
+    }
+}
diff --git a/indra/newview/llpanelpulldown.h b/indra/newview/llpanelpulldown.h
new file mode 100644
index 0000000000000000000000000000000000000000..705e76d0ab2556771806218d07aa790b731b429b
--- /dev/null
+++ b/indra/newview/llpanelpulldown.h
@@ -0,0 +1,55 @@
+/** 
+ * @file llpanelpulldown.h
+ * @brief A panel that serves as a basis for multiple toolbar pulldown panels
+ *
+ * $LicenseInfo:firstyear=2020&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2020, 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$
+ */
+
+#ifndef LL_LLPANELPULLDOWN_H
+#define LL_LLPANELPULLDOWN_H
+
+#include "linden_common.h"
+
+#include "llpanel.h"
+
+class LLFrameTimer;
+
+class LLPanelPulldown : public LLPanel
+{
+public:
+    LLPanelPulldown();
+    /*virtual*/ void onMouseEnter(S32 x, S32 y, MASK mask);
+    /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask);
+    /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+    /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+    /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+    /*virtual*/ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+    /*virtual*/ void onTopLost();
+    /*virtual*/ void onVisibilityChange(BOOL new_visibility);
+
+    /*virtual*/ void draw();
+
+protected:
+    LLFrameTimer mHoverTimer;
+};
+
+#endif // LL_LLPANELPULLDOWN_H
diff --git a/indra/newview/llpanelvolumepulldown.cpp b/indra/newview/llpanelvolumepulldown.cpp
index f063d8427283b670a1f732f8691b9947e3040de3..6f11e76a72d304fa5923bd5fcf20371ac5a04552 100644
--- a/indra/newview/llpanelvolumepulldown.cpp
+++ b/indra/newview/llpanelvolumepulldown.cpp
@@ -41,9 +41,6 @@
 #include "llfloaterpreference.h"
 #include "llsliderctrl.h"
 
-/* static */ const F32 LLPanelVolumePulldown::sAutoCloseFadeStartTimeSec = 2.0f;
-/* static */ const F32 LLPanelVolumePulldown::sAutoCloseTotalTimeSec = 3.0f;
-
 ///----------------------------------------------------------------------------
 /// Class LLPanelVolumePulldown
 ///----------------------------------------------------------------------------
@@ -51,8 +48,6 @@
 // Default constructor
 LLPanelVolumePulldown::LLPanelVolumePulldown()
 {
-	mHoverTimer.stop();
-
 	mCommitCallbackRegistrar.add("Vol.setControlFalse", boost::bind(&LLPanelVolumePulldown::setControlFalse, this, _2));
 	mCommitCallbackRegistrar.add("Vol.SetSounds", boost::bind(&LLPanelVolumePulldown::onClickSetSounds, this));
 	mCommitCallbackRegistrar.add("Vol.updateMediaAutoPlayCheckbox",	boost::bind(&LLPanelVolumePulldown::updateMediaAutoPlayCheckbox, this, _1));
@@ -62,41 +57,7 @@ LLPanelVolumePulldown::LLPanelVolumePulldown()
 
 BOOL LLPanelVolumePulldown::postBuild()
 {
-	return LLPanel::postBuild();
-}
-
-/*virtual*/
-void LLPanelVolumePulldown::onMouseEnter(S32 x, S32 y, MASK mask)
-{
-	mHoverTimer.stop();
-	LLPanel::onMouseEnter(x,y,mask);
-}
-
-/*virtual*/
-void LLPanelVolumePulldown::onTopLost()
-{
-	setVisible(FALSE);
-}
-
-/*virtual*/
-void LLPanelVolumePulldown::onMouseLeave(S32 x, S32 y, MASK mask)
-{
-	mHoverTimer.start();
-	LLPanel::onMouseLeave(x,y,mask);
-}
-
-/*virtual*/ 
-void LLPanelVolumePulldown::onVisibilityChange ( BOOL new_visibility )
-{
-	if (new_visibility)	
-	{
-		mHoverTimer.start(); // timer will be stopped when mouse hovers over panel
-	}
-	else
-	{
-		mHoverTimer.stop();
-
-	}
+	return LLPanelPulldown::postBuild();
 }
 
 void LLPanelVolumePulldown::onAdvancedButtonClick(const LLSD& user_data)
@@ -150,20 +111,3 @@ void LLPanelVolumePulldown::onClickSetSounds()
 	// or if sound effects are disabled.
 	getChild<LLCheckBoxCtrl>("gesture_audio_play_btn")->setEnabled(!gSavedSettings.getBOOL("MuteSounds"));
 }
-
-//virtual
-void LLPanelVolumePulldown::draw()
-{
-	F32 alpha = mHoverTimer.getStarted() 
-		? clamp_rescale(mHoverTimer.getElapsedTimeF32(), sAutoCloseFadeStartTimeSec, sAutoCloseTotalTimeSec, 1.f, 0.f)
-		: 1.0f;
-	LLViewDrawContext context(alpha);
-
-	LLPanel::draw();
-
-	if (alpha == 0.f)
-	{
-		setVisible(FALSE);
-	}
-}
-
diff --git a/indra/newview/llpanelvolumepulldown.h b/indra/newview/llpanelvolumepulldown.h
index 4f23112f5000112a43a4206dd7679cb35201237f..e907bb0c78fa36dbd4623c8187a14bc39d2e0799 100644
--- a/indra/newview/llpanelvolumepulldown.h
+++ b/indra/newview/llpanelvolumepulldown.h
@@ -30,19 +30,12 @@
 
 #include "linden_common.h"
 
-#include "llpanel.h"
+#include "llpanelpulldown.h"
 
-class LLFrameTimer;
-
-class LLPanelVolumePulldown : public LLPanel
+class LLPanelVolumePulldown : public LLPanelPulldown
 {
  public:
 	LLPanelVolumePulldown();
-	/*virtual*/ void draw();
-	/*virtual*/ void onMouseEnter(S32 x, S32 y, MASK mask);
-	/*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask);
-	/*virtual*/ void onTopLost();
-	/*virtual*/ void onVisibilityChange ( BOOL new_visibility );
 	/*virtual*/ BOOL postBuild();
 	
  private:
@@ -52,10 +45,6 @@ class LLPanelVolumePulldown : public LLPanel
 	// "Streaming Music" and "Media" are unchecked. Otherwise enables it.
 	void updateMediaAutoPlayCheckbox(LLUICtrl* ctrl);
 	void onAdvancedButtonClick(const LLSD& user_data);
-
-	LLFrameTimer mHoverTimer;
-	static const F32 sAutoCloseFadeStartTimeSec;
-	static const F32 sAutoCloseTotalTimeSec;
 };
 
 
diff --git a/indra/newview/llpresetsmanager.cpp b/indra/newview/llpresetsmanager.cpp
index df93572508283b925c5caf22388adbd6a764dc7f..c267c3c699520752b8d15dfc8453a153fec088dc 100644
--- a/indra/newview/llpresetsmanager.cpp
+++ b/indra/newview/llpresetsmanager.cpp
@@ -39,6 +39,8 @@
 #include "llfloaterpreference.h"
 #include "llfloaterreg.h"
 #include "llfeaturemanager.h"
+#include "llagentcamera.h"
+#include "llfile.h"
 
 LLPresetsManager::LLPresetsManager()
 {
@@ -46,6 +48,12 @@ LLPresetsManager::LLPresetsManager()
 
 LLPresetsManager::~LLPresetsManager()
 {
+	mCameraChangedSignal.disconnect();
+}
+
+void LLPresetsManager::triggerChangeCameraSignal()
+{
+	mPresetListChangeCameraSignal();
 }
 
 void LLPresetsManager::triggerChangeSignal()
@@ -53,41 +61,92 @@ void LLPresetsManager::triggerChangeSignal()
 	mPresetListChangeSignal();
 }
 
-void LLPresetsManager::createMissingDefault()
+void LLPresetsManager::createMissingDefault(const std::string& subdirectory)
 {
 	if(gDirUtilp->getLindenUserDir().empty())
 	{
 		return;
 	}
-	std::string default_file = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, PRESETS_DIR, PRESETS_GRAPHIC, PRESETS_DEFAULT + ".xml");
+
+	if (PRESETS_CAMERA == subdirectory)
+	{
+		createCameraDefaultPresets();
+		return;
+	}
+
+	std::string default_file = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, PRESETS_DIR,
+		subdirectory, PRESETS_DEFAULT + ".xml");
 	if (!gDirUtilp->fileExists(default_file))
 	{
 		LL_INFOS() << "No default preset found -- creating one at " << default_file << LL_ENDL;
 
-		// Write current graphic settings as the default
-        savePreset(PRESETS_GRAPHIC, PRESETS_DEFAULT, true);
+		// Write current settings as the default
+		savePreset(subdirectory, PRESETS_DEFAULT, true);
+	}
+	else
+	{
+		LL_DEBUGS() << "default preset exists; no-op" << LL_ENDL;
+	}
+}
+
+void LLPresetsManager::createCameraDefaultPresets()
+{
+	bool is_default_created = createDefaultCameraPreset(PRESETS_REAR_VIEW);
+	is_default_created |= createDefaultCameraPreset(PRESETS_FRONT_VIEW);
+	is_default_created |= createDefaultCameraPreset(PRESETS_SIDE_VIEW);
+
+	if (is_default_created)
+	{
+		triggerChangeCameraSignal();
+	}
+}
+
+void LLPresetsManager::startWatching(const std::string& subdirectory)
+{
+	if (PRESETS_CAMERA == subdirectory)
+	{
+		std::vector<std::string> name_list;
+		getControlNames(name_list);
+
+		for (std::vector<std::string>::iterator it = name_list.begin(); it != name_list.end(); ++it)
+		{
+			std::string ctrl_name = *it;
+			if (gSavedSettings.controlExists(ctrl_name))
+			{
+				LLPointer<LLControlVariable> cntrl_ptr = gSavedSettings.getControl(ctrl_name);
+				if (cntrl_ptr.isNull())
+				{
+					LL_WARNS("Init") << "Unable to set signal on global setting '" << ctrl_name
+						<< "'" << LL_ENDL;
+				}
+				else
+				{
+					mCameraChangedSignal = cntrl_ptr->getCommitSignal()->connect(boost::bind(&settingChanged));
+				}
+			}
+		}
 	}
-    else
-    {
-        LL_DEBUGS() << "default preset exists; no-op" << LL_ENDL;
-    }
 }
 
 std::string LLPresetsManager::getPresetsDir(const std::string& subdirectory)
 {
 	std::string presets_path = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, PRESETS_DIR);
-	std::string full_path;
 
 	LLFile::mkdir(presets_path);
 
-	full_path = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, PRESETS_DIR, subdirectory);
-	LLFile::mkdir(full_path);
+	std::string dest_path = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, PRESETS_DIR, subdirectory);
+	if (!gDirUtilp->fileExists(dest_path))
+		LLFile::mkdir(dest_path);
 
-	return full_path;
+	return dest_path;
 }
 
-void LLPresetsManager::loadPresetNamesFromDir(const std::string& dir, preset_name_list_t& presets, EDefaultOptions default_option)
+void LLPresetsManager::loadPresetNamesFromDir(const std::string& subdirectory, preset_name_list_t& presets, EDefaultOptions default_option)
 {
+	bool IS_CAMERA = (PRESETS_CAMERA == subdirectory);
+	bool IS_GRAPHIC = (PRESETS_GRAPHIC == subdirectory);
+
+	std::string dir = LLPresetsManager::getInstance()->getPresetsDir(subdirectory);
 	LL_INFOS("AppInit") << "Loading list of preset names from " << dir << LL_ENDL;
 
 	mPresetNames.clear();
@@ -103,16 +162,33 @@ void LLPresetsManager::loadPresetNamesFromDir(const std::string& dir, preset_nam
 		{
 			std::string path = gDirUtilp->add(dir, file);
 			std::string name = LLURI::unescape(gDirUtilp->getBaseFileName(path, /*strip_exten = */ true));
-            LL_DEBUGS() << "  Found preset '" << name << "'" << LL_ENDL;
+			LL_DEBUGS() << "  Found preset '" << name << "'" << LL_ENDL;
 
-			if (PRESETS_DEFAULT != name)
+			if (IS_CAMERA)
 			{
+				if (isTemplateCameraPreset(name))
+				{
+					continue;
+				}
+				if ((default_option == DEFAULT_HIDE) || (default_option == DEFAULT_BOTTOM))
+				{
+					if (isDefaultCameraPreset(name))
+					{
+						continue;
+					}
+				}
 				mPresetNames.push_back(name);
 			}
-			else
+			if (IS_GRAPHIC)
 			{
-				switch (default_option)
+				if (PRESETS_DEFAULT != name)
 				{
+					mPresetNames.push_back(name);
+				}
+				else
+				{
+					switch (default_option)
+					{
 					case DEFAULT_SHOW:
 						mPresetNames.push_back(LLTrans::getString(PRESETS_DEFAULT));
 						break;
@@ -124,16 +200,77 @@ void LLPresetsManager::loadPresetNamesFromDir(const std::string& dir, preset_nam
 					case DEFAULT_HIDE:
 					default:
 						break;
+					}
 				}
 			}
 		}
 	}
 
+	if (IS_CAMERA)
+	{
+		mPresetNames.sort(LLStringUtil::precedesDict);
+		if (default_option == DEFAULT_BOTTOM)
+		{
+			mPresetNames.push_back(PRESETS_FRONT_VIEW);
+			mPresetNames.push_back(PRESETS_REAR_VIEW);
+			mPresetNames.push_back(PRESETS_SIDE_VIEW);
+		}
+	}
+
 	presets = mPresetNames;
 }
 
+bool LLPresetsManager::mCameraDirty = false;
+bool LLPresetsManager::mIgnoreChangedSignal = false;
+
+void LLPresetsManager::setCameraDirty(bool dirty)
+{
+	mCameraDirty = dirty;
+}
+
+bool LLPresetsManager::isCameraDirty()
+{
+	return mCameraDirty;
+}
+
+void LLPresetsManager::settingChanged()
+{
+	setCameraDirty(true);
+
+	static LLCachedControl<std::string> preset_camera_active(gSavedSettings, "PresetCameraActive", "");
+	std::string preset_name = preset_camera_active;
+	if (!preset_name.empty() && !mIgnoreChangedSignal)
+	{
+		gSavedSettings.setString("PresetCameraActive", "");
+
+		// Hack call because this is a static routine
+		LLPresetsManager::getInstance()->triggerChangeCameraSignal();
+	}
+}
+
+void LLPresetsManager::getControlNames(std::vector<std::string>& names)
+{
+	const std::vector<std::string> camera_controls = boost::assign::list_of
+		// From panel_preferences_move.xml
+		("CameraAngle")
+		("CameraOffsetScale")
+		("EditCameraMovement")
+		("AppearanceCameraMovement")
+		// From llagentcamera.cpp
+		("CameraOffsetBuild")
+		("TrackFocusObject")
+		("CameraOffsetRearView")
+		("FocusOffsetRearView")
+		("AvatarSitRotation")
+        ;
+    names = camera_controls;
+}
+
 bool LLPresetsManager::savePreset(const std::string& subdirectory, std::string name, bool createDefault)
 {
+	bool IS_CAMERA = (PRESETS_CAMERA == subdirectory);
+	bool IS_GRAPHIC = (PRESETS_GRAPHIC == subdirectory);
+
 	if (LLTrans::getString(PRESETS_DEFAULT) == name)
 	{
 		name = PRESETS_DEFAULT;
@@ -144,126 +281,174 @@ bool LLPresetsManager::savePreset(const std::string& subdirectory, std::string n
 		return false;
 	}
 
+	if (isTemplateCameraPreset(name))
+	{
+		LL_WARNS() << "Should not overwrite template presets" << LL_ENDL;
+		return false;
+	}
+
 	bool saved = false;
 	std::vector<std::string> name_list;
 
-	if(PRESETS_GRAPHIC == subdirectory)
+	if (IS_GRAPHIC)
 	{
 		LLFloaterPreference* instance = LLFloaterReg::findTypedInstance<LLFloaterPreference>("preferences");
 		if (instance && !createDefault)
 		{
-            gSavedSettings.setString("PresetGraphicActive", name);
+			gSavedSettings.setString("PresetGraphicActive", name);
 			instance->getControlNames(name_list);
-            LL_DEBUGS() << "saving preset '" << name << "'; " << name_list.size() << " names" << LL_ENDL;
+			LL_DEBUGS() << "saving preset '" << name << "'; " << name_list.size() << " names" << LL_ENDL;
 			name_list.push_back("PresetGraphicActive");
 		}
-        else
+		else
         {
-            LL_WARNS() << "preferences floater instance not found" << LL_ENDL;
-        }
+			LL_WARNS("Presets") << "preferences floater instance not found" << LL_ENDL;
+		}
 	}
-    else if(PRESETS_CAMERA == subdirectory)
+	else if (IS_CAMERA)
 	{
 		name_list.clear();
-		name_list.push_back("Placeholder");
+		getControlNames(name_list);
+		name_list.push_back("PresetCameraActive");
 	}
-    else
-    {
-        LL_ERRS() << "Invalid presets directory '" << subdirectory << "'" << LL_ENDL;
-    }
-    
-    if (name_list.size() > 1 // if the active preset name is the only thing in the list, don't save the list
-        || (createDefault && name == PRESETS_DEFAULT && subdirectory == PRESETS_GRAPHIC)) // or create a default graphics preset from hw recommended settings 
-    {
-        // make an empty llsd
-        LLSD paramsData(LLSD::emptyMap());
+	else
+	{
+		LL_ERRS() << "Invalid presets directory '" << subdirectory << "'" << LL_ENDL;
+	}
+ 
+	// make an empty llsd
+	LLSD paramsData(LLSD::emptyMap());
 
-        if (createDefault)
-        {
-            paramsData = LLFeatureManager::getInstance()->getRecommendedSettingsMap();
-            if (gSavedSettings.getU32("RenderAvatarMaxComplexity") == 0)
-            {
-                // use the recommended setting as an initial one (MAINT-6435)
-                gSavedSettings.setU32("RenderAvatarMaxComplexity", paramsData["RenderAvatarMaxComplexity"]["Value"].asInteger());
-            }
-        }
-        else
-        {
-            for (std::vector<std::string>::iterator it = name_list.begin(); it != name_list.end(); ++it)
-            {
-                std::string ctrl_name = *it;
-                LLControlVariable* ctrl = gSavedSettings.getControl(ctrl_name).get();
-                std::string comment = ctrl->getComment();
-                std::string type = LLControlGroup::typeEnumToString(ctrl->type());
-                LLSD value = ctrl->getValue();
-
-                paramsData[ctrl_name]["Comment"] = comment;
-                paramsData[ctrl_name]["Persist"] = 1;
-                paramsData[ctrl_name]["Type"] = type;
-                paramsData[ctrl_name]["Value"] = value;
-            }
-        }
-
-        std::string pathName(getPresetsDir(subdirectory) + gDirUtilp->getDirDelimiter() + LLURI::escape(name) + ".xml");
-
-        // write to file
-        llofstream presetsXML(pathName.c_str());
-        if (presetsXML.is_open())
-        {
-            
-            LLPointer<LLSDFormatter> formatter = new LLSDXMLFormatter();
-            formatter->format(paramsData, presetsXML, LLSDFormatter::OPTIONS_PRETTY);
-            presetsXML.close();
-            saved = true;
+	// Create a default graphics preset from hw recommended settings 
+	if (IS_GRAPHIC && createDefault && name == PRESETS_DEFAULT)
+	{
+		paramsData = LLFeatureManager::getInstance()->getRecommendedSettingsMap();
+		if (gSavedSettings.getU32("RenderAvatarMaxComplexity") == 0)
+		{
+			// use the recommended setting as an initial one (MAINT-6435)
+			gSavedSettings.setU32("RenderAvatarMaxComplexity", paramsData["RenderAvatarMaxComplexity"]["Value"].asInteger());
+		}
+	}
+	else
+	{
+		ECameraPreset new_camera_preset = (ECameraPreset)gSavedSettings.getU32("CameraPresetType");
+		bool new_camera_offsets = false;
+		if (IS_CAMERA)
+		{
+			if (isDefaultCameraPreset(name))
+			{
+				if (PRESETS_REAR_VIEW == name)
+				{
+					new_camera_preset = CAMERA_PRESET_REAR_VIEW;
+				}
+				else if (PRESETS_SIDE_VIEW == name)
+				{
+					new_camera_preset = CAMERA_PRESET_GROUP_VIEW;
+				}
+				else if (PRESETS_FRONT_VIEW == name)
+				{
+					new_camera_preset = CAMERA_PRESET_FRONT_VIEW;
+				}
+			}
+			else 
+			{
+				new_camera_preset = CAMERA_PRESET_CUSTOM;
+			}
+			new_camera_offsets = (!isDefaultCameraPreset(name) || (ECameraPreset)gSavedSettings.getU32("CameraPresetType") != new_camera_preset);
+		}
+		for (std::vector<std::string>::iterator it = name_list.begin(); it != name_list.end(); ++it)
+		{
+			std::string ctrl_name = *it;
+
+			LLControlVariable* ctrl = gSavedSettings.getControl(ctrl_name).get();
+			if (ctrl)
+			{
+				std::string comment = ctrl->getComment();
+				std::string type = LLControlGroup::typeEnumToString(ctrl->type());
+				LLSD value = ctrl->getValue();
+
+				paramsData[ctrl_name]["Comment"] = comment;
+				paramsData[ctrl_name]["Persist"] = 1;
+				paramsData[ctrl_name]["Type"] = type;
+				paramsData[ctrl_name]["Value"] = value;
+			}
+		}
+		if (IS_CAMERA)
+		{
+			gSavedSettings.setU32("CameraPresetType", new_camera_preset);
+		}
+	}
+
+	std::string pathName(getPresetsDir(subdirectory) + gDirUtilp->getDirDelimiter() + LLURI::escape(name) + ".xml");
+
+ // If the active preset name is the only thing in the list, don't save the list
+	if (paramsData.size() > 1)
+	{
+		// write to file
+		llofstream presetsXML(pathName.c_str());
+		if (presetsXML.is_open())
+		{
+			LLPointer<LLSDFormatter> formatter = new LLSDXMLFormatter();
+			formatter->format(paramsData, presetsXML, LLSDFormatter::OPTIONS_PRETTY);
+			presetsXML.close();
+			saved = true;
             
-            LL_DEBUGS() << "saved preset '" << name << "'; " << paramsData.size() << " parameters" << LL_ENDL;
-
-            if (!createDefault)
-            {
-                gSavedSettings.setString("PresetGraphicActive", name);
-                // signal interested parties
-                triggerChangeSignal();
-            }
-        }
-        else
-        {
-            LL_WARNS("Presets") << "Cannot open for output preset file " << pathName << LL_ENDL;
-        }
-    }
+			LL_DEBUGS() << "saved preset '" << name << "'; " << paramsData.size() << " parameters" << LL_ENDL;
+
+			if (IS_GRAPHIC)
+			{
+				gSavedSettings.setString("PresetGraphicActive", name);
+				// signal interested parties
+				triggerChangeSignal();
+			}
+
+			if (IS_CAMERA)
+			{
+				gSavedSettings.setString("PresetCameraActive", name);
+				setCameraDirty(false);
+				// signal interested parties
+				triggerChangeCameraSignal();
+			}
+		}
+		else
+		{
+			LL_WARNS("Presets") << "Cannot open for output preset file " << pathName << LL_ENDL;
+		}
+	}
     else
-    {
-        LL_INFOS() << "No settings found; preferences floater has not yet been created" << LL_ENDL;
-    }
+	{
+		LL_INFOS() << "No settings available to be saved" << LL_ENDL;
+	}
     
 	return saved;
 }
 
-void LLPresetsManager::setPresetNamesInComboBox(const std::string& subdirectory, LLComboBox* combo, EDefaultOptions default_option)
+bool LLPresetsManager::setPresetNamesInComboBox(const std::string& subdirectory, LLComboBox* combo, EDefaultOptions default_option)
 {
+	bool sts = true;
+
 	combo->clearRows();
+	combo->setEnabled(TRUE);
 
-	std::string presets_dir = getPresetsDir(subdirectory);
+	std::list<std::string> preset_names;
+	loadPresetNamesFromDir(subdirectory, preset_names, default_option);
 
-	if (!presets_dir.empty())
+	if (preset_names.begin() != preset_names.end())
 	{
-		std::list<std::string> preset_names;
-		loadPresetNamesFromDir(presets_dir, preset_names, default_option);
-
-		std::string preset_graphic_active = gSavedSettings.getString("PresetGraphicActive");
-
-		if (preset_names.begin() != preset_names.end())
-		{
-			for (std::list<std::string>::const_iterator it = preset_names.begin(); it != preset_names.end(); ++it)
-			{
-				const std::string& name = *it;
-				combo->add(name, LLSD().with(0, name));
-			}
-		}
-		else
+		for (std::list<std::string>::const_iterator it = preset_names.begin(); it != preset_names.end(); ++it)
 		{
-			combo->setLabel(LLTrans::getString("preset_combo_label"));
+			const std::string& name = *it;
+			combo->add(name, name);
 		}
 	}
+	else
+	{
+		combo->setLabel(LLTrans::getString("preset_combo_label"));
+		combo->setEnabled(PRESETS_CAMERA != subdirectory);
+		sts = false;
+	}
+
+	return sts;
 }
 
 void LLPresetsManager::loadPreset(const std::string& subdirectory, std::string name)
@@ -277,24 +462,32 @@ void LLPresetsManager::loadPreset(const std::string& subdirectory, std::string n
 
     LL_DEBUGS() << "attempting to load preset '"<<name<<"' from '"<<full_path<<"'" << LL_ENDL;
 
+	mIgnoreChangedSignal = true;
 	if(gSavedSettings.loadFromFile(full_path, false, true) > 0)
 	{
+		mIgnoreChangedSignal = false;
 		if(PRESETS_GRAPHIC == subdirectory)
 		{
 			gSavedSettings.setString("PresetGraphicActive", name);
-		}
 
-		LLFloaterPreference* instance = LLFloaterReg::findTypedInstance<LLFloaterPreference>("preferences");
-		if (instance)
+			LLFloaterPreference* instance = LLFloaterReg::findTypedInstance<LLFloaterPreference>("preferences");
+			if (instance)
+			{
+				instance->refreshEnabledGraphics();
+			}
+			triggerChangeSignal();
+		}
+		if(PRESETS_CAMERA == subdirectory)
 		{
-			instance->refreshEnabledGraphics();
+			gSavedSettings.setString("PresetCameraActive", name);
+			triggerChangeCameraSignal();
 		}
-		triggerChangeSignal();
 	}
-    else
-    {
-        LL_WARNS() << "failed to load preset '"<<name<<"' from '"<<full_path<<"'" << LL_ENDL;
-    }
+	else
+	{
+		mIgnoreChangedSignal = false;
+		LL_WARNS("Presets") << "failed to load preset '"<<name<<"' from '"<<full_path<<"'" << LL_ENDL;
+	}
 }
 
 bool LLPresetsManager::deletePreset(const std::string& subdirectory, std::string name)
@@ -320,17 +513,70 @@ bool LLPresetsManager::deletePreset(const std::string& subdirectory, std::string
 	}
 
 	// If you delete the preset that is currently marked as loaded then also indicate that no preset is loaded.
-	if (gSavedSettings.getString("PresetGraphicActive") == name)
+	if(PRESETS_GRAPHIC == subdirectory)
 	{
-		gSavedSettings.setString("PresetGraphicActive", "");
+		if (gSavedSettings.getString("PresetGraphicActive") == name)
+		{
+			gSavedSettings.setString("PresetGraphicActive", "");
+		}
+		// signal interested parties
+		triggerChangeSignal();
 	}
 
-	// signal interested parties
-	triggerChangeSignal();
+	if(PRESETS_CAMERA == subdirectory)
+	{
+		if (gSavedSettings.getString("PresetCameraActive") == name)
+		{
+			gSavedSettings.setString("PresetCameraActive", "");
+		}
+		// signal interested parties
+		triggerChangeCameraSignal();
+	}
 
 	return sts;
 }
 
+bool LLPresetsManager::isDefaultCameraPreset(std::string preset_name)
+{
+	return (preset_name == PRESETS_REAR_VIEW || preset_name == PRESETS_SIDE_VIEW || preset_name == PRESETS_FRONT_VIEW);
+}
+
+bool LLPresetsManager::isTemplateCameraPreset(std::string preset_name)
+{
+	return (preset_name == PRESETS_REAR || preset_name == PRESETS_SIDE || preset_name == PRESETS_FRONT);
+}
+
+void LLPresetsManager::resetCameraPreset(std::string preset_name)
+{
+	if (isDefaultCameraPreset(preset_name))
+	{
+		createDefaultCameraPreset(preset_name, true);
+
+		if (gSavedSettings.getString("PresetCameraActive") == preset_name)
+		{
+			loadPreset(PRESETS_CAMERA, preset_name);
+		}
+	}
+}
+
+bool LLPresetsManager::createDefaultCameraPreset(std::string preset_name, bool force_reset)
+{
+	std::string preset_file = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, PRESETS_DIR,
+		PRESETS_CAMERA, LLURI::escape(preset_name) + ".xml");
+	if (!gDirUtilp->fileExists(preset_file) || force_reset)
+	{
+		std::string template_name = preset_name.substr(0, preset_name.size() - PRESETS_VIEW_SUFFIX.size());
+		std::string default_template_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, PRESETS_CAMERA, template_name + ".xml");
+		return LLFile::copy(default_template_file, preset_file);
+	}
+	return false;
+}
+
+boost::signals2::connection LLPresetsManager::setPresetListChangeCameraCallback(const preset_list_signal_t::slot_type& cb)
+{
+	return mPresetListChangeCameraSignal.connect(cb);
+}
+
 boost::signals2::connection LLPresetsManager::setPresetListChangeCallback(const preset_list_signal_t::slot_type& cb)
 {
 	return mPresetListChangeSignal.connect(cb);
diff --git a/indra/newview/llpresetsmanager.h b/indra/newview/llpresetsmanager.h
index 0014e32267ffd026a9c698e29a3dd20d8b2fb7e6..d5b384ceb9559e25052fedd444581e918ed42d79 100644
--- a/indra/newview/llpresetsmanager.h
+++ b/indra/newview/llpresetsmanager.h
@@ -36,11 +36,19 @@ static const std::string PRESETS_DEFAULT = "Default";
 static const std::string PRESETS_DIR = "presets";
 static const std::string PRESETS_GRAPHIC = "graphic";
 static const std::string PRESETS_CAMERA = "camera";
+static const std::string PRESETS_REAR = "Rear";
+static const std::string PRESETS_FRONT = "Front";
+static const std::string PRESETS_SIDE = "Side";
+static const std::string PRESETS_VIEW_SUFFIX = " View";
+static const std::string PRESETS_REAR_VIEW = PRESETS_REAR + PRESETS_VIEW_SUFFIX;
+static const std::string PRESETS_FRONT_VIEW = PRESETS_FRONT + PRESETS_VIEW_SUFFIX;
+static const std::string PRESETS_SIDE_VIEW = PRESETS_SIDE + PRESETS_VIEW_SUFFIX;
 
 enum EDefaultOptions
 {
 	DEFAULT_SHOW,
 	DEFAULT_TOP,
+	DEFAULT_BOTTOM,
 	DEFAULT_HIDE				// Do not display "Default" in a list
 };
 
@@ -54,26 +62,47 @@ class LLPresetsManager : public LLSingleton<LLPresetsManager>
 	typedef std::list<std::string> preset_name_list_t;
 	typedef boost::signals2::signal<void()> preset_list_signal_t;
 
-	void createMissingDefault();
+	void createMissingDefault(const std::string& subdirectory);
+	void startWatching(const std::string& subdirectory);
+	void triggerChangeCameraSignal();
 	void triggerChangeSignal();
 	static std::string getPresetsDir(const std::string& subdirectory);
-	void setPresetNamesInComboBox(const std::string& subdirectory, LLComboBox* combo, EDefaultOptions default_option);
-	void loadPresetNamesFromDir(const std::string& dir, preset_name_list_t& presets, EDefaultOptions default_option);
+	bool setPresetNamesInComboBox(const std::string& subdirectory, LLComboBox* combo, EDefaultOptions default_option);
+	void loadPresetNamesFromDir(const std::string& subdirectory, preset_name_list_t& presets, EDefaultOptions default_option);
 	bool savePreset(const std::string& subdirectory, std::string name, bool createDefault = false);
 	void loadPreset(const std::string& subdirectory, std::string name);
 	bool deletePreset(const std::string& subdirectory, std::string name);
+	bool isCameraDirty();
+	static void setCameraDirty(bool dirty);
+
+	void createCameraDefaultPresets();
+
+	bool isTemplateCameraPreset(std::string preset_name);
+	bool isDefaultCameraPreset(std::string preset_name);
+	void resetCameraPreset(std::string preset_name);
+	bool createDefaultCameraPreset(std::string preset_name, bool force_reset = false);
 
 	// Emitted when a preset gets loaded, deleted, or saved.
+	boost::signals2::connection setPresetListChangeCameraCallback(const preset_list_signal_t::slot_type& cb);
 	boost::signals2::connection setPresetListChangeCallback(const preset_list_signal_t::slot_type& cb);
 
 	// Emitted when a preset gets loaded or saved.
 
 	preset_name_list_t mPresetNames;
 
+	preset_list_signal_t mPresetListChangeCameraSignal;
 	preset_list_signal_t mPresetListChangeSignal;
 
   private:
-    LOG_CLASS(LLPresetsManager);
+	LOG_CLASS(LLPresetsManager);
+
+	void getControlNames(std::vector<std::string>& names);
+	static void settingChanged();
+
+	boost::signals2::connection	mCameraChangedSignal;
+
+	static bool	mCameraDirty;
+	static bool mIgnoreChangedSignal;
 };
 
 #endif // LL_PRESETSMANAGER_H
diff --git a/indra/newview/llstatusbar.h b/indra/newview/llstatusbar.h
index ad769a00f7b6d220e951311344c3a854ca6c5196..db7949c916ac66df578a86a8152f4f5a6d18f649 100644
--- a/indra/newview/llstatusbar.h
+++ b/indra/newview/llstatusbar.h
@@ -42,6 +42,7 @@ class LLUICtrl;
 class LLUUID;
 class LLFrameTimer;
 class LLStatGraph;
+class LLPanelPresetsCameraPulldown;
 class LLPanelPresetsPulldown;
 class LLPanelVolumePulldown;
 class LLPanelNearByMedia;
@@ -92,6 +93,7 @@ class LLStatusBar
 	
 	void onVolumeChanged(const LLSD& newvalue);
 
+	void onMouseEnterPresetsCamera();
 	void onMouseEnterPresets();
 	void onMouseEnterVolume();
 	void onMouseEnterNearbyMedia();
@@ -110,7 +112,8 @@ class LLStatusBar
 	LLStatGraph *mSGBandwidth;
 	LLStatGraph *mSGPacketLoss;
 
-	LLIconCtrl	*mIconPresets;
+	LLIconCtrl	*mIconPresetsCamera;
+	LLIconCtrl	*mIconPresetsGraphic;
 	LLButton	*mBtnVolume;
 	LLTextBox	*mBoxBalance;
 	LLButton	*mMediaToggle;
@@ -122,6 +125,7 @@ class LLStatusBar
 	S32				mSquareMetersCredit;
 	S32				mSquareMetersCommitted;
 	LLFrameTimer*	mHealthTimer;
+	LLPanelPresetsCameraPulldown* mPanelPresetsCameraPulldown;
 	LLPanelPresetsPulldown* mPanelPresetsPulldown;
 	LLPanelVolumePulldown* mPanelVolumePulldown;
 	LLPanelNearByMedia*	mPanelNearByMedia;
diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
index 34c8088ea98f3b248750300c53dc7e39b2f7f1dd..452c184964a9f00842c90f4ccb7e850ee19072e0 100644
--- a/indra/newview/llviewercontrol.cpp
+++ b/indra/newview/llviewercontrol.cpp
@@ -75,6 +75,7 @@
 #include "llslurl.h"
 #include "llstartup.h"
 // [RLVa:KB] - Checked: 2015-12-27 (RLVa-1.5.0)
+#include "rlvactions.h"
 #include "rlvcommon.h"
 // [/RLVa:KB]
 
@@ -154,6 +155,15 @@ static bool handleAvatarHoverOffsetChanged(const LLSD& newvalue)
 
 static bool handleSetShaderChanged(const LLSD& newvalue)
 {
+// [RLVa:KB] - @setenv
+	if ( (!RlvActions::canChangeEnvironment()) && (LLFeatureManager::getInstance()->isFeatureAvailable("WindLightUseAtmosShaders")) && (!gSavedSettings.getBOOL("WindLightUseAtmosShaders")) )
+	{
+		gSavedSettings.setBOOL("WindLightUseAtmosShaders", TRUE);
+		return true;
+	}
+// [/RLVa:KB]
+
+
 	// changing shader level may invalidate existing cached bump maps, as the shader type determines the format of the bump map it expects - clear and repopulate the bump cache
 	gBumpImageList.destroyGL();
 	gBumpImageList.restoreGL();
diff --git a/indra/newview/llviewerkeyboard.cpp b/indra/newview/llviewerkeyboard.cpp
index 722eb5e1f6ccfc21d2484eef890e051fdd2984c1..0591f13334a3665ee9fa91b55511fc5f59ad0082 100644
--- a/indra/newview/llviewerkeyboard.cpp
+++ b/indra/newview/llviewerkeyboard.cpp
@@ -353,6 +353,7 @@ void camera_spin_around_ccw_sitting( EKeystate s )
 	else
 	{
 		//change camera but do not send keystrokes
+		gAgentCamera.unlockView();
 		gAgentCamera.setOrbitLeftKey( get_orbit_rate() );
 	}
 }
@@ -369,6 +370,7 @@ void camera_spin_around_cw_sitting( EKeystate s )
 	else
 	{
 		//change camera but do not send keystrokes
+		gAgentCamera.unlockView();
 		gAgentCamera.setOrbitRightKey( get_orbit_rate() );
 	}
 }
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 3e69277b742dbc1edf77a01af704f6875d697449..cfb78c69ce43ff0aaf87895089c087a95a915ce0 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -9208,11 +9208,13 @@ class LLWorldEnvSettings : public view_listener_t
 	{
 // [RLVa:KB] - Checked: 2010-03-18 (RLVa-1.2.0a) | Modified: RLVa-1.0.0g
 		if (gRlvHandler.hasBehaviour(RLV_BHVR_SETENV))
+// [RLVa:KB] - @setenv
+		if (!RlvActions::canChangeEnvironment())
 			return true;
 // [/RLVa:KB]
 
 		std::string event_name = userdata.asString();
-
+		
 		if (event_name == "sunrise")
 		{
             LLEnvironment::instance().setEnvironment(LLEnvironment::ENV_LOCAL, LLEnvironment::KNOWN_SKY_SUNRISE);
@@ -10261,11 +10263,8 @@ void initialize_menus()
 
 // [RLVa:KB] - Checked: RLVa-2.0.0
 	enable.add("RLV.MainToggleVisible", boost::bind(&rlvMenuMainToggleVisible, _1));
-	if (RlvActions::isRlvEnabled())
-	{
-		enable.add("RLV.CanShowName", boost::bind(&rlvMenuCanShowName));
-		enable.add("RLV.EnableIfNot", boost::bind(&rlvMenuEnableIfNot, _2));
-	}
+	enable.add("RLV.CanShowName", boost::bind(&rlvMenuCanShowName));
+	enable.add("RLV.EnableIfNot", boost::bind(&rlvMenuEnableIfNot, _2));
 // [/RLVa:KB]
 //	//BD - Re/DeAlpha
 	view_listener_t::addMenu(new BDObjectSetAlpha(), "Object.SetAlphaMode");
diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp
index 043cb287766d736bd26f3d57e57c95152c13a453..1a5ad139ffa829a537f14df7bd39c1188bac0c92 100644
--- a/indra/newview/llviewerparcelmgr.cpp
+++ b/indra/newview/llviewerparcelmgr.cpp
@@ -1902,7 +1902,8 @@ void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **use
                     // If there is a new music URL and it's valid, play it.
                     if (music_url.size() > 12)
                     {
-                        if (music_url.substr(0, 7) == "http://")
+                        if (music_url.substr(0, 7) == "http://"
+                            || music_url.substr(0, 8) == "https://")
                         {
                             LLViewerRegion *region = LLWorld::getInstance()->getRegion(msg->getSender());
                             optionally_start_music(music_url, parcel->mLocalID, region->getRegionID());
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 0fa3fcddf3ca2091ef61dcdfe042a25c80cbad60..a0a21b9a0e50ca8003b4c90127510c97c1f61fa1 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -5339,6 +5339,14 @@ void LLViewerWindow::revealIntroPanel()
 	}
 }
 
+void LLViewerWindow::initTextures(S32 location_id)
+{
+    if (mProgressView)
+    {
+        mProgressView->initTextures(location_id, LLGridManager::getInstance()->isInProductionGrid());
+    }
+}
+
 void LLViewerWindow::setShowProgress(const BOOL show)
 {
 	if (mProgressView)
@@ -5395,7 +5403,6 @@ void LLViewerWindow::setProgressCancelButtonVisible( BOOL b, const std::string&
 	}
 }
 
-
 LLProgressView *LLViewerWindow::getProgressView() const
 {
 	return mProgressView;
diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h
index 9dca889c7785ec1f4b2d5069bb3959f3a6d912c0..bb7a8a3801b3aefef0d3363bdfa5e8697c574c02 100644
--- a/indra/newview/llviewerwindow.h
+++ b/indra/newview/llviewerwindow.h
@@ -304,6 +304,7 @@ class LLViewerWindow : public LLWindowCallbacks
 	BOOL            getCursorHidden() { return mCursorHidden; }
 	void			moveCursorToCenter();								// move to center of window
 													
+	void			initTextures(S32 location_id);
 	void			setShowProgress(const BOOL show);
 	BOOL			getShowProgress() const;
 	void			setProgressString(const std::string& string);
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index f6db9be57a4af438e575f8f2505ee60709380cbd..4ab301c78947ab8c7bad46fd989c3c6aa603ac60 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -7527,7 +7527,8 @@ void LLVOAvatar::sitOnObject(LLViewerObject *sit_object)
 	mRoot->updateWorldMatrixChildren();
 
 	stopMotion(ANIM_AGENT_BODY_NOISE);
-
+	
+	gAgentCamera.setInitSitRot(gAgent.getFrameAgent().getQuaternion());
 }
 
 //-----------------------------------------------------------------------------
@@ -7706,12 +7707,12 @@ void LLVOAvatar::rebuildAttachments()
 	{
 		for (LLViewerObject* pAttachObj : kvpAttachPt.second->mAttachedObjects)
 		{
-			if (LLVOVolume* pAttachVol = (pAttachObj->isMesh()) ? pAttachObj->asVolume() : nullptr)
+			if (LLVOVolume* pAttachVol = (pAttachObj->isMesh()) ? dynamic_cast<LLVOVolume*>(pAttachObj) : nullptr)
 			{
 				pAttachVol->forceLOD(3);
 				for (LLViewerObject* pChildObj : pAttachObj->getChildren())
 				{
-					if (LLVOVolume* pChildVol = (pChildObj->isMesh()) ? pChildObj->asVolume() : nullptr)
+					if (LLVOVolume* pChildVol = (pChildObj->isMesh()) ? dynamic_cast<LLVOVolume*>(pChildObj) : nullptr)
 						pAttachVol->forceLOD(3);
 				}
 			}
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index 456121ac01dc1da140651d14b58e53229eb16982..487cdf79d9cfd2c2c1fa91b9813e6fd2bdca9f11 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -432,6 +432,7 @@ class LLVOAvatar :
 	bool		isVisuallyMuted();
 	bool 		isInMuteList() const;
 	bool		isInBuddyList() const;
+	bool 		isInMuteList();
 // [RLVa:KB] - Checked: RLVa-2.2 (@setcam_avdist)
 	bool        isRlvSilhouette();
 // [/RLVa:KB]
@@ -488,6 +489,8 @@ class LLVOAvatar :
 	mutable F64			mCachedMuteListUpdateTime;
 	mutable bool		mCachedInBuddyList;
 	mutable F64			mCachedBuddyListUpdateTime;
+	bool		mCachedInMuteList;
+	F64			mCachedMuteListUpdateTime;
 // [RLVa:KB] - Checked: RLVa-2.2 (@setcam_avdist)
 	mutable bool mCachedIsRlvSilhouette = false;
 	mutable F64  mCachedRlvSilhouetteUpdateTime = 0.f;
diff --git a/indra/newview/rlvactions.cpp b/indra/newview/rlvactions.cpp
index f5c5dc52a6ef18ece4eae1823d320e27a0c9d77a..793824a316bbefb8f816d484fed051637398d95e 100644
--- a/indra/newview/rlvactions.cpp
+++ b/indra/newview/rlvactions.cpp
@@ -42,7 +42,7 @@ bool RlvActions::canChangeCameraPreset(const LLUUID& idRlvObject)
 	// NOTE: if an object has exclusive camera control then all other objects are locked out
 	return
 		( (!gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM)) || (gRlvHandler.hasBehaviour(idRlvObject, RLV_BHVR_SETCAM)) ) &&
-		(!gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_EYEOFFSET)) && (!gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_FOCUSOFFSET));
+		(!gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_EYEOFFSET)) && (!gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_EYEOFFSETSCALE)) && (!gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_FOCUSOFFSET));
 }
 
 bool RlvActions::canChangeToMouselook()
@@ -70,7 +70,9 @@ bool RlvActions::isCameraFOVClamped()
 
 bool RlvActions::isCameraPresetLocked()
 {
-	return (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM)) || (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_EYEOFFSET)) || (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_FOCUSOFFSET));
+	return
+		(gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM)) ||
+		(gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_EYEOFFSET)) || (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_EYEOFFSETSCALE)) || (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_FOCUSOFFSET));
 }
 
 bool RlvActions::getCameraAvatarDistanceLimits(float& nDistMin, float& nDistMax)
@@ -351,6 +353,15 @@ bool RlvActions::isLocalTp(const LLVector3d& posGlobal)
 	return nDistSq < RLV_MODIFIER_TPLOCAL_DEFAULT * RLV_MODIFIER_TPLOCAL_DEFAULT;
 }
 
+// ============================================================================
+// WindLight
+//
+
+bool RlvActions::canChangeEnvironment()
+{
+	return !gRlvHandler.hasBehaviour(RLV_BHVR_SETENV);
+}
+
 // ============================================================================
 // World interaction
 //
diff --git a/indra/newview/rlvactions.h b/indra/newview/rlvactions.h
index 90b1069f6bd27487233e6a058860efef9795b4df..be22f4ce6ae20a1b2aa9eb41f0680527b480f608 100644
--- a/indra/newview/rlvactions.h
+++ b/indra/newview/rlvactions.h
@@ -26,6 +26,7 @@
 
 class LLInventoryCategory;
 class LLInventoryItem;
+class LLViewerObject;
 
 // ============================================================================
 // RlvActions class declaration - developer-friendly non-RLVa code facing class, use in lieu of RlvHandler whenever possible
@@ -207,6 +208,16 @@ class RlvActions
 	 */
 	static bool isLocalTp(const LLVector3d& posGlobal);
 
+	// =========
+	// WindLight
+	// =========
+public:
+	/*
+	 * Returns true if the user can make changes to their WindLight environment 
+	 */
+	static bool canChangeEnvironment();
+
+
 	// =================
 	// World interaction
 	// =================
diff --git a/indra/newview/rlvcommon.cpp b/indra/newview/rlvcommon.cpp
index 5ed426f2710b204787a502f583278edb4f173334..0be5447fd309189f4f53aa3b9bb596d28476373d 100644
--- a/indra/newview/rlvcommon.cpp
+++ b/indra/newview/rlvcommon.cpp
@@ -439,6 +439,11 @@ std::string RlvStrings::getVersionNum(const LLUUID& idRlvObject)
 		(!fCompatMode) ? RLV_VERSION_PATCH : RLV_VERSION_PATCH_COMPAT, (!fCompatMode) ? RLV_VERSION_BUILD : RLV_VERSION_BUILD_COMPAT);
 }
 
+std::string RlvStrings::getVersionImplNum()
+{
+	return llformat("%d%02d%02d%02d", RLVa_VERSION_MAJOR, RLVa_VERSION_MAJOR, RLVa_VERSION_PATCH, RLVa_IMPL_ID);
+}
+
 // Checked: 2011-11-08 (RLVa-1.5.0)
 bool RlvStrings::hasString(const std::string& strStringName, bool fCheckCustom)
 {
@@ -728,8 +733,13 @@ void rlvMenuToggleVisible()
 
 bool rlvMenuCanShowName()
 {
-  const LLVOAvatar* pAvatar = find_avatar_from_object(LLSelectMgr::getInstance()->getSelection()->getPrimaryObject());
-  return (pAvatar) && (RlvActions::canShowName(RlvActions::SNC_DEFAULT, pAvatar->getID()));
+	bool fEnable = true;
+	if (rlv_handler_t::isEnabled())
+	{
+		const LLVOAvatar* pAvatar = find_avatar_from_object(LLSelectMgr::getInstance()->getSelection()->getPrimaryObject());
+		fEnable = (pAvatar) && (RlvActions::canShowName(RlvActions::SNC_DEFAULT, pAvatar->getID()));
+	}
+	return fEnable;
 }
 
 // Checked: 2010-04-23 (RLVa-1.2.0g) | Modified: RLVa-1.2.0g
diff --git a/indra/newview/rlvcommon.h b/indra/newview/rlvcommon.h
index 47a8ebf1483cf92c87d1e554b06ef102eb01572b..82fca44f91b6772692f6708f4ee3a20e9d3a831d 100644
--- a/indra/newview/rlvcommon.h
+++ b/indra/newview/rlvcommon.h
@@ -56,7 +56,7 @@ class RlvObject;
 
 struct RlvException;
 typedef boost::variant<std::string, LLUUID, S32, ERlvBehaviour> RlvExceptionOption;
-typedef boost::variant<int, float, bool, LLVector3, LLUUID> RlvBehaviourModifierValue;
+typedef boost::variant<int, float, bool, LLVector3, LLVector3d, LLUUID> RlvBehaviourModifierValue;
 
 class RlvGCTimer;
 
@@ -151,6 +151,7 @@ class RlvStrings
 	static const std::string& getStringMapPath() { return m_StringMapPath; }
 	static std::string        getVersion(const LLUUID& idRlvObject, bool fLegacy = false);
 	static std::string        getVersionAbout();
+	static std::string        getVersionImplNum();
 	static std::string        getVersionNum(const LLUUID& idRlvObject);
 	static bool               hasString(const std::string& strStringName, bool fCheckCustom = false);
 	static void               setCustomString(const std::string& strStringName, const std::string& strStringValue);
diff --git a/indra/newview/rlvdefines.h b/indra/newview/rlvdefines.h
index 8026b59b57ccd590bd958b22525cba5ef20027d6..a3938876a00319e428565109050ea56bbe13322c 100644
--- a/indra/newview/rlvdefines.h
+++ b/indra/newview/rlvdefines.h
@@ -23,8 +23,8 @@
 
 // Version of the specifcation we report
 const S32 RLV_VERSION_MAJOR = 3;
-const S32 RLV_VERSION_MINOR = 2;
-const S32 RLV_VERSION_PATCH = 1;
+const S32 RLV_VERSION_MINOR = 3;
+const S32 RLV_VERSION_PATCH = 3;
 const S32 RLV_VERSION_BUILD = 0;
 
 // Version of the specifcation we report (in compatibility mode)
@@ -35,8 +35,9 @@ const S32 RLV_VERSION_BUILD_COMPAT = 0;
 
 // Implementation version
 const S32 RLVa_VERSION_MAJOR = 2;
-const S32 RLVa_VERSION_MINOR = 2;
-const S32 RLVa_VERSION_PATCH = 2;
+const S32 RLVa_VERSION_MINOR = 3;
+const S32 RLVa_VERSION_PATCH = 0;
+const S32 RLVa_IMPL_ID = 13;
 
 // Uncomment before a final release
 //#define RLV_RELEASE
@@ -214,6 +215,7 @@ enum ERlvBehaviour {
 	RLV_BHVR_SETCAM_ORIGINDISTMIN,	// Enforces a minimum distance from the camera origin (in m)
 	RLV_BHVR_SETCAM_ORIGINDISTMAX,	// Enforces a maximum distance from the camera origin (in m)
 	RLV_BHVR_SETCAM_EYEOFFSET,      // Changes the default camera offset
+	RLV_BHVR_SETCAM_EYEOFFSETSCALE, // Changes the default camera offset scale
 	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
 	RLV_BHVR_SETCAM_FOV,			// Changes the current - vertical - field of view
@@ -267,6 +269,7 @@ enum ERlvBehaviourModifier
 	RLV_MODIFIER_SETCAM_ORIGINDISTMIN,	// Minimum distance between the camera position and the origin point (normal value)
 	RLV_MODIFIER_SETCAM_ORIGINDISTMAX,	// Maximum distance between the camera position and the origin point (normal value)
 	RLV_MODIFIER_SETCAM_EYEOFFSET,		// Specifies the default camera's offset from the camera (vector)
+	RLV_MODIFIER_SETCAM_EYEOFFSETSCALE,	// Specifies the default camera's offset scale (multiplier)
 	RLV_MODIFIER_SETCAM_FOCUSOFFSET,	// Specifies the default camera's focus (vector)
 	RLV_MODIFIER_SETCAM_FOVMIN,			// Minimum value for the camera's field of view (angle in radians)
 	RLV_MODIFIER_SETCAM_FOVMAX,			// Maximum value for the camera's field of view (angle in radians)
diff --git a/indra/newview/rlvenvironment.cpp b/indra/newview/rlvenvironment.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d723630fdb117e830ba48ba7d93ed96e27cfd6fb
--- /dev/null
+++ b/indra/newview/rlvenvironment.cpp
@@ -0,0 +1,698 @@
+/**
+ *
+ * Copyright (c) 2009-2020, Kitty Barnett
+ *
+ * The source code in this file is provided to you under the terms of the
+ * GNU Lesser General Public License, version 2.1, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. Terms of the LGPL can be found in doc/LGPL-licence.txt
+ * in this distribution, or online at http://www.gnu.org/licenses/lgpl-2.1.txt
+ *
+ * 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.
+ *
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llinventoryfunctions.h"
+#include "llsettingsvo.h"
+#include <boost/algorithm/string.hpp>
+
+#include "rlvactions.h"
+#include "rlvenvironment.h"
+#include "rlvhelper.h"
+
+// ================================================================================================
+// Constants and helper functions
+//
+
+namespace
+{
+	const F32 SLIDER_SCALE_BLUE_HORIZON_DENSITY(2.0f);
+	const F32 SLIDER_SCALE_DENSITY_MULTIPLIER(0.001f);
+	const F32 SLIDER_SCALE_GLOW_R(20.0f);
+	const F32 SLIDER_SCALE_GLOW_B(-5.0f);
+	const F32 SLIDER_SCALE_SUN_AMBIENT(3.0f);
+
+	const std::string RLV_GETENV_PREFIX = "getenv_";
+	const std::string RLV_SETENV_PREFIX = "setenv_";
+
+	U32 rlvGetColorComponentFromCharacter(char ch)
+	{
+		if ( ('r' == ch) || ('x' == ch) )      return VRED;
+		else if ( ('g' == ch) || ('y' == ch )) return VGREEN;
+		else if ( ('b' == ch) || ('d' == ch) ) return VBLUE;
+		else if ('i' == ch)                    return VALPHA;
+		return U32_MAX;
+	}
+
+	const LLUUID& rlvGetLibraryEnvironmentsFolder()
+	{
+		LLInventoryModel::cat_array_t cats;
+		LLInventoryModel::item_array_t items;
+		LLNameCategoryCollector f("Environments");
+		gInventory.collectDescendentsIf(gInventory.getLibraryRootFolderID(), cats, items, LLInventoryModel::EXCLUDE_TRASH, f);
+		return (!cats.empty()) ? cats.front()->getUUID() : LLUUID::null;
+	}
+
+	// Legacy WindLight values we need tend to be expressed as a fraction of the [0, 2PI[ domain
+	F32 normalize_angle_domain(F32 angle)
+	{
+		while (angle < 0)
+			angle += F_TWO_PI;
+		while (angle > F_TWO_PI)
+			angle -= F_TWO_PI;
+		return angle;
+	}
+}
+
+/*
+ * Reasoning (Reference - https://upload.wikimedia.org/wikipedia/commons/thumb/f/f7/Azimuth-Altitude_schematic.svg/1024px-Azimuth-Altitude_schematic.svg.png)
+ *
+ * Given a(zimuth angle) and e(levation angle) - in the SL axis - we know that it calculates the quaternion as follows:
+ *
+ * | cos a	sin a	0 |   | cos e |   | cos a x cos e | = | x |
+ * | sin a	cos a	0 | x |     0 | = | sin a x cos e | = | y | (normalized direction vector identifying the sun position on a unit sphere)
+ * |     0	    0	1 |   | sin e |   |         sin e | = | z |
+ *
+ * As a result we can reverse the above by: quaternion -> rotate it around X-axis
+ *   x = cos a x cos e <=> cos a = x / cos e   \
+ *                                              | (if we divide them we can get rid of cos e)
+ *                                              | <=> sin a / cos a = y / x <=> tan a = y / x <=> a = atan2(y, x)
+ *   y = sin a x cos e <=> sin a = y / cos e   /
+ *   z = sin e         <=> e = asin z
+ *
+ * If we look at the resulting domain azimuth lies in ]-PI, PI] and elevation lies in [-PI/2, PI/2] which I actually prefer most. Going forward people should get the sun in a wind
+ * direction by manipulating the azimuth and then deal with the elevation (which ends up mimicking how a camera or an observer behave in real life).
+ *
+ * Special cases:
+ *   x = 0 => (1) cos e = 0 -> sin e = 1 so y = 0     and z = 1                                    => in (0, 0, 1) we loose all information about the azimuth since cos e = 0
+ *         OR (2) cos a = 0 -> sin a = 1 so y = cos e and z = sin e => tan e = z/y (with y != 0)   => in (0, Y, Z) azimuth is PI/2 (or 3PI/2) and elevation can have an extended domain of ]-PI, PI]
+ *         => When x = 0 (and y != 0) return PI/2 for azimuth and atan2(z, y) for elevation
+ *   y = 0 => (1) sin a = 0 -> cos a = 1 so x = cos e and z = sin e => tan e = z/x (with x != 0)   => in (X, 0, Z) azimuth is    0 (or PI)    and elevation can have an extended domain of ]-PI, PI]
+ *         OR (2) cos e = 0 -> see above
+		   => When y = 0 (and x != 0) return 0 for azimuth and atan2(z, x) for elevation
+ *   z = 0 =>     sin e = 0 -> cos e = 1 so x = cos a and y = sin a => tan a = y / x               => in (X, Y, 0) elevation is  0 (or PI)    and azimuth has its normal domain of ]-PI, PI]
+ *         => When z = 0 return 0 for elevation and a = atan2(y, x) for azimuth
+ *
+ * We still need to convert all that back/forth between legacy WindLight's odd choices:
+ *   east angle   = SL's azimuth rotates from E (1, 0, 0) to N (0, 1, 0) to W (-1, 0, 0) to S (0, -1, O) but the legacy east angle rotates the opposite way from E to S to W to N so invert the angle
+ *                  (the resulting number then needs to be positive and reported as a fraction of 2PI)
+ *   sunposition  = sun elevation reported as a fraction of 2PI
+ *   moonposition = the moon always has sun's azimuth but its negative elevation
+ *
+ * Pre-EEP both azimuth and elevation have a 2PI range which means that two different a and e value combinations could yield the same sun direction which causes us problems now since we
+ * can't differentiate between the two. Pre-EEP likely favoured elevation over azimuth since people might naturally get the time of day they're thinking of and then try to adjust the
+ * azimuth to get the sun in the correct wind direction; however I've already decided that we'll favour azimuth going forward (see above).
+ *
+ * Comparison of pre-EEP and post-EEP legacy values:
+ *   east angle = 0 (aka azimuth = 0)   -> y = 0 so e = atan2(z, x) -> elevation has a range of 2PI so we correctly report pre-EEP values
+ *   sunmoonpos = 0 (aka elevation = 0) -> z = 0 so a = atan2(y, x) -> azimuth has a range of 2PI so we correctly report pre-EEP values
+ *   -PI/2 < sunmoonpos < PI/2          -> general case             -> post-EEP ranges match pre-EEP ranges so we correctly report pre-EEP values
+ *   sunmoonpos > PI/2                  -> elevation went beyond our new maxium so the post-EEP sunmoonpos will actually be off by PI/2 (or 0.25)
+ *                                         (and the resulting east angle is off by PI or 0.5 - for example smp 0.375 and ea 0.875 are equivalent with smp 0.125 and ea 0.375)
+ *
+ * In reverse this means that when setting values through RLVa:
+ *   sunmoonpos without eastangle (=0) => always correct
+ *   eastangle without sunmoonpos (=0) => always correct
+ *   eastangle before sunmoonpos       => always correct
+ *   sunmoonpos before eastangle       => correct   for -0.25 <= sunmoonpos <= 0.25
+ *                                        incorrect for  0.75  > sunmoonpos  > 0.25
+ */
+F32 rlvGetAzimuthFromDirectionVector(const LLVector3& vecDir)
+{
+	if (is_zero(vecDir.mV[VY]))
+		return 0.f;
+	else if (is_zero(vecDir.mV[VX]))
+		return F_PI_BY_TWO;
+
+	F32 radAzimuth = atan2f(vecDir.mV[VY], vecDir.mV[VX]);
+	return (radAzimuth >= 0.f) ? radAzimuth : radAzimuth + F_TWO_PI;
+}
+
+F32 rlvGetElevationFromDirectionVector(const LLVector3& vecDir)
+{
+	if (is_zero(vecDir.mV[VZ]))
+		return 0.f;
+
+	F32 radElevation;
+	if ( (is_zero(vecDir.mV[VX])) && (!is_zero(vecDir.mV[VY])) )
+		radElevation = atan2f(vecDir.mV[VZ], vecDir.mV[VY]);
+	else if ( (!is_zero(vecDir.mV[VX])) && (is_zero(vecDir.mV[VY])) )
+		radElevation = atan2f(vecDir.mV[VZ], vecDir.mV[VX]);
+	else
+		radElevation = asinf(vecDir.mV[VZ]);
+	return (radElevation >= 0.f) ? radElevation : radElevation + F_TWO_PI;
+}
+
+// Defined in llsettingssky.cpp
+LLQuaternion convert_azimuth_and_altitude_to_quat(F32 azimuth, F32 altitude);
+
+// ================================================================================================
+// RlvIsOfSettingsType - Inventory collector for settings of a specific subtype
+//
+
+class RlvIsOfSettingsType : public LLInventoryCollectFunctor
+{
+public:
+	RlvIsOfSettingsType(LLSettingsType::type_e eSettingsType, const std::string& strNameMatch = LLStringUtil::null)
+		: m_eSettingsType(eSettingsType)
+		, m_strNameMatch(strNameMatch)
+	{
+	}
+
+	~RlvIsOfSettingsType() override
+	{
+	}
+
+	bool operator()(LLInventoryCategory*, LLInventoryItem* pItem) override
+	{
+		if ( (pItem) && (LLAssetType::AT_SETTINGS == pItem->getActualType()) )
+		{
+			return
+				(m_eSettingsType == LLSettingsType::fromInventoryFlags(pItem->getFlags())) &&
+				( (m_strNameMatch.empty()) || (boost::iequals(pItem->getName(), m_strNameMatch)) );
+		}
+		return false;
+	}
+
+protected:
+	LLSettingsType::type_e m_eSettingsType;
+	std::string            m_strNameMatch;
+};
+
+// ================================================================================================
+// RlvEnvironment
+//
+
+RlvEnvironment::RlvEnvironment()
+{
+	//
+	// Presets
+	//
+	registerSetEnvFn<LLUUID>("asset", [](LLEnvironment::EnvSelection_t env, const LLUUID& idAsset)
+		{
+			if (idAsset.isNull())
+				return RLV_RET_FAILED_OPTION;
+
+			LLEnvironment::instance().setEnvironment(env, idAsset);
+			return RLV_RET_SUCCESS;
+		});
+	// Deprecated
+	auto fnApplyLibraryPreset = [](LLEnvironment::EnvSelection_t env, const std::string& strPreset, LLSettingsType::type_e eSettingsType)
+		{
+			LLUUID idAsset(strPreset);
+			if (idAsset.isNull())
+			{
+				const LLUUID& idLibraryEnv = rlvGetLibraryEnvironmentsFolder();
+				LLInventoryModel::cat_array_t cats;
+				LLInventoryModel::item_array_t items;
+				RlvIsOfSettingsType f(eSettingsType, strPreset);
+				gInventory.collectDescendentsIf(idLibraryEnv, cats, items, LLInventoryModel::EXCLUDE_TRASH, f);
+				if (!items.empty())
+					idAsset = items.front()->getAssetUUID();
+			}
+
+			if (idAsset.isNull())
+				return RLV_RET_FAILED_OPTION;
+
+			LLEnvironment::instance().setEnvironment(env, idAsset);
+			return RLV_RET_SUCCESS;
+		};
+	registerSetEnvFn<std::string>("preset",   [&fnApplyLibraryPreset](LLEnvironment::EnvSelection_t env, const std::string& strPreset) { return fnApplyLibraryPreset(env, strPreset, LLSettingsType::ST_SKY); });
+	registerSetEnvFn<std::string>("daycycle", [&fnApplyLibraryPreset](LLEnvironment::EnvSelection_t env, const std::string& strPreset) { return fnApplyLibraryPreset(env, strPreset, LLSettingsType::ST_DAYCYCLE); });
+
+	//
+	// Atmosphere & Lighting tab
+	//
+
+	// SETTING_AMBIENT
+	registerSkyFn<LLColor3>("ambient",		[](LLSettingsSky::ptr_t pSky) { return pSky->getAmbientColor() * (1.f / SLIDER_SCALE_SUN_AMBIENT); },
+											[](LLSettingsSky::ptr_t pSky, const LLColor3& clrValue) { pSky->setAmbientColor(clrValue * SLIDER_SCALE_SUN_AMBIENT); });
+	registerLegacySkyFn<LLColor3>("ambient",[](LLSettingsSky::ptr_t pSky) { return pSky->getAmbientColor() * (1.f / SLIDER_SCALE_SUN_AMBIENT); },
+											[](LLSettingsSky::ptr_t pSky, const LLColor3& clrValue) { pSky->setAmbientColor(clrValue * SLIDER_SCALE_SUN_AMBIENT); });
+
+	// SETTING_BLUE_DENSITY
+	registerSkyFn<LLColor3>("bluedensity",	[](LLSettingsSky::ptr_t pSky) { return pSky->getBlueDensity() * (1.f / SLIDER_SCALE_BLUE_HORIZON_DENSITY); },
+											[](LLSettingsSky::ptr_t pSky, const LLColor3& clrValue) { pSky->setBlueDensity(clrValue * SLIDER_SCALE_BLUE_HORIZON_DENSITY); });
+	registerLegacySkyFn<LLColor3>("bluedensity",[](LLSettingsSky::ptr_t pSky) { return pSky->getBlueDensity() * (1.f / SLIDER_SCALE_BLUE_HORIZON_DENSITY); },
+											[](LLSettingsSky::ptr_t pSky, const LLColor3& clrValue) { pSky->setBlueDensity(clrValue * SLIDER_SCALE_BLUE_HORIZON_DENSITY); });
+
+	// SETTING_BLUE_HORIZON
+	registerSkyFn<LLColor3>("bluehorizon",	[](LLSettingsSky::ptr_t pSky) { return pSky->getBlueHorizon() * (1.f / SLIDER_SCALE_BLUE_HORIZON_DENSITY); },
+											[](LLSettingsSky::ptr_t pSky, const LLColor3& clrValue) { pSky->setBlueHorizon(clrValue * SLIDER_SCALE_BLUE_HORIZON_DENSITY); });
+	registerLegacySkyFn<LLColor3>("bluehorizon",[](LLSettingsSky::ptr_t pSky) { return pSky->getBlueHorizon() * (1.f / SLIDER_SCALE_BLUE_HORIZON_DENSITY); },
+											[](LLSettingsSky::ptr_t pSky, const LLColor3& clrValue) { pSky->setBlueHorizon(clrValue * SLIDER_SCALE_BLUE_HORIZON_DENSITY); });
+
+	// SETTING_DENSITY_MULTIPLIER
+	registerSkyFn<F32>("densitymultiplier",	[](LLSettingsSky::ptr_t pSky) { return pSky->getDensityMultiplier() / SLIDER_SCALE_DENSITY_MULTIPLIER; },
+											[](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setDensityMultiplier(nValue * SLIDER_SCALE_DENSITY_MULTIPLIER); });
+
+	// SETTING_DISTANCE_MULTIPLIER
+	registerSkyFn<F32>("distancemultiplier",[](LLSettingsSky::ptr_t pSky) { return pSky->getDistanceMultiplier(); },
+											[](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setDistanceMultiplier(nValue); });
+
+
+	// SETTING_SKY_DROPLET_RADIUS
+	registerSkyFn<F32>("dropletradius",		[](LLSettingsSky::ptr_t pSky) { return pSky->getSkyDropletRadius(); },
+											[](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setSkyDropletRadius(nValue); });
+
+	// SETTING_HAZE_DENSITY
+	registerSkyFn<F32>("hazedensity",		[](LLSettingsSky::ptr_t pSky) { return pSky->getHazeDensity(); },
+											[](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setHazeDensity(nValue); });
+
+	// SETTING_HAZE_HORIZON
+	registerSkyFn<F32>("hazehorizon",		[](LLSettingsSky::ptr_t pSky) { return pSky->getHazeHorizon(); },
+											[](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setHazeHorizon(nValue); });
+
+	// SETTING_SKY_ICE_LEVEL
+	registerSkyFn<F32>("icelevel",			[](LLSettingsSky::ptr_t pSky) { return pSky->getSkyIceLevel(); },
+											[](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setSkyIceLevel(nValue); });
+
+	// SETTING_MAX_Y
+	registerSkyFn<F32>("maxaltitude",		[](LLSettingsSky::ptr_t pSky) { return pSky->getMaxY(); },
+											[](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setMaxY(nValue); });
+
+	// SETTING_SKY_MOISTURE_LEVEL
+	registerSkyFn<F32>("moisturelevel",		[](LLSettingsSky::ptr_t pSky) { return pSky->getSkyMoistureLevel(); },
+											[](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setSkyMoistureLevel(nValue); });
+
+	//	SETTING_GAMMA
+	registerSkyFn<F32>("scenegamma",		[](LLSettingsSky::ptr_t pSky) { return pSky->getGamma(); },
+											[](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setGamma(nValue); });
+
+	//
+	// Clouds tab
+	//
+
+	// SETTING_CLOUD_COLOR
+	registerSkyFn<LLColor3>("cloudcolor",	[](LLSettingsSky::ptr_t pSky) { return pSky->getCloudColor(); },
+											[](LLSettingsSky::ptr_t pSky, const LLColor3& clrValue) { pSky->setCloudColor(clrValue); });
+	registerLegacySkyFn<LLColor3>("cloudcolor",	[](LLSettingsSky::ptr_t pSky) { return pSky->getCloudColor(); },
+											[](LLSettingsSky::ptr_t pSky, const LLColor3& clrValue) { pSky->setCloudColor(clrValue); });
+
+	// SETTING_CLOUD_SHADOW
+	registerSkyFn<F32>("cloudcoverage",		[](LLSettingsSky::ptr_t pSky) { return pSky->getCloudShadow(); },
+											[](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setCloudShadow(nValue); });
+
+	// SETTING_CLOUD_POS_DENSITY1
+	registerSkyFn<LLColor3>("clouddensity",	[](LLSettingsSky::ptr_t pSky) { return pSky->getCloudPosDensity1(); },
+											[](LLSettingsSky::ptr_t pSky, const LLColor3& clrValue) { pSky->setCloudPosDensity1(clrValue); });
+	registerLegacySkyFn<LLColor3>("cloud",	[](LLSettingsSky::ptr_t pSky) { return pSky->getCloudPosDensity1(); },
+											[](LLSettingsSky::ptr_t pSky, const LLColor3& clrValue) { pSky->setCloudPosDensity1(clrValue); });
+
+	// SETTING_CLOUD_POS_DENSITY2
+	registerSkyFn<LLColor3>("clouddetail",	[](LLSettingsSky::ptr_t pSky) { return pSky->getCloudPosDensity2(); },
+											[](LLSettingsSky::ptr_t pSky, const LLColor3& clrValue) { pSky->setCloudPosDensity2(clrValue); });
+	registerLegacySkyFn<LLColor3>("clouddetail",[](LLSettingsSky::ptr_t pSky) { return pSky->getCloudPosDensity2(); },
+											[](LLSettingsSky::ptr_t pSky, const LLColor3& clrValue) { pSky->setCloudPosDensity2(clrValue); });
+
+	// SETTING_CLOUD_SCALE
+	registerSkyFn<F32>("cloudscale",		[](LLSettingsSky::ptr_t pSky) { return pSky->getCloudScale(); },
+											[](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setCloudScale(nValue); });
+
+	// SETTING_CLOUD_SCROLL_RATE
+	registerSkyFn<LLVector2>("cloudscroll",	[](LLSettingsSky::ptr_t pSky) { return pSky->getCloudScrollRate(); },
+											[](LLSettingsSky::ptr_t pSky, const LLVector2& vecValue) { pSky->setCloudScrollRate(vecValue); });
+	registerLegacySkyFn<LLVector2>("cloudscroll", [](LLSettingsSky::ptr_t pSky) { return pSky->getCloudScrollRate(); },
+											[](LLSettingsSky::ptr_t pSky, const LLVector2& vecValue) { pSky->setCloudScrollRate(vecValue); });
+
+	// SETTING_CLOUD_TEXTUREID
+	registerSkyFn<LLUUID>("cloudtexture",	[](LLSettingsSky::ptr_t pSky) { return pSky->getCloudNoiseTextureId();  },
+											[](LLSettingsSky::ptr_t pSky, const LLUUID& idTexture) { pSky->setCloudNoiseTextureId(idTexture);  });
+
+	// SETTING_CLOUD_VARIANCE
+	registerSkyFn<F32>("cloudvariance",		[](LLSettingsSky::ptr_t pSky) { return pSky->getCloudVariance(); },
+											[](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setCloudVariance(nValue); });
+
+	//
+	// Sun & Moon
+	//
+
+	// SETTING_MOON_BRIGHTNESS
+	registerSkyFn<F32>("moonbrightness",	[](LLSettingsSky::ptr_t pSky) { return pSky->getMoonBrightness(); },
+											[](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setMoonBrightness(nValue); });
+
+	// SETTING_MOON_SCALE
+	registerSkyFn<F32>("moonscale",			[](LLSettingsSky::ptr_t pSky) { return pSky->getMoonScale(); },
+											[](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setMoonScale(nValue); });
+
+	// SETTING_MOON_TEXTUREID
+	registerSkyFn<LLUUID>("moontexture",	[](LLSettingsSky::ptr_t pSky) { return pSky->getMoonTextureId(); },
+											[](LLSettingsSky::ptr_t pSky, const LLUUID& idTexture) { pSky->setMoonTextureId(idTexture); });
+
+	// SETTING_GLOW
+	registerSkyFn<float>("sunglowsize",		[](LLSettingsSky::ptr_t pSky) { return 2.0 - (pSky->getGlow().mV[VRED] / SLIDER_SCALE_GLOW_R); },
+											[](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setGlow(LLColor3((2.0f - nValue) * SLIDER_SCALE_GLOW_R, .0f, pSky->getGlow().mV[VBLUE])); });
+	registerSkyFn<float>("sunglowfocus",	[](LLSettingsSky::ptr_t pSky) { return pSky->getGlow().mV[VBLUE] / SLIDER_SCALE_GLOW_B; },
+											[](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setGlow(LLColor3(pSky->getGlow().mV[VRED], .0f, nValue * SLIDER_SCALE_GLOW_B)); });
+
+	// SETTING_SUNLIGHT_COLOR
+	registerSkyFn<LLColor3>("sunlightcolor",[](LLSettingsSky::ptr_t pSky) { return pSky->getSunlightColor() * (1.f / SLIDER_SCALE_SUN_AMBIENT); },
+											[](LLSettingsSky::ptr_t pSky, const LLColor3& clrValue) { pSky->setSunlightColor(clrValue * SLIDER_SCALE_SUN_AMBIENT); });
+	registerLegacySkyFn<LLColor3>("sunmooncolor", [](LLSettingsSky::ptr_t pSky) { return pSky->getSunlightColor() * (1.f / SLIDER_SCALE_SUN_AMBIENT); },
+											[](LLSettingsSky::ptr_t pSky, const LLColor3& clrValue) { pSky->setSunlightColor(clrValue * SLIDER_SCALE_SUN_AMBIENT); });
+
+	// SETTING_SUN_SCALE
+	registerSkyFn<float>("sunscale",		[](LLSettingsSky::ptr_t pSky) { return pSky->getSunScale(); },
+											[](LLSettingsSky::ptr_t pSky, F32 nValue) { pSky->setSunScale(nValue); });
+
+	// SETTING_SUN_TEXTUREID
+	registerSkyFn<LLUUID>("suntexture",		[](LLSettingsSky::ptr_t pSky) { return pSky->getSunTextureId(); },
+											[](LLSettingsSky::ptr_t pSky, const LLUUID& idTexture) { pSky->setSunTextureId(idTexture); });
+
+	// SETTING_STAR_BRIGHTNESS
+	registerSkyFn<F32>("starbrightness",	[](LLSettingsSky::ptr_t pSky) { return pSky->getStarBrightness(); },
+											[](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setStarBrightness(nValue); });
+
+	// SETTING_SUN_ROTATION
+	registerSkyFn<F32>("sunazimuth",		[](LLSettingsSky::ptr_t pSky) { return rlvGetAzimuthFromDirectionVector(LLVector3::x_axis * pSky->getSunRotation()); },
+											[](LLSettingsSky::ptr_t pSky, const F32& radAzimuth) {
+												pSky->setSunRotation(convert_azimuth_and_altitude_to_quat(radAzimuth, rlvGetElevationFromDirectionVector(LLVector3::x_axis* pSky->getSunRotation())));
+											});
+	registerSkyFn<F32>("sunelevation",		[](LLSettingsSky::ptr_t pSky) { return rlvGetElevationFromDirectionVector(LLVector3::x_axis * pSky->getSunRotation()); },
+											[](LLSettingsSky::ptr_t pSky, F32 radElevation) {
+												radElevation = llclamp(radElevation, -F_PI_BY_TWO, F_PI_BY_TWO);
+												pSky->setSunRotation(convert_azimuth_and_altitude_to_quat(rlvGetAzimuthFromDirectionVector(LLVector3::x_axis* pSky->getSunRotation()), radElevation));
+											});
+
+	// SETTING_MOON_ROTATION
+	registerSkyFn<F32>("moonazimuth",		[](LLSettingsSky::ptr_t pSky) { return rlvGetAzimuthFromDirectionVector(LLVector3::x_axis * pSky->getMoonRotation()); },
+											[](LLSettingsSky::ptr_t pSky, const F32& radAzimuth) {
+												pSky->setMoonRotation(convert_azimuth_and_altitude_to_quat(radAzimuth, rlvGetElevationFromDirectionVector(LLVector3::x_axis* pSky->getMoonRotation())));
+											});
+	registerSkyFn<F32>("moonelevation",		[](LLSettingsSky::ptr_t pSky) { return rlvGetElevationFromDirectionVector(LLVector3::x_axis * pSky->getMoonRotation()); },
+											[](LLSettingsSky::ptr_t pSky, F32 radElevation) {
+												radElevation = llclamp(radElevation, -F_PI_BY_TWO, F_PI_BY_TWO);
+												pSky->setMoonRotation(convert_azimuth_and_altitude_to_quat(rlvGetAzimuthFromDirectionVector(LLVector3::x_axis* pSky->getMoonRotation()), radElevation));
+											});
+
+	// Legacy WindLight support (see remarks at the top of this file)
+	registerSkyFn<F32>("eastangle",			[](LLSettingsSky::ptr_t pSky) { return normalize_angle_domain(-rlvGetAzimuthFromDirectionVector(LLVector3::x_axis * pSky->getSunRotation())) / F_TWO_PI; },
+											[](LLSettingsSky::ptr_t pSky, const F32& radEastAngle)
+											{
+												const F32 radAzimuth = -radEastAngle * F_TWO_PI;
+												const F32 radElevation = rlvGetElevationFromDirectionVector(LLVector3::x_axis * pSky->getSunRotation());
+												pSky->setSunRotation(convert_azimuth_and_altitude_to_quat(radAzimuth, radElevation));
+												pSky->setMoonRotation(convert_azimuth_and_altitude_to_quat(radAzimuth + F_PI, -radElevation));
+											});
+
+	registerSkyFn<F32>("sunmoonposition",	[](LLSettingsSky::ptr_t pSky) { return rlvGetElevationFromDirectionVector(LLVector3::x_axis * pSky->getSunRotation()) / F_TWO_PI; },
+											[](LLSettingsSky::ptr_t pSky, const F32& nValue)
+											{
+												const F32 radAzimuth = rlvGetAzimuthFromDirectionVector(LLVector3::x_axis * pSky->getSunRotation());
+												const F32 radElevation = nValue * F_TWO_PI;
+												pSky->setSunRotation(convert_azimuth_and_altitude_to_quat(radAzimuth, radElevation));
+												pSky->setMoonRotation(convert_azimuth_and_altitude_to_quat(radAzimuth + F_PI, -radElevation));
+											});
+
+	// Create a fixed sky from the nearest daycycle (local > experience > parcel > region)
+	registerSetEnvFn<F32>("daytime",		[](LLEnvironment::EnvSelection_t env, const F32& nValue)
+											{
+												if ((nValue >= 0.f) && (nValue <= 1.0f))
+												{
+													LLSettingsDay::ptr_t pDay;
+													if (LLEnvironment::ENV_EDIT != env)
+													{
+														LLEnvironment::EnvSelection_t envs[] = { LLEnvironment::ENV_LOCAL, LLEnvironment::ENV_PUSH, LLEnvironment::ENV_PARCEL, LLEnvironment::ENV_REGION };
+														for (size_t idxEnv = 0, cntEnv = sizeof(envs) / sizeof(LLEnvironment::EnvSelection_t); idxEnv < cntEnv && !pDay; idxEnv++)
+															pDay = LLEnvironment::instance().getEnvironmentDay(envs[idxEnv]);
+													}
+													else
+													{
+														pDay = LLEnvironment::instance().getEnvironmentDay(LLEnvironment::ENV_EDIT);
+													}
+
+													if (pDay)
+													{
+														auto pNewSky = LLSettingsVOSky::buildDefaultSky();
+														auto pSkyBlender = std::make_shared<LLTrackBlenderLoopingManual>(pNewSky, pDay, 1);
+														pSkyBlender->setPosition(nValue);
+
+														LLEnvironment::instance().setEnvironment(env, pNewSky);
+														LLEnvironment::instance().updateEnvironment(LLEnvironment::TRANSITION_INSTANT);
+													}
+												}
+												else if (nValue == -1)
+												{
+													LLEnvironment::instance().clearEnvironment(env);
+													LLEnvironment::instance().setSelectedEnvironment(env);
+													LLEnvironment::instance().updateEnvironment();
+//													defocusEnvFloaters();
+												}
+												else
+												{
+													return RLV_RET_FAILED_OPTION;
+												}
+
+												return RLV_RET_SUCCESS;
+											});
+	registerGetEnvFn("daytime",				[](LLEnvironment::EnvSelection_t env)
+											{
+												// I forgot how much I hate this command... it literally makes no sense since time of day only has any meaning in an
+												// actively animating day cycle (but in that case we have to return -1).
+												if (!LLEnvironment::instance().getEnvironmentFixedSky(env)) {
+													return std::to_string(-1.f);
+												}
+
+												// It's invalid input for @setenv_daytime (see above) so it can be fed in without changing the current environment
+												return std::to_string(2.f);
+											});
+}
+
+RlvEnvironment::~RlvEnvironment()
+{
+}
+
+// static
+LLEnvironment::EnvSelection_t RlvEnvironment::getTargetEnvironment()
+{
+	return RlvActions::canChangeEnvironment() ? LLEnvironment::ENV_LOCAL : LLEnvironment::ENV_EDIT;
+}
+
+// static
+bool RlvEnvironment::onHandleCommand(const RlvCommand& rlvCmd, ERlvCmdRet& cmdRet, const std::string& strCmdPrefix, const handler_map_t& fnLookup, const legacy_handler_map_t& legacyFnLookup)
+{
+	if ( (rlvCmd.getBehaviour().length() > strCmdPrefix.length() + 2) && (boost::starts_with(rlvCmd.getBehaviour(), strCmdPrefix)) )
+	{
+		std::string strEnvCommand = rlvCmd.getBehaviour().substr(strCmdPrefix.length());
+
+		handler_map_t::const_iterator itFnEntry = fnLookup.find(strEnvCommand);
+		if (fnLookup.end() != itFnEntry)
+		{
+			cmdRet = itFnEntry->second((RLV_TYPE_FORCE == rlvCmd.getParamType()) ? rlvCmd.getOption() : rlvCmd.getParam());
+			return true;
+		}
+
+		// Legacy handling (blargh)
+		U32 idxComponent = rlvGetColorComponentFromCharacter(strEnvCommand.back());
+		if (idxComponent <= VALPHA)
+		{
+			strEnvCommand.pop_back();
+
+			legacy_handler_map_t::const_iterator itLegacyFnEntry = legacyFnLookup.find(strEnvCommand);
+			if (legacyFnLookup.end() != itLegacyFnEntry)
+			{
+				cmdRet = itLegacyFnEntry->second((RLV_TYPE_FORCE == rlvCmd.getParamType()) ? rlvCmd.getOption() : rlvCmd.getParam(), idxComponent);
+				return true;
+			}
+		}
+	}
+
+	return false;
+}
+
+bool RlvEnvironment::onReplyCommand(const RlvCommand& rlvCmd, ERlvCmdRet& cmdRet)
+{
+	return onHandleCommand(rlvCmd, cmdRet, RLV_GETENV_PREFIX, m_GetFnLookup, m_LegacyGetFnLookup);
+}
+
+bool RlvEnvironment::onForceCommand(const RlvCommand& rlvCmd, ERlvCmdRet& cmdRet)
+{
+	return onHandleCommand(rlvCmd, cmdRet, RLV_SETENV_PREFIX, m_SetFnLookup, m_LegacySetFnLookup);
+}
+
+template<>
+std::string RlvEnvironment::handleGetFn<float>(const std::function<float(LLSettingsSky::ptr_t)>& fn)
+{
+	LLSettingsSky::ptr_t pSky = LLEnvironment::instance().getCurrentSky();
+	return std::to_string(fn(pSky));
+}
+
+template<>
+std::string RlvEnvironment::handleGetFn<LLUUID>(const std::function<LLUUID(LLSettingsSky::ptr_t)>& fn)
+{
+	LLSettingsSky::ptr_t pSky = LLEnvironment::instance().getCurrentSky();
+	return fn(pSky).asString();
+}
+
+template<>
+std::string RlvEnvironment::handleGetFn<LLVector2>(const std::function<LLVector2(LLSettingsSky::ptr_t)>& fn)
+{
+	LLSettingsSky::ptr_t pSky = LLEnvironment::instance().getCurrentSky();
+	LLVector2 replyVec = fn(pSky);
+	return llformat("%f/%f", replyVec.mV[VX], replyVec.mV[VY]);
+}
+
+template<>
+std::string RlvEnvironment::handleGetFn<LLColor3>(const std::function<LLColor3(LLSettingsSky::ptr_t)>& fn)
+{
+	LLSettingsSky::ptr_t pSky = LLEnvironment::instance().getCurrentSky();
+	LLColor3 replyColor = fn(pSky);
+	return llformat("%f/%f/%f", replyColor.mV[VX], replyColor.mV[VY], replyColor.mV[VZ]);
+}
+
+template<typename T>
+ERlvCmdRet RlvEnvironment::handleSetFn(const std::string& strRlvOption, const std::function<void(LLSettingsSky::ptr_t, const T&)>& fn)
+{
+	T optionValue;
+	if (!RlvCommandOptionHelper::parseOption<T>(strRlvOption, optionValue))
+		return RLV_RET_FAILED_PARAM;
+
+	LLSettingsSky::ptr_t pSky = LLEnvironment::instance().getCurrentSky();
+	fn(pSky, optionValue);
+	pSky->update();
+	return RLV_RET_SUCCESS;
+}
+
+template<>
+std::string RlvEnvironment::handleLegacyGetFn<LLVector2>(const std::function<const LLVector2& (LLSettingsSkyPtr_t)>& getFn, U32 idxComponent)
+{
+	if (idxComponent > 2)
+		return LLStringUtil::null;
+	return std::to_string(getFn(LLEnvironment::instance().getCurrentSky()).mV[idxComponent]);
+}
+
+template<>
+std::string RlvEnvironment::handleLegacyGetFn<LLColor3>(const std::function<const LLColor3& (LLSettingsSkyPtr_t)>& getFn, U32 idxComponent)
+{
+	if ( (idxComponent >= VRED) && (idxComponent <= VBLUE) )
+	{
+		return std::to_string(getFn(LLEnvironment::instance().getCurrentSky()).mV[idxComponent]);
+	}
+	else if (idxComponent == VALPHA)
+	{
+		const LLColor3& clr = getFn(LLEnvironment::instance().getCurrentSky());
+		return std::to_string(llmax(clr.mV[VRED], clr.mV[VGREEN], clr.mV[VBLUE]));
+	}
+	return LLStringUtil::null;
+}
+
+template<>
+ERlvCmdRet RlvEnvironment::handleLegacySetFn<LLVector2>(float optionValue, LLVector2 curValue, const std::function<void(LLSettingsSkyPtr_t, const LLVector2&)>& setFn, U32 idxComponent)
+{
+	if (idxComponent > 2)
+		return RLV_RET_FAILED_UNKNOWN;
+
+	LLSettingsSky::ptr_t pSky = LLEnvironment::instance().getCurrentSky();
+	curValue.mV[idxComponent] = optionValue;
+	setFn(pSky, curValue);
+	pSky->update();
+
+	return RLV_RET_SUCCESS;
+}
+
+template<>
+ERlvCmdRet RlvEnvironment::handleLegacySetFn<LLColor3>(float optionValue, LLColor3 curValue, const std::function<void(LLSettingsSkyPtr_t, const LLColor3&)>& setFn, U32 idxComponent)
+{
+	LLSettingsSky::ptr_t pSky = LLEnvironment::instance().getCurrentSky();
+	if ( (idxComponent >= VRED) && (idxComponent <= VBLUE) )
+	{
+		curValue.mV[idxComponent] = optionValue;
+	}
+	else if (idxComponent == VALPHA)
+	{
+		const F32 curMax = llmax(curValue.mV[VRED], curValue.mV[VGREEN], curValue.mV[VBLUE]);
+		if ( (0.0f == optionValue) || (0.0f == curMax) )
+		{
+			curValue.mV[VRED] = curValue.mV[VGREEN] = curValue.mV[VBLUE] = optionValue;
+		}
+		else
+		{
+			const F32 nDelta = (optionValue - curMax) / curMax;
+			curValue.mV[VRED] *= (1.0f + nDelta);
+			curValue.mV[VGREEN] *= (1.0f + nDelta);
+			curValue.mV[VBLUE] *= (1.0f + nDelta);
+		}
+	}
+	else
+	{
+		return RLV_RET_FAILED_UNKNOWN;
+	}
+
+	setFn(pSky, curValue);
+	pSky->update();
+
+	return RLV_RET_SUCCESS;
+}
+
+
+template<typename T>
+void RlvEnvironment::registerSkyFn(const std::string& strFnName, const std::function<T(LLSettingsSkyPtr_t)>& getFn, const std::function<void(LLSettingsSkyPtr_t, const T&)>& setFn)
+{
+	RLV_ASSERT(m_GetFnLookup.end() == m_GetFnLookup.find(strFnName));
+	m_GetFnLookup.insert(std::make_pair(strFnName, [this, getFn](const std::string& strRlvParam)
+		{
+			if (RlvUtil::sendChatReply(strRlvParam, handleGetFn<T>(getFn)))
+				return RLV_RET_SUCCESS;
+			return RLV_RET_FAILED_PARAM;
+		}));
+
+	RLV_ASSERT(m_SetFnLookup.end() == m_SetFnLookup.find(strFnName));
+	m_SetFnLookup.insert(std::make_pair(strFnName, [this, setFn](const std::string& strRlvOption)
+		{
+			return handleSetFn<T>(strRlvOption, setFn);
+		}));
+}
+
+void RlvEnvironment::registerGetEnvFn(const std::string& strFnName, const std::function<std::string(LLEnvironment::EnvSelection_t env)>& getFn)
+{
+	RLV_ASSERT(m_GetFnLookup.end() == m_GetFnLookup.find(strFnName));
+	m_GetFnLookup.insert(std::make_pair(strFnName, [getFn](const std::string& strRlvParam)
+		{
+			if (RlvUtil::sendChatReply(strRlvParam, getFn(getTargetEnvironment())))
+				return RLV_RET_SUCCESS;
+			return RLV_RET_FAILED_PARAM;
+		}));
+}
+
+template<typename T>
+void RlvEnvironment::registerSetEnvFn(const std::string& strFnName, const std::function<ERlvCmdRet(LLEnvironment::EnvSelection_t env, const T& strRlvOption)>& setFn)
+{
+	RLV_ASSERT(m_SetFnLookup.end() == m_SetFnLookup.find(strFnName));
+	m_SetFnLookup.insert(std::make_pair(strFnName, [setFn](const std::string& strRlvOption)
+		{
+			T optionValue;
+			if (!RlvCommandOptionHelper::parseOption<T>(strRlvOption, optionValue))
+				return RLV_RET_FAILED_PARAM;
+			return setFn(getTargetEnvironment(), optionValue);
+		}));
+}
+
+template<typename T>
+void RlvEnvironment::registerLegacySkyFn(const std::string& strFnName, const std::function<const T& (LLSettingsSkyPtr_t)>& getFn, const std::function<void(LLSettingsSkyPtr_t, const T&)>& setFn)
+{
+	RLV_ASSERT(m_LegacyGetFnLookup.end() == m_LegacyGetFnLookup.find(strFnName));
+	m_LegacyGetFnLookup.insert(std::make_pair(strFnName, [this, getFn](const std::string& strRlvParam, U32 idxComponent)
+		{
+			const std::string strReply = handleLegacyGetFn<T>(getFn, idxComponent);
+			if (strReply.empty())
+				return RLV_RET_FAILED_UNKNOWN;
+			else if (RlvUtil::sendChatReply(strRlvParam, strReply))
+				return RLV_RET_SUCCESS;
+			return RLV_RET_FAILED_PARAM;
+		}));
+
+	RLV_ASSERT(m_LegacySetFnLookup.end() == m_LegacySetFnLookup.find(strFnName));
+	m_LegacySetFnLookup.insert(std::make_pair(strFnName, [this, getFn, setFn](const std::string& strRlvOption, U32 idxComponent)
+		{
+			float optionValue;
+			if (!RlvCommandOptionHelper::parseOption(strRlvOption, optionValue))
+				return RLV_RET_FAILED_PARAM;
+			return handleLegacySetFn<T>(optionValue, getFn(LLEnvironment::instance().getCurrentSky()), setFn, idxComponent);;
+		}));
+}
+
+// ================================================================================================
diff --git a/indra/newview/rlvenvironment.h b/indra/newview/rlvenvironment.h
new file mode 100644
index 0000000000000000000000000000000000000000..0f70e24cae29896103440ce6e8a798a608bf0721
--- /dev/null
+++ b/indra/newview/rlvenvironment.h
@@ -0,0 +1,66 @@
+/**
+ *
+ * Copyright (c) 2009-2020, Kitty Barnett
+ *
+ * The source code in this file is provided to you under the terms of the
+ * GNU Lesser General Public License, version 2.1, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. Terms of the LGPL can be found in doc/LGPL-licence.txt
+ * in this distribution, or online at http://www.gnu.org/licenses/lgpl-2.1.txt
+ *
+ * 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.
+ *
+ */
+
+#pragma once
+
+#include "llenvironment.h"
+
+#include "rlvcommon.h"
+
+// ============================================================================
+// RlvEnvironment - viewer-side scripted environment changes
+//
+
+class RlvEnvironment : public RlvExtCommandHandler
+{
+public:
+	RlvEnvironment();
+	~RlvEnvironment() override;
+
+	bool onReplyCommand(const RlvCommand& rlvCmd, ERlvCmdRet& cmdRet) override;
+	bool onForceCommand(const RlvCommand& rlvCmd, ERlvCmdRet& cmdRet) override;
+protected:
+	static LLEnvironment::EnvSelection_t getTargetEnvironment();
+	typedef std::map<std::string, std::function<ERlvCmdRet(const std::string&)>> handler_map_t;
+	typedef std::map<std::string, std::function<ERlvCmdRet(const std::string&, U32)>> legacy_handler_map_t;
+	static bool onHandleCommand(const RlvCommand& rlvCmd, ERlvCmdRet& cmdRet, const std::string& strCmdPrefix, const handler_map_t& fnLookup, const legacy_handler_map_t& legacyFnLookup);
+
+	/*
+	 * Command registration
+	 */
+protected:
+	                     void registerGetEnvFn(const std::string& strFnName, const std::function<std::string(LLEnvironment::EnvSelection_t env)>& getFn);
+	template<typename T> void registerSetEnvFn(const std::string& strFnName, const std::function<ERlvCmdRet(LLEnvironment::EnvSelection_t env, const T& strRlvOption)>& setFn);
+	template<typename T> void registerSkyFn(const std::string& strFnName, const std::function<T(LLSettingsSky::ptr_t)>& getFn, const std::function<void(LLSettingsSky::ptr_t, const T&)>& setFn);
+	template<typename T> void registerLegacySkyFn(const std::string& strFnName, const std::function<const T& (LLSettingsSky::ptr_t)>& getFn, const std::function<void(LLSettingsSky::ptr_t, const T&)>& setFn);
+
+	// Command handling helpers
+	template<typename T> std::string handleGetFn(const std::function<T(LLSettingsSky::ptr_t)>& fn);
+	template<typename T> ERlvCmdRet  handleSetFn(const std::string& strRlvOption, const std::function<void(LLSettingsSky::ptr_t, const T&)>& fn);
+	template<typename T> std::string handleLegacyGetFn(const std::function<const T& (LLSettingsSky::ptr_t)>& getFn, U32 idxComponent);
+	template<typename T> ERlvCmdRet  handleLegacySetFn(float optionValue, T value, const std::function<void(LLSettingsSky::ptr_t, const T&)>& setFn, U32 idxComponent);
+
+	/*
+	 * Member variables
+	 */
+protected:
+	handler_map_t m_GetFnLookup;
+	handler_map_t m_SetFnLookup;
+	legacy_handler_map_t m_LegacyGetFnLookup;
+	legacy_handler_map_t m_LegacySetFnLookup;
+};
+
+// ============================================================================
diff --git a/indra/newview/rlvextensions.cpp b/indra/newview/rlvextensions.cpp
index 2819019d6d0a659caaf1364c2eb14c574d018878..23ff85e6ab7385d493dab3399982699e91302303 100644
--- a/indra/newview/rlvextensions.cpp
+++ b/indra/newview/rlvextensions.cpp
@@ -1,17 +1,17 @@
-/** 
+/**
  *
  * Copyright (c) 2009-2011, Kitty Barnett
- * 
- * The source code in this file is provided to you under the terms of the 
+ *
+ * The source code in this file is provided to you under the terms of the
  * GNU Lesser General Public License, version 2.1, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 
- * PARTICULAR PURPOSE. Terms of the LGPL can be found in doc/LGPL-licence.txt 
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. Terms of the LGPL can be found in doc/LGPL-licence.txt
  * in this distribution, or online at http://www.gnu.org/licenses/lgpl-2.1.txt
- * 
+ *
  * By copying, modifying or distributing this software, you acknowledge that
- * you have read and understood your obligations described above, and agree to 
+ * you have read and understood your obligations described above, and agree to
  * abide by those obligations.
- * 
+ *
  */
 
 #include "llviewerprecompiledheaders.h"
@@ -23,352 +23,6 @@
 #include "rlvhandler.h"
 #include "rlvhelper.h"
 
-// ============================================================================
-/*
-class RlvWindLightControl
-{
-public:
-	enum EType			 { TYPE_COLOR, TYPE_COLOR_R, TYPE_FLOAT, TYPE_UNKNOWN };
-	enum EColorComponent { COMPONENT_R, COMPONENT_G, COMPONENT_B, COMPONENT_I, COMPONENT_NONE };
-public:
-	RlvWindLightControl(WLColorControl* pCtrl, bool fColorR) : m_eType((!fColorR) ? TYPE_COLOR: TYPE_COLOR_R), m_pColourCtrl(pCtrl), m_pFloatCtrl(NULL) {}
-	RlvWindLightControl(WLFloatControl* pCtrl) : m_eType(TYPE_FLOAT), m_pColourCtrl(NULL), m_pFloatCtrl(pCtrl) {}
-
-	EType		getControlType() const	{ return m_eType; }
-	bool		isColorType() const		{ return (TYPE_COLOR == m_eType) || (TYPE_COLOR_R == m_eType); }
-	bool		isFloatType() const		{ return (TYPE_FLOAT == m_eType); }
-	// TYPE_COLOR and TYPE_COLOR_R
-	F32			getColorComponent(EColorComponent eComponent, bool& fError) const;
-	LLVector4	getColorVector(bool& fError) const;
-	bool		setColorComponent(EColorComponent eComponent, F32 nValue);
-	// TYPE_FLOAT
-	F32			getFloat(bool& fError) const;
-	bool		setFloat(F32 nValue);
-
-	static EColorComponent getComponentFromCharacter(char ch);
-protected:
-	EType			m_eType;			// Type of the WindLight control
-	WLColorControl*	m_pColourCtrl;
-	WLFloatControl*	m_pFloatCtrl;
-};
-
-// Checked: 2011-08-29 (RLVa-1.4.1a) | Added: RLVa-1.4.1a
-static F32 get_intensity_from_color(const LLVector4& v)
-{
-	return llmax(v.mV[0], v.mV[1], v.mV[2]);
-}
-
-// Checked: 2011-08-29 (RLVa-1.4.1a) | Added: RLVa-1.4.1a
-F32 RlvWindLightControl::getColorComponent(EColorComponent eComponent, bool& fError) const
-{
-	switch (eComponent)
-	{
-		case COMPONENT_R: return getColorVector(fError).mV[0];
-		case COMPONENT_G: return getColorVector(fError).mV[1];
-		case COMPONENT_B: return getColorVector(fError).mV[2];
-		case COMPONENT_I: return get_intensity_from_color(getColorVector(fError));	// SL-2.8: Always seems to be 1.0 so get it manually
-		default         : RLV_ASSERT(false); fError = true; return 0.0;
-	}
-}
-
-// Checked: 2011-08-29 (RLVa-1.4.1a) | Added: RLVa-1.4.1a
-RlvWindLightControl::EColorComponent RlvWindLightControl::getComponentFromCharacter(char ch)
-{
-	if (('r' == ch) || ('x' == ch))
-		return COMPONENT_R;
-	else if (('g' == ch) || ('y' == ch))
-		return COMPONENT_G;
-	else if (('b' == ch) || ('d' == ch))
-		return COMPONENT_B;
-	else if ('i' == ch)
-		return COMPONENT_I;
-	return COMPONENT_NONE;
-}
-
-// Checked: 2011-08-29 (RLVa-1.4.1a) | Added: RLVa-1.4.1a
-LLVector4 RlvWindLightControl::getColorVector(bool& fError) const
-{
-	if ((fError = !isColorType()))
-		return LLVector4(0, 0, 0, 0);
-	F32 nMult = (m_pColourCtrl->isSunOrAmbientColor) ? 3.0f : ((m_pColourCtrl->isBlueHorizonOrDensity) ? 2.0f : 1.0f);
-	return LLWLParamManager::getInstance()->mCurParams.getVector(m_pColourCtrl->mName, fError) / nMult;
-}
-
-// Checked: 2011-08-28 (RLVa-1.4.1a) | Added: RLVa-1.4.1a
-bool RlvWindLightControl::setColorComponent(EColorComponent eComponent, F32 nValue)
-{
-	if (isColorType())
-	{
-		nValue *= (m_pColourCtrl->isSunOrAmbientColor) ? 3.0f : ((m_pColourCtrl->isBlueHorizonOrDensity) ? 2.0f : 1.0f);
-		if (COMPONENT_I == eComponent)								// (See: LLFloaterWindLight::onColorControlIMoved)
-		{
-			if (m_pColourCtrl->hasSliderName)
-			{
-				F32 curMax = llmax(m_pColourCtrl->r, m_pColourCtrl->g, m_pColourCtrl->b);
-				if ( (0.0f == nValue) || (0.0f == curMax) )
-				{
-					m_pColourCtrl->r = m_pColourCtrl->g = m_pColourCtrl->b = m_pColourCtrl->i = nValue;
-				}
-				else
-				{
-					F32 nDelta = (nValue - curMax) / curMax;
-					m_pColourCtrl->r *= (1.0f + nDelta);
-					m_pColourCtrl->g *= (1.0f + nDelta);
-					m_pColourCtrl->b *= (1.0f + nDelta);
-					m_pColourCtrl->i = nValue;
-				}
-			}
-		}
-		else														// (See: LLFloaterWindLight::onColorControlRMoved)
-		{
-			F32* pnValue = (COMPONENT_R == eComponent) ? &m_pColourCtrl->r : (COMPONENT_G == eComponent) ? &m_pColourCtrl->g : (COMPONENT_B == eComponent) ? &m_pColourCtrl->b : NULL;
-			if (pnValue)
-				*pnValue = nValue;
-			if (m_pColourCtrl->hasSliderName)
-				m_pColourCtrl->i = llmax(m_pColourCtrl->r, m_pColourCtrl->g, m_pColourCtrl->b);
-		}
-		m_pColourCtrl->update(LLWLParamManager::getInstance()->mCurParams);
-		LLWLParamManager::getInstance()->propagateParameters();
-	}
-	return isColorType();
-}
-
-// Checked: 2011-08-29 (RLVa-1.4.1a) | Added: RLVa-1.4.1a
-F32 RlvWindLightControl::getFloat(bool& fError) const
-{
-	return (!(fError = (TYPE_FLOAT != m_eType))) ? LLWLParamManager::getInstance()->mCurParams.getVector(m_pFloatCtrl->mName, fError).mV[0] * m_pFloatCtrl->mult : 0.0;
-}
-
-// Checked: 2011-08-28 (RLVa-1.4.1a) | Added: RLVa-1.4.1a
-bool RlvWindLightControl::setFloat(F32 nValue)
-{
-	if (TYPE_FLOAT == m_eType)
-	{
-		m_pFloatCtrl->x = nValue / m_pFloatCtrl->mult;
-		m_pFloatCtrl->update(LLWLParamManager::getInstance()->mCurParams);
-		LLWLParamManager::getInstance()->propagateParameters();
-	}
-	return (TYPE_FLOAT == m_eType);
-}
-
-// ============================================================================
-
-class RlvWindLight : public LLSingleton<RlvWindLight>
-{
-	LLSINGLETON(RlvWindLight);
-public:
-	std::string	getValue(const std::string& strSetting, bool& fError);
-	bool		setValue(const std::string& strRlvName, const std::string& strValue);
-
-protected:
-	std::map<std::string, RlvWindLightControl> m_ControlLookupMap;
-};
-
-// Checked: 2011-08-29 (RLVa-1.4.1a) | Added: RLVa-1.4.1a
-RlvWindLight::RlvWindLight()
-{
-	LLWLParamManager* pWLParamMgr = LLWLParamManager::getInstance();
-
-	// TYPE_FLOAT
-	m_ControlLookupMap.insert(std::pair<std::string, RlvWindLightControl>("cloudcoverage",	RlvWindLightControl(&pWLParamMgr->mCloudCoverage)));
-	m_ControlLookupMap.insert(std::pair<std::string, RlvWindLightControl>("cloudscale",		RlvWindLightControl(&pWLParamMgr->mCloudScale)));
-	m_ControlLookupMap.insert(std::pair<std::string, RlvWindLightControl>("densitymultiplier", RlvWindLightControl(&pWLParamMgr->mDensityMult)));
-	m_ControlLookupMap.insert(std::pair<std::string, RlvWindLightControl>("distancemultiplier", RlvWindLightControl(&pWLParamMgr->mDistanceMult)));
-	m_ControlLookupMap.insert(std::pair<std::string, RlvWindLightControl>("maxaltitude",	RlvWindLightControl(&pWLParamMgr->mMaxAlt)));
-	m_ControlLookupMap.insert(std::pair<std::string, RlvWindLightControl>("scenegamma",		RlvWindLightControl(&pWLParamMgr->mWLGamma)));
-	m_ControlLookupMap.insert(std::pair<std::string, RlvWindLightControl>("hazedensity",	RlvWindLightControl(&pWLParamMgr->mHazeDensity)));
-	m_ControlLookupMap.insert(std::pair<std::string, RlvWindLightControl>("hazehorizon",	RlvWindLightControl(&pWLParamMgr->mHazeHorizon)));
-	// TYPE_COLOR
-	m_ControlLookupMap.insert(std::pair<std::string, RlvWindLightControl>("ambient",		RlvWindLightControl(&pWLParamMgr->mAmbient, false)));
-	m_ControlLookupMap.insert(std::pair<std::string, RlvWindLightControl>("bluedensity",	RlvWindLightControl(&pWLParamMgr->mBlueDensity, false)));
-	m_ControlLookupMap.insert(std::pair<std::string, RlvWindLightControl>("bluehorizon",	RlvWindLightControl(&pWLParamMgr->mBlueHorizon, false)));
-	m_ControlLookupMap.insert(std::pair<std::string, RlvWindLightControl>("cloud",			RlvWindLightControl(&pWLParamMgr->mCloudMain, false)));
-	m_ControlLookupMap.insert(std::pair<std::string, RlvWindLightControl>("cloudcolor",		RlvWindLightControl(&pWLParamMgr->mCloudColor, false)));
-	m_ControlLookupMap.insert(std::pair<std::string, RlvWindLightControl>("clouddetail",	RlvWindLightControl(&pWLParamMgr->mCloudDetail, false)));
-	m_ControlLookupMap.insert(std::pair<std::string, RlvWindLightControl>("sunmooncolor",	RlvWindLightControl(&pWLParamMgr->mSunlight, false)));
-}
-
-// Checked: 2011-08-29 (RLVa-1.4.1a) | Added: RLVa-1.4.1a
-std::string RlvWindLight::getValue(const std::string& strSetting, bool& fError)
-{
-	LLWLParamManager* pWLParams = LLWLParamManager::getInstance(); 
-	LLEnvManagerNew* pEnvMgr = LLEnvManagerNew::getInstance();
-
-	fError = false;							// Assume we won't fail
-	if ("preset" == strSetting)
-		return (pEnvMgr->getUseFixedSky()) ? pEnvMgr->getSkyPresetName() : std::string();
-	else if ("daycycle" == strSetting)
-		return (pEnvMgr->getUseDayCycle()) ? pEnvMgr->getDayCycleName() : std::string();
-
-	F32 nValue = 0.0f;
-	if ("daytime" == strSetting)
-	{
-		nValue = (pEnvMgr->getUseFixedSky()) ? pWLParams->mCurParams.getFloat("sun_angle", fError) / F_TWO_PI : -1.0f;
-	}
-	else if (("sunglowfocus" == strSetting) || ("sunglowsize" == strSetting))
-	{
-		pWLParams->mGlow = pWLParams->mCurParams.getVector(pWLParams->mGlow.mName, fError);
-		RLV_ASSERT_DBG(!fError);
-
-		if ("sunglowfocus" == strSetting) 
-			nValue = -pWLParams->mGlow.b / 5.0f;
-		else
-			nValue = 2 - pWLParams->mGlow.r / 20.0f;
-	}
-	else if ("starbrightness" == strSetting)		nValue = pWLParams->mCurParams.getStarBrightness();
-	else if ("eastangle" == strSetting)				nValue = pWLParams->mCurParams.getEastAngle() / F_TWO_PI;
-	else if ("sunmoonposition" == strSetting)		nValue = pWLParams->mCurParams.getSunAngle() / F_TWO_PI;
-	else if ("cloudscrollx" == strSetting)			nValue = pWLParams->mCurParams.getCloudScrollX() - 10.0f;
-	else if ("cloudscrolly" == strSetting)			nValue = pWLParams->mCurParams.getCloudScrollY() - 10.0f;
-	else
-	{
-		std::map<std::string, RlvWindLightControl>::const_iterator itControl = m_ControlLookupMap.find(strSetting);
-		if (m_ControlLookupMap.end() != itControl)
-		{
-			switch (itControl->second.getControlType())
-			{
-				case RlvWindLightControl::TYPE_FLOAT:
-					nValue = itControl->second.getFloat(fError);
-					break;
-				case RlvWindLightControl::TYPE_COLOR_R:
-					nValue = itControl->second.getColorComponent(RlvWindLightControl::COMPONENT_R, fError);
-					break;
-				default:
-					fError = true;
-					break;
-			}
-		}
-		else
-		{
-			// Couldn't find the exact name, check for a color control name
-			RlvWindLightControl::EColorComponent eComponent = RlvWindLightControl::getComponentFromCharacter(strSetting[strSetting.length() - 1]);
-			if (RlvWindLightControl::COMPONENT_NONE != eComponent)
-				itControl = m_ControlLookupMap.find(strSetting.substr(0, strSetting.length() - 1));
-			if ( (m_ControlLookupMap.end() != itControl) && (itControl->second.isColorType()) )
-				nValue = itControl->second.getColorComponent(eComponent, fError);
-			else
-				fError = true;
-		}
-	}
-	return llformat("%f", nValue);
-}
-
-// Checked: 2011-08-29 (RLVa-1.4.1a) | Added: RLVa-1.4.1a
-bool RlvWindLight::setValue(const std::string& strRlvName, const std::string& strValue)
-{
-	F32 nValue = 0.0f;
-	// Sanity check - make sure strValue specifies a number for all settings except "preset" and "daycycle"
-	if ( (RlvSettings::getNoSetEnv()) || 
-		 ( (!LLStringUtil::convertToF32(strValue, nValue)) && (("preset" != strRlvName) && ("daycycle" != strRlvName)) ) )
- 	{
-		return false;
-	}
-
-	LLWLParamManager* pWLParams = LLWLParamManager::getInstance(); 
-	LLEnvManagerNew* pEnvMgr = LLEnvManagerNew::getInstance();
-
-	if ("daytime" == strRlvName)
-	{
-		if (0.0f <= nValue)
-		{
-			pWLParams->mAnimator.deactivate();
-			pWLParams->mAnimator.setDayTime(nValue);
-			pWLParams->mAnimator.update(pWLParams->mCurParams);
-		}
-		else
-		{
-			pEnvMgr->setUserPrefs(pEnvMgr->getWaterPresetName(), pEnvMgr->getSkyPresetName(), pEnvMgr->getDayCycleName(), false, true);
-		}
-		return true;
-	}
-	else if ("preset" == strRlvName)
-	{
-		std::string strPresetName = pWLParams->findPreset(strValue, LLEnvKey::SCOPE_LOCAL);
-		if (!strPresetName.empty())
-			pEnvMgr->useSkyPreset(strPresetName);
-		return !strPresetName.empty();
-	}
-	else if ("daycycle" == strRlvName)
-	{
-		std::string strPresetName = LLDayCycleManager::instance().findPreset(strValue);
-		if (!strPresetName.empty())
-			pEnvMgr->useDayCycle(strValue, LLEnvKey::SCOPE_LOCAL);
-		return !strPresetName.empty();
-	}
-
-	bool fError = false;
-	pWLParams->mAnimator.deactivate();
-	if (("sunglowfocus" == strRlvName) || ("sunglowsize" == strRlvName))
-	{
-		pWLParams->mGlow = pWLParams->mCurParams.getVector(pWLParams->mGlow.mName, fError);
-		RLV_ASSERT_DBG(!fError);
-
-		if ("sunglowfocus" == strRlvName)
-			pWLParams->mGlow.b = -nValue * 5;
-		else
-			pWLParams->mGlow.r = (2 - nValue) * 20;
-
-		pWLParams->mGlow.update(pWLParams->mCurParams);
-		pWLParams->propagateParameters();
-		return true;
-	}
-	else if ("starbrightness" == strRlvName)
-	{
-		pWLParams->mCurParams.setStarBrightness(nValue);
-		return true;
-	}
-	else if (("eastangle" == strRlvName) || ("sunmoonposition" == strRlvName))
-	{
-		if ("eastangle" == strRlvName)
-			pWLParams->mCurParams.setEastAngle(F_TWO_PI * nValue);
-		else
-			pWLParams->mCurParams.setSunAngle(F_TWO_PI * nValue);
-
-		// Set the sun vector
-		pWLParams->mLightnorm.r = -sin(pWLParams->mCurParams.getEastAngle()) * cos(pWLParams->mCurParams.getSunAngle());
-		pWLParams->mLightnorm.g = sin(pWLParams->mCurParams.getSunAngle());
-		pWLParams->mLightnorm.b = cos(pWLParams->mCurParams.getEastAngle()) * cos(pWLParams->mCurParams.getSunAngle());
-		pWLParams->mLightnorm.i = 1.f;
-
-		pWLParams->propagateParameters();
-		return true;
-	}
-	else if ("cloudscrollx" == strRlvName)
-	{
-		pWLParams->mCurParams.setCloudScrollX(nValue + 10.0f);
-		return true;
-	}
-	else if ("cloudscrolly" == strRlvName)
-	{
-		pWLParams->mCurParams.setCloudScrollY(nValue + 10.0f);
-		return true;
-	}
-
-	std::map<std::string, RlvWindLightControl>::iterator itControl = m_ControlLookupMap.find(strRlvName);
-	if (m_ControlLookupMap.end() != itControl)
-	{
-		switch (itControl->second.getControlType())
-		{
-			case RlvWindLightControl::TYPE_FLOAT:
-				return itControl->second.setFloat(nValue);
-			case RlvWindLightControl::TYPE_COLOR_R:
-				return itControl->second.setColorComponent(RlvWindLightControl::COMPONENT_R, nValue);
-			default:
-				RLV_ASSERT(false);
-		}
-	}
-	else
-	{
-		// Couldn't find the exact name, check for a color control name
-		RlvWindLightControl::EColorComponent eComponent = RlvWindLightControl::getComponentFromCharacter(strRlvName[strRlvName.length() - 1]);
-		if (RlvWindLightControl::COMPONENT_NONE != eComponent)
-			itControl = m_ControlLookupMap.find(strRlvName.substr(0, strRlvName.length() - 1));
-		if ( (m_ControlLookupMap.end() != itControl) && (itControl->second.isColorType()) )
-			return itControl->second.setColorComponent(eComponent, nValue);
-	}
-	return false;
-}
-*/
 // ============================================================================
 
 std::map<std::string, S16> RlvExtGetSet::m_DbgAllowed;
@@ -437,24 +91,6 @@ bool RlvExtGetSet::processCommand(const RlvCommand& rlvCmd, ERlvCmdRet& eRet)
 				return true;
 			}
 		}
-		else if ("env" == strBehaviour)
-		{
-			//bool fError = false;
-			//if ( ("get" == strGetSet) && (RLV_TYPE_REPLY == rlvCmd.getParamType()) )
-			//{
-			//	RlvUtil::sendChatReply(rlvCmd.getParam(), RlvWindLight::instance().getValue(strSetting, fError));
-			//	eRet = (!fError) ? RLV_RET_SUCCESS : RLV_RET_FAILED_UNKNOWN;
-			//	return true;
-			//}
-			//else if ( ("set" == strGetSet) && (RLV_TYPE_FORCE == rlvCmd.getParamType()) )
-			//{
-			//	if (!gRlvHandler.hasBehaviourExcept(RLV_BHVR_SETENV, rlvCmd.getObjectID()))
-			//		eRet = (RlvWindLight::instance().setValue(strSetting, rlvCmd.getOption())) ? RLV_RET_SUCCESS : RLV_RET_FAILED_UNKNOWN;
-			//	else
-			//		eRet = RLV_RET_FAILED_LOCK;
-			//	return true;
-			//}
-		}
 	}
 	else if ("setrot" == rlvCmd.getBehaviour())
 	{
diff --git a/indra/newview/rlvhandler.cpp b/indra/newview/rlvhandler.cpp
index 9d973b3e612d24ddf9ede6ef3e118da12b4d759b..38b95768ae7e85171b5b9bf76516326738e903bb 100644
--- a/indra/newview/rlvhandler.cpp
+++ b/indra/newview/rlvhandler.cpp
@@ -37,6 +37,7 @@
 #include "llavataractions.h"            // @stopim IM query
 #include "llavatarnamecache.h"			// @shownames
 #include "llavatarlist.h"				// @shownames
+#include "llfloatercamera.h"			// @setcam family
 #include "llfloatersidepanelcontainer.h"// @shownames
 #include "llnotifications.h"			// @list IM query
 #include "llnotificationsutil.h"
@@ -55,6 +56,7 @@
 
 // RLVa includes
 #include "rlvactions.h"
+#include "rlvenvironment.h"
 #include "rlvfloaters.h"
 #include "rlvactions.h"
 #include "rlvhandler.h"
@@ -144,14 +146,59 @@ RlvHandler::RlvHandler() : m_fCanCancelTp(true), m_posSitSource(), m_pGCTimer(NU
 
 RlvHandler::~RlvHandler()
 {
+	cleanup();
+}
+
+void RlvHandler::cleanup()
+{
+	// Nothing to clean if we're not enabled (or already cleaned up)
+	if (!m_fEnabled)
+		return;
+>>>>>>> RLVa/rlva/development
+
+	//
+	// Clean up any restrictions that are still active
+	//
+	RLV_ASSERT(LLApp::isQuitting());	// Several commands toggle debug settings but won't if they know the viewer is quitting
+
+	// Assume we have no way to predict how m_Objects will change so make a copy ahead of time
+	uuid_vec_t idRlvObjects;
+	idRlvObjects.reserve(m_Objects.size());
+	std::transform(m_Objects.begin(), m_Objects.end(), std::back_inserter(idRlvObjects), [](const rlv_object_map_t::value_type& kvPair) {return kvPair.first; });
+	for (const LLUUID & idRlvObj : idRlvObjects)
+	{
+		processCommand(idRlvObj, "clear", true);
+	}
+
+	// Sanity check
+	RLV_ASSERT(m_Objects.empty());
+	RLV_ASSERT(m_Exceptions.empty());
+	RLV_ASSERT(std::all_of(m_Behaviours, m_Behaviours + RLV_BHVR_COUNT, [](S16 cnt) { return !cnt; }));
+	RLV_ASSERT(m_CurCommandStack.empty());
+	RLV_ASSERT(m_CurObjectStack.empty());
+	RLV_ASSERT(m_pOverlayImage.isNull());
+
+	//
+	// Clean up what's left
+	//
 	gAgent.removeListener(this);
+	m_Retained.clear();
+	//delete m_pGCTimer;	// <- deletes itself
+
 	if (m_PendingGroupChange.first.notNull())
 	{
-		LLGroupMgr::instance().removeObserver(m_PendingGroupChange.first, this);
+		if (LLGroupMgr::instanceExists())
+			LLGroupMgr::instance().removeObserver(m_PendingGroupChange.first, this);
 		m_PendingGroupChange = std::make_pair(LLUUID::null, LLStringUtil::null);
 	}
 
-	//delete m_pGCTimer;	// <- deletes itself
+	for (RlvExtCommandHandler* pCmdHandler : m_CommandHandlers)
+	{
+		delete pCmdHandler;
+	}
+	m_CommandHandlers.clear();
+
+	m_fEnabled = false;
 }
 
 // ============================================================================
@@ -302,7 +349,7 @@ void RlvHandler::removeBlockedObject(const LLUUID& idObj)
 		}), m_BlockedObjects.end());
 }
 
-void RlvHandler::getAttachmentResourcesCoro(const std::string strUrl)
+void RlvHandler::getAttachmentResourcesCoro(const std::string& strUrl)
 {
 	LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
 	LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("RlvHandler::getAttachmentResourcesCoro", httpPolicy));
@@ -393,42 +440,46 @@ bool RlvHandler::notifyCommandHandlers(rlvExtCommandHandler f, const RlvCommand&
 }
 
 // Checked: 2009-11-25 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f
-ERlvCmdRet RlvHandler::processCommand(const RlvCommand& rlvCmd, bool fFromObj)
+ERlvCmdRet RlvHandler::processCommand(std::reference_wrapper<const RlvCommand> rlvCmd, bool fFromObj)
 {
-	RLV_DEBUGS << "[" << rlvCmd.getObjectID() << "]: " << rlvCmd.asString() << RLV_ENDL;
-
-	if ( (isBlockedObject(rlvCmd.getObjectID())) && (RLV_TYPE_REMOVE != rlvCmd.getParamType()) && (RLV_TYPE_CLEAR != rlvCmd.getParamType()) )
-	{
-		RLV_DEBUGS << "\t-> blocked object" << RLV_ENDL;
-		return RLV_RET_FAILED_BLOCKED;
-	}
-	if (!rlvCmd.isValid())
 	{
-		RLV_DEBUGS << "\t-> invalid syntax" << RLV_ENDL;
-		return RLV_RET_FAILED_SYNTAX;
-	}
-	if (rlvCmd.isBlocked())
-	{
-		RLV_DEBUGS << "\t-> blocked command" << RLV_ENDL;
-		return RLV_RET_FAILED_DISABLED;
+		const RlvCommand& rlvCmdTmp = rlvCmd; // Reference to the temporary with limited variable scope since we don't want it to leak below
+
+		RLV_DEBUGS << "[" << rlvCmdTmp.getObjectID() << "]: " << rlvCmdTmp.asString() << RLV_ENDL;
+
+		if ( (isBlockedObject(rlvCmdTmp.getObjectID())) && (RLV_TYPE_REMOVE != rlvCmdTmp.getParamType()) && (RLV_TYPE_CLEAR != rlvCmdTmp.getParamType()) )
+		{
+			RLV_DEBUGS << "\t-> blocked object" << RLV_ENDL;
+			return RLV_RET_FAILED_BLOCKED;
+		}
+		if (!rlvCmdTmp.isValid())
+		{
+			RLV_DEBUGS << "\t-> invalid syntax" << RLV_ENDL;
+			return RLV_RET_FAILED_SYNTAX;
+		}
+		if (rlvCmdTmp.isBlocked())
+		{
+			RLV_DEBUGS << "\t-> blocked command" << RLV_ENDL;
+			return RLV_RET_FAILED_DISABLED;
+		}
 	}
 
 	// Using a stack for executing commands solves a few problems:
 	//   - if we passed RlvObject::m_idObj for idObj somewhere and process a @clear then idObj points to invalid/cleared memory at the end
 	//   - if command X triggers command Y along the way then getCurrentCommand()/getCurrentObject() still return Y even when finished
-	m_CurCommandStack.push(&rlvCmd); m_CurObjectStack.push(rlvCmd.getObjectID());
+	m_CurCommandStack.push(rlvCmd); m_CurObjectStack.push(rlvCmd.get().getObjectID());
 	const LLUUID& idCurObj = m_CurObjectStack.top();
 
 	ERlvCmdRet eRet = RLV_RET_UNKNOWN;
-	switch (rlvCmd.getParamType())
+	switch (rlvCmd.get().getParamType())
 	{
 		case RLV_TYPE_ADD:		// Checked: 2009-11-26 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f
 			{
-				if ( (m_Behaviours[rlvCmd.getBehaviourType()]) && 
-					 ( (RLV_BHVR_SETCAM == rlvCmd.getBehaviourType()) || (RLV_BHVR_SETDEBUG == rlvCmd.getBehaviourType()) || (RLV_BHVR_SETENV == rlvCmd.getBehaviourType()) ) )
+				ERlvBehaviour eBhvr = rlvCmd.get().getBehaviourType();
+				if ( (m_Behaviours[eBhvr]) && ( (RLV_BHVR_SETCAM == eBhvr) || (RLV_BHVR_SETDEBUG == eBhvr) || (RLV_BHVR_SETENV == eBhvr) ) )
 				{
 					// Some restrictions can only be held by one single object to avoid deadlocks
-					RLV_DEBUGS << "\t- " << rlvCmd.getBehaviour() << " is already set by another object => discarding" << RLV_ENDL;
+					RLV_DEBUGS << "\t- " << rlvCmd.get().getBehaviour() << " is already set by another object => discarding" << RLV_ENDL;
 					eRet = RLV_RET_FAILED_LOCK;
 					break;
 				}
@@ -436,14 +487,14 @@ ERlvCmdRet RlvHandler::processCommand(const RlvCommand& rlvCmd, bool fFromObj)
 				rlv_object_map_t::iterator itObj = m_Objects.find(idCurObj); bool fAdded = false;
 				if (itObj != m_Objects.end())
 				{
-					RlvObject& rlvObj = itObj->second;
-					fAdded = rlvObj.addCommand(rlvCmd);
+					// Add the command to an existing object
+					rlvCmd = itObj->second.addCommand(rlvCmd, fAdded);
 				}
 				else
 				{
-					RlvObject rlvObj(idCurObj);
-					fAdded = rlvObj.addCommand(rlvCmd);
-					itObj = m_Objects.insert(std::pair<LLUUID, RlvObject>(idCurObj, rlvObj)).first;
+					// Create a new RLV object and then add the command to it (and grab its reference)
+					itObj = m_Objects.insert(std::pair<LLUUID, RlvObject>(idCurObj, RlvObject(idCurObj))).first;
+					rlvCmd = itObj->second.addCommand(rlvCmd, fAdded);
 				}
 
 				RLV_DEBUGS << "\t- " << ( (fAdded) ? "adding behaviour" : "skipping duplicate" ) << RLV_ENDL;
@@ -518,12 +569,13 @@ ERlvCmdRet RlvHandler::processCommand(const RlvCommand& rlvCmd, bool fFromObj)
 // Checked: 2009-11-25 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f
 ERlvCmdRet RlvHandler::processCommand(const LLUUID& idObj, const std::string& strCommand, bool fFromObj)
 {
+	const RlvCommand rlvCmd(idObj, strCommand);
 	if (STATE_STARTED != LLStartUp::getStartupState())
 	{
-		m_Retained.push_back(RlvCommand(idObj, strCommand));
+		m_Retained.push_back(rlvCmd);
 		return RLV_RET_RETAINED;
 	}
-	return processCommand(RlvCommand(idObj, strCommand), fFromObj);
+	return processCommand(std::ref(rlvCmd), fFromObj);
 }
 
 // Checked: 2010-02-27 (RLVa-1.2.0a) | Modified: RLVa-1.1.0f
@@ -538,7 +590,7 @@ void RlvHandler::processRetainedCommands(ERlvBehaviour eBhvrFilter /*=RLV_BHVR_U
 		if ( ((RLV_BHVR_UNKNOWN == eBhvrFilter) || (rlvCmd.getBehaviourType() == eBhvrFilter)) && 
 			 ((RLV_TYPE_UNKNOWN == eTypeFilter) || (rlvCmd.getParamType() == eTypeFilter)) )
 		{
-			processCommand(rlvCmd, true);
+			processCommand(std::ref(rlvCmd), true);
 			m_Retained.erase(itCurCmd);
 		}
 	}
@@ -807,6 +859,23 @@ void RlvHandler::setActiveGroupRole(const LLUUID& idGroup, const std::string& st
 	m_PendingGroupChange = std::make_pair(LLUUID::null, LLStringUtil::null);
 }
 
+// @setcam family
+void RlvHandler::setCameraOverride(bool fOverride)
+{
+	if ( (fOverride) && (CAMERA_RLV_SETCAM_VIEW != gAgentCamera.getCameraPreset()) )
+	{
+		m_strCameraPresetRestore = gSavedSettings.getString("PresetCameraActive");
+		gAgentCamera.switchCameraPreset(CAMERA_RLV_SETCAM_VIEW);
+	}
+	else if ( (!fOverride) && (CAMERA_RLV_SETCAM_VIEW == gAgentCamera.getCameraPreset() && (!RlvActions::isCameraPresetLocked())) )
+	{
+		// We need to clear it or it won't reset properly
+		gSavedSettings.setString("PresetCameraActive", LLStringUtil::null);
+		LLFloaterCamera::switchToPreset(m_strCameraPresetRestore);
+		m_strCameraPresetRestore.clear();
+	}
+}
+
 // ============================================================================
 // Externally invoked event handlers
 //
@@ -1031,6 +1100,12 @@ bool RlvHandler::onGC()
 	return (0 != m_Objects.size());	// GC will kill itself if it has nothing to do
 }
 
+// static
+void RlvHandler::cleanupClass()
+{
+	gRlvHandler.cleanup();
+}
+
 // Checked: 2009-11-26 (RLVa-1.1.0f) | Added: RLVa-1.1.0f
 void RlvHandler::onIdleStartup(void* pParam)
 {
@@ -1432,6 +1507,7 @@ bool RlvHandler::setEnabled(bool fEnable)
 		RlvSettings::initClass();
 		RlvStrings::initClass();
 
+		RlvHandler::instance().addCommandHandler(new RlvEnvironment());
 		RlvHandler::instance().addCommandHandler(new RlvExtGetSet());
 
 		// Make sure we get notified when login is successful
@@ -2050,20 +2126,23 @@ void RlvBehaviourModifierHandler<RLV_MODIFIER_SETCAM_AVDISTMIN>::onValueChange()
 		gAgentCamera.changeCameraToThirdPerson();
 }
 
-// Handles: @setcam_eyeoffset:<vector3>=n|y and @setcam_focusoffset:<vector3>=n|y toggles
+// Handles: @setcam_eyeoffset:<vector3>=n|y, @setcam_eyeoffsetscale:<float>=n|y and @setcam_focusoffset:<vector3>=n|y toggles
 template<> template<>
 void RlvBehaviourCamEyeFocusOffsetHandler::onCommandToggle(ERlvBehaviour eBhvr, bool fHasBhvr)
 {
 	if (fHasBhvr)
 	{
-		gAgentCamera.switchCameraPreset("RLVa View");
+		gRlvHandler.setCameraOverride(true);
 	}
 	else
 	{
-		const RlvBehaviourModifier* pBhvrEyeModifier = RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_EYEOFFSET);
-		const RlvBehaviourModifier* pBhvrOffsetModifier = RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_FOCUSOFFSET);
-		if ( (!pBhvrEyeModifier->hasValue()) && (!pBhvrOffsetModifier->hasValue()) )
-			gAgentCamera.switchCameraPreset("Group View");
+		const RlvBehaviourModifier* pBhvrEyeOffsetModifier = RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_EYEOFFSET);
+		const RlvBehaviourModifier* pBhvrEyeOffsetScaleModifier = RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_EYEOFFSETSCALE);
+		const RlvBehaviourModifier* pBhvrFocusOffsetModifier = RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_FOCUSOFFSET);
+		if ( (!pBhvrEyeOffsetModifier->hasValue()) && (!pBhvrEyeOffsetScaleModifier->hasValue()) && (!pBhvrFocusOffsetModifier->hasValue()) )
+		{
+			gRlvHandler.setCameraOverride(false);
+		}
 	}
 }
 
@@ -2081,7 +2160,21 @@ void RlvBehaviourModifierHandler<RLV_MODIFIER_SETCAM_EYEOFFSET>::onValueChange()
 	}
 }
 
-// Handles: @setcam_focusoffset:<vector3>=n|y changes
+// Handles: @setcam_eyeoffsetscale:<float>=n|y changes
+template<>
+void RlvBehaviourModifierHandler<RLV_MODIFIER_SETCAM_EYEOFFSETSCALE>::onValueChange() const
+{
+	if (RlvBehaviourModifier* pBhvrModifier = RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_EYEOFFSETSCALE))
+	{
+		LLControlVariable* pControl = gSavedSettings.getControl("CameraOffsetScaleRLVa");
+		if (pBhvrModifier->hasValue())
+			pControl->setValue(pBhvrModifier->getValue<float>());
+		else
+			pControl->resetToDefault();
+	}
+}
+
+// Handles: @setcam_focusoffset:<vector3d>=n|y changes
 template<>
 void RlvBehaviourModifierHandler<RLV_MODIFIER_SETCAM_FOCUSOFFSET>::onValueChange() const
 {
@@ -2089,7 +2182,7 @@ void RlvBehaviourModifierHandler<RLV_MODIFIER_SETCAM_FOCUSOFFSET>::onValueChange
 	{
 		LLControlVariable* pControl = gSavedSettings.getControl("FocusOffsetRLVaView");
 		if (pBhvrModifier->hasValue())
-			pControl->setValue(pBhvrModifier->getValue<LLVector3>().getValue());
+			pControl->setValue(pBhvrModifier->getValue<LLVector3d>().getValue());
 		else
 			pControl->resetToDefault();
 	}
@@ -2208,12 +2301,13 @@ void RlvBehaviourToggleHandler<RLV_BHVR_SETCAM>::onCommandToggle(ERlvBehaviour e
 	if (fHasCamUnlock != gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_UNLOCK))
 		RlvBehaviourToggleHandler<RLV_BHVR_SETCAM_UNLOCK>::onCommandToggle(RLV_BHVR_SETCAM_UNLOCK, !fHasCamUnlock);
 
-	gAgentCamera.switchCameraPreset( (fHasBhvr) ? "RLVa View" : "Group View" );
+	gRlvHandler.setCameraOverride(fHasBhvr);
 	RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_AVDISTMIN)->setPrimaryObject(idRlvObject);
 	RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_AVDISTMAX)->setPrimaryObject(idRlvObject);
 	RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_ORIGINDISTMIN)->setPrimaryObject(idRlvObject);
 	RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_ORIGINDISTMAX)->setPrimaryObject(idRlvObject);
 	RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_EYEOFFSET)->setPrimaryObject(idRlvObject);
+	RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_EYEOFFSETSCALE)->setPrimaryObject(idRlvObject);
 	RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_FOCUSOFFSET)->setPrimaryObject(idRlvObject);
 	RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_FOVMIN)->setPrimaryObject(idRlvObject);
 	RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_FOVMAX)->setPrimaryObject(idRlvObject);
@@ -2235,30 +2329,43 @@ void RlvBehaviourToggleHandler<RLV_BHVR_SETDEBUG>::onCommandToggle(ERlvBehaviour
 template<> template<>
 void RlvBehaviourToggleHandler<RLV_BHVR_SETENV>::onCommandToggle(ERlvBehaviour eBhvr, bool fHasBhvr)
 {
-	//const std::string strEnvFloaters[] = { "env_post_process", "env_settings", "env_delete_preset", "env_edit_sky", "env_edit_water", "env_edit_day_cycle" };
-	//for (int idxFloater = 0, cntFloater = sizeof(strEnvFloaters) / sizeof(std::string); idxFloater < cntFloater; idxFloater++)
-	//{
-	//	if (fHasBhvr)
-	//	{
-	//		// Hide the floater if it's currently visible
-	//		LLFloaterReg::const_instance_list_t envFloaters = LLFloaterReg::getFloaterList(strEnvFloaters[idxFloater]);
-	//		for (LLFloater* pFloater : envFloaters)
-	//			pFloater->closeFloater();
-	//		RlvUIEnabler::instance().addGenericFloaterFilter(strEnvFloaters[idxFloater]);
-	//	}
-	//	else
-	//	{
-	//		RlvUIEnabler::instance().removeGenericFloaterFilter(strEnvFloaters[idxFloater]);
-	//	}
-	//}
-
-	//// Don't allow toggling "Basic Shaders" and/or "Atmopsheric Shaders" through the debug settings under @setenv=n
-	//gSavedSettings.getControl("VertexShaderEnable")->setHiddenFromSettingsEditor(fHasBhvr);
-	//gSavedSettings.getControl("WindLightUseAtmosShaders")->setHiddenFromSettingsEditor(fHasBhvr);
-
-	//// Restore the user's WindLight preferences when releasing
-	//if (!fHasBhvr)
-	//	LLEnvManagerNew::instance().usePrefs();
+	const std::string strEnvFloaters[] = { "env_adjust_snapshot", "env_edit_extdaycycle", "env_fixed_environmentent_sky", "env_fixed_environmentent_water", "my_environments" };
+	for (int idxFloater = 0, cntFloater = sizeof(strEnvFloaters) / sizeof(std::string); idxFloater < cntFloater; idxFloater++)
+	{
+		if (fHasBhvr)
+		{
+			// Hide the floater if it's currently visible
+			LLFloaterReg::const_instance_list_t envFloaters = LLFloaterReg::getFloaterList(strEnvFloaters[idxFloater]);
+			for (LLFloater* pFloater : envFloaters)
+				pFloater->closeFloater();
+			RlvUIEnabler::instance().addGenericFloaterFilter(strEnvFloaters[idxFloater]);
+		}
+		else
+		{
+			RlvUIEnabler::instance().removeGenericFloaterFilter(strEnvFloaters[idxFloater]);
+		}
+	}
+
+	// Don't allow toggling "Atmopsheric Shaders" through the debug settings under @setenv=n
+	gSavedSettings.getControl("WindLightUseAtmosShaders")->setHiddenFromSettingsEditor(fHasBhvr);
+
+	if (fHasBhvr)
+	{
+		// Usurp the 'edit' environment for RLVa locking so TPV tools like quick prefs and phototools are automatically locked out as well
+		// (these needed per-feature awareness of RLV in the previous implementation which often wasn't implemented)
+		LLEnvironment* pEnv = LLEnvironment::getInstance();
+		LLSettingsSky::ptr_t pRlvSky = pEnv->getEnvironmentFixedSky(LLEnvironment::ENV_LOCAL, true)->buildClone();
+		pEnv->setEnvironment(LLEnvironment::ENV_EDIT, pRlvSky);
+		pEnv->setSelectedEnvironment(LLEnvironment::ENV_EDIT, LLEnvironment::TRANSITION_INSTANT);
+		pEnv->updateEnvironment(LLEnvironment::TRANSITION_INSTANT);
+	}
+	else
+	{
+		// Restore the user's WindLight preferences when releasing
+		LLEnvironment::instance().clearEnvironment(LLEnvironment::ENV_EDIT);
+		LLEnvironment::instance().setSelectedEnvironment(LLEnvironment::ENV_LOCAL);
+		LLEnvironment::instance().updateEnvironment();
+	}
 }
 
 // Handles: @showhovertext:<uuid>=n|y
@@ -2657,7 +2764,7 @@ ERlvCmdRet RlvForceHandler<RLV_BHVR_REMOUTFIT>::onCommand(const RlvCommand& rlvC
 	return RLV_RET_SUCCESS;
 }
 
-// Handles: @setcam_eyeoffset[:<vector3>]=force and @setcam_focusoffset[:<vector3>]=force
+// Handles: @setcam_eyeoffset[:<vector3>]=force, @setcam_eyeoffsetscale[:<float>]=force and @setcam_focusoffset[:<vector3>]=force
 template<> template<>
 ERlvCmdRet RlvForceCamEyeFocusOffsetHandler::onCommand(const RlvCommand& rlvCmd)
 {
@@ -2665,22 +2772,54 @@ ERlvCmdRet RlvForceCamEyeFocusOffsetHandler::onCommand(const RlvCommand& rlvCmd)
 	if (!RlvActions::canChangeCameraPreset(rlvCmd.getObjectID()))
 		return RLV_RET_FAILED_LOCK;
 
-	LLControlVariable* pOffsetControl = gSavedSettings.getControl("CameraOffsetRLVaView");
-	LLControlVariable* pFocusControl = gSavedSettings.getControl("FocusOffsetRLVaView");
-	LLControlVariable* pControl = (rlvCmd.getBehaviourType() == RLV_BHVR_SETCAM_EYEOFFSET) ? pOffsetControl : pFocusControl;
-	if (rlvCmd.hasOption())
+	LLControlVariable* pEyeOffsetControl = gSavedSettings.getControl("CameraOffsetRLVaView");
+	LLControlVariable* pEyeOffsetScaleControl = gSavedSettings.getControl("CameraOffsetScaleRLVa");
+	LLControlVariable* pFocusOffsetControl = gSavedSettings.getControl("FocusOffsetRLVaView");
+
+	LLControlVariable* pControl; LLSD sdControlValue;
+	switch (rlvCmd.getBehaviourType())
 	{
-		LLVector3 vecOffset;
-		if (!RlvCommandOptionHelper::parseOption(rlvCmd.getOption(), vecOffset))
-			return RLV_RET_FAILED_OPTION;
-		pControl->setValue(vecOffset.getValue());
+		case RLV_BHVR_SETCAM_EYEOFFSET:
+			if (rlvCmd.hasOption())
+			{
+				LLVector3 vecOffset;
+				if (!RlvCommandOptionHelper::parseOption(rlvCmd.getOption(), vecOffset))
+					return RLV_RET_FAILED_OPTION;
+				sdControlValue = vecOffset.getValue();
+			}
+			pControl = pEyeOffsetControl;
+			break;
+		case RLV_BHVR_SETCAM_EYEOFFSETSCALE:
+			if (rlvCmd.hasOption())
+			{
+				float nScale;
+				if (!RlvCommandOptionHelper::parseOption(rlvCmd.getOption(), nScale))
+					return RLV_RET_FAILED_OPTION;
+				sdControlValue = nScale;
+			}
+			pControl = pEyeOffsetScaleControl;
+			break;
+		case RLV_BHVR_SETCAM_FOCUSOFFSET:
+			if (rlvCmd.hasOption())
+			{
+				LLVector3d vecOffset;
+				if (!RlvCommandOptionHelper::parseOption(rlvCmd.getOption(), vecOffset))
+					return RLV_RET_FAILED_OPTION;
+				sdControlValue = vecOffset.getValue();
+			}
+			pControl = pFocusOffsetControl;
+			break;
+		default:
+			return RLV_RET_FAILED;
 	}
+
+	if (!sdControlValue.isUndefined())
+		pControl->setValue(sdControlValue);
 	else
-	{
 		pControl->resetToDefault();
-	}
 
-	gAgentCamera.switchCameraPreset( ((pOffsetControl->isDefault()) && (pFocusControl->isDefault())) ? "Group View" : "RLVa View");
+	// NOTE: this doesn't necessarily release the camera preset even if all 3 are at their default now (e.g. @setcam is currently set)
+	gRlvHandler.setCameraOverride( (!pEyeOffsetControl->isDefault()) || (!pEyeOffsetScaleControl->isDefault()) || (!pFocusOffsetControl->isDefault()) );
 	return RLV_RET_SUCCESS;
 }
 
@@ -3053,7 +3192,10 @@ ERlvCmdRet RlvHandler::processReplyCommand(const RlvCommand& rlvCmd) const
 			break;
 		case RLV_BHVR_VERSIONNUM:		// @versionnum=<channel>				- Checked: 2010-03-27 (RLVa-1.4.0a) | Added: RLVa-1.0.4b
 			// NOTE: RLV will respond even if there's an option
-			strReply = RlvStrings::getVersionNum(rlvCmd.getObjectID());
+			if (!rlvCmd.hasOption())
+				strReply = RlvStrings::getVersionNum(rlvCmd.getObjectID());
+			else if ("impl" == rlvCmd.getOption())
+				strReply = RlvStrings::getVersionImplNum();
 			break;
 		case RLV_BHVR_GETATTACH:		// @getattach[:<layer>]=<channel>
 			eRet = onGetAttach(rlvCmd, strReply);
diff --git a/indra/newview/rlvhandler.h b/indra/newview/rlvhandler.h
index eba2c328830a131c34af0d273ff89e4a7c04cbbf..f06f3a3a872280f8c6da392496c43c98fc0fe7b6 100644
--- a/indra/newview/rlvhandler.h
+++ b/indra/newview/rlvhandler.h
@@ -133,7 +133,7 @@ class RlvHandler : public LLOldEvents::LLSimpleListener, public LLParticularGrou
 	bool       processIMQuery(const LLUUID& idSender, const std::string& strCommand);
 
 	// Returns a pointer to the currently executing command (do *not* save this pointer)
-	const RlvCommand* getCurrentCommand() const { return (!m_CurCommandStack.empty()) ? m_CurCommandStack.top() : NULL; }
+	const RlvCommand* getCurrentCommand() const { return (!m_CurCommandStack.empty()) ? &m_CurCommandStack.top().get() : nullptr; }
 	// Returns the UUID of the object we're currently executing a command for
 	const LLUUID&     getCurrentObject() const	{ return (!m_CurObjectStack.empty()) ? m_CurObjectStack.top() : LLUUID::null; }
 
@@ -147,6 +147,10 @@ class RlvHandler : public LLOldEvents::LLSimpleListener, public LLParticularGrou
 	void clearOverlayImage();                                                                   // @setoverlay=n
 	void setActiveGroup(const LLUUID& idGroup);                                                 // @setgroup=force
 	void setActiveGroupRole(const LLUUID& idGroup, const std::string& strRole);                 // @setgroup=force
+<<<<<<< HEAD
+=======
+	void setCameraOverride(bool fOverride);                                                     // @setcam family
+>>>>>>> RLVa/rlva/development
 	void setOverlayImage(const LLUUID& idTexture);                                              // @setoverlay=n
 
 	void onIMQueryListResponse(const LLSD& sdNotification, const LLSD sdResponse);
@@ -173,6 +177,10 @@ class RlvHandler : public LLOldEvents::LLSimpleListener, public LLParticularGrou
 
 	// Externally invoked event handlers
 public:
+<<<<<<< HEAD
+=======
+	void cleanup();
+>>>>>>> RLVa/rlva/development
 	void onActiveGroupChanged();
 	void onAttach(const LLViewerObject* pAttachObj, const LLViewerJointAttachment* pAttachPt);
 	void onDetach(const LLViewerObject* pAttachObj, const LLViewerJointAttachment* pAttachPt);
@@ -183,9 +191,14 @@ class RlvHandler : public LLOldEvents::LLSimpleListener, public LLParticularGrou
 	void onSitOrStand(bool fSitting);
 	void onTeleportFailed();
 	void onTeleportFinished(const LLVector3d& posArrival);
+	static void cleanupClass();
 	static void onIdleStartup(void* pParam);
 protected:
+<<<<<<< HEAD
 	void getAttachmentResourcesCoro(const std::string strUrl);
+=======
+	void getAttachmentResourcesCoro(const std::string& strUrl);
+>>>>>>> RLVa/rlva/development
 	void onTeleportCallback(U64 hRegion, const LLVector3& posRegion, const LLVector3& vecLookAt, const LLUUID& idRlvObj);
 
 	/*
@@ -201,7 +214,7 @@ class RlvHandler : public LLOldEvents::LLSimpleListener, public LLParticularGrou
 	 * Command processing
 	 */
 protected:
-	ERlvCmdRet processCommand(const RlvCommand& rlvCmd, bool fFromObj);
+	ERlvCmdRet processCommand(std::reference_wrapper<const RlvCommand> rlvCmdRef, bool fFromObj);
 	ERlvCmdRet processClearCommand(const RlvCommand& rlvCmd);
 
 	// Command handlers (RLV_TYPE_ADD and RLV_TYPE_CLEAR)
@@ -242,7 +255,7 @@ class RlvHandler : public LLOldEvents::LLSimpleListener, public LLParticularGrou
 	rlv_command_list_t    m_Retained;
 	RlvGCTimer*           m_pGCTimer;
 
-	std::stack<const RlvCommand*> m_CurCommandStack;// Convenience (see @tpto)
+	std::stack<std::reference_wrapper<const RlvCommand>> m_CurCommandStack; // Convenience (see @tpto)
 	std::stack<LLUUID>    m_CurObjectStack;			// Convenience (see @tpto)
 
 	rlv_behaviour_signal_t m_OnBehaviour;
@@ -263,6 +276,8 @@ class RlvHandler : public LLOldEvents::LLSimpleListener, public LLParticularGrou
 	LLPointer<LLViewerFetchedTexture>       m_pOverlayImage = nullptr;		// @setoverlay=n
 	int                                     m_nOverlayOrigBoost = 0;		// @setoverlay=n
 
+	std::string                             m_strCameraPresetRestore;       // @setcam_eyeoffset, @setcam_eyeoffsetscale and @setcam_focusoffset
+
 	friend class RlvSharedRootFetcher;				// Fetcher needs access to m_fFetchComplete
 	friend class RlvGCTimer;						// Timer clear its own point at destruction
 	template<ERlvBehaviourOptionType> friend struct RlvBehaviourGenericHandler;
diff --git a/indra/newview/rlvhelper.cpp b/indra/newview/rlvhelper.cpp
index 4a4ab3c7c0b43787a1b994b4a855b21b24efb538..3f4c2f3e505a947f7b1ea1d0d78d010e1e70c08e 100644
--- a/indra/newview/rlvhelper.cpp
+++ b/indra/newview/rlvhelper.cpp
@@ -189,10 +189,12 @@ RlvBehaviourDictionary::RlvBehaviourDictionary()
 	addModifier(RLV_BHVR_SETCAM_ORIGINDISTMIN, RLV_MODIFIER_SETCAM_ORIGINDISTMIN, new RlvBehaviourModifier("Camera - Focus Distance (Min)", 0.0f, true, new RlvBehaviourModifierCompMax));
 	addEntry(new RlvBehaviourGenericProcessor<RLV_OPTION_MODIFIER>("setcam_origindistmax", RLV_BHVR_SETCAM_ORIGINDISTMAX, RlvBehaviourInfo::BHVR_EXPERIMENTAL));
 	addModifier(RLV_BHVR_SETCAM_ORIGINDISTMAX, RLV_MODIFIER_SETCAM_ORIGINDISTMAX, new RlvBehaviourModifier("Camera - Focus Distance (Max)", F32_MAX, true, new RlvBehaviourModifierCompMin));
-	addEntry(new RlvBehaviourGenericToggleProcessor<RLV_BHVR_SETCAM_EYEOFFSET, RLV_OPTION_MODIFIER, RlvBehaviourCamEyeFocusOffsetHandler>("setcam_eyeoffset", RlvBehaviourInfo::BHVR_EXPERIMENTAL));
+	addEntry(new RlvBehaviourGenericToggleProcessor<RLV_BHVR_SETCAM_EYEOFFSET, RLV_OPTION_MODIFIER, RlvBehaviourCamEyeFocusOffsetHandler>("setcam_eyeoffset"));
 	addModifier(RLV_BHVR_SETCAM_EYEOFFSET, RLV_MODIFIER_SETCAM_EYEOFFSET, new RlvBehaviourModifierHandler<RLV_MODIFIER_SETCAM_EYEOFFSET>("Camera - Eye Offset", LLVector3::zero, true, nullptr));
-	addEntry(new RlvBehaviourGenericToggleProcessor<RLV_BHVR_SETCAM_FOCUSOFFSET, RLV_OPTION_MODIFIER, RlvBehaviourCamEyeFocusOffsetHandler>("setcam_focusoffset", RlvBehaviourInfo::BHVR_EXPERIMENTAL));
-	addModifier(RLV_BHVR_SETCAM_FOCUSOFFSET, RLV_MODIFIER_SETCAM_FOCUSOFFSET, new RlvBehaviourModifierHandler<RLV_MODIFIER_SETCAM_FOCUSOFFSET>("Camera - Focus Offset", LLVector3::zero, true, nullptr));
+	addEntry(new RlvBehaviourGenericToggleProcessor<RLV_BHVR_SETCAM_EYEOFFSETSCALE, RLV_OPTION_MODIFIER, RlvBehaviourCamEyeFocusOffsetHandler>("setcam_eyeoffsetscale"));
+	addModifier(RLV_BHVR_SETCAM_EYEOFFSETSCALE, RLV_MODIFIER_SETCAM_EYEOFFSETSCALE, new RlvBehaviourModifierHandler<RLV_MODIFIER_SETCAM_EYEOFFSETSCALE>("Camera - Eye Offset Scale", 0, true, nullptr));
+	addEntry(new RlvBehaviourGenericToggleProcessor<RLV_BHVR_SETCAM_FOCUSOFFSET, RLV_OPTION_MODIFIER, RlvBehaviourCamEyeFocusOffsetHandler>("setcam_focusoffset"));
+	addModifier(RLV_BHVR_SETCAM_FOCUSOFFSET, RLV_MODIFIER_SETCAM_FOCUSOFFSET, new RlvBehaviourModifierHandler<RLV_MODIFIER_SETCAM_FOCUSOFFSET>("Camera - Focus Offset", LLVector3d::zero, true, nullptr));
 	addEntry(new RlvBehaviourProcessor<RLV_BHVR_SETCAM_FOVMIN, RlvBehaviourSetCamFovHandler>("setcam_fovmin"));
 	addModifier(RLV_BHVR_SETCAM_FOVMIN, RLV_MODIFIER_SETCAM_FOVMIN, new RlvBehaviourModifierHandler<RLV_MODIFIER_SETCAM_FOVMIN>("Camera - FOV (Min)", DEFAULT_FIELD_OF_VIEW, true, new RlvBehaviourModifierCompMax));
 	addEntry(new RlvBehaviourProcessor<RLV_BHVR_SETCAM_FOVMAX, RlvBehaviourSetCamFovHandler>("setcam_fovmax"));
@@ -261,8 +263,9 @@ RlvBehaviourDictionary::RlvBehaviourDictionary()
 	addEntry(new RlvForceProcessor<RLV_BHVR_DETACHME>("detachme"));
 	addEntry(new RlvForceProcessor<RLV_BHVR_FLY>("fly"));
 	addEntry(new RlvForceProcessor<RLV_BHVR_SETCAM_FOCUS>("setcam_focus", RlvBehaviourInfo::BHVR_EXPERIMENTAL));
-	addEntry(new RlvForceProcessor<RLV_BHVR_SETCAM_EYEOFFSET, RlvForceCamEyeFocusOffsetHandler>("setcam_eyeoffset", RlvBehaviourInfo::BHVR_EXPERIMENTAL));
-	addEntry(new RlvForceProcessor<RLV_BHVR_SETCAM_FOCUSOFFSET, RlvForceCamEyeFocusOffsetHandler>("setcam_focusoffset", RlvBehaviourInfo::BHVR_EXPERIMENTAL));
+	addEntry(new RlvForceProcessor<RLV_BHVR_SETCAM_EYEOFFSET, RlvForceCamEyeFocusOffsetHandler>("setcam_eyeoffset"));
+	addEntry(new RlvForceProcessor<RLV_BHVR_SETCAM_EYEOFFSETSCALE, RlvForceCamEyeFocusOffsetHandler>("setcam_eyeoffsetscale"));
+	addEntry(new RlvForceProcessor<RLV_BHVR_SETCAM_FOCUSOFFSET, RlvForceCamEyeFocusOffsetHandler>("setcam_focusoffset"));
 	addEntry(new RlvForceProcessor<RLV_BHVR_SETCAM_FOV>("setcam_fov", RlvBehaviourInfo::BHVR_EXPERIMENTAL));
 	addEntry(new RlvForceProcessor<RLV_BHVR_SETCAM_MODE>("setcam_mode", RlvBehaviourInfo::BHVR_EXPERIMENTAL));
 	addEntry(new RlvForceProcessor<RLV_BHVR_SETGROUP>("setgroup"));
@@ -642,8 +645,9 @@ RlvCommand::RlvCommand(const LLUUID& idObj, const std::string& strCommand)
 }
 
 RlvCommand::RlvCommand(const RlvCommand& rlvCmd, ERlvParamType eParamType)
-	: m_fValid(rlvCmd.m_fValid), m_idObj(rlvCmd.m_idObj), m_strBehaviour(rlvCmd.m_strBehaviour), m_pBhvrInfo(rlvCmd.m_pBhvrInfo),
-	  m_eParamType( (RLV_TYPE_UNKNOWN == eParamType) ? rlvCmd.m_eParamType : eParamType),m_fStrict(rlvCmd.m_fStrict), m_strOption(rlvCmd.m_strOption), m_strParam(rlvCmd.m_strParam), m_fRefCounted(false)
+	: m_fValid(rlvCmd.m_fValid), m_idObj(rlvCmd.m_idObj), m_strBehaviour(rlvCmd.m_strBehaviour), m_pBhvrInfo(rlvCmd.m_pBhvrInfo)
+	, m_eParamType( (RLV_TYPE_UNKNOWN == eParamType) ? rlvCmd.m_eParamType : eParamType),m_fStrict(rlvCmd.m_fStrict), m_strOption(rlvCmd.m_strOption)
+	, m_strParam(rlvCmd.m_strParam), m_fRefCounted(rlvCmd.m_fRefCounted)
 {
 }
 
@@ -685,6 +689,13 @@ bool RlvCommand::parseCommand(const std::string& strCommand, std::string& strBeh
 // Command option parsing utility classes
 //
 
+template<>
+bool RlvCommandOptionHelper::parseOption<std::string>(const std::string& strOption, std::string& valueOption)
+{
+	valueOption = strOption;
+	return true;
+}
+
 template<>
 bool RlvCommandOptionHelper::parseOption<LLUUID>(const std::string& strOption, LLUUID& idOption)
 {
@@ -766,6 +777,17 @@ bool RlvCommandOptionHelper::parseOption<LLViewerInventoryCategory*>(const std::
 	return pFolder != NULL;
 }
 
+template<>
+bool RlvCommandOptionHelper::parseOption<LLVector2>(const std::string& strOption, LLVector2& vecOption)
+{
+	if (!strOption.empty())
+	{
+		S32 cntToken = sscanf(strOption.c_str(), "%f/%f", vecOption.mV + 0, vecOption.mV + 1);
+		return (2 == cntToken);
+	}
+	return false;
+}
+
 template<>
 bool RlvCommandOptionHelper::parseOption<LLVector3>(const std::string& strOption, LLVector3& vecOption)
  {
@@ -788,6 +810,17 @@ bool RlvCommandOptionHelper::parseOption<LLVector3d>(const std::string& strOptio
 	return false;
 }
 
+template<>
+bool RlvCommandOptionHelper::parseOption<LLColor3>(const std::string& strOption, LLColor3& clrOption)
+{
+	if (!strOption.empty())
+	{
+		S32 cntToken = sscanf(strOption.c_str(), "%f/%f/%f", clrOption.mV + 0, clrOption.mV + 1, clrOption.mV + 2);
+		return (3 == cntToken);
+	}
+	return false;
+}
+
 template<>
 bool RlvCommandOptionHelper::parseOption<RlvCommandOptionGeneric>(const std::string& strOption, RlvCommandOptionGeneric& genericOption)
 {
@@ -989,7 +1022,7 @@ RlvObject::RlvObject(const LLUUID& idObj) : m_idObj(idObj), m_nLookupMisses(0)
 	m_idRoot = (pObj) ? pObj->getRootEdit()->getID() : LLUUID::null;
 }
 
-bool RlvObject::addCommand(const RlvCommand& rlvCmd)
+const RlvCommand& RlvObject::addCommand(const RlvCommand& rlvCmd, bool& fAdded)
 {
 	RLV_ASSERT(RLV_TYPE_ADD == rlvCmd.getParamType());
 
@@ -999,14 +1032,15 @@ bool RlvObject::addCommand(const RlvCommand& rlvCmd)
 		if ( (itCmd->getBehaviour() == rlvCmd.getBehaviour()) && (itCmd->getOption() == rlvCmd.getOption()) && 
 			 (itCmd->isStrict() == rlvCmd.isStrict() ) )
 		{
-			return false;
+			fAdded = false;
+			return *itCmd;
 		}
 	}
 
 	// Now that we know it's not a duplicate, add it to the end of the list
 	m_Commands.push_back(rlvCmd);
-
-	return true;
+	fAdded = true;
+	return m_Commands.back();
 }
 
 bool RlvObject::removeCommand(const RlvCommand& rlvCmd)
diff --git a/indra/newview/rlvhelper.h b/indra/newview/rlvhelper.h
index 6cf2c5c62ebc886f22ba952ffb039e75dc212fe3..cbe79ec57a0347790b4227ab014eee427933df1e 100644
--- a/indra/newview/rlvhelper.h
+++ b/indra/newview/rlvhelper.h
@@ -172,7 +172,7 @@ template<ERlvBehaviour templBhvr> using RlvForceHandler = RlvCommandHandler<RLV_
 template<ERlvBehaviour templBhvr> using RlvReplyHandler = RlvCommandHandler<RLV_TYPE_REPLY, templBhvr>;
 
 // List of shared handlers
-typedef RlvBehaviourToggleHandler<RLV_BHVR_SETCAM_EYEOFFSET> RlvBehaviourCamEyeFocusOffsetHandler;	// Shared between @setcam_eyeoffset and @setcam_focusoffset
+typedef RlvBehaviourToggleHandler<RLV_BHVR_SETCAM_EYEOFFSET> RlvBehaviourCamEyeFocusOffsetHandler;	// Shared between @setcam_eyeoffset, @setcam_eyeoffsetscale and @setcam_focusoffset
 typedef RlvBehaviourHandler<RLV_BHVR_REMATTACH> RlvBehaviourAddRemAttachHandler;					// Shared between @addattach and @remattach
 typedef RlvBehaviourHandler<RLV_BHVR_SENDCHANNEL> RlvBehaviourSendChannelHandler;					// Shared between @sendchannel and @sendchannel_except
 typedef RlvBehaviourHandler<RLV_BHVR_SENDIM> RlvBehaviourRecvSendStartIMHandler;					// Shared between @recvim, @sendim and @startim
@@ -181,7 +181,7 @@ typedef RlvBehaviourToggleHandler<RLV_BHVR_SHOWSELF> RlvBehaviourShowSelfToggleH
 typedef RlvBehaviourHandler<RLV_BHVR_CAMZOOMMIN> RlvBehaviourCamZoomMinMaxHandler;					// Shared between @camzoommin and @camzoommax (deprecated)
 typedef RlvReplyHandler<RLV_BHVR_GETCAM_AVDISTMIN> RlvReplyCamMinMaxModifierHandler;				// Shared between @getcam_avdistmin and @getcam_avdistmax
 typedef RlvForceHandler<RLV_BHVR_REMATTACH> RlvForceRemAttachHandler;								// Shared between @remattach and @detach
-typedef RlvForceHandler<RLV_BHVR_SETCAM_EYEOFFSET> RlvForceCamEyeFocusOffsetHandler;				// Shared between @setcam_eyeoffset and @setcam_focusoffset
+typedef RlvForceHandler<RLV_BHVR_SETCAM_EYEOFFSET> RlvForceCamEyeFocusOffsetHandler;				// Shared between @setcam_eyeoffset, @setcam_eyeoffsetscale and @setcam_focusoffset
 
 //
 // RlvCommandProcessor - Templated glue class that brings RlvBehaviourInfo, RlvCommandHandlerBaseImpl and RlvCommandHandler together
@@ -434,8 +434,8 @@ class RlvObject
 	 * Member functions
 	 */
 public:
-	bool addCommand(const RlvCommand& rlvCmd);
-	bool removeCommand(const RlvCommand& rlvCmd);
+	const RlvCommand& addCommand(const RlvCommand& rlvCmd, bool& fAdded);
+	bool              removeCommand(const RlvCommand& rlvCmd);
 
 	std::string getStatusString(const std::string& strFilter, const std::string& strSeparator) const;
 	bool        hasBehaviour(ERlvBehaviour eBehaviour, bool fStrictOnly) const;
diff --git a/indra/newview/rlvinventory.cpp b/indra/newview/rlvinventory.cpp
index 082cc6065b5d1eeeb2ed9b7a260bb0192915e838..e3d06e2bdc67686bc13d3570751a6e76cf2a8e6b 100644
--- a/indra/newview/rlvinventory.cpp
+++ b/indra/newview/rlvinventory.cpp
@@ -578,36 +578,56 @@ void RlvGiveToRLVOffer::onCategoryCreateCallback(LLUUID idFolder, RlvGiveToRLVOf
 	pInstance->onDestinationCreated(idFolder, pInstance->m_DestPath.front());
 }
 
-// Checked: 2014-01-07 (RLVa-1.4.10)
-void RlvGiveToRLVOffer::moveAndRename(const LLUUID& idFolder, const LLUUID& idDestination, const std::string& strName)
+// static
+void RlvGiveToRLVOffer::moveAndRename(const LLUUID& idFolder, const LLUUID& idDestination, const std::string& strName, const LLPointer<LLInventoryCallback> cbFinal)
 {
-	const LLViewerInventoryCategory* pDest = gInventory.getCategory(idDestination);
 	const LLViewerInventoryCategory* pFolder = gInventory.getCategory(idFolder);
-	if ( (pDest) && (pFolder) )
+	if ( (idDestination.notNull()) && (pFolder) )
 	{
-		LLPointer<LLViewerInventoryCategory> pNewFolder = new LLViewerInventoryCategory(pFolder);
-		if (pDest->getUUID() != pFolder->getParentUUID())
+		bool needsRename = (pFolder->getName() != strName);
+
+		LLPointer<LLInventoryCallback> cbMove;
+		if (idDestination != pFolder->getParentUUID())
 		{
-			LLInventoryModel::update_list_t update;
-			LLInventoryModel::LLCategoryUpdate updOldParent(pFolder->getParentUUID(), -1);
-			update.push_back(updOldParent);
-			LLInventoryModel::LLCategoryUpdate updNewParent(pDest->getUUID(), 1);
-			update.push_back(updNewParent);
-			gInventory.accountForUpdate(update);
-
-			pNewFolder->setParent(pDest->getUUID());
-			pNewFolder->updateParentOnServer(FALSE);
-		}
+			// We have to move *after* the rename operation completes or AIS will drop it
+			if (!needsRename)
+			{
+				LLInventoryModel::update_list_t update;
+				LLInventoryModel::LLCategoryUpdate updOldParent(pFolder->getParentUUID(), -1);
+				update.push_back(updOldParent);
+				LLInventoryModel::LLCategoryUpdate updNewParent(idDestination, 1);
+				update.push_back(updNewParent);
+				gInventory.accountForUpdate(update);
 
-		pNewFolder->rename(strName);
-		pNewFolder->updateServer(FALSE);
-		gInventory.updateCategory(pNewFolder);
+				LLPointer<LLViewerInventoryCategory> pNewFolder = new LLViewerInventoryCategory(pFolder);
+				pNewFolder->setParent(idDestination);
+				pNewFolder->updateParentOnServer(FALSE);
 
-		gInventory.notifyObservers();
+				gInventory.updateCategory(pNewFolder);
+				gInventory.notifyObservers();
+
+				if (cbFinal)
+				{
+					cbFinal.get()->fire(idFolder);
+				}
+			}
+			else
+			{
+				cbMove = new LLBoostFuncInventoryCallback(boost::bind(RlvGiveToRLVOffer::moveAndRename, _1, idDestination, strName, cbFinal));
+			}
+		}
+
+		if (needsRename)
+		{
+			rename_category(&gInventory, idFolder, strName, (cbMove) ? cbMove : cbFinal);
+		}
+	}
+	else if (cbFinal)
+	{
+		cbFinal.get()->fire(LLUUID::null);
 	}
 }
 
-// Checked: 2010-04-18 (RLVa-1.2.0)
 void RlvGiveToRLVTaskOffer::changed(U32 mask)
 {
 	if (mask & LLInventoryObserver::ADD)
@@ -633,7 +653,6 @@ void RlvGiveToRLVTaskOffer::changed(U32 mask)
 	}
 }
 
-// Checked: 2010-04-18 (RLVa-1.2.0)
 void RlvGiveToRLVTaskOffer::done()
 {
 	gInventory.removeObserver(this);
@@ -642,22 +661,29 @@ void RlvGiveToRLVTaskOffer::done()
 	doOnIdleOneTime(boost::bind(&RlvGiveToRLVTaskOffer::doneIdle, this));
 }
 
-// Checked: 2014-01-07 (RLVa-1.4.10)
 void RlvGiveToRLVTaskOffer::doneIdle()
 {
-	const LLViewerInventoryCategory* pFolder = (m_Folders.size()) ? gInventory.getCategory(m_Folders.front()) : NULL;
+	const LLViewerInventoryCategory* pFolder = (m_Folders.size()) ? gInventory.getCategory(m_Folders.front()) : nullptr;
 	if ( (!pFolder) || (!createDestinationFolder(pFolder->getName())) )
 		delete this;
 }
 
-// Checked: 2010-04-18 (RLVa-1.2.0)
-void RlvGiveToRLVTaskOffer::onDestinationCreated(const LLUUID& idFolder, const std::string& strName)
+void RlvGiveToRLVTaskOffer::onDestinationCreated(const LLUUID& idDestFolder, const std::string& strName)
+{
+	if (const LLViewerInventoryCategory* pTarget = (idDestFolder.notNull()) ? gInventory.getCategory(idDestFolder) : nullptr)
+	{
+		moveAndRename(m_Folders.front(), idDestFolder, strName, new LLBoostFuncInventoryCallback(boost::bind(&RlvGiveToRLVTaskOffer::onOfferCompleted, this, _1)));
+	}
+	else
+	{
+		onOfferCompleted(LLUUID::null);
+	}
+}
+
+void RlvGiveToRLVTaskOffer::onOfferCompleted(const LLUUID& idOfferedFolder)
 {
-	const LLViewerInventoryCategory* pTarget = (idFolder.notNull()) ? gInventory.getCategory(idFolder) : NULL;
-	if (pTarget)
+	if (idOfferedFolder.notNull())
 	{
-		const LLUUID& idOfferedFolder = m_Folders.front();
-		moveAndRename(idOfferedFolder, idFolder, strName);
 		RlvBehaviourNotifyHandler::sendNotification("accepted_in_rlv inv_offer " + RlvInventory::instance().getSharedPath(idOfferedFolder));
 	}
 	delete this;
@@ -684,7 +710,7 @@ void RlvGiveToRLVAgentOffer::doneIdle()
 void RlvGiveToRLVAgentOffer::onDestinationCreated(const LLUUID& idFolder, const std::string& strName)
 {
 	if ( (idFolder.notNull()) && (mComplete.size()) )
-		moveAndRename(mComplete[0], idFolder, strName);
+		moveAndRename(mComplete[0], idFolder, strName, nullptr);
 	delete this;
 }
 
diff --git a/indra/newview/rlvinventory.h b/indra/newview/rlvinventory.h
index 1279dff2f85bdbb670050ac848df3066ee773cec..2cb7f8bc8edac693c8a7cc9a22831631597f685d 100644
--- a/indra/newview/rlvinventory.h
+++ b/indra/newview/rlvinventory.h
@@ -131,8 +131,8 @@ class RlvGiveToRLVOffer
 	virtual ~RlvGiveToRLVOffer() {}
 protected:
 	bool         createDestinationFolder(const std::string& strPath);
-	virtual void onDestinationCreated(const LLUUID& idFolder, const std::string& strName) = 0;
-	void         moveAndRename(const LLUUID& idFolder, const LLUUID& idDestination, const std::string& strName);
+	virtual void onDestinationCreated(const LLUUID& idDestFolder, const std::string& strName) = 0;
+	static void  moveAndRename(const LLUUID& idFolder, const LLUUID& idDestination, const std::string& strName, LLPointer<LLInventoryCallback> cb);
 private:
 	static void  onCategoryCreateCallback(LLUUID idFolder, RlvGiveToRLVOffer* pInstance);
 
@@ -147,11 +147,12 @@ class RlvGiveToRLVTaskOffer : public LLInventoryObserver, RlvGiveToRLVOffer
 {
 public:
 	RlvGiveToRLVTaskOffer(const LLUUID& idTransaction) : RlvGiveToRLVOffer(), m_idTransaction(idTransaction) {}
-	/*virtual*/ void changed(U32 mask);
+	void changed(U32 mask) override;
 protected:
-	/*virtual*/ void done();
-	            void doneIdle();
-	/*virtual*/ void onDestinationCreated(const LLUUID& idFolder, const std::string& strName);
+	void done();
+	void doneIdle();
+	void onDestinationCreated(const LLUUID& idDestFolder, const std::string& strName) override;
+	void onOfferCompleted(const LLUUID& idOfferedFolder);
 
 protected:
 	typedef std::vector<LLUUID> folder_ref_t;
diff --git a/indra/newview/skins/default/textures/3p_icons/fmod_logo.png b/indra/newview/skins/default/textures/3p_icons/fmod_logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..5a50e0ad34b54c63357a87094a102ba26d0782d6
Binary files /dev/null and b/indra/newview/skins/default/textures/3p_icons/fmod_logo.png differ
diff --git a/indra/newview/skins/default/textures/3p_icons/havok_logo.png b/indra/newview/skins/default/textures/3p_icons/havok_logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..ff1ea3a72e8baf13cf235021482f9d57bab2ef08
Binary files /dev/null and b/indra/newview/skins/default/textures/3p_icons/havok_logo.png differ
diff --git a/indra/newview/skins/default/textures/3p_icons/vivox_logo.png b/indra/newview/skins/default/textures/3p_icons/vivox_logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..6f20e87b7ae817116bad7d2939b545cea3029c3e
Binary files /dev/null and b/indra/newview/skins/default/textures/3p_icons/vivox_logo.png differ
diff --git a/indra/newview/skins/default/textures/bottomtray/Cam_Rotate_Center.png b/indra/newview/skins/default/textures/bottomtray/Cam_Rotate_Center.png
new file mode 100644
index 0000000000000000000000000000000000000000..ffc3c85ea2a96cb82eeb487625eac03bb1c90aa2
Binary files /dev/null and b/indra/newview/skins/default/textures/bottomtray/Cam_Rotate_Center.png differ
diff --git a/indra/newview/skins/default/textures/bottomtray/Cam_Tracking_Center.png b/indra/newview/skins/default/textures/bottomtray/Cam_Tracking_Center.png
new file mode 100644
index 0000000000000000000000000000000000000000..2812d614e6abc12f8f3ec1bcbad8f2f445e6f3e2
Binary files /dev/null and b/indra/newview/skins/default/textures/bottomtray/Cam_Tracking_Center.png differ
diff --git a/indra/newview/skins/default/textures/icons/Presets_Icon_Graphic.png b/indra/newview/skins/default/textures/icons/Presets_Icon_Graphic.png
new file mode 100644
index 0000000000000000000000000000000000000000..5a6628816b34fca5623902249385a0b7e836fd68
Binary files /dev/null and b/indra/newview/skins/default/textures/icons/Presets_Icon_Graphic.png differ
diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml
index 86cdf4c5be19dccde2aef6201464768f3df55a8b..4fb9389ab9f6c473a18122040b51e30aab795778 100644
--- a/indra/newview/skins/default/textures/textures.xml
+++ b/indra/newview/skins/default/textures/textures.xml
@@ -81,7 +81,7 @@ with the same filename but different name
   <texture name="BuyArrow_Over" file_name="navbar/BuyArrow_Over.png" preload="true" scale.left="0" scale.top="1" scale.right="0" scale.bottom="0"  />
   <texture name="BuyArrow_Press" file_name="navbar/BuyArrow_Press.png" preload="true" scale.left="1" scale.top="1" scale.right="0" scale.bottom="0"  />
 
-  <texture name="Cam_Avatar_Off" file_name="bottomtray/Cam_Avatar_Off.png" preload="false" />
+  <texture name="Cam_Avatar_Off" file_name="bottomtray/Cam_Avatar_Off.png" preload="true" />
   <texture name="Cam_FreeCam_Off" file_name="bottomtray/Cam_FreeCam_Off.png" preload="false" />
   <texture name="Cam_Orbit_Off" file_name="bottomtray/Cam_Orbit_Off.png" preload="false" />
   <texture name="Cam_Pan_Off" file_name="bottomtray/Cam_Pan_Off.png" preload="false" />
@@ -96,8 +96,10 @@ with the same filename but different name
 
   <texture name="Cam_Rotate_In" file_name="bottomtray/Cam_Rotate_In.png" preload="false" />
   <texture name="Cam_Rotate_Out" file_name="bottomtray/Cam_Rotate_Out.png" preload="false" />
+  <texture name="Cam_Rotate_Center" file_name="bottomtray/Cam_Rotate_Center.png" preload="false" />
   <texture name="Cam_Tracking_In" file_name="bottomtray/Cam_Tracking_In.png" preload="false" />
   <texture name="Cam_Tracking_Out" file_name="bottomtray/Cam_Tracking_Out.png" preload="false" />
+  <texture name="Cam_Tracking_Center" file_name="bottomtray/Cam_Tracking_Center.png" preload="false" />
 
   <texture name="Checkbox_Off_Disabled" file_name="widgets/Checkbox_Disabled.png" preload="true" />
   <texture name="Checkbox_On_Disabled" file_name="widgets/Checkbox_On_Disabled.png" preload="true" />
@@ -185,7 +187,9 @@ with the same filename but different name
 
   <texture name="Presets_Icon" file_name="icons/Presets_Icon.png" preload="true" />
 
-  <texture name="Favorite_Star_Active" file_name="navbar/Favorite_Star_Active.png" preload="false" />
+  <texture name="Presets_Icon" file_name="icons/Presets_Icon.png" preload="true" />
+  <texture name="Presets_Icon_Graphic" file_name="icons/Presets_Icon_Graphic.png" preload="true" />
+ <texture name="Favorite_Star_Active" file_name="navbar/Favorite_Star_Active.png" preload="false" />
   <texture name="Favorite_Star_Off" file_name="navbar/Favorite_Star_Off.png" preload="false" />
   <texture name="Favorite_Star_Press" file_name="navbar/Favorite_Star_Press.png" preload="false" />
   <texture name="Favorite_Star_Over" file_name="navbar/Favorite_Star_Over.png" preload="false" />
@@ -235,6 +239,7 @@ with the same filename but different name
   <texture name="Icon_Gear_Background" file_name="windows/Icon_Gear_Background.png" preload="false" />
   <texture name="Icon_Gear_Foreground" file_name="windows/Icon_Gear_Foreground.png" preload="false" />
   <texture name="Icon_Gear_Press" file_name="windows/Icon_Gear_Press.png" preload="false" />
+  <texture name="Icon_Gear" file_name="windows/Icon_Gear.png" preload="false" />
 
   <texture name="Icon_Help_Foreground" file_name="windows/Icon_Help_Foreground.png" preload="true" />
   <texture name="Icon_Help_Press" file_name="windows/Icon_Help_Press.png" preload="true" />
diff --git a/indra/newview/skins/default/textures/widgets/ProgressBarSolid.png b/indra/newview/skins/default/textures/widgets/ProgressBarSolid.png
new file mode 100644
index 0000000000000000000000000000000000000000..ec0926bfa1cbc70b172f51704227bc30e418cf6b
Binary files /dev/null and b/indra/newview/skins/default/textures/widgets/ProgressBarSolid.png differ
diff --git a/indra/newview/skins/default/textures/windows/Icon_Gear.png b/indra/newview/skins/default/textures/windows/Icon_Gear.png
new file mode 100644
index 0000000000000000000000000000000000000000..e1e89b8f32dfd9c70f89d25776a587d1726871b5
Binary files /dev/null and b/indra/newview/skins/default/textures/windows/Icon_Gear.png differ
diff --git a/indra/newview/skins/default/xui/en/floater_camera_presets.xml b/indra/newview/skins/default/xui/en/floater_camera_presets.xml
new file mode 100644
index 0000000000000000000000000000000000000000..930357f5688d88ced839975a44beeb0f9a0e0deb
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_camera_presets.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ legacy_header_height="18"
+ can_resize="true"
+ height="200"
+ min_height="150"
+ title="MY CAMERA PRESETS"
+ layout="topleft"
+ name="floater_camera_presets"
+ single_instance="true"
+ min_width="185"
+ width="250">
+  <flat_list_view
+    allow_select="true"
+    follows="all"
+    height="165"
+    layout="topleft"
+    left="3"
+    multi_select="false"
+    name="preset_list"
+    top="20"
+    width="245" />
+  </floater>
diff --git a/indra/newview/skins/default/xui/en/floater_delete_pref_preset.xml b/indra/newview/skins/default/xui/en/floater_delete_pref_preset.xml
index 0688fdb42c2f8d23079d3704c8aa9d7a80f50728..3360d7bec9766fd3868ea01850a6c7dd1fd1ce41 100644
--- a/indra/newview/skins/default/xui/en/floater_delete_pref_preset.xml
+++ b/indra/newview/skins/default/xui/en/floater_delete_pref_preset.xml
@@ -4,7 +4,7 @@
  height="130"
  help_topic="floater_delete_preset"
  layout="topleft"
- name="Delete Pref Preset"
+ name="delete_pref_preset"
  save_rect="true"
  title="DELETE PREF PRESET"
  width="300">
diff --git a/indra/newview/skins/default/xui/en/floater_edit_hover_height.xml b/indra/newview/skins/default/xui/en/floater_edit_hover_height.xml
index 6c520467fa9fb68afae47ac977f00d949ddb966f..0fa93135330ceec9f2677996be8e9dd13b6e0cfa 100644
--- a/indra/newview/skins/default/xui/en/floater_edit_hover_height.xml
+++ b/indra/newview/skins/default/xui/en/floater_edit_hover_height.xml
@@ -4,16 +4,17 @@
  can_minimize="true"
  can_close="true"
  min_width="515"
- height="29"
+ height="59"
+ can_resize="false"
+ width="515"
  layout="topleft"
  name="HoverHeight"
  single_instance="true"
  help_topic="hover_height"
  save_rect="true"
  save_visibility="true"
- title="Set Hover Height"
- width="515">
-     <slider
+ title="Set Hover Height">
+      <slider
      enabled="false"
      control_name="HoverHeightSlider"
      decimal_digits="3"
@@ -29,4 +30,13 @@
      name="HoverHeightSlider"
      top="5"
      can_edit_text="true"/>
+  <check_box
+     control_name="HoverHeightAffectsCamera"
+     follows="all"
+     height="15"
+     label="Bind Camera view"
+     layout="topleft"
+     name="BindCameraCheck"
+     top_pad="3"
+     width="237"/>
 </floater>
diff --git a/indra/newview/skins/default/xui/en/floater_inventory_view_finder.xml b/indra/newview/skins/default/xui/en/floater_inventory_view_finder.xml
index f774b4dca2520051c9ae5b5bb451947145ea3a16..fabb89cbdc4cc345414f6521681e7a92cab25483 100644
--- a/indra/newview/skins/default/xui/en/floater_inventory_view_finder.xml
+++ b/indra/newview/skins/default/xui/en/floater_inventory_view_finder.xml
@@ -2,7 +2,7 @@
 <floater
  legacy_header_height="18"
  can_minimize="false"
- height="464"
+ height="466"
  layout="topleft"
  name="Inventory Finder"
  help_topic="inventory_finder"
@@ -210,7 +210,24 @@
      layout="topleft"
      left_pad="2"
      name="check_snapshot"
-     top_delta="1"
+     top_delta="0"
+     width="126" />
+    <icon
+     height="16"
+     image_name="Inv_Settings"
+     layout="topleft"
+     left="8"
+     mouse_opaque="true"
+     name="icon_settings"
+     top="242"
+     width="16" />
+    <check_box
+     height="16"
+     label="Settings"
+     layout="topleft"
+     left_pad="2"
+     name="check_settings"
+     top_delta="0"
      width="126" />
     <button
      image_hover_unselected="Toolbar_Middle_Over"
@@ -223,8 +240,8 @@
      layout="topleft"
      left="10"
      name="All"
-     top_pad="8"
-     width="90" />
+     top="262"
+     width="100" />
     <button
      image_hover_unselected="Toolbar_Middle_Over"
      image_selected="Toolbar_Middle_Selected"
@@ -381,4 +398,14 @@
      width="80">
         Days
     </text>
+    <button
+     follows="top|right"
+     height="20"
+     label="Close"
+     label_selected="Close"
+     layout="topleft"
+     name="Close"
+     right="-6"
+     top="434"
+     width="76" />
 </floater>
diff --git a/indra/newview/skins/default/xui/en/floater_load_pref_preset.xml b/indra/newview/skins/default/xui/en/floater_load_pref_preset.xml
index 5f2eb770e25a4670f3e59b16fad27e11c47093d7..49c21f1ea794d2e3bbb9f25b15a2f2f44351932e 100644
--- a/indra/newview/skins/default/xui/en/floater_load_pref_preset.xml
+++ b/indra/newview/skins/default/xui/en/floater_load_pref_preset.xml
@@ -4,7 +4,7 @@
  height="130"
  help_topic="floater_load_preset"
  layout="topleft"
- name="Load Pref Preset"
+ name="load_pref_preset"
  save_rect="true"
  title="LOAD PREF PRESET"
  width="300">
diff --git a/indra/newview/skins/default/xui/en/floater_preferences_view_advanced.xml b/indra/newview/skins/default/xui/en/floater_preferences_view_advanced.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4c3c7e49300f0bdb0b28f46c2aceb80bd018d59d
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_preferences_view_advanced.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ height="190"
+ layout="topleft"
+ name="floaterpreferencesviewadvanced"
+ help_topic="floaterviewadvanced"
+ title="CAMERA POSITION"
+ save_rect="true"
+ width="280">
+
+  <text
+    follows="top|left|right"
+    name="camera_offset_lbl"
+    height="16"
+    layout="topleft"
+    left="10"
+    top="10"
+    width="100">
+      Camera offset:
+  </text>
+
+  <spinner
+   height="20"
+   label="X"
+   label_width="12"
+   follows="top|left"
+   left="10"
+   name="camera_x"
+   top_pad="5"
+   min_val="-1e+007"
+   max_val="1e+007"
+   width="70">
+    <spinner.commit_callback
+     function="CommitSettings" />
+  </spinner>
+
+  <spinner
+   height="20"
+   label="Y"
+   label_width="12"
+   follows="top|left"
+   name="camera_y"
+   left_pad="20"
+   min_val="-1e+007"
+   max_val="1e+007"
+   width="70">
+    <spinner.commit_callback
+     function="CommitSettings" />
+  </spinner>
+
+  <spinner
+   height="20"
+   label="Z"
+   label_width="12"
+   follows="top|left"
+   name="camera_z"
+   left_pad="20"
+   min_val="-1e+007"
+   max_val="1e+007"
+   width="70">
+    <spinner.commit_callback
+     function="CommitSettings" />
+  </spinner>
+
+  <text
+    follows="top|left|right"
+    name="focus_offset_lbl"
+    height="16"
+    layout="topleft"
+    left="10"
+    top_pad="20"
+    width="100">
+      Focus offset:
+  </text>
+
+  <spinner
+   height="20"
+   label="X"
+   label_width="12"
+   follows="top|left"
+   left="10"
+   name="focus_x"
+   top_pad="5"
+   min_val="-1e+007"
+   max_val="1e+007"
+   width="70">
+    <spinner.commit_callback
+     function="CommitSettings" />
+  </spinner>
+
+  <spinner
+   height="20"
+   label="Y"
+   label_width="12"
+   follows="top|left"
+   name="focus_y"
+   left_pad="20"
+   min_val="-1e+007"
+   max_val="1e+007"
+   width="70">
+    <spinner.commit_callback
+     function="CommitSettings" />
+  </spinner>
+
+  <spinner
+   height="20"
+   label="Z"
+   label_width="12"
+   follows="top|left"
+   name="focus_z"
+   left_pad="20"
+   min_val="-1e+007"
+   max_val="1e+007"
+   width="70">
+    <spinner.commit_callback
+     function="CommitSettings" />
+  </spinner>
+
+   <text
+    follows="top|left|right"
+    name="offset_scale_lbl"
+    height="16"
+    layout="topleft"
+    left="10"
+    top_pad="20"
+    width="140">
+      Camera offset scale:
+  </text>
+
+  <slider
+   control_name="CameraOffsetScale"
+   follows="top|left|right"
+   height="16"
+   top_pad="5"
+   increment="0.1"
+   min_val="-3"
+   max_val="5"
+   show_text="false"
+   layout="topleft"
+   left="3"
+   name="offset_scale_sld"
+   width="196" />
+  <spinner
+   control_name="CameraOffsetScale"
+   height="20"
+   follows="top|left|right"
+   left_pad="5"
+   name="offset_scale_ctrl"
+   min_val="-3"
+   max_val="5"
+   width="58">
+    <spinner.commit_callback
+     function="CommitSettings" />
+  </spinner>
+
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_save_camera_preset.xml b/indra/newview/skins/default/xui/en/floater_save_camera_preset.xml
new file mode 100644
index 0000000000000000000000000000000000000000..54fdb6d1670810eefa4628e383a6eb33073e99a1
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_save_camera_preset.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<floater
+ legacy_header_height="18"
+ height="185"
+ help_topic="floater_save_preset"
+ layout="topleft"
+ name="save_camera_preset"
+ save_rect="true"
+ title="Save Camera Preset"
+ width="280">
+
+    <string name="btn_label_save">Save</string>
+    <string name="btn_label_replace">Replace</string>
+    <radio_group
+     height="85"
+     layout="topleft"
+     left="20"
+     top="15"
+     width="150"
+     name="radio_save_preset">
+     <radio_item
+      label="Save as a new preset"
+      name="new_preset"
+      top="10" 
+      layout="topleft"
+      height="16" 
+      value="0"/>
+     <radio_item
+      label="Replace a preset"
+      name="replace_preset"
+      layout="topleft"
+      top="70" 
+      height="16" 
+      value="1"/>
+    </radio_group> 
+    <line_editor
+     commit_on_focus_lost = "true"
+     follows="top|left"
+     height="23"
+     layout="topleft"
+     left="41"
+     name="preset_txt_editor"
+     width="200"
+     top="45"/>
+    <button
+     follows="top|left"
+     height="25"
+     label="Save"
+     layout="topleft"
+     top="145"
+     left="25"
+     name="save"
+     width="110"/>
+    <button
+     follows="bottom|right"
+     height="25"
+     label="Cancel"
+     layout="topleft"
+     left_pad="20"
+     name="cancel"
+     width="110"/>
+<!-- *HACK to correctly draw drop-down list over the buttons-->
+    <combo_box
+     follows="top|left"
+     layout="topleft"
+     left="41"
+     name="preset_combo"
+     top_delta="-40"
+     width="200"/>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_save_pref_preset.xml b/indra/newview/skins/default/xui/en/floater_save_pref_preset.xml
index 7dee28eff3a1d5111c3e685e1730ee1435f906ee..62260274f5cfca9e4dd4edc067ae02443952bdfd 100644
--- a/indra/newview/skins/default/xui/en/floater_save_pref_preset.xml
+++ b/indra/newview/skins/default/xui/en/floater_save_pref_preset.xml
@@ -4,14 +4,11 @@
  height="145"
  help_topic="floater_save_preset"
  layout="topleft"
- name="Save Pref Preset"
+ name="save_pref_preset"
  save_rect="true"
- title="SAVE PREF PRESET"
+ title="Save Graphic Preset"
  width="300">
 
-    <string name="title_graphic">Save Graphic Preset</string>
-    <string name="title_camera">Save Camera Preset</string>
-
     <text
      follows="top|left|right"
      height="32"
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index c8f405727f28b85b39d9884bcf9735df58945a81..3ce428e543d6cf0ac90626d230ae2de26f14352c 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -207,7 +207,10 @@
 	     	 name="my_environs">
 	     	 	<menu_item_call.on_click
 	     	 	 function="World.EnvSettings"
-                 parameter="my_environs" />
+          parameter="my_environs" />
+         <menu_item_call.on_enable
+          function="RLV.EnableIfNot"
+          parameter="setenv" />
 	     	</menu_item_call>
         <menu
          label="Voice morphing"
@@ -766,7 +769,7 @@
                  parameter="region" />
             </menu_item_check>
         </menu>
-     <menu
+	    <menu
 	     create_jump_keys="true"
 	     label="Environment Editor"
 	     name="Environment Editor"
@@ -777,6 +780,9 @@
 	     	 	<menu_item_call.on_click
 	     	 	 function="World.EnvSettings"
           parameter="editor"/>
+         <menu_item_call.on_enable
+         function="RLV.EnableIfNot"
+         parameter="setenv" />
 	     	</menu_item_call>
 
 	     	<menu_item_separator/>
@@ -787,6 +793,9 @@
 	     	 	 	<menu_item_call.on_click
 	     	 	 	function="World.EnvPreset"
 	     	 	 	parameter="edit_sky" />
+           <menu_item_call.on_enable
+           function="RLV.EnableIfNot"
+           parameter="setenv" />
 	     	 	</menu_item_call>
 
 	     	<menu_item_call
@@ -795,6 +804,9 @@
 	     	 	 	<menu_item_call.on_click
 	     	 	 	function="World.EnvPreset"
 	     	 	 	parameter="edit_water" />
+           <menu_item_call.on_enable
+         function="RLV.EnableIfNot"
+         parameter="setenv" />
 	     	 	</menu_item_call>
 
 	     	<menu_item_call
@@ -807,10 +819,13 @@
             <menu_item_call.on_enable
              function="Floater.CanShow"
              parameter="env_edit_extdaycycle" />
+            <menu_item_call.on_enable
+         function="RLV.EnableIfNot"
+         parameter="setenv" />
 	     	 	</menu_item_call>
        
        <menu_item_separator/>
-	     	 	 <menu_item_check
+    	    <menu_item_check
 	     	 	 label="Pause Clouds"
 	     	 	 name="pause_clouds">
 	     	 	 	<menu_item_check.on_click
@@ -819,6 +834,9 @@
 	     	 	 	<menu_item_check.on_check
 	     	 	 	function="World.EnableEnvSettings" 
 	     	 	 	parameter="pause_clouds" />
+          <menu_item_call.on_enable
+           function="RLV.EnableIfNot"
+           parameter="setenv" />
 	     	 	</menu_item_check>
 	    </menu>
     </menu>
@@ -1541,24 +1559,45 @@
       	 parameter="RestrainedLoveCanOOC" />
       </menu_item_check>
       <menu_item_check
-       label="Forbid Give to #RLV"
-       name="Forbid Give to #RLV">
+       label="Show Filtered Chat"
+       name="Show Filtered Chat">
       	<menu_item_check.on_check
       	 function="CheckControl"
-      	 parameter="RestrainedLoveForbidGiveToRLV" />
+      	 parameter="RestrainedLoveShowEllipsis" />
       	<menu_item_check.on_click
       	 function="ToggleControl"
-      	 parameter="RestrainedLoveForbidGiveToRLV" />
+      	 parameter="RestrainedLoveShowEllipsis" />
       </menu_item_check>
       <menu_item_check
-       label="Show Filtered Chat"
-       name="Show Filtered Chat">
+       label="Split Long Redirected Chat"
+       name="Split Long Redirected Chat">
+        <menu_item_check.on_check
+      	 function="CheckControl"
+      	 parameter="RLVaSplitRedirectChat" />
+        <menu_item_check.on_click
+      	 function="ToggleControl"
+      	 parameter="RLVaSplitRedirectChat" />
+      </menu_item_check>
+      <menu_item_separator />
+      <menu_item_check
+       label="Allow Temporary Attachments"
+       name="Allow Temporary Attachments">
       	<menu_item_check.on_check
       	 function="CheckControl"
-      	 parameter="RestrainedLoveShowEllipsis" />
+      	 parameter="RLVaEnableTemporaryAttachments" />
       	<menu_item_check.on_click
       	 function="ToggleControl"
-      	 parameter="RestrainedLoveShowEllipsis" />
+      	 parameter="RLVaEnableTemporaryAttachments" />
+      </menu_item_check>
+      <menu_item_check
+       label="Forbid Give to #RLV"
+       name="Forbid Give to #RLV">
+      	<menu_item_check.on_check
+      	 function="CheckControl"
+      	 parameter="RestrainedLoveForbidGiveToRLV" />
+      	<menu_item_check.on_click
+      	 function="ToggleControl"
+      	 parameter="RestrainedLoveForbidGiveToRLV" />
       </menu_item_check>
       <menu_item_check
        label="Wear Replaces Unlocked"
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 2e42eca21b008cf849c2675cbd1ceb3cf3d7ab75..df04ecfba60ad1b008ac982637ae134998a58cdd 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -6553,6 +6553,14 @@ The string [STRING_NAME] is missing from strings.xml
 [MESSAGE]
   </notification>
   
+  <notification
+   icon="notifytip.tga"
+   name="ChatSystemMessageTip"
+   log_to_chat="true"
+   type="notifytip">
+[MESSAGE]
+  </notification>
+
   <notification
    icon="notifytip.tga"
    name="IMSystemMessageTip"
@@ -8575,6 +8583,18 @@ Error saving preset [NAME].
 Can not overwrite default preset.
   </notification>
 
+  <notification
+    icon="alertmodal.tga"
+    name="PresetAlreadyExists"
+    type="alertmodal">
+&apos;[NAME]&apos; is in use. You may replace
+this preset or choose another name.
+    <tag>fail</tag>
+    <usetemplate
+     name="okbutton"
+     yestext="OK"/>
+  </notification>
+
   <notification
     icon="notifytip.tga"
     name="PresetNotDeleted"
@@ -11444,7 +11464,34 @@ Cannot create large prims that intersect other residents.  Please re-try when ot
    type="alertmodal">
 Changes won't take effect until after you restart [APP_NAME].
   </notification>
-  
+
+  <notification
+   icon="notify.tga"
+   name="RLVaListRequested"
+   label="Restriction request from [NAME_LABEL]"
+   log_to_im="true"
+   log_to_chat="false"
+   type="offer">
+[NAME_SLURL] has requested to be sent a list of your currently active RLV restrictions.
+    <tag>confirm</tag>
+    <form name="form">
+      <button
+       index="0"
+       default="true"
+       name="Allow"
+       text="Allow"/>
+      <button
+       index="1"
+       name="Always Allow"
+       text="Always Allow"/>
+      <button
+       index="2"
+       name="Deny"
+       text="Deny"/>
+      <ignore text="Confirm before sending anyone a list of my current RLV restrictions."/>
+    </form>
+  </notification>
+
   <notification
    icon="alertmodal.tga"
    name="PreferenceChatClearLog"
diff --git a/indra/newview/skins/default/xui/en/panel_camera_preset_item.xml b/indra/newview/skins/default/xui/en/panel_camera_preset_item.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9417ab4ac2d0de1b36cf36e69b6550bad7dbd1cb
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_camera_preset_item.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ follows="top|right|left"
+ height="20"
+ layout="topleft"
+ left="0"
+ name="camera_preset_item"
+ top="0"
+ width="280">
+    <icon
+     follows="top|right|left"
+     height="20"
+     image_name="ListItem_Over"
+     layout="topleft"
+     left="0"
+     name="hovered_icon"
+     top="0"
+     visible="false"
+     width="380" />
+    <icon
+     height="20"
+     follows="top|right|left"
+     image_name="ListItem_Select"
+     layout="topleft"
+     left="0"
+     name="selected_icon"
+     top="0"
+     visible="false"
+     width="380" />
+    <text
+     follows="left"
+     height="20"
+     layout="topleft"
+     left="10"
+     parse_urls="false"
+     use_ellipses="true"
+     name="preset_name"
+     text_color="White"
+     top="2"
+     value="Default"
+     width="159" />
+    <button
+     follows="right"
+     image_selected="TrashItem_Off"
+     image_pressed="TrashItem_Off"
+     image_unselected="TrashItem_Off"
+     is_toggle="true"
+     layout="topleft"
+     left_pad="5"
+     right="-10"
+     name="delete_btn"
+     tool_tip="Delete preset"
+     top="3"
+     height="18"
+     width="18" >
+      <button.commit_callback
+        function="CameraPresets.Delete"/>
+    </button>
+  <button
+   follows="right"
+   image_selected="Refresh_Off"
+   image_pressed="Refresh_Off"
+   image_unselected="Refresh_Off"
+   is_toggle="true"
+   layout="topleft"
+   left_pad="5"
+   right="-10"
+   name="reset_btn"
+   tool_tip="Reset preset to default"
+   top="2"
+   height="20"
+   width="20" >
+    <button.commit_callback
+      function="CameraPresets.Reset"/>
+  </button>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_presets_camera_pulldown.xml b/indra/newview/skins/default/xui/en/panel_presets_camera_pulldown.xml
new file mode 100644
index 0000000000000000000000000000000000000000..25d9c47449fac1ba042d2b7c32f3b8029a7b0fa8
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_presets_camera_pulldown.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ background_opaque="true"
+ background_visible="true"
+ bg_opaque_image="Volume_Background"
+ bg_alpha_image="Volume_Background"
+ border_visible="false"
+ border="false"
+ chrome="true"
+ follows="bottom"
+ height="155"
+ layout="topleft"
+ name="presets_camera_pulldown"
+ width="225">
+  <text
+    type="string"
+    length="1"
+    follows="left|top"
+    height="12"
+    layout="topleft"
+    top="4"
+    left_delta="5"
+    font.style="BOLD"
+    name="Camera Presets"
+    width="120">
+      Camera Presets
+  </text>
+  <scroll_list
+    follows="left|top"
+    layout="topleft"
+    column_padding="0"
+    height="100"
+    width="215"
+    draw_heading="false"
+    draw_stripes="false"
+    bg_stripe_color="0.25 0.25 0.25 0.25"
+    top_delta="15"
+    left_delta="0"
+    name="preset_camera_list">
+    <scroll_list.columns
+      name="icon"
+      width="16" />
+    <scroll_list.columns
+      relative_width="1"
+      name="preset_name" />
+      <scroll_list.commit_callback
+      function="PresetsCamera.RowClick" />
+  </scroll_list>
+  <view_border
+    bevel_style="none"
+    follows="top|left"
+    height="0"
+    layout="topleft"
+    left="5"
+    name="horiz_separator"
+    top_delta="105"
+    width="215" />
+  <button
+    name="open_prefs_btn"
+    label="Open Camera floater"
+    tool_tip = "Bring up Camera floater"
+    top_delta="5"
+    left="15"
+    height="20"
+    width="200">
+    <button.commit_callback
+      function="Presets.toggleCameraFloater" />
+  </button>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/rlva_strings.xml b/indra/newview/skins/default/xui/en/rlva_strings.xml
index 0e3c27ca0a945d5b12a5137b2894d6137f32c587..5b3e9f20a1dc684bcccc6590d2202c4be7b4bac2 100644
--- a/indra/newview/skins/default/xui/en/rlva_strings.xml
+++ b/indra/newview/skins/default/xui/en/rlva_strings.xml
@@ -45,6 +45,61 @@
 			<boolean>1</boolean>
 		</map>
 
+		<!-- Sent to the remote party when they issue @list or @except as an IM query (if enabled) -->
+		<key>imquery_list_deny</key>
+		<map>
+			<key>value</key>
+			<string>*** The other party respectfully requests you mind your own business (bunnies made me do it!)</string>
+			<key>description</key>
+			<string>Sent to the remote party when you deny their request to list your active RLV restrictions)</string>
+			<key>label</key>
+			<string>@list and @except command (remote)</string>
+			<key>customizable</key>
+			<boolean>1</boolean>
+		</map>
+
+		<!-- Sent to the remote party as a suffix to @list to inform them there might be more information -->
+		<key>imquery_list_suffix</key>
+		<map>
+			<key>value</key>
+			<string>(Use @except to see the list of active exceptions)</string>
+			<key>description</key>
+			<string>Sent to the remote party as a suffix to @list to inform them how to request your exceptions</string>
+			<key>label</key>
+			<string>@list command suffix (remote)</string>
+			<key>customizable</key>
+			<boolean>0</boolean>
+		</map>
+
+		<!-- Sent to the remote party when they issue @stopim as an IM query (if enabled) -->
+		<key>stopim_nosession</key>
+		<map>
+			<key>value</key>
+			<string>*** The other party is not under a @startim restriction</string>
+			<key>description</key>
+			<string>Sent to the remote party when they attempt to forcefully close your IM conversation with them (and no such session exists)</string>
+			<key>label</key>
+			<string>@stopim command with no session (remote)</string>
+			<key>customizable</key>
+			<boolean>1</boolean>
+		</map>
+		<key>stopim_endsession_remote</key>
+		<map>
+			<key>value</key>
+			<string>*** Session has been ended for the other party</string>
+			<key>description</key>
+			<string>Sent to the remote party when they attempt to forcefully close the IM conversation (and it exists)</string>
+			<key>label</key>
+			<string>@stopim command with an active session (remote)</string>
+			<key>customizable</key>
+			<boolean>1</boolean>
+		</map>
+		<key>stopim_endsession_local</key>
+		<map>
+			<key>value</key>
+			<string>[NAME] has remotely closed the IM conversation with @stopim</string>
+		</map>
+
 		<!-- Shown as notifications -->
 		<key>blocked_generic</key>
 		<map>