diff --git a/.gitignore b/.gitignore
index 27b629a578c0f410ffd187c2babda05f5c9eaa56..2d32e94c782df03ab0c3fdf58e0e07d1b43a32fa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@
 *.bak
 *.diff
 *.orig
+*.patch
 *.pyc
 *.rej
 *.swp
diff --git a/autobuild.xml b/autobuild.xml
index 513013700e51516b2f904c612ecac9d52f79731e..0e356f1a0c91508c3ad899d09f52a6033fa1d3c8 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -729,38 +729,28 @@
         <key>version</key>
         <string>2.1.1.500375</string>
       </map>
-      <key>fmodex</key>
+      <key>fmodstudio</key>
       <map>
         <key>copyright</key>
-        <string>COPYRIGHT 2014 FIRELIGHT TECHNOLOGIES PTY LTD. ALL RIGHTS RESERVED</string>
+        <string>FMOD Studio by Firelight Technologies Pty Ltd.</string>
+        <key>description</key>
+        <string>FMOD Studio API</string>
         <key>license</key>
-        <string>fmodex</string>
+        <string>fmod</string>
         <key>license_file</key>
-        <string>LICENSES/fmodex.txt</string>
+        <string>LICENSES/fmodstudio.txt</string>
         <key>name</key>
-        <string>fmodex</string>
+        <string>fmodstudio</string>
         <key>platforms</key>
         <map>
-          <key>darwin</key>
-          <map>
-            <key>archive</key>
-            <map>
-              <key>hash</key>
-              <string>ed0d8767652aecd65a7fef3e28645bad</string>
-              <key>url</key>
-              <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/fmodex_3p-update-fmodex/rev/297261/arch/Darwin/installer/fmodex-4.44.31.297261-darwin-297261.tar.bz2</string>
-            </map>
-            <key>name</key>
-            <string>darwin</string>
-          </map>
           <key>darwin64</key>
           <map>
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>cba1feed7f6bb671d791a517fddf205a</string>
+              <string>dc4c9122de8bf77f34cfc8227d10a272</string>
               <key>url</key>
-              <string>https://downloads.catznip.com/packages/fmodex-44461-darwin-201601300040-r23.tar.bz2</string>
+              <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/59038/554626/fmodstudio-2.00.07.541681-darwin64-541681.tar.bz2</string>
             </map>
             <key>name</key>
             <string>darwin64</string>
@@ -770,9 +760,9 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>b847ec838da1ad1dd646df9d74e9b395</string>
+              <string>c491bdc1690f3d920c66be509ccc6ef2</string>
               <key>url</key>
-              <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/p64_3p-fmodex/rev/314207/arch/Linux/installer/fmodex-4.44.61.314207-linux-314207.tar.bz2</string>
+              <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/59039/554632/fmodstudio-2.00.07.541681-linux-541681.tar.bz2</string>
             </map>
             <key>name</key>
             <string>linux</string>
@@ -782,9 +772,9 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>89a75d8719f7b2cbe1e54cd8407bb992</string>
+              <string>ae75cdb1cc9da824c9e270bf97bfdd6c</string>
               <key>url</key>
-              <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/1544/3476/fmodex-4.44.64.501533-linux64-501533.tar.bz2</string>
+              <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/54589/506866/fmodstudio-2.00.07.538806-linux64-538806.tar.bz2</string>
             </map>
             <key>name</key>
             <string>linux64</string>
@@ -794,9 +784,9 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>2038da4ab71da8dc086738007c0acdd3</string>
+              <string>fbd53fad464526e9331332fc0b3fee0b</string>
               <key>url</key>
-              <string>http://viewer.catznip.com/downloads/packages/fmodex-4.44.61-windows-171261212.tar.bz2</string>
+              <string>https://downloads.catznip.com/packages/fmodstudio-2.00.09-windows-201512304.tar.bz2</string>
             </map>
             <key>name</key>
             <string>windows</string>
@@ -806,16 +796,16 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>b4b73cd64bfd72e7ae84aad429d69cf6</string>
+              <string>af7446fdf8d39e114feae354878ec54b</string>
               <key>url</key>
-              <string>http://viewer.catznip.com/downloads/packages/fmodex-4.44.61-windows64-171261211.tar.bz2</string>
+              <string>https://downloads.catznip.com/packages/fmodstudio-2.00.09-windows64-201512258.tar.bz2</string>
             </map>
             <key>name</key>
             <string>windows64</string>
           </map>
         </map>
         <key>version</key>
-        <string>4.44.64.501533</string>
+        <string>2.00.07.541681</string>
       </map>
       <key>fontconfig</key>
       <map>
diff --git a/doc/contributions.txt b/doc/contributions.txt
index 2153e108882ae9445823a59db37ec683c5f5d388..7bbbf6430a52d84a7c080bc372caf750f71742e3 100755
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -1517,6 +1517,7 @@ Whirly Fizzle
 	STORM-1930
 	BUG-6659
 	STORM-2078
+	BUG-17349
 Whoops Babii
 	VWR-631
 	VWR-1640
diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt
index 84e1c5d6fdee0b417b144d9b314a47817eb8cbac..3a14bf522f0732342bbed80eeac1f059cbeaae0b 100644
--- a/indra/cmake/CMakeLists.txt
+++ b/indra/cmake/CMakeLists.txt
@@ -28,7 +28,6 @@ set(cmake_SOURCE_FILES
     FindAPR.cmake
     FindAutobuild.cmake
     FindBerkeleyDB.cmake
-    FindFMODEX.cmake
     FindGLH.cmake
     FindGoogleBreakpad.cmake
     FindHUNSPELL.cmake
@@ -39,7 +38,7 @@ set(cmake_SOURCE_FILES
     FindURIPARSER.cmake
     FindXmlRpcEpi.cmake
     FindZLIB.cmake
-    FMODEX.cmake
+    FMODSTUDIO.cmake
     FreeType.cmake
     GLEXT.cmake
     GLH.cmake
diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake
index c73a1fdb47b3e78f5de6bfe751d0d76a8b73eb06..82cd5d62e85931060f6bbcc903aa58ccdc6f984b 100644
--- a/indra/cmake/Copy3rdPartyLibs.cmake
+++ b/indra/cmake/Copy3rdPartyLibs.cmake
@@ -62,14 +62,10 @@ if(WINDOWS)
       endif(ADDRESS_SIZE EQUAL 32)
     endif (BUGSPLAT_DB)
 
-    if (FMODEX)
-
-        if(ADDRESS_SIZE EQUAL 32)
-            set(release_files ${release_files} fmodex.dll)
-        else(ADDRESS_SIZE EQUAL 32)
-            set(release_files ${release_files} fmodex64.dll)
-        endif(ADDRESS_SIZE EQUAL 32)
-    endif (FMODEX)
+    if (FMODSTUDIO)
+        set(debug_files ${debug_files} fmodL.dll)
+        set(release_files ${release_files} fmod.dll)
+    endif (FMODSTUDIO)
 
     #*******************************
     # Copy MS C runtime dlls, required for packaging.
@@ -192,10 +188,10 @@ elseif(DARWIN)
         libnghttp2.14.14.0.dylib
        )
 
-    if (FMODEX)
-      set(debug_files ${debug_files} libfmodexL.dylib)
-      set(release_files ${release_files} libfmodex.dylib)
-    endif (FMODEX)
+    if (FMODSTUDIO)
+      set(debug_files ${debug_files} libfmodL.dylib)
+      set(release_files ${release_files} libfmod.dylib)
+    endif (FMODSTUDIO)
 
 elseif(LINUX)
     # linux is weird, multiple side by side configurations aren't supported
@@ -242,10 +238,10 @@ elseif(LINUX)
         libfontconfig.so.1
        )
 
-    if (FMODEX)
-      set(debug_files ${debug_files} "libfmodexL.so")
-      set(release_files ${release_files} "libfmodex.so")
-    endif (FMODEX)
+    if (FMODSTUDIO)
+      set(debug_files ${debug_files} "libfmodL.so")
+      set(release_files ${release_files} "libfmod.so")
+    endif (FMODSTUDIO)
 
 else(WINDOWS)
     message(STATUS "WARNING: unrecognized platform for staging 3rd party libs, skipping...")
diff --git a/indra/cmake/FMODEX.cmake b/indra/cmake/FMODEX.cmake
deleted file mode 100644
index 9753412de7b63f85d4f0c6c3837307e6ddbcade5..0000000000000000000000000000000000000000
--- a/indra/cmake/FMODEX.cmake
+++ /dev/null
@@ -1,52 +0,0 @@
-# -*- cmake -*-
-
-# FMOD can be set when launching the make using the argument -DFMOD:BOOL=ON
-# When building using proprietary binaries though (i.e. having access to LL private servers),
-# we always build with FMODEX.
-# Open source devs should use the -DFMODEX:BOOL=ON then if they want to build with FMOD, whether
-# they are using USESYSTEMLIBS or not.
-if (INSTALL_PROPRIETARY)
-  set(FMODEX ON CACHE BOOL "Using FMOD Ex sound library.")
-endif (INSTALL_PROPRIETARY)
-
-if (FMODEX)
-  if (USESYSTEMLIBS)
-    # In that case, we use the version of the library installed on the system
-    set(FMODEX_FIND_REQUIRED ON)
-    include(FindFMODEX)
-  else (USESYSTEMLIBS)
-    if (FMODEX_LIBRARY AND FMODEX_INCLUDE_DIR)
-      # If the path have been specified in the arguments, use that
-      set(FMODEX_LIBRARIES ${FMODEX_LIBRARY})
-      MESSAGE(STATUS "Using FMODEX path: ${FMODEX_LIBRARIES}, ${FMODEX_INCLUDE_DIR}")
-    else (FMODEX_LIBRARY AND FMODEX_INCLUDE_DIR)
-      # If not, we're going to try to get the package listed in autobuild.xml
-      # Note: if you're not using INSTALL_PROPRIETARY, the package URL should be local (file:/// URL) 
-      # as accessing the private LL location will fail if you don't have the credential
-      include(Prebuilt)
-      use_prebuilt_binary(fmodex)    
-      if (WINDOWS)
-        if (ADDRESS_SIZE EQUAL 32)
-            set(FMODEX_LIBRARY
-                debug fmodexL_vc
-                optimized fmodex_vc)
-        else (ADDRESS_SIZE EQUAL 64)
-            set(FMODEX_LIBRARY
-                debug fmodexL64_vc
-                optimized fmodex64_vc)
-        endif (ADDRESS_SIZE EQUAL 32)
-      elseif (DARWIN)
-        set(FMODEX_LIBRARY 
-            debug fmodexL
-            optimized fmodex)
-      elseif (LINUX)
-        set(FMODEX_LIBRARY 
-            debug fmodexL
-            optimized fmodex)
-      endif (WINDOWS)
-      set(FMODEX_LIBRARIES ${FMODEX_LIBRARY})
-      set(FMODEX_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/fmodex)
-    endif (FMODEX_LIBRARY AND FMODEX_INCLUDE_DIR)
-  endif (USESYSTEMLIBS)
-endif (FMODEX)
-
diff --git a/indra/cmake/FMODSTUDIO.cmake b/indra/cmake/FMODSTUDIO.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..8840354ac6c3d53d7d46040f65019fcc7903d66d
--- /dev/null
+++ b/indra/cmake/FMODSTUDIO.cmake
@@ -0,0 +1,38 @@
+# -*- cmake -*-
+
+# FMODSTUDIO can be set when launching the make using the argument -DFMODSTUDIO:BOOL=ON
+# When building using proprietary binaries though (i.e. having access to LL private servers),
+# we always build with FMODSTUDIO.
+if (INSTALL_PROPRIETARY)
+  set(FMODSTUDIO ON CACHE BOOL "Using FMODSTUDIO sound library.")
+endif (INSTALL_PROPRIETARY)
+
+if (FMODSTUDIO)
+  if (FMODSTUDIO_LIBRARY AND FMODSTUDIO_INCLUDE_DIR)
+    # If the path have been specified in the arguments, use that
+    set(FMODSTUDIO_LIBRARIES ${FMODSTUDIO_LIBRARY})
+  else (FMODSTUDIO_LIBRARY AND FMODSTUDIO_INCLUDE_DIR)
+    # If not, we're going to try to get the package listed in autobuild.xml
+    # Note: if you're not using INSTALL_PROPRIETARY, the package URL should be local (file:/// URL)
+    # as accessing the private LL location will fail if you don't have the credential
+    include(Prebuilt)
+    use_prebuilt_binary(fmodstudio)
+    if (WINDOWS)
+      set(FMODSTUDIO_LIBRARY
+          debug fmodL_vc
+          optimized fmod_vc)
+    elseif (DARWIN)
+      #despite files being called libfmod.dylib, we are searching for fmod
+      set(FMODSTUDIO_LIBRARY
+          debug fmodL
+          optimized fmod)
+    elseif (LINUX)
+      set(FMODSTUDIO_LIBRARY
+          debug fmodL
+          optimized fmod)
+    endif (WINDOWS)
+    set(FMODSTUDIO_LIBRARIES ${FMODSTUDIO_LIBRARY})
+    set(FMODSTUDIO_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/fmodstudio)
+  endif (FMODSTUDIO_LIBRARY AND FMODSTUDIO_INCLUDE_DIR)
+endif (FMODSTUDIO)
+
diff --git a/indra/cmake/FindFMODEX.cmake b/indra/cmake/FindFMODEX.cmake
deleted file mode 100644
index b621727c0ea490c8d55c7d89f243813ab698bda6..0000000000000000000000000000000000000000
--- a/indra/cmake/FindFMODEX.cmake
+++ /dev/null
@@ -1,65 +0,0 @@
-# -*- cmake -*-
-
-# - Find FMODEX
-# Find the FMODEX includes and library
-# This module defines
-#  FMODEX_INCLUDE_DIR, where to find fmod.h and fmod_errors.h
-#  FMODEX_LIBRARIES, the libraries needed to use FMODEX.
-#  FMODEX, If false, do not try to use FMODEX.
-# also defined, but not for general use are
-#  FMODEX_LIBRARY, where to find the FMODEX library.
-
-FIND_PATH(FMODEX_INCLUDE_DIR fmod.h PATH_SUFFIXES fmod)
-
-SET(FMODEX_NAMES ${FMODEX_NAMES} fmodex fmodvc fmodexL_vc)
-FIND_LIBRARY(FMODEX_LIBRARY
-  NAMES ${FMODEX_NAMES}
-  PATH_SUFFIXES fmodex
-  )
-
-IF (FMODEX_SDK_DIR OR WINDOWS)
-    if(WINDOWS)
-        set(FMODEX_SDK_DIR "$ENV{PROGRAMFILES}/FMOD SoundSystem/FMOD Programmers API Windows" CACHE PATH "Path to FMODEX")
-        STRING(REGEX REPLACE "\\\\" "/" FMODEX_SDK_DIR ${FMODEX_SDK_DIR}) 
-    endif(WINDOWS)
-    find_library(FMODEX_LIBRARY
-             fmodex_vc fmodexL_vc 
-             PATHS
-             ${FMODEX_SDK_DIR}/api/lib
-             ${FMODEX_SDK_DIR}/api
-             ${FMODEX_SDK_DIR}
-             )
-    find_path(FMODEX_INCLUDE_DIR fmod.h
-        ${FMODEX_SDK_DIR}/api/inc
-        ${FMODEX_SDK_DIR}/api
-        ${FMODEX_SDK_DIR}
-      )
-    find_path(FMODEX_INCLUDE_DIR fmod.h
-        ${FMODEX_SDK_DIR}/api/inc
-        ${FMODEX_SDK_DIR}/api
-        ${FMODEX_SDK_DIR}
-      )
-    IF (FMODEX_LIBRARY AND FMODEX_INCLUDE_DIR)
-      SET(FMODEX_LIBRARIES ${FMODEX_LIBRARY})
-      SET(FMODEX_FOUND "YES")
-    endif (FMODEX_LIBRARY AND FMODEX_INCLUDE_DIR)
-ENDIF (FMODEX_SDK_DIR OR WINDOWS)
-
-IF (FMODEX_FOUND)
-  IF (NOT FMODEX_FIND_QUIETLY)
-    MESSAGE(STATUS "Found FMODEX: ${FMODEX_LIBRARIES}")
-  ENDIF (NOT FMODEX_FIND_QUIETLY)
-ELSE (FMODEX_FOUND)
-  IF (FMODEX_FIND_REQUIRED)
-    MESSAGE(FATAL_ERROR "Could not find FMODEX library")
-  ENDIF (FMODEX_FIND_REQUIRED)
-ENDIF (FMODEX_FOUND)
-
-# Deprecated declarations.
-SET (NATIVE_FMODEX_INCLUDE_PATH ${FMODEX_INCLUDE_DIR} )
-GET_FILENAME_COMPONENT (NATIVE_FMODEX_LIB_PATH ${FMODEX_LIBRARY} PATH)
-
-MARK_AS_ADVANCED(
-  FMODEX_LIBRARY
-  FMODEX_INCLUDE_DIR
-  )
diff --git a/indra/llaudio/CMakeLists.txt b/indra/llaudio/CMakeLists.txt
index e943dd5d5ccd059100c1ad41507a3f5d377b73d1..8b628a058e0b74b7d8d603185f5a9a3ce76a675e 100644
--- a/indra/llaudio/CMakeLists.txt
+++ b/indra/llaudio/CMakeLists.txt
@@ -4,7 +4,7 @@ project(llaudio)
 
 include(00-Common)
 include(LLAudio)
-include(FMODEX)
+include(FMODSTUDIO)
 include(OPENAL)
 include(LLCommon)
 include(LLMath)
@@ -42,22 +42,22 @@ set(llaudio_HEADER_FILES
     llwindgen.h
     )
 
-if (FMODEX)
+if (FMODSTUDIO)
     include_directories(
-        ${FMODEX_INCLUDE_DIR}
+        ${FMODSTUDIO_INCLUDE_DIR}
         )
     list(APPEND llaudio_SOURCE_FILES
-         llaudioengine_fmodex.cpp
-         lllistener_fmodex.cpp
-         llstreamingaudio_fmodex.cpp
+         llaudioengine_fmodstudio.cpp
+         lllistener_fmodstudio.cpp
+         llstreamingaudio_fmodstudio.cpp
          )
 
     list(APPEND llaudio_HEADER_FILES
-         llaudioengine_fmodex.h
-         lllistener_fmodex.h
-         llstreamingaudio_fmodex.h
+         llaudioengine_fmodstudio.h
+         lllistener_fmodstudio.h
+         llstreamingaudio_fmodstudio.h
          )
-endif (FMODEX)
+endif (FMODSTUDIO)
 
 if (OPENAL)
   list(APPEND llaudio_SOURCE_FILES
diff --git a/indra/llaudio/llaudioengine.cpp b/indra/llaudio/llaudioengine.cpp
index f49028aad51955fbc5a606cda3ebcf9f2985e43e..1d447f32ae77ee4f0eb508ff2091e751f4839861 100644
--- a/indra/llaudio/llaudioengine.cpp
+++ b/indra/llaudio/llaudioengine.cpp
@@ -111,7 +111,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_fmodex.cpp b/indra/llaudio/llaudioengine_fmodex.cpp
deleted file mode 100644
index 7e65a05e48ab561f7c65b9cdd406571c5f2bba42..0000000000000000000000000000000000000000
--- a/indra/llaudio/llaudioengine_fmodex.cpp
+++ /dev/null
@@ -1,767 +0,0 @@
-/** 
- * @file audioengine_fmodex.cpp
- * @brief Implementation of LLAudioEngine class abstracting the audio 
- * support as a FMODEX implementation
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2014, 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 "linden_common.h"
-
-#include "llstreamingaudio.h"
-#include "llstreamingaudio_fmodex.h"
-
-#include "llaudioengine_fmodex.h"
-#include "lllistener_fmodex.h"
-
-#include "llerror.h"
-#include "llmath.h"
-#include "llrand.h"
-
-#include "fmod.hpp"
-#include "fmod_errors.h"
-#include "lldir.h"
-#include "llapr.h"
-
-#include "sound_ids.h"
-
-FMOD_RESULT F_CALLBACK windCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels);
-
-FMOD::ChannelGroup *LLAudioEngine_FMODEX::mChannelGroups[LLAudioEngine::AUDIO_TYPE_COUNT] = {0};
-
-LLAudioEngine_FMODEX::LLAudioEngine_FMODEX(bool enable_profiler)
-{
-	mInited = false;
-	mWindGen = NULL;
-	mWindDSP = NULL;
-	mSystem = NULL;
-	mEnableProfiler = enable_profiler;
-	mWindDSPDesc = new FMOD_DSP_DESCRIPTION();
-}
-
-
-LLAudioEngine_FMODEX::~LLAudioEngine_FMODEX()
-{
-	delete mWindDSPDesc;
-}
-
-
-inline bool Check_FMOD_Error(FMOD_RESULT result, const char *string)
-{
-	if(result == FMOD_OK)
-		return false;
-	LL_DEBUGS() << string << " Error: " << FMOD_ErrorString(result) << LL_ENDL;
-	return true;
-}
-
-void* F_STDCALL decode_alloc(unsigned int size, FMOD_MEMORY_TYPE type, const char *sourcestr)
-{
-	if(type & FMOD_MEMORY_STREAM_DECODE)
-	{
-		LL_INFOS() << "Decode buffer size: " << size << LL_ENDL;
-	}
-	else if(type & FMOD_MEMORY_STREAM_FILE)
-	{
-		LL_INFOS() << "Strean buffer size: " << size << LL_ENDL;
-	}
-	return new char[size];
-}
-void* F_STDCALL decode_realloc(void *ptr, unsigned int size, FMOD_MEMORY_TYPE type, const char *sourcestr)
-{
-	memset(ptr,0,size);
-	return ptr;
-}
-void F_STDCALL decode_dealloc(void *ptr, FMOD_MEMORY_TYPE type, const char *sourcestr)
-{
-	delete[] (char*)ptr;
-}
-
-bool LLAudioEngine_FMODEX::init(const S32 num_channels, void* userdata)
-{
-	U32 version;
-	FMOD_RESULT result;
-
-	LL_DEBUGS("AppInit") << "LLAudioEngine_FMODEX::init() initializing FMOD" << LL_ENDL;
-
-	//result = FMOD::Memory_Initialize(NULL, 0, &decode_alloc, &decode_realloc, &decode_dealloc, FMOD_MEMORY_STREAM_DECODE | FMOD_MEMORY_STREAM_FILE);
-	//if(Check_FMOD_Error(result, "FMOD::Memory_Initialize"))
-	//	return false;
-
-	// turn off non-error log spam to fmod.log (TODO: why do we even have an fmod.log if we don't link against log lib?)
-	FMOD::Debug_SetLevel(FMOD_DEBUG_LEVEL_ERROR);
-
-	result = FMOD::System_Create(&mSystem);
-	if(Check_FMOD_Error(result, "FMOD::System_Create"))
-		return false;
-
-	//will call LLAudioEngine_FMODEX::allocateListener, which needs a valid mSystem pointer.
-	LLAudioEngine::init(num_channels, userdata);	
-	
-	result = mSystem->getVersion(&version);
-	Check_FMOD_Error(result, "FMOD::System::getVersion");
-
-	if (version < FMOD_VERSION)
-	{
-		LL_WARNS("AppInit") << "Error : You are using the wrong FMOD Ex version (" << version
-			<< ")!  You should be using FMOD Ex" << FMOD_VERSION << LL_ENDL;
-	}
-
-	result = mSystem->setSoftwareFormat(44100, FMOD_SOUND_FORMAT_PCM16, 0, 0, FMOD_DSP_RESAMPLER_LINEAR);
-	Check_FMOD_Error(result,"FMOD::System::setSoftwareFormat");
-
-	// In this case, all sounds, PLUS wind and stream will be software.
-	result = mSystem->setSoftwareChannels(num_channels + 2);
-	Check_FMOD_Error(result,"FMOD::System::setSoftwareChannels");
-
-	U32 fmod_flags = FMOD_INIT_NORMAL;
-	if(mEnableProfiler)
-	{
-		fmod_flags |= FMOD_INIT_ENABLE_PROFILE;
-		mSystem->createChannelGroup("None", &mChannelGroups[AUDIO_TYPE_NONE]);
-		mSystem->createChannelGroup("SFX", &mChannelGroups[AUDIO_TYPE_SFX]);
-		mSystem->createChannelGroup("UI", &mChannelGroups[AUDIO_TYPE_UI]);
-		mSystem->createChannelGroup("Ambient", &mChannelGroups[AUDIO_TYPE_AMBIENT]);
-	}
-
-#if LL_LINUX
-	bool audio_ok = false;
-
-	if (!audio_ok)
-	{
-		if (NULL == getenv("LL_BAD_FMOD_PULSEAUDIO")) /*Flawfinder: ignore*/
-		{
-			LL_DEBUGS("AppInit") << "Trying PulseAudio audio output..." << LL_ENDL;
-			if(mSystem->setOutput(FMOD_OUTPUTTYPE_PULSEAUDIO) == FMOD_OK &&
-				(result = mSystem->init(num_channels + 2, fmod_flags, 0)) == FMOD_OK)
-			{
-				LL_DEBUGS("AppInit") << "PulseAudio output initialized OKAY"	<< LL_ENDL;
-				audio_ok = true;
-			}
-			else 
-			{
-				Check_FMOD_Error(result, "PulseAudio audio output FAILED to initialize");
-			}
-		} 
-		else 
-		{
-			LL_DEBUGS("AppInit") << "PulseAudio audio output SKIPPED" << LL_ENDL;
-		}	
-	}
-	if (!audio_ok)
-	{
-		if (NULL == getenv("LL_BAD_FMOD_ALSA"))		/*Flawfinder: ignore*/
-		{
-			LL_DEBUGS("AppInit") << "Trying ALSA audio output..." << LL_ENDL;
-			if(mSystem->setOutput(FMOD_OUTPUTTYPE_ALSA) == FMOD_OK &&
-			    (result = mSystem->init(num_channels + 2, fmod_flags, 0)) == FMOD_OK)
-			{
-				LL_DEBUGS("AppInit") << "ALSA audio output initialized OKAY" << LL_ENDL;
-				audio_ok = true;
-			} 
-			else 
-			{
-				Check_FMOD_Error(result, "ALSA audio output FAILED to initialize");
-			}
-		} 
-		else 
-		{
-			LL_DEBUGS("AppInit") << "ALSA audio output SKIPPED" << LL_ENDL;
-		}
-	}
-	if (!audio_ok)
-	{
-		if (NULL == getenv("LL_BAD_FMOD_OSS")) 	 /*Flawfinder: ignore*/
-		{
-			LL_DEBUGS("AppInit") << "Trying OSS audio output..." << LL_ENDL;
-			if(mSystem->setOutput(FMOD_OUTPUTTYPE_OSS) == FMOD_OK &&
-			    (result = mSystem->init(num_channels + 2, fmod_flags, 0)) == FMOD_OK)
-			{
-				LL_DEBUGS("AppInit") << "OSS audio output initialized OKAY" << LL_ENDL;
-				audio_ok = true;
-			}
-			else
-			{
-				Check_FMOD_Error(result, "OSS audio output FAILED to initialize");
-			}
-		}
-		else 
-		{
-			LL_DEBUGS("AppInit") << "OSS audio output SKIPPED" << LL_ENDL;
-		}
-	}
-	if (!audio_ok)
-	{
-		LL_WARNS("AppInit") << "Overall audio init failure." << LL_ENDL;
-		return false;
-	}
-
-	// We're interested in logging which output method we
-	// ended up with, for QA purposes.
-	FMOD_OUTPUTTYPE output_type;
-	mSystem->getOutput(&output_type);
-	switch (output_type)
-	{
-		case FMOD_OUTPUTTYPE_NOSOUND: 
-			LL_INFOS("AppInit") << "Audio output: NoSound" << LL_ENDL; break;
-		case FMOD_OUTPUTTYPE_PULSEAUDIO:	
-			LL_INFOS("AppInit") << "Audio output: PulseAudio" << LL_ENDL; break;
-		case FMOD_OUTPUTTYPE_ALSA: 
-			LL_INFOS("AppInit") << "Audio output: ALSA" << LL_ENDL; break;
-		case FMOD_OUTPUTTYPE_OSS:	
-			LL_INFOS("AppInit") << "Audio output: OSS" << LL_ENDL; break;
-		default:
-			LL_INFOS("AppInit") << "Audio output: Unknown!" << LL_ENDL; break;
-	};
-#else // LL_LINUX
-
-	// initialize the FMOD engine
-	result = mSystem->init( num_channels + 2, fmod_flags, 0);
-	if (result == FMOD_ERR_OUTPUT_CREATEBUFFER)
-	{
-		/*
-		Ok, the speaker mode selected isn't supported by this soundcard. Switch it
-		back to stereo...
-		*/
-		result = mSystem->setSpeakerMode(FMOD_SPEAKERMODE_STEREO);
-		Check_FMOD_Error(result,"Error falling back to stereo mode");
-		/*
-		... and re-init.
-		*/
-		result = mSystem->init( num_channels + 2, fmod_flags, 0);
-	}
-	if(Check_FMOD_Error(result, "Error initializing FMOD Ex"))
-		return false;
-#endif
-
-	// set up our favourite FMOD-native streaming audio implementation if none has already been added
-	if (!getStreamingAudioImpl()) // no existing implementation added
-		setStreamingAudioImpl(new LLStreamingAudio_FMODEX(mSystem));
-
-	LL_INFOS("AppInit") << "LLAudioEngine_FMODEX::init() FMOD Ex initialized correctly" << LL_ENDL;
-
-	int r_numbuffers, r_samplerate, r_channels, r_bits;
-	unsigned int r_bufferlength;
-	mSystem->getDSPBufferSize(&r_bufferlength, &r_numbuffers);
-	LL_INFOS("AppInit") << "LLAudioEngine_FMODEX::init(): r_bufferlength=" << r_bufferlength << " bytes" << LL_ENDL;
-	LL_INFOS("AppInit") << "LLAudioEngine_FMODEX::init(): r_numbuffers=" << r_numbuffers << LL_ENDL;
-
-	mSystem->getSoftwareFormat(&r_samplerate, NULL, &r_channels, NULL, NULL, &r_bits);
-	LL_INFOS("AppInit") << "LLAudioEngine_FMODEX::init(): r_samplerate=" << r_samplerate << "Hz" << LL_ENDL;
-	LL_INFOS("AppInit") << "LLAudioEngine_FMODEX::init(): r_channels=" << r_channels << LL_ENDL;
-	LL_INFOS("AppInit") << "LLAudioEngine_FMODEX::init(): r_bits =" << r_bits << LL_ENDL;
-
-	char r_name[512];
-	mSystem->getDriverInfo(0, r_name, 511, 0);
-	r_name[511] = '\0';
-	LL_INFOS("AppInit") << "LLAudioEngine_FMODEX::init(): r_name=\"" << r_name << "\"" <<  LL_ENDL;
-
-	int latency = 100; // optimistic default - i suspect if sample rate is 0, everything breaks. 
-	if ( r_samplerate != 0 )
-		latency = (int)(1000.0f * r_bufferlength * r_numbuffers / r_samplerate);
-	LL_INFOS("AppInit") << "LLAudioEngine_FMODEX::init(): latency=" << latency << "ms" << LL_ENDL;
-
-	mInited = true;
-
-	LL_INFOS("AppInit") << "LLAudioEngine_FMODEX::init(): initialization complete." << LL_ENDL;
-
-	return true;
-}
-
-
-std::string LLAudioEngine_FMODEX::getDriverName(bool verbose)
-{
-	llassert_always(mSystem);
-	if (verbose)
-	{
-		U32 version;
-		if(!Check_FMOD_Error(mSystem->getVersion(&version), "FMOD::System::getVersion"))
-		{
-			return llformat("FMOD Ex %1x.%02x.%02x", version >> 16, version >> 8 & 0x000000FF, version & 0x000000FF);
-		}
-	}
-	return "FMODEx";
-}
-
-
-void LLAudioEngine_FMODEX::allocateListener(void)
-{	
-	mListenerp = (LLListener *) new LLListener_FMODEX(mSystem);
-	if (!mListenerp)
-	{
-		LL_WARNS() << "Listener creation failed" << LL_ENDL;
-	}
-}
-
-
-void LLAudioEngine_FMODEX::shutdown()
-{
-	stopInternetStream();
-
-	LL_INFOS() << "About to LLAudioEngine::shutdown()" << LL_ENDL;
-	LLAudioEngine::shutdown();
-	
-	LL_INFOS() << "LLAudioEngine_FMODEX::shutdown() closing FMOD Ex" << LL_ENDL;
-	if ( mSystem ) // speculative fix for MAINT-2657
-	{
-		mSystem->close();
-		mSystem->release();
-	}
-	LL_INFOS() << "LLAudioEngine_FMODEX::shutdown() done closing FMOD Ex" << LL_ENDL;
-
-	delete mListenerp;
-	mListenerp = NULL;
-}
-
-
-LLAudioBuffer * LLAudioEngine_FMODEX::createBuffer()
-{
-	return new LLAudioBufferFMODEX(mSystem);
-}
-
-
-LLAudioChannel * LLAudioEngine_FMODEX::createChannel()
-{
-	return new LLAudioChannelFMODEX(mSystem);
-}
-
-bool LLAudioEngine_FMODEX::initWind()
-{
-	mNextWindUpdate = 0.0;
-
-	if (!mWindDSP)
-	{
-		memset(mWindDSPDesc, 0, sizeof(*mWindDSPDesc));	//Set everything to zero
-		strncpy(mWindDSPDesc->name, "Wind Unit", sizeof(mWindDSPDesc->name));
-		mWindDSPDesc->channels = 2;
-		mWindDSPDesc->read = &windCallback; // Assign callback - may be called from arbitrary threads
-		if (Check_FMOD_Error(mSystem->createDSP(mWindDSPDesc, &mWindDSP), "FMOD::createDSP"))
-			return false;
-
-		if (mWindGen)
-			delete mWindGen;
-	
-		float frequency = 44100;
-		mWindDSP->getDefaults(&frequency,0,0,0);
-		mWindGen = new LLWindGen<MIXBUFFERFORMAT>((U32)frequency);
-		mWindDSP->setUserData((void*)mWindGen);
-	}
-
-	// *TODO:  Should this guard against multiple plays?
-	if (mWindDSP)
-	{
-		mSystem->playDSP(FMOD_CHANNEL_FREE, mWindDSP, false, 0);
-		return true;
-	}
-	return false;
-}
-
-
-void LLAudioEngine_FMODEX::cleanupWind()
-{
-	if (mWindDSP)
-	{
-		mWindDSP->remove();
-		mWindDSP->release();
-		mWindDSP = NULL;
-	}
-
-	delete mWindGen;
-	mWindGen = NULL;
-}
-
-
-//-----------------------------------------------------------------------
-void LLAudioEngine_FMODEX::updateWind(LLVector3 wind_vec, F32 camera_height_above_water)
-{
-	LLVector3 wind_pos;
-	F64 pitch;
-	F64 center_freq;
-
-	if (!mEnableWind)
-	{
-		return;
-	}
-	
-	if (mWindUpdateTimer.checkExpirationAndReset(LL_WIND_UPDATE_INTERVAL))
-	{
-		
-		// wind comes in as Linden coordinate (+X = forward, +Y = left, +Z = up)
-		// need to convert this to the conventional orientation DS3D and OpenAL use
-		// where +X = right, +Y = up, +Z = backwards
-
-		wind_vec.setVec(-wind_vec.mV[1], wind_vec.mV[2], -wind_vec.mV[0]);
-
-		// cerr << "Wind update" << endl;
-
-		pitch = 1.0 + mapWindVecToPitch(wind_vec);
-		center_freq = 80.0 * pow(pitch,2.5*(mapWindVecToGain(wind_vec)+1.0));
-		
-		mWindGen->mTargetFreq = (F32)center_freq;
-		mWindGen->mTargetGain = (F32)mapWindVecToGain(wind_vec) * mMaxWindGain;
-		mWindGen->mTargetPanGainR = (F32)mapWindVecToPan(wind_vec);
-  	}
-}
-
-//-----------------------------------------------------------------------
-void LLAudioEngine_FMODEX::setInternalGain(F32 gain)
-{
-	if (!mInited)
-	{
-		return;
-	}
-
-	gain = llclamp( gain, 0.0f, 1.0f );
-
-	FMOD::ChannelGroup *master_group;
-	mSystem->getMasterChannelGroup(&master_group);
-
-	master_group->setVolume(gain);
-
-	LLStreamingAudioInterface *saimpl = getStreamingAudioImpl();
-	if ( saimpl )
-	{
-		// fmod likes its streaming audio channel gain re-asserted after
-		// master volume change.
-		saimpl->setGain(saimpl->getGain());
-	}
-}
-
-//
-// LLAudioChannelFMODEX implementation
-//
-
-LLAudioChannelFMODEX::LLAudioChannelFMODEX(FMOD::System *system) : LLAudioChannel(), mSystemp(system), mChannelp(NULL), mLastSamplePos(0)
-{
-}
-
-
-LLAudioChannelFMODEX::~LLAudioChannelFMODEX()
-{
-	cleanup();
-}
-
-bool LLAudioChannelFMODEX::updateBuffer()
-{
-	if (LLAudioChannel::updateBuffer())
-	{
-		// Base class update returned true, which means that we need to actually
-		// set up the channel for a different buffer.
-
-		LLAudioBufferFMODEX *bufferp = (LLAudioBufferFMODEX *)mCurrentSourcep->getCurrentBuffer();
-
-		// Grab the FMOD sample associated with the buffer
-		FMOD::Sound *soundp = bufferp->getSound();
-		if (!soundp)
-		{
-			// This is bad, there should ALWAYS be a sound associated with a legit
-			// buffer.
-			LL_ERRS() << "No FMOD sound!" << LL_ENDL;
-			return false;
-		}
-
-
-		// Actually play the sound.  Start it off paused so we can do all the necessary
-		// setup.
-		if(!mChannelp)
-		{
-			FMOD_RESULT result = getSystem()->playSound(FMOD_CHANNEL_FREE, soundp, true, &mChannelp);
-			Check_FMOD_Error(result, "FMOD::System::playSound");
-		}
-
-		//LL_INFOS() << "Setting up channel " << std::hex << mChannelID << std::dec << LL_ENDL;
-	}
-
-	// If we have a source for the channel, we need to update its gain.
-	if (mCurrentSourcep)
-	{
-		// SJB: warnings can spam and hurt framerate, disabling
-		//FMOD_RESULT result;
-
-		mChannelp->setVolume(getSecondaryGain() * mCurrentSourcep->getGain());
-		//Check_FMOD_Error(result, "FMOD::Channel::setVolume");
-
-		mChannelp->setMode(mCurrentSourcep->isLoop() ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF);
-		/*if(Check_FMOD_Error(result, "FMOD::Channel::setMode"))
-		{
-			S32 index;
-			mChannelp->getIndex(&index);
- 			LL_WARNS() << "Channel " << index << "Source ID: " << mCurrentSourcep->getID()
- 					<< " at " << mCurrentSourcep->getPositionGlobal() << LL_ENDL;		
-		}*/
-	}
-
-	return true;
-}
-
-
-void LLAudioChannelFMODEX::update3DPosition()
-{
-	if (!mChannelp)
-	{
-		// We're not actually a live channel (i.e., we're not playing back anything)
-		return;
-	}
-
-	LLAudioBufferFMODEX  *bufferp = (LLAudioBufferFMODEX  *)mCurrentBufferp;
-	if (!bufferp)
-	{
-		// We don't have a buffer associated with us (should really have been picked up
-		// by the above if.
-		return;
-	}
-
-	if (mCurrentSourcep->isAmbient())
-	{
-		// Ambient sound, don't need to do any positional updates.
-		set3DMode(false);
-	}
-	else
-	{
-		// Localized sound.  Update the position and velocity of the sound.
-		set3DMode(true);
-
-		LLVector3 float_pos;
-		float_pos.setVec(mCurrentSourcep->getPositionGlobal());
-		FMOD_RESULT result = mChannelp->set3DAttributes((FMOD_VECTOR*)float_pos.mV, (FMOD_VECTOR*)mCurrentSourcep->getVelocity().mV);
-		Check_FMOD_Error(result, "FMOD::Channel::set3DAttributes");
-	}
-}
-
-
-void LLAudioChannelFMODEX::updateLoop()
-{
-	if (!mChannelp)
-	{
-		// May want to clear up the loop/sample counters.
-		return;
-	}
-
-	//
-	// Hack:  We keep track of whether we looped or not by seeing when the
-	// sample position looks like it's going backwards.  Not reliable; may
-	// yield false negatives.
-	//
-	U32 cur_pos;
-	mChannelp->getPosition(&cur_pos,FMOD_TIMEUNIT_PCMBYTES);
-
-	if (cur_pos < (U32)mLastSamplePos)
-	{
-		mLoopedThisFrame = true;
-	}
-	mLastSamplePos = cur_pos;
-}
-
-
-void LLAudioChannelFMODEX::cleanup()
-{
-	if (!mChannelp)
-	{
-		//LL_INFOS() << "Aborting cleanup with no channel handle." << LL_ENDL;
-		return;
-	}
-
-	//LL_INFOS() << "Cleaning up channel: " << mChannelID << LL_ENDL;
-	Check_FMOD_Error(mChannelp->stop(),"FMOD::Channel::stop");
-
-	mCurrentBufferp = NULL;
-	mChannelp = NULL;
-}
-
-
-void LLAudioChannelFMODEX::play()
-{
-	if (!mChannelp)
-	{
-		LL_WARNS() << "Playing without a channel handle, aborting" << LL_ENDL;
-		return;
-	}
-
-	Check_FMOD_Error(mChannelp->setPaused(false), "FMOD::Channel::pause");
-
-	getSource()->setPlayedOnce(true);
-
-	if(LLAudioEngine_FMODEX::mChannelGroups[getSource()->getType()])
-		mChannelp->setChannelGroup(LLAudioEngine_FMODEX::mChannelGroups[getSource()->getType()]);
-}
-
-
-void LLAudioChannelFMODEX::playSynced(LLAudioChannel *channelp)
-{
-	LLAudioChannelFMODEX *fmod_channelp = (LLAudioChannelFMODEX*)channelp;
-	if (!(fmod_channelp->mChannelp && mChannelp))
-	{
-		// Don't have channels allocated to both the master and the slave
-		return;
-	}
-
-	U32 cur_pos;
-	if(Check_FMOD_Error(mChannelp->getPosition(&cur_pos,FMOD_TIMEUNIT_PCMBYTES), "Unable to retrieve current position"))
-		return;
-
-	cur_pos %= mCurrentBufferp->getLength();
-	
-	// Try to match the position of our sync master
-	Check_FMOD_Error(mChannelp->setPosition(cur_pos,FMOD_TIMEUNIT_PCMBYTES),"Unable to set current position");
-
-	// Start us playing
-	play();
-}
-
-
-bool LLAudioChannelFMODEX::isPlaying()
-{
-	if (!mChannelp)
-	{
-		return false;
-	}
-
-	bool paused, playing;
-	mChannelp->getPaused(&paused);
-	mChannelp->isPlaying(&playing);
-	return !paused && playing;
-}
-
-
-//
-// LLAudioChannelFMODEX implementation
-//
-
-
-LLAudioBufferFMODEX::LLAudioBufferFMODEX(FMOD::System *system) : mSystemp(system), mSoundp(NULL)
-{
-}
-
-
-LLAudioBufferFMODEX::~LLAudioBufferFMODEX()
-{
-	if(mSoundp)
-	{
-		mSoundp->release();
-		mSoundp = NULL;
-	}
-}
-
-
-bool LLAudioBufferFMODEX::loadWAV(const std::string& filename)
-{
-	// Try to open a wav file from disk.  This will eventually go away, as we don't
-	// really want to block doing this.
-	if (filename.empty())
-	{
-		// invalid filename, abort.
-		return false;
-	}
-
-	if (!LLAPRFile::isExist(filename, NULL, LL_APR_RPB))
-	{
-		// File not found, abort.
-		return false;
-	}
-	
-	if (mSoundp)
-	{
-		// If there's already something loaded in this buffer, clean it up.
-		mSoundp->release();
-		mSoundp = NULL;
-	}
-
-	FMOD_MODE base_mode = FMOD_LOOP_NORMAL | FMOD_SOFTWARE;
-	FMOD_CREATESOUNDEXINFO exinfo;
-	memset(&exinfo,0,sizeof(exinfo));
-	exinfo.cbsize = sizeof(exinfo);
-	exinfo.suggestedsoundtype = FMOD_SOUND_TYPE_WAV;	//Hint to speed up loading.
-	// Load up the wav file into an fmod sample
-#if LL_WINDOWS
-	FMOD_RESULT result = getSystem()->createSound((const char*)utf8str_to_utf16str(filename).c_str(), base_mode | FMOD_UNICODE, &exinfo, &mSoundp);
-#else
-	FMOD_RESULT result = getSystem()->createSound(filename.c_str(), base_mode, &exinfo, &mSoundp);
-#endif
-
-	if (result != FMOD_OK)
-	{
-		// We failed to load the file for some reason.
-		LL_WARNS() << "Could not load data '" << filename << "': " << FMOD_ErrorString(result) << LL_ENDL;
-
-		//
-		// If we EVER want to load wav files provided by end users, we need
-		// to rethink this!
-		//
-		// file is probably corrupt - remove it.
-		LLFile::remove(filename);
-		return false;
-	}
-
-	// Everything went well, return true
-	return true;
-}
-
-
-U32 LLAudioBufferFMODEX::getLength()
-{
-	if (!mSoundp)
-	{
-		return 0;
-	}
-
-	U32 length;
-	mSoundp->getLength(&length, FMOD_TIMEUNIT_PCMBYTES);
-	return length;
-}
-
-
-void LLAudioChannelFMODEX::set3DMode(bool use3d)
-{
-	FMOD_MODE current_mode;
-	if(mChannelp->getMode(&current_mode) != FMOD_OK)
-		return;
-	FMOD_MODE new_mode = current_mode;	
-	new_mode &= ~(use3d ? FMOD_2D : FMOD_3D);
-	new_mode |= use3d ? FMOD_3D : FMOD_2D;
-
-	if(current_mode != new_mode)
-	{
-		mChannelp->setMode(new_mode);
-	}
-}
-
-// *NOTE:  This is almost certainly being called on the mixer thread,
-// not the main thread.  May have implications for callees or audio
-// engine shutdown.
-
-FMOD_RESULT F_CALLBACK windCallback(FMOD_DSP_STATE *dsp_state, float *originalbuffer, float *newbuffer, unsigned int length, int inchannels, int outchannels)
-{
-	// originalbuffer = fmod's original mixbuffer.
-	// newbuffer = the buffer passed from the previous DSP unit.
-	// length = length in samples at this mix time.
-	// userdata = user parameter passed through in FSOUND_DSP_Create.
-	
-	LLWindGen<LLAudioEngine_FMODEX::MIXBUFFERFORMAT> *windgen;
-	FMOD::DSP *thisdsp = (FMOD::DSP *)dsp_state->instance;
-
-	thisdsp->getUserData((void **)&windgen);
-	S32 channels, configwidth, configheight;
-	thisdsp->getInfo(0, 0, &channels, &configwidth, &configheight);
-	
-	windgen->windGenerate((LLAudioEngine_FMODEX::MIXBUFFERFORMAT *)newbuffer, length);
-
-	return FMOD_OK;
-}
diff --git a/indra/llaudio/llaudioengine_fmodstudio.cpp b/indra/llaudio/llaudioengine_fmodstudio.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..70b3a0847388a0b7639e1501a62a8bb8980ad16d
--- /dev/null
+++ b/indra/llaudio/llaudioengine_fmodstudio.cpp
@@ -0,0 +1,753 @@
+/** 
+ * @file audioengine_fmodstudio.cpp
+ * @brief Implementation of LLAudioEngine class abstracting the audio 
+ * support as a FMODSTUDIO implementation
+ *
+ * $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 "linden_common.h"
+
+#include "llstreamingaudio.h"
+#include "llstreamingaudio_fmodstudio.h"
+
+#include "llaudioengine_fmodstudio.h"
+#include "lllistener_fmodstudio.h"
+
+#include "llerror.h"
+#include "llmath.h"
+#include "llrand.h"
+
+#include "fmodstudio/fmod.hpp"
+#include "fmodstudio/fmod_errors.h"
+#include "lldir.h"
+#include "llapr.h"
+
+#include "sound_ids.h"
+
+FMOD_RESULT F_CALLBACK windCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int *outchannels);
+
+FMOD::ChannelGroup *LLAudioEngine_FMODSTUDIO::mChannelGroups[LLAudioEngine::AUDIO_TYPE_COUNT] = {0};
+
+LLAudioEngine_FMODSTUDIO::LLAudioEngine_FMODSTUDIO(bool enable_profiler)
+:   mInited(false),
+    mWindGen(NULL),
+    mWindDSP(NULL),
+    mSystem(NULL),
+    mEnableProfiler(enable_profiler),
+    mWindDSPDesc(NULL)
+{
+}
+
+
+LLAudioEngine_FMODSTUDIO::~LLAudioEngine_FMODSTUDIO()
+{
+    // mWindDSPDesc, mWindGen and mWindDSP get cleaned up on cleanupWind in LLAudioEngine::shutdown()
+    // mSystem gets cleaned up at shutdown()
+}
+
+
+static inline bool Check_FMOD_Error(FMOD_RESULT result, const char *string)
+{
+    if (result == FMOD_OK)
+        return false;
+    LL_DEBUGS("FMOD") << string << " Error: " << FMOD_ErrorString(result) << LL_ENDL;
+    return true;
+}
+
+bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, const std::string &app_title)
+{
+    U32 version;
+    FMOD_RESULT result;
+
+    LL_DEBUGS("AppInit") << "LLAudioEngine_FMODSTUDIO::init() initializing FMOD" << LL_ENDL;
+
+    result = FMOD::System_Create(&mSystem);
+    if (Check_FMOD_Error(result, "FMOD::System_Create"))
+        return false;
+
+    //will call LLAudioEngine_FMODSTUDIO::allocateListener, which needs a valid mSystem pointer.
+    LLAudioEngine::init(num_channels, userdata, app_title);
+
+    result = mSystem->getVersion(&version);
+    Check_FMOD_Error(result, "FMOD::System::getVersion");
+
+    if (version < FMOD_VERSION)
+    {
+        LL_WARNS("AppInit") << "FMOD Studio version mismatch, actual: " << version
+            << " expected:" << FMOD_VERSION << LL_ENDL;
+    }
+
+    // In this case, all sounds, PLUS wind and stream will be software.
+    result = mSystem->setSoftwareChannels(num_channels + 2);
+    Check_FMOD_Error(result, "FMOD::System::setSoftwareChannels");
+
+    FMOD_ADVANCEDSETTINGS settings;
+    memset(&settings, 0, sizeof(settings));
+    settings.cbSize = sizeof(FMOD_ADVANCEDSETTINGS);
+    settings.resamplerMethod = FMOD_DSP_RESAMPLER_LINEAR;
+
+    result = mSystem->setAdvancedSettings(&settings);
+    Check_FMOD_Error(result, "FMOD::System::setAdvancedSettings");
+
+    // FMOD_INIT_THREAD_UNSAFE Disables thread safety for API calls.
+    // Only use this if FMOD is being called from a single thread, and if Studio API is not being used.
+    U32 fmod_flags = FMOD_INIT_NORMAL | FMOD_INIT_3D_RIGHTHANDED | FMOD_INIT_THREAD_UNSAFE;
+    if (mEnableProfiler)
+    {
+        fmod_flags |= FMOD_INIT_PROFILE_ENABLE;
+    }
+
+#if LL_LINUX
+    bool audio_ok = false;
+
+    if (!audio_ok)
+    {
+        const char* env_string = getenv("LL_BAD_FMOD_PULSEAUDIO");
+        if (NULL == env_string)
+        {
+            LL_DEBUGS("AppInit") << "Trying PulseAudio audio output..." << LL_ENDL;
+            if (mSystem->setOutput(FMOD_OUTPUTTYPE_PULSEAUDIO) == FMOD_OK &&
+                (result = mSystem->init(num_channels + 2, fmod_flags, const_cast<char*>(app_title.c_str()))) == FMOD_OK)
+            {
+                LL_DEBUGS("AppInit") << "PulseAudio output initialized OKAY" << LL_ENDL;
+                audio_ok = true;
+            }
+            else
+            {
+                Check_FMOD_Error(result, "PulseAudio audio output FAILED to initialize");
+            }
+        }
+        else
+        {
+            LL_DEBUGS("AppInit") << "PulseAudio audio output SKIPPED" << LL_ENDL;
+        }
+    }
+    if (!audio_ok)
+    {
+        const char* env_string = getenv("LL_BAD_FMOD_ALSA");
+        if (NULL == env_string)
+        {
+            LL_DEBUGS("AppInit") << "Trying ALSA audio output..." << LL_ENDL;
+            if (mSystem->setOutput(FMOD_OUTPUTTYPE_ALSA) == FMOD_OK &&
+                (result = mSystem->init(num_channels + 2, fmod_flags, 0)) == FMOD_OK)
+            {
+                LL_DEBUGS("AppInit") << "ALSA audio output initialized OKAY" << LL_ENDL;
+                audio_ok = true;
+            }
+            else
+            {
+                Check_FMOD_Error(result, "ALSA audio output FAILED to initialize");
+            }
+        }
+        else
+        {
+            LL_DEBUGS("AppInit") << "ALSA audio output SKIPPED" << LL_ENDL;
+        }
+    }
+    if (!audio_ok)
+    {
+        LL_WARNS("AppInit") << "Overall audio init failure." << LL_ENDL;
+        return false;
+    }
+
+    // We're interested in logging which output method we
+    // ended up with, for QA purposes.
+    FMOD_OUTPUTTYPE output_type;
+    mSystem->getOutput(&output_type);
+    switch (output_type)
+    {
+    case FMOD_OUTPUTTYPE_NOSOUND:
+        LL_INFOS("AppInit") << "Audio output: NoSound" << LL_ENDL; break;
+    case FMOD_OUTPUTTYPE_PULSEAUDIO:
+        LL_INFOS("AppInit") << "Audio output: PulseAudio" << LL_ENDL; break;
+    case FMOD_OUTPUTTYPE_ALSA:
+        LL_INFOS("AppInit") << "Audio output: ALSA" << LL_ENDL; break;
+    default:
+        LL_INFOS("AppInit") << "Audio output: Unknown!" << LL_ENDL; break;
+    };
+#else // LL_LINUX
+
+    // initialize the FMOD engine
+    // number of channel in this case looks to be identiacal to number of max simultaneously
+    // playing objects and we can set practically any number
+    result = mSystem->init(num_channels + 2, fmod_flags, 0);
+    if (Check_FMOD_Error(result, "Error initializing FMOD Studio with default settins, retrying with other format"))
+    {
+        result = mSystem->setSoftwareFormat(44100, FMOD_SPEAKERMODE_STEREO, 0/*- ignore*/);
+        if (Check_FMOD_Error(result, "Error setting sotware format. Can't init."))
+        {
+            return false;
+        }
+        result = mSystem->init(num_channels + 2, fmod_flags, 0);
+    }
+    if (Check_FMOD_Error(result, "Error initializing FMOD Studio"))
+    {
+        // If it fails here and (result == FMOD_ERR_OUTPUT_CREATEBUFFER),
+        // we can retry with other settings
+        return false;
+    }
+#endif
+
+    // set up our favourite FMOD-native streaming audio implementation if none has already been added
+    if (!getStreamingAudioImpl()) // no existing implementation added
+        setStreamingAudioImpl(new LLStreamingAudio_FMODSTUDIO(mSystem));
+
+    LL_INFOS("AppInit") << "LLAudioEngine_FMODSTUDIO::init() FMOD Studio initialized correctly" << LL_ENDL;
+
+    int r_numbuffers, r_samplerate, r_channels;
+    unsigned int r_bufferlength;
+    char r_name[512];
+    int latency = 100;
+    mSystem->getDSPBufferSize(&r_bufferlength, &r_numbuffers);
+    LL_INFOS("AppInit") << "LLAudioEngine_FMODSTUDIO::init(): r_bufferlength=" << r_bufferlength << " bytes" << LL_ENDL;
+    LL_INFOS("AppInit") << "LLAudioEngine_FMODSTUDIO::init(): r_numbuffers=" << r_numbuffers << LL_ENDL;
+
+    mSystem->getDriverInfo(0, r_name, 511, NULL, &r_samplerate, NULL, &r_channels);
+    r_name[511] = '\0';
+    LL_INFOS("AppInit") << "LLAudioEngine_FMODSTUDIO::init(): r_name=\"" << r_name << "\"" << LL_ENDL;
+
+    if (r_samplerate != 0)
+        latency = (int)(1000.0f * r_bufferlength * r_numbuffers / r_samplerate);
+    LL_INFOS("AppInit") << "LLAudioEngine_FMODSTUDIO::init(): latency=" << latency << "ms" << LL_ENDL;
+
+    mInited = true;
+
+    LL_INFOS("AppInit") << "LLAudioEngine_FMODSTUDIO::init(): initialization complete." << LL_ENDL;
+
+    return true;
+}
+
+
+std::string LLAudioEngine_FMODSTUDIO::getDriverName(bool verbose)
+{
+    llassert_always(mSystem);
+    if (verbose)
+    {
+        U32 version;
+        if (!Check_FMOD_Error(mSystem->getVersion(&version), "FMOD::System::getVersion"))
+        {
+            return llformat("FMOD Studio %1x.%02x.%02x", version >> 16, version >> 8 & 0x000000FF, version & 0x000000FF);
+        }
+    }
+    return "FMOD STUDIO";
+}
+
+
+void LLAudioEngine_FMODSTUDIO::allocateListener(void)
+{
+    mListenerp = (LLListener *) new LLListener_FMODSTUDIO(mSystem);
+    if (!mListenerp)
+    {
+        LL_WARNS("FMOD") << "Listener creation failed" << LL_ENDL;
+    }
+}
+
+
+void LLAudioEngine_FMODSTUDIO::shutdown()
+{
+    stopInternetStream();
+
+    LL_INFOS("FMOD") << "About to LLAudioEngine::shutdown()" << LL_ENDL;
+    LLAudioEngine::shutdown();
+
+    LL_INFOS("FMOD") << "LLAudioEngine_FMODSTUDIO::shutdown() closing FMOD Studio" << LL_ENDL;
+    if (mSystem)
+    {
+        mSystem->close();
+        mSystem->release();
+    }
+    LL_INFOS("FMOD") << "LLAudioEngine_FMODSTUDIO::shutdown() done closing FMOD Studio" << LL_ENDL;
+
+    delete mListenerp;
+    mListenerp = NULL;
+}
+
+
+LLAudioBuffer * LLAudioEngine_FMODSTUDIO::createBuffer()
+{
+    return new LLAudioBufferFMODSTUDIO(mSystem);
+}
+
+
+LLAudioChannel * LLAudioEngine_FMODSTUDIO::createChannel()
+{
+    return new LLAudioChannelFMODSTUDIO(mSystem);
+}
+
+bool LLAudioEngine_FMODSTUDIO::initWind()
+{
+    mNextWindUpdate = 0.0;
+
+    if (!mWindDSPDesc)
+    {
+        mWindDSPDesc = new FMOD_DSP_DESCRIPTION();
+    }
+
+    if (!mWindDSP)
+    {
+        memset(mWindDSPDesc, 0, sizeof(*mWindDSPDesc));	//Set everything to zero
+        strncpy(mWindDSPDesc->name, "Wind Unit", sizeof(mWindDSPDesc->name));
+        mWindDSPDesc->pluginsdkversion = FMOD_PLUGIN_SDK_VERSION;
+        mWindDSPDesc->read = &windCallback; // Assign callback - may be called from arbitrary threads
+        if (Check_FMOD_Error(mSystem->createDSP(mWindDSPDesc, &mWindDSP), "FMOD::createDSP"))
+            return false;
+
+        if (mWindGen)
+            delete mWindGen;
+
+        int frequency = 44100;
+
+        FMOD_SPEAKERMODE mode;
+        if (Check_FMOD_Error(mSystem->getSoftwareFormat(&frequency, &mode, nullptr), "FMOD::System::getSoftwareFormat"))
+        {
+            cleanupWind();
+            return false;
+        }
+
+        mWindGen = new LLWindGen<MIXBUFFERFORMAT>((U32)frequency);
+
+        if (Check_FMOD_Error(mWindDSP->setUserData((void*)mWindGen), "FMOD::DSP::setUserData"))
+        {
+            cleanupWind();
+            return false;
+        }
+        if (Check_FMOD_Error(mWindDSP->setChannelFormat(FMOD_CHANNELMASK_STEREO, 2, mode), "FMOD::DSP::setChannelFormat"))
+        {
+            cleanupWind();
+            return false;
+        }
+    }
+
+    // *TODO:  Should this guard against multiple plays?
+    if (Check_FMOD_Error(mSystem->playDSP(mWindDSP, nullptr, false, nullptr), "FMOD::System::playDSP"))
+    {
+        cleanupWind();
+        return false;
+    }
+    return true;
+}
+
+
+void LLAudioEngine_FMODSTUDIO::cleanupWind()
+{
+    if (mWindDSP)
+    {
+        FMOD::ChannelGroup* master_group = NULL;
+        if (!Check_FMOD_Error(mSystem->getMasterChannelGroup(&master_group), "FMOD::System::getMasterChannelGroup")
+            && master_group)
+        {
+            master_group->removeDSP(mWindDSP);
+        }
+        mWindDSP->release();
+        mWindDSP = NULL;
+    }
+
+    delete mWindDSPDesc;
+    mWindDSPDesc = NULL;
+
+    delete mWindGen;
+    mWindGen = NULL;
+}
+
+
+//-----------------------------------------------------------------------
+void LLAudioEngine_FMODSTUDIO::updateWind(LLVector3 wind_vec, F32 camera_height_above_water)
+{
+    LLVector3 wind_pos;
+    F64 pitch;
+    F64 center_freq;
+
+    if (!mEnableWind)
+    {
+        return;
+    }
+
+    if (mWindUpdateTimer.checkExpirationAndReset(LL_WIND_UPDATE_INTERVAL))
+    {
+
+        // wind comes in as Linden coordinate (+X = forward, +Y = left, +Z = up)
+        // need to convert this to the conventional orientation DS3D and OpenAL use
+        // where +X = right, +Y = up, +Z = backwards
+
+        wind_vec.setVec(-wind_vec.mV[1], wind_vec.mV[2], -wind_vec.mV[0]);
+
+        // cerr << "Wind update" << endl;
+
+        pitch = 1.0 + mapWindVecToPitch(wind_vec);
+        center_freq = 80.0 * pow(pitch, 2.5*(mapWindVecToGain(wind_vec) + 1.0));
+
+        mWindGen->mTargetFreq = (F32)center_freq;
+        mWindGen->mTargetGain = (F32)mapWindVecToGain(wind_vec) * mMaxWindGain;
+        mWindGen->mTargetPanGainR = (F32)mapWindVecToPan(wind_vec);
+    }
+}
+
+//-----------------------------------------------------------------------
+void LLAudioEngine_FMODSTUDIO::setInternalGain(F32 gain)
+{
+    if (!mInited)
+    {
+        return;
+    }
+
+    gain = llclamp(gain, 0.0f, 1.0f);
+
+    FMOD::ChannelGroup* master_group = NULL;
+    if (!Check_FMOD_Error(mSystem->getMasterChannelGroup(&master_group), "FMOD::System::getMasterChannelGroup")
+        && master_group)
+    {
+        master_group->setVolume(gain);
+    }
+
+    LLStreamingAudioInterface *saimpl = getStreamingAudioImpl();
+    if (saimpl)
+    {
+        // fmod likes its streaming audio channel gain re-asserted after
+        // master volume change.
+        saimpl->setGain(saimpl->getGain());
+    }
+}
+
+//
+// LLAudioChannelFMODSTUDIO implementation
+//
+
+LLAudioChannelFMODSTUDIO::LLAudioChannelFMODSTUDIO(FMOD::System *system) : LLAudioChannel(), mSystemp(system), mChannelp(NULL), mLastSamplePos(0)
+{
+}
+
+
+LLAudioChannelFMODSTUDIO::~LLAudioChannelFMODSTUDIO()
+{
+    cleanup();
+}
+
+bool LLAudioChannelFMODSTUDIO::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
+        // set up the channel for a different buffer.
+
+        LLAudioBufferFMODSTUDIO *bufferp = (LLAudioBufferFMODSTUDIO *)mCurrentSourcep->getCurrentBuffer();
+
+        // Grab the FMOD sample associated with the buffer
+        FMOD::Sound *soundp = bufferp->getSound();
+        if (!soundp)
+        {
+            // This is bad, there should ALWAYS be a sound associated with a legit
+            // buffer.
+            LL_ERRS() << "No FMOD sound!" << LL_ENDL;
+            return false;
+        }
+
+
+        // Actually play the sound.  Start it off paused so we can do all the necessary
+        // setup.
+        if (!mChannelp)
+        {
+            FMOD_RESULT result = getSystem()->playSound(soundp, NULL /*free channel?*/, true, &mChannelp);
+            Check_FMOD_Error(result, "FMOD::System::playSound");
+        }
+
+        // Setting up channel mChannelID
+    }
+
+    // If we have a source for the channel, we need to update its gain.
+    if (mCurrentSourcep)
+    {
+        // SJB: warnings can spam and hurt framerate, disabling
+        //FMOD_RESULT result;
+
+        mChannelp->setVolume(getSecondaryGain() * mCurrentSourcep->getGain());
+        //Check_FMOD_Error(result, "FMOD::Channel::setVolume");
+
+        mChannelp->setMode(mCurrentSourcep->isLoop() ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF);
+        /*if(Check_FMOD_Error(result, "FMOD::Channel::setMode"))
+        {
+        S32 index;
+        mChannelp->getIndex(&index);
+        LL_WARNS() << "Channel " << index << "Source ID: " << mCurrentSourcep->getID()
+        << " at " << mCurrentSourcep->getPositionGlobal() << LL_ENDL;
+        }*/
+    }
+
+    return true;
+}
+
+
+void LLAudioChannelFMODSTUDIO::update3DPosition()
+{
+    if (!mChannelp)
+    {
+        // We're not actually a live channel (i.e., we're not playing back anything)
+        return;
+    }
+
+    LLAudioBufferFMODSTUDIO  *bufferp = (LLAudioBufferFMODSTUDIO  *)mCurrentBufferp;
+    if (!bufferp)
+    {
+        // We don't have a buffer associated with us (should really have been picked up
+        // by the above if.
+        return;
+    }
+
+    if (mCurrentSourcep->isAmbient())
+    {
+        // Ambient sound, don't need to do any positional updates.
+        set3DMode(false);
+    }
+    else
+    {
+        // Localized sound.  Update the position and velocity of the sound.
+        set3DMode(true);
+
+        LLVector3 float_pos;
+        float_pos.setVec(mCurrentSourcep->getPositionGlobal());
+        FMOD_RESULT result = mChannelp->set3DAttributes((FMOD_VECTOR*)float_pos.mV, (FMOD_VECTOR*)mCurrentSourcep->getVelocity().mV);
+        Check_FMOD_Error(result, "FMOD::Channel::set3DAttributes");
+    }
+}
+
+
+void LLAudioChannelFMODSTUDIO::updateLoop()
+{
+    if (!mChannelp)
+    {
+        // May want to clear up the loop/sample counters.
+        return;
+    }
+
+    //
+    // Hack:  We keep track of whether we looped or not by seeing when the
+    // sample position looks like it's going backwards.  Not reliable; may
+    // yield false negatives.
+    //
+    U32 cur_pos;
+    mChannelp->getPosition(&cur_pos, FMOD_TIMEUNIT_PCMBYTES);
+
+    if (cur_pos < (U32)mLastSamplePos)
+    {
+        mLoopedThisFrame = true;
+    }
+    mLastSamplePos = cur_pos;
+}
+
+
+void LLAudioChannelFMODSTUDIO::cleanup()
+{
+    if (!mChannelp)
+    {
+        // Aborting cleanup with no channel handle.
+        return;
+    }
+
+    //Cleaning up channel mChannelID
+    Check_FMOD_Error(mChannelp->stop(), "FMOD::Channel::stop");
+
+    mCurrentBufferp = NULL;
+    mChannelp = NULL;
+}
+
+
+void LLAudioChannelFMODSTUDIO::play()
+{
+    if (!mChannelp)
+    {
+        LL_WARNS() << "Playing without a channel handle, aborting" << LL_ENDL;
+        return;
+    }
+
+    Check_FMOD_Error(mChannelp->setPaused(false), "FMOD::Channel::pause");
+
+    getSource()->setPlayedOnce(true);
+
+    if (LLAudioEngine_FMODSTUDIO::mChannelGroups[getSource()->getType()])
+        mChannelp->setChannelGroup(LLAudioEngine_FMODSTUDIO::mChannelGroups[getSource()->getType()]);
+}
+
+
+void LLAudioChannelFMODSTUDIO::playSynced(LLAudioChannel *channelp)
+{
+    LLAudioChannelFMODSTUDIO *fmod_channelp = (LLAudioChannelFMODSTUDIO*)channelp;
+    if (!(fmod_channelp->mChannelp && mChannelp))
+    {
+        // Don't have channels allocated to both the master and the slave
+        return;
+    }
+
+    U32 cur_pos;
+    if (Check_FMOD_Error(mChannelp->getPosition(&cur_pos, FMOD_TIMEUNIT_PCMBYTES), "Unable to retrieve current position"))
+        return;
+
+    cur_pos %= mCurrentBufferp->getLength();
+
+    // Try to match the position of our sync master
+    Check_FMOD_Error(mChannelp->setPosition(cur_pos, FMOD_TIMEUNIT_PCMBYTES), "Unable to set current position");
+
+    // Start us playing
+    play();
+}
+
+
+bool LLAudioChannelFMODSTUDIO::isPlaying()
+{
+    if (!mChannelp)
+    {
+        return false;
+    }
+
+    bool paused, playing;
+    mChannelp->getPaused(&paused);
+    mChannelp->isPlaying(&playing);
+    return !paused && playing;
+}
+
+
+//
+// LLAudioChannelFMODSTUDIO implementation
+//
+
+
+LLAudioBufferFMODSTUDIO::LLAudioBufferFMODSTUDIO(FMOD::System *system) : mSystemp(system), mSoundp(NULL)
+{
+}
+
+
+LLAudioBufferFMODSTUDIO::~LLAudioBufferFMODSTUDIO()
+{
+    if (mSoundp)
+    {
+        mSoundp->release();
+        mSoundp = NULL;
+    }
+}
+
+
+bool LLAudioBufferFMODSTUDIO::loadWAV(const std::string& filename)
+{
+    // Try to open a wav file from disk.  This will eventually go away, as we don't
+    // really want to block doing this.
+    if (filename.empty())
+    {
+        // invalid filename, abort.
+        return false;
+    }
+
+    if (!LLAPRFile::isExist(filename, NULL, LL_APR_RPB))
+    {
+        // File not found, abort.
+        return false;
+    }
+
+    if (mSoundp)
+    {
+        // If there's already something loaded in this buffer, clean it up.
+        mSoundp->release();
+        mSoundp = NULL;
+    }
+
+    FMOD_MODE base_mode = FMOD_LOOP_NORMAL;
+    FMOD_CREATESOUNDEXINFO exinfo;
+    memset(&exinfo, 0, sizeof(exinfo));
+    exinfo.cbsize = sizeof(exinfo);
+    exinfo.suggestedsoundtype = FMOD_SOUND_TYPE_WAV;	//Hint to speed up loading.
+    // Load up the wav file into an fmod sample (since 1.05 fmod studio expects everything in UTF-8)
+    FMOD_RESULT result = getSystem()->createSound(filename.c_str(), base_mode, &exinfo, &mSoundp);
+
+    if (result != FMOD_OK)
+    {
+        // We failed to load the file for some reason.
+        LL_WARNS() << "Could not load data '" << filename << "': " << FMOD_ErrorString(result) << LL_ENDL;
+
+        //
+        // If we EVER want to load wav files provided by end users, we need
+        // to rethink this!
+        //
+        // file is probably corrupt - remove it.
+        LLFile::remove(filename);
+        return false;
+    }
+
+    // Everything went well, return true
+    return true;
+}
+
+
+U32 LLAudioBufferFMODSTUDIO::getLength()
+{
+    if (!mSoundp)
+    {
+        return 0;
+    }
+
+    U32 length;
+    mSoundp->getLength(&length, FMOD_TIMEUNIT_PCMBYTES);
+    return length;
+}
+
+
+void LLAudioChannelFMODSTUDIO::set3DMode(bool use3d)
+{
+    FMOD_MODE current_mode;
+    if (mChannelp->getMode(&current_mode) != FMOD_OK)
+        return;
+    FMOD_MODE new_mode = current_mode;
+    new_mode &= ~(use3d ? FMOD_2D : FMOD_3D);
+    new_mode |= use3d ? FMOD_3D : FMOD_2D;
+
+    if (current_mode != new_mode)
+    {
+        mChannelp->setMode(new_mode);
+    }
+}
+
+// *NOTE:  This is almost certainly being called on the mixer thread,
+// not the main thread.  May have implications for callees or audio
+// engine shutdown.
+
+FMOD_RESULT F_CALLBACK windCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int *outchannels)
+{
+    // inbuffer = fmod's original mixbuffer.
+    // outbuffer = the buffer passed from the previous DSP unit.
+    // length = length in samples at this mix time.
+
+    LLWindGen<LLAudioEngine_FMODSTUDIO::MIXBUFFERFORMAT> *windgen = NULL;
+    FMOD::DSP *thisdsp = (FMOD::DSP *)dsp_state->instance;
+
+    thisdsp->getUserData((void **)&windgen);
+
+    if (windgen)
+    {
+        windgen->windGenerate((LLAudioEngine_FMODSTUDIO::MIXBUFFERFORMAT *)outbuffer, length);
+    }
+
+    return FMOD_OK;
+}
diff --git a/indra/llaudio/llaudioengine_fmodex.h b/indra/llaudio/llaudioengine_fmodstudio.h
similarity index 75%
rename from indra/llaudio/llaudioengine_fmodex.h
rename to indra/llaudio/llaudioengine_fmodstudio.h
index ca389d489fd981359e666ae50b250668a7489c90..f2361df1b618005d5ab9f7db595d0353e394afe5 100644
--- a/indra/llaudio/llaudioengine_fmodex.h
+++ b/indra/llaudio/llaudioengine_fmodstudio.h
@@ -1,11 +1,11 @@
 /** 
- * @file audioengine_fmodex.h
+ * @file audioengine_fmodstudio.h
  * @brief Definition of LLAudioEngine class abstracting the audio 
- * support as a FMODEX implementation
+ * support as a FMODSTUDIO implementation
  *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * $LicenseInfo:firstyear=2020&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2014, 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
@@ -25,14 +25,14 @@
  * $/LicenseInfo$
  */
 
-#ifndef LL_AUDIOENGINE_FMODEX_H
-#define LL_AUDIOENGINE_FMODEX_H
+#ifndef LL_AUDIOENGINE_FMODSTUDIO_H
+#define LL_AUDIOENGINE_FMODSTUDIO_H
 
 #include "llaudioengine.h"
 #include "llwindgen.h"
 
 //Stubs
-class LLAudioStreamManagerFMODEX;
+class LLAudioStreamManagerFMODSTUDIO;
 namespace FMOD
 {
 	class System;
@@ -44,14 +44,14 @@ namespace FMOD
 typedef struct FMOD_DSP_DESCRIPTION FMOD_DSP_DESCRIPTION;
 
 //Interfaces
-class LLAudioEngine_FMODEX : public LLAudioEngine 
+class LLAudioEngine_FMODSTUDIO : public LLAudioEngine 
 {
 public:
-	LLAudioEngine_FMODEX(bool enable_profiler);
-	virtual ~LLAudioEngine_FMODEX();
+	LLAudioEngine_FMODSTUDIO(bool enable_profiler);
+	virtual ~LLAudioEngine_FMODSTUDIO();
 
 	// initialization/startup/shutdown
-	virtual bool init(const S32 num_channels, void *user_data);
+	virtual bool init(const S32 num_channels, void *user_data, const std::string &app_title);
 	virtual std::string getDriverName(bool verbose);
 	virtual void allocateListener();
 
@@ -85,11 +85,11 @@ class LLAudioEngine_FMODEX : public LLAudioEngine
 };
 
 
-class LLAudioChannelFMODEX : public LLAudioChannel
+class LLAudioChannelFMODSTUDIO : public LLAudioChannel
 {
 public:
-	LLAudioChannelFMODEX(FMOD::System *audioengine);
-	virtual ~LLAudioChannelFMODEX();
+    LLAudioChannelFMODSTUDIO(FMOD::System *audioengine);
+    virtual ~LLAudioChannelFMODSTUDIO();
 
 protected:
 	/*virtual*/ void play();
@@ -110,15 +110,15 @@ class LLAudioChannelFMODEX : public LLAudioChannel
 };
 
 
-class LLAudioBufferFMODEX : public LLAudioBuffer
+class LLAudioBufferFMODSTUDIO : public LLAudioBuffer
 {
 public:
-	LLAudioBufferFMODEX(FMOD::System *audioengine);
-	virtual ~LLAudioBufferFMODEX();
+    LLAudioBufferFMODSTUDIO(FMOD::System *audioengine);
+    virtual ~LLAudioBufferFMODSTUDIO();
 
 	/*virtual*/ bool loadWAV(const std::string& filename);
 	/*virtual*/ U32 getLength();
-	friend class LLAudioChannelFMODEX;
+	friend class LLAudioChannelFMODSTUDIO;
 protected:
 	FMOD::System *getSystem()	const {return mSystemp;}
 	FMOD::System *mSystemp;
@@ -127,4 +127,4 @@ class LLAudioBufferFMODEX : public LLAudioBuffer
 };
 
 
-#endif // LL_AUDIOENGINE_FMODEX_H
+#endif // LL_AUDIOENGINE_FMODSTUDIO_H
diff --git a/indra/llaudio/llaudioengine_openal.cpp b/indra/llaudio/llaudioengine_openal.cpp
index e6ac586618e3cb2855e76a3b308c2a6ab181a8c4..a38d8291fa900a3e907c17b186920858d2d33133 100644
--- a/indra/llaudio/llaudioengine_openal.cpp
+++ b/indra/llaudio/llaudioengine_openal.cpp
@@ -52,7 +52,7 @@ LLAudioEngine_OpenAL::~LLAudioEngine_OpenAL()
 }
 
 // virtual
-bool LLAudioEngine_OpenAL::init(const S32 num_channels, void* userdata)
+bool LLAudioEngine_OpenAL::init(const S32 num_channels, void* userdata, const std::string &app_title)
 {
 	mWindGen = NULL;
 	LLAudioEngine::init(num_channels, userdata);
@@ -239,6 +239,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 6639d9dfe648772a5b718c552284765463cffbcf..366f9259e31e825f139763ea04ff7290abe6f9a7 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();
 
-		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_fmodex.cpp b/indra/llaudio/lllistener_fmodex.cpp
deleted file mode 100644
index 31ab47a635f34d93fd1ee3e85676c785affa0e28..0000000000000000000000000000000000000000
--- a/indra/llaudio/lllistener_fmodex.cpp
+++ /dev/null
@@ -1,140 +0,0 @@
-/** 
- * @file listener_fmodex.cpp
- * @brief Implementation of LISTENER class abstracting the audio
- * support as a FMODEX implementation
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-#include "llaudioengine.h"
-#include "lllistener_fmodex.h"
-#include "fmod.hpp"
-
-//-----------------------------------------------------------------------
-// constructor
-//-----------------------------------------------------------------------
-LLListener_FMODEX::LLListener_FMODEX(FMOD::System *system)
-{
-	mSystem = system;
-	init();
-}
-
-//-----------------------------------------------------------------------
-LLListener_FMODEX::~LLListener_FMODEX()
-{
-}
-
-//-----------------------------------------------------------------------
-void LLListener_FMODEX::init(void)
-{
-	// do inherited
-	LLListener::init();
-	mDopplerFactor = 1.0f;
-	mRolloffFactor = 1.0f;
-}
-
-//-----------------------------------------------------------------------
-void LLListener_FMODEX::translate(LLVector3 offset)
-{
-	LLListener::translate(offset);
-
-	mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)mPosition.mV, NULL, (FMOD_VECTOR*)mListenAt.mV, (FMOD_VECTOR*)mListenUp.mV);
-}
-
-//-----------------------------------------------------------------------
-void LLListener_FMODEX::setPosition(LLVector3 pos)
-{
-	LLListener::setPosition(pos);
-
-	mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)mPosition.mV, NULL, (FMOD_VECTOR*)mListenAt.mV, (FMOD_VECTOR*)mListenUp.mV);
-}
-
-//-----------------------------------------------------------------------
-void LLListener_FMODEX::setVelocity(LLVector3 vel)
-{
-	LLListener::setVelocity(vel);
-
-	mSystem->set3DListenerAttributes(0, NULL, (FMOD_VECTOR*)mVelocity.mV, (FMOD_VECTOR*)mListenAt.mV, (FMOD_VECTOR*)mListenUp.mV);
-}
-
-//-----------------------------------------------------------------------
-void LLListener_FMODEX::orient(LLVector3 up, LLVector3 at)
-{
-	LLListener::orient(up, at);
-
-	// Welcome to the transition between right and left
-	// (coordinate systems, that is)
-	// Leaving the at vector alone results in a L/R reversal
-	// since DX is left-handed and we (LL, OpenGL, OpenAL) are right-handed
-	at = -at;
-
-	mSystem->set3DListenerAttributes(0, NULL, NULL, (FMOD_VECTOR*)at.mV, (FMOD_VECTOR*)up.mV);
-}
-
-//-----------------------------------------------------------------------
-void LLListener_FMODEX::commitDeferredChanges()
-{
-	if(!mSystem)
-	{
-		return;
-	}
-
-	mSystem->update();
-}
-
-
-void LLListener_FMODEX::setRolloffFactor(F32 factor)
-{
-	//An internal FMODEx optimization skips 3D updates if there have not been changes to the 3D sound environment.
-	//Sadly, a change in rolloff is not accounted for, thus we must touch the listener properties as well.
-	//In short: Changing the position ticks a dirtyflag inside fmodex, which makes it not skip 3D processing next update call.
-	if(mRolloffFactor != factor)
-	{
-		LLVector3 pos = mVelocity - LLVector3(0.f,0.f,.1f);
-		mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)pos.mV, NULL, NULL, NULL);
-		mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)mVelocity.mV, NULL, NULL, NULL);
-	}
-	mRolloffFactor = factor;
-	mSystem->set3DSettings(mDopplerFactor, 1.f, mRolloffFactor);
-}
-
-
-F32 LLListener_FMODEX::getRolloffFactor()
-{
-	return mRolloffFactor;
-}
-
-
-void LLListener_FMODEX::setDopplerFactor(F32 factor)
-{
-	mDopplerFactor = factor;
-	mSystem->set3DSettings(mDopplerFactor, 1.f, mRolloffFactor);
-}
-
-
-F32 LLListener_FMODEX::getDopplerFactor()
-{
-	return mDopplerFactor;
-}
-
-
diff --git a/indra/llaudio/lllistener_fmodstudio.cpp b/indra/llaudio/lllistener_fmodstudio.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..abd5e345b57c9c07ed0839596e911b916d49ef10
--- /dev/null
+++ b/indra/llaudio/lllistener_fmodstudio.cpp
@@ -0,0 +1,136 @@
+/** 
+ * @file listener_fmodstudio.cpp
+ * @brief Implementation of LISTENER class abstracting the audio
+ * support as a FMODSTUDIO implementation
+ *
+ * $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 "linden_common.h"
+#include "llaudioengine.h"
+#include "lllistener_fmodstudio.h"
+#include "fmodstudio/fmod.hpp"
+
+//-----------------------------------------------------------------------
+// constructor
+//-----------------------------------------------------------------------
+LLListener_FMODSTUDIO::LLListener_FMODSTUDIO(FMOD::System *system)
+{
+    mSystem = system;
+    init();
+}
+
+//-----------------------------------------------------------------------
+LLListener_FMODSTUDIO::~LLListener_FMODSTUDIO()
+{
+}
+
+//-----------------------------------------------------------------------
+void LLListener_FMODSTUDIO::init(void)
+{
+    // do inherited
+    LLListener::init();
+    mDopplerFactor = 1.0f;
+    mRolloffFactor = 1.0f;
+}
+
+//-----------------------------------------------------------------------
+void LLListener_FMODSTUDIO::translate(LLVector3 offset)
+{
+    LLListener::translate(offset);
+
+    mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)mPosition.mV, NULL, (FMOD_VECTOR*)mListenAt.mV, (FMOD_VECTOR*)mListenUp.mV);
+}
+
+//-----------------------------------------------------------------------
+void LLListener_FMODSTUDIO::setPosition(LLVector3 pos)
+{
+    LLListener::setPosition(pos);
+
+    mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)mPosition.mV, NULL, (FMOD_VECTOR*)mListenAt.mV, (FMOD_VECTOR*)mListenUp.mV);
+}
+
+//-----------------------------------------------------------------------
+void LLListener_FMODSTUDIO::setVelocity(LLVector3 vel)
+{
+    LLListener::setVelocity(vel);
+
+    mSystem->set3DListenerAttributes(0, NULL, (FMOD_VECTOR*)mVelocity.mV, (FMOD_VECTOR*)mListenAt.mV, (FMOD_VECTOR*)mListenUp.mV);
+}
+
+//-----------------------------------------------------------------------
+void LLListener_FMODSTUDIO::orient(LLVector3 up, LLVector3 at)
+{
+    LLListener::orient(up, at);
+
+    // at = -at; by default Fmod studio is 'left-handed' but we are providing
+    // flag FMOD_INIT_3D_RIGHTHANDED so no correction are needed
+
+    mSystem->set3DListenerAttributes(0, NULL, NULL, (FMOD_VECTOR*)at.mV, (FMOD_VECTOR*)up.mV);
+}
+
+//-----------------------------------------------------------------------
+void LLListener_FMODSTUDIO::commitDeferredChanges()
+{
+    if (!mSystem)
+    {
+        return;
+    }
+
+    mSystem->update();
+}
+
+
+void LLListener_FMODSTUDIO::setRolloffFactor(F32 factor)
+{
+    //An internal FMOD optimization skips 3D updates if there have not been changes to the 3D sound environment.
+    // (this was true for FMODex, looks to be still true for FMOD STUDIO, but needs a recheck)
+    //Sadly, a change in rolloff is not accounted for, thus we must touch the listener properties as well.
+    //In short: Changing the position ticks a dirtyflag inside fmod, which makes it not skip 3D processing next update call.
+    if (mRolloffFactor != factor)
+    {
+        LLVector3 pos = mPosition - LLVector3(0.f, 0.f, .1f);
+        mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)pos.mV, NULL, NULL, NULL);
+        mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)mPosition.mV, NULL, NULL, NULL);
+    }
+    mRolloffFactor = factor;
+    mSystem->set3DSettings(mDopplerFactor, 1.f, mRolloffFactor);
+}
+
+
+F32 LLListener_FMODSTUDIO::getRolloffFactor()
+{
+    return mRolloffFactor;
+}
+
+
+void LLListener_FMODSTUDIO::setDopplerFactor(F32 factor)
+{
+    mDopplerFactor = factor;
+    mSystem->set3DSettings(mDopplerFactor, 1.f, mRolloffFactor);
+}
+
+
+F32 LLListener_FMODSTUDIO::getDopplerFactor()
+{
+    return mDopplerFactor;
+}
diff --git a/indra/llaudio/lllistener_fmodex.h b/indra/llaudio/lllistener_fmodstudio.h
similarity index 53%
rename from indra/llaudio/lllistener_fmodex.h
rename to indra/llaudio/lllistener_fmodstudio.h
index 073b65d53adc5c68f5f486f3e2d3bc68cbdea5b0..6ad85d9700ca161fdebe67d3023fe544808a5474 100644
--- a/indra/llaudio/lllistener_fmodex.h
+++ b/indra/llaudio/lllistener_fmodstudio.h
@@ -1,11 +1,11 @@
 /** 
- * @file listener_fmodex.h
+ * @file listener_fmodstudio.h
  * @brief Description of LISTENER class abstracting the audio support
- * as an FMOD 3D implementation (windows and Linux)
+ * as an FMOD 3D implementation
  *
- * $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
@@ -25,8 +25,8 @@
  * $/LicenseInfo$
  */
 
-#ifndef LL_LISTENER_FMODEX_H
-#define LL_LISTENER_FMODEX_H
+#ifndef LL_LISTENER_FMODSTUDIO_H
+#define LL_LISTENER_FMODSTUDIO_H
 
 #include "lllistener.h"
 
@@ -37,27 +37,27 @@ namespace FMOD
 }
 
 //Interfaces
-class LLListener_FMODEX : public LLListener
+class LLListener_FMODSTUDIO : public LLListener
 {
- public:  
-	LLListener_FMODEX(FMOD::System *system);
-	virtual ~LLListener_FMODEX();
-	virtual void init();  
+public:
+    LLListener_FMODSTUDIO(FMOD::System *system);
+    virtual ~LLListener_FMODSTUDIO();
+    virtual void init();
 
-	virtual void translate(LLVector3 offset);
-	virtual void setPosition(LLVector3 pos);
-	virtual void setVelocity(LLVector3 vel);
-	virtual void orient(LLVector3 up, LLVector3 at);
-	virtual void commitDeferredChanges();
+    virtual void translate(LLVector3 offset);
+    virtual void setPosition(LLVector3 pos);
+    virtual void setVelocity(LLVector3 vel);
+    virtual void orient(LLVector3 up, LLVector3 at);
+    virtual void commitDeferredChanges();
 
-	virtual void setDopplerFactor(F32 factor);
-	virtual F32 getDopplerFactor();
-	virtual void setRolloffFactor(F32 factor);
-	virtual F32 getRolloffFactor();
- protected:
-	 FMOD::System *mSystem;
-	 F32 mDopplerFactor;
-	 F32 mRolloffFactor;
+    virtual void setDopplerFactor(F32 factor);
+    virtual F32 getDopplerFactor();
+    virtual void setRolloffFactor(F32 factor);
+    virtual F32 getRolloffFactor();
+protected:
+    FMOD::System *mSystem;
+    F32 mDopplerFactor;
+    F32 mRolloffFactor;
 };
 
 #endif
diff --git a/indra/llaudio/llstreamingaudio_fmodex.cpp b/indra/llaudio/llstreamingaudio_fmodex.cpp
deleted file mode 100644
index 9c9e85c00cb6d93a924390a6b7b51633a52cf028..0000000000000000000000000000000000000000
--- a/indra/llaudio/llstreamingaudio_fmodex.cpp
+++ /dev/null
@@ -1,392 +0,0 @@
-/** 
- * @file streamingaudio_fmodex.cpp
- * @brief LLStreamingAudio_FMODEX implementation
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "llmath.h"
-
-#include "fmod.hpp"
-#include "fmod_errors.h"
-
-#include "llstreamingaudio_fmodex.h"
-
-
-class LLAudioStreamManagerFMODEX
-{
-public:
-	LLAudioStreamManagerFMODEX(FMOD::System *system, const std::string& url);
-	FMOD::Channel* startStream();
-	bool stopStream(); // Returns true if the stream was successfully stopped.
-	bool ready();
-
-	const std::string& getURL() 	{ return mInternetStreamURL; }
-
-	FMOD_OPENSTATE getOpenState(unsigned int* percentbuffered=NULL, bool* starving=NULL, bool* diskbusy=NULL);
-protected:
-	FMOD::System* mSystem;
-	FMOD::Channel* mStreamChannel;
-	FMOD::Sound* mInternetStream;
-	bool mReady;
-
-	std::string mInternetStreamURL;
-};
-
-
-
-//---------------------------------------------------------------------------
-// Internet Streaming
-//---------------------------------------------------------------------------
-LLStreamingAudio_FMODEX::LLStreamingAudio_FMODEX(FMOD::System *system) :
-	mSystem(system),
-	mCurrentInternetStreamp(NULL),
-	mFMODInternetStreamChannelp(NULL),
-	mGain(1.0f)
-{
-	// Number of milliseconds of audio to buffer for the audio card.
-	// Must be larger than the usual Second Life frame stutter time.
-	const U32 buffer_seconds = 10;		//sec
-	const U32 estimated_bitrate = 128;	//kbit/sec
-	mSystem->setStreamBufferSize(estimated_bitrate * buffer_seconds * 128/*bytes/kbit*/, FMOD_TIMEUNIT_RAWBYTES);
-
-	// Here's where we set the size of the network buffer and some buffering 
-	// parameters.  In this case we want a network buffer of 16k, we want it 
-	// to prebuffer 40% of that when we first connect, and we want it 
-	// to rebuffer 80% of that whenever we encounter a buffer underrun.
-
-	// Leave the net buffer properties at the default.
-	//FSOUND_Stream_Net_SetBufferProperties(20000, 40, 80);
-}
-
-
-LLStreamingAudio_FMODEX::~LLStreamingAudio_FMODEX()
-{
-	// nothing interesting/safe to do.
-}
-
-
-void LLStreamingAudio_FMODEX::start(const std::string& url)
-{
-	//if (!mInited)
-	//{
-	//	LL_WARNS() << "startInternetStream before audio initialized" << LL_ENDL;
-	//	return;
-	//}
-
-	// "stop" stream but don't clear url, etc. in case url == mInternetStreamURL
-	stop();
-
-	if (!url.empty())
-	{
-		LL_INFOS() << "Starting internet stream: " << url << LL_ENDL;
-		mCurrentInternetStreamp = new LLAudioStreamManagerFMODEX(mSystem,url);
-		mURL = url;
-	}
-	else
-	{
-		LL_INFOS() << "Set internet stream to null" << LL_ENDL;
-		mURL.clear();
-	}
-}
-
-
-void LLStreamingAudio_FMODEX::update()
-{
-	// Kill dead internet streams, if possible
-	std::list<LLAudioStreamManagerFMODEX *>::iterator iter;
-	for (iter = mDeadStreams.begin(); iter != mDeadStreams.end();)
-	{
-		LLAudioStreamManagerFMODEX *streamp = *iter;
-		if (streamp->stopStream())
-		{
-			LL_INFOS() << "Closed dead stream" << LL_ENDL;
-			delete streamp;
-			mDeadStreams.erase(iter++);
-		}
-		else
-		{
-			iter++;
-		}
-	}
-
-	// Don't do anything if there are no streams playing
-	if (!mCurrentInternetStreamp)
-	{
-		return;
-	}
-
-	unsigned int progress;
-	bool starving;
-	bool diskbusy;
-	FMOD_OPENSTATE open_state = mCurrentInternetStreamp->getOpenState(&progress, &starving, &diskbusy);
-
-	if (open_state == FMOD_OPENSTATE_READY)
-	{
-		// Stream is live
-
-		// start the stream if it's ready
-		if (!mFMODInternetStreamChannelp &&
-			(mFMODInternetStreamChannelp = mCurrentInternetStreamp->startStream()))
-		{
-			// Reset volume to previously set volume
-			setGain(getGain());
-			mFMODInternetStreamChannelp->setPaused(false);
-		}
-	}
-	else if(open_state == FMOD_OPENSTATE_ERROR)
-	{
-		stop();
-		return;
-	}
-
-	if(mFMODInternetStreamChannelp)
-	{
-		FMOD::Sound *sound = NULL;
-
-		if(mFMODInternetStreamChannelp->getCurrentSound(&sound) == FMOD_OK && sound)
-		{
-			FMOD_TAG tag;
-			S32 tagcount, dirtytagcount;
-
-			if(sound->getNumTags(&tagcount, &dirtytagcount) == FMOD_OK && dirtytagcount)
-			{
-				for(S32 i = 0; i < tagcount; ++i)
-				{
-					if(sound->getTag(NULL, i, &tag)!=FMOD_OK)
-						continue;
-
-					if (tag.type == FMOD_TAGTYPE_FMOD)
-					{
-						if (!strcmp(tag.name, "Sample Rate Change"))
-						{
-							LL_INFOS() << "Stream forced changing sample rate to " << *((float *)tag.data) << LL_ENDL;
-							mFMODInternetStreamChannelp->setFrequency(*((float *)tag.data));
-						}
-						continue;
-					}
-				}
-			}
-
-			if(starving)
-			{
-				bool paused = false;
-				mFMODInternetStreamChannelp->getPaused(&paused);
-				if(!paused)
-				{
-					LL_INFOS() << "Stream starvation detected! Pausing stream until buffer nearly full." << LL_ENDL;
-					LL_INFOS() << "  (diskbusy="<<diskbusy<<")" << LL_ENDL;
-					LL_INFOS() << "  (progress="<<progress<<")" << LL_ENDL;
-					mFMODInternetStreamChannelp->setPaused(true);
-				}
-			}
-			else if(progress > 80)
-			{
-				mFMODInternetStreamChannelp->setPaused(false);
-			}
-		}
-	}
-}
-
-void LLStreamingAudio_FMODEX::stop()
-{
-	if (mFMODInternetStreamChannelp)
-	{
-		mFMODInternetStreamChannelp->setPaused(true);
-		mFMODInternetStreamChannelp->setPriority(0);
-		mFMODInternetStreamChannelp = NULL;
-	}
-
-	if (mCurrentInternetStreamp)
-	{
-		LL_INFOS() << "Stopping internet stream: " << mCurrentInternetStreamp->getURL() << LL_ENDL;
-		if (mCurrentInternetStreamp->stopStream())
-		{
-			delete mCurrentInternetStreamp;
-		}
-		else
-		{
-			LL_WARNS() << "Pushing stream to dead list: " << mCurrentInternetStreamp->getURL() << LL_ENDL;
-			mDeadStreams.push_back(mCurrentInternetStreamp);
-		}
-		mCurrentInternetStreamp = NULL;
-		//mURL.clear();
-	}
-}
-
-void LLStreamingAudio_FMODEX::pause(int pauseopt)
-{
-	if (pauseopt < 0)
-	{
-		pauseopt = mCurrentInternetStreamp ? 1 : 0;
-	}
-
-	if (pauseopt)
-	{
-		if (mCurrentInternetStreamp)
-		{
-			stop();
-		}
-	}
-	else
-	{
-		start(getURL());
-	}
-}
-
-
-// A stream is "playing" if it has been requested to start.  That
-// doesn't necessarily mean audio is coming out of the speakers.
-int LLStreamingAudio_FMODEX::isPlaying()
-{
-	if (mCurrentInternetStreamp)
-	{
-		return 1; // Active and playing
-	}
-	else if (!mURL.empty())
-	{
-		return 2; // "Paused"
-	}
-	else
-	{
-		return 0;
-	}
-}
-
-
-F32 LLStreamingAudio_FMODEX::getGain()
-{
-	return mGain;
-}
-
-
-std::string LLStreamingAudio_FMODEX::getURL()
-{
-	return mURL;
-}
-
-
-void LLStreamingAudio_FMODEX::setGain(F32 vol)
-{
-	mGain = vol;
-
-	if (mFMODInternetStreamChannelp)
-	{
-		vol = llclamp(vol * vol, 0.f, 1.f);	//should vol be squared here?
-
-		mFMODInternetStreamChannelp->setVolume(vol);
-	}
-}
-
-///////////////////////////////////////////////////////
-// manager of possibly-multiple internet audio streams
-
-LLAudioStreamManagerFMODEX::LLAudioStreamManagerFMODEX(FMOD::System *system, const std::string& url) :
-	mSystem(system),
-	mStreamChannel(NULL),
-	mInternetStream(NULL),
-	mReady(false)
-{
-	mInternetStreamURL = url;
-
-	FMOD_RESULT result = mSystem->createStream(url.c_str(), FMOD_2D | FMOD_NONBLOCKING | FMOD_IGNORETAGS, 0, &mInternetStream);
-
-	if (result!= FMOD_OK)
-	{
-		LL_WARNS() << "Couldn't open fmod stream, error "
-			<< FMOD_ErrorString(result)
-			<< LL_ENDL;
-		mReady = false;
-		return;
-	}
-
-	mReady = true;
-}
-
-FMOD::Channel *LLAudioStreamManagerFMODEX::startStream()
-{
-	// We need a live and opened stream before we try and play it.
-	if (!mInternetStream || getOpenState() != FMOD_OPENSTATE_READY)
-	{
-		LL_WARNS() << "No internet stream to start playing!" << LL_ENDL;
-		return NULL;
-	}
-
-	if(mStreamChannel)
-		return mStreamChannel;	//Already have a channel for this stream.
-
-	mSystem->playSound(FMOD_CHANNEL_FREE, mInternetStream, true, &mStreamChannel);
-	return mStreamChannel;
-}
-
-bool LLAudioStreamManagerFMODEX::stopStream()
-{
-	if (mInternetStream)
-	{
-
-
-		bool close = true;
-		switch (getOpenState())
-		{
-		case FMOD_OPENSTATE_CONNECTING:
-			close = false;
-			break;
-		default:
-			close = true;
-		}
-
-		if (close)
-		{
-			mInternetStream->release();
-			mStreamChannel = NULL;
-			mInternetStream = NULL;
-			return true;
-		}
-		else
-		{
-			return false;
-		}
-	}
-	else
-	{
-		return true;
-	}
-}
-
-FMOD_OPENSTATE LLAudioStreamManagerFMODEX::getOpenState(unsigned int* percentbuffered, bool* starving, bool* diskbusy)
-{
-	FMOD_OPENSTATE state;
-	mInternetStream->getOpenState(&state, percentbuffered, starving, diskbusy);
-	return state;
-}
-
-void LLStreamingAudio_FMODEX::setBufferSizes(U32 streambuffertime, U32 decodebuffertime)
-{
-	mSystem->setStreamBufferSize(streambuffertime/1000*128*128, FMOD_TIMEUNIT_RAWBYTES);
-	FMOD_ADVANCEDSETTINGS settings;
-	memset(&settings,0,sizeof(settings));
-	settings.cbsize=sizeof(settings);
-	settings.defaultDecodeBufferSize = decodebuffertime;//ms
-	mSystem->setAdvancedSettings(&settings);
-}
diff --git a/indra/llaudio/llstreamingaudio_fmodex.h b/indra/llaudio/llstreamingaudio_fmodex.h
deleted file mode 100644
index 2787840ba1a8581bba725f3574c0620fa9c7ea3e..0000000000000000000000000000000000000000
--- a/indra/llaudio/llstreamingaudio_fmodex.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/** 
- * @file streamingaudio_fmodex.h
- * @brief Definition of LLStreamingAudio_FMODEX implementation
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_STREAMINGAUDIO_FMODEX_H
-#define LL_STREAMINGAUDIO_FMODEX_H
-
-#include "stdtypes.h" // from llcommon
-
-#include "llstreamingaudio.h"
-#include "lltimer.h"
-
-//Stubs
-class LLAudioStreamManagerFMODEX;
-namespace FMOD
-{
-	class System;
-	class Channel;
-}
-
-//Interfaces
-class LLStreamingAudio_FMODEX : public LLStreamingAudioInterface
-{
- public:
-	LLStreamingAudio_FMODEX(FMOD::System *system);
-	/*virtual*/ ~LLStreamingAudio_FMODEX();
-
-	/*virtual*/ void start(const std::string& url);
-	/*virtual*/ void stop();
-	/*virtual*/ void pause(S32 pause);
-	/*virtual*/ void update();
-	/*virtual*/ S32 isPlaying();
-	/*virtual*/ void setGain(F32 vol);
-	/*virtual*/ F32 getGain();
-	/*virtual*/ std::string getURL();
-
-	/*virtual*/ bool supportsAdjustableBufferSizes(){return true;}
-	/*virtual*/ void setBufferSizes(U32 streambuffertime, U32 decodebuffertime);
-private:
-	FMOD::System *mSystem;
-
-	LLAudioStreamManagerFMODEX *mCurrentInternetStreamp;
-	FMOD::Channel *mFMODInternetStreamChannelp;
-	std::list<LLAudioStreamManagerFMODEX *> mDeadStreams;
-
-	std::string mURL;
-	F32 mGain;
-};
-
-
-#endif // LL_STREAMINGAUDIO_FMODEX_H
diff --git a/indra/llaudio/llstreamingaudio_fmodstudio.cpp b/indra/llaudio/llstreamingaudio_fmodstudio.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..08d19209aadf0a80a43f3a49e0df1bc544a0c645
--- /dev/null
+++ b/indra/llaudio/llstreamingaudio_fmodstudio.cpp
@@ -0,0 +1,392 @@
+/** 
+ * @file streamingaudio_fmodstudio.cpp
+ * @brief LLStreamingAudio_FMODSTUDIO implementation
+ *
+ * $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 "linden_common.h"
+
+#include "llmath.h"
+
+#include "fmodstudio/fmod.hpp"
+#include "fmodstudio/fmod_errors.h"
+
+#include "llstreamingaudio_fmodstudio.h"
+
+
+class LLAudioStreamManagerFMODSTUDIO
+{
+public:
+    LLAudioStreamManagerFMODSTUDIO(FMOD::System *system, const std::string& url);
+    FMOD::Channel* startStream();
+    bool stopStream(); // Returns true if the stream was successfully stopped.
+    bool ready();
+
+    const std::string& getURL() 	{ return mInternetStreamURL; }
+
+    FMOD_OPENSTATE getOpenState(unsigned int* percentbuffered = NULL, bool* starving = NULL, bool* diskbusy = NULL);
+protected:
+    FMOD::System* mSystem;
+    FMOD::Channel* mStreamChannel;
+    FMOD::Sound* mInternetStream;
+    bool mReady;
+
+    std::string mInternetStreamURL;
+};
+
+
+
+//---------------------------------------------------------------------------
+// Internet Streaming
+//---------------------------------------------------------------------------
+LLStreamingAudio_FMODSTUDIO::LLStreamingAudio_FMODSTUDIO(FMOD::System *system) :
+mSystem(system),
+mCurrentInternetStreamp(NULL),
+mFMODInternetStreamChannelp(NULL),
+mGain(1.0f)
+{
+    // Number of milliseconds of audio to buffer for the audio card.
+    // Must be larger than the usual Second Life frame stutter time.
+    const U32 buffer_seconds = 10;		//sec
+    const U32 estimated_bitrate = 128;	//kbit/sec
+    mSystem->setStreamBufferSize(estimated_bitrate * buffer_seconds * 128/*bytes/kbit*/, FMOD_TIMEUNIT_RAWBYTES);
+
+    // Here's where we set the size of the network buffer and some buffering 
+    // parameters.  In this case we want a network buffer of 16k, we want it 
+    // to prebuffer 40% of that when we first connect, and we want it 
+    // to rebuffer 80% of that whenever we encounter a buffer underrun.
+
+    // Leave the net buffer properties at the default.
+    //FSOUND_Stream_Net_SetBufferProperties(20000, 40, 80);
+}
+
+
+LLStreamingAudio_FMODSTUDIO::~LLStreamingAudio_FMODSTUDIO()
+{
+    // nothing interesting/safe to do.
+}
+
+
+void LLStreamingAudio_FMODSTUDIO::start(const std::string& url)
+{
+    //if (!mInited)
+    //{
+    //	LL_WARNS() << "startInternetStream before audio initialized" << LL_ENDL;
+    //	return;
+    //}
+
+    // "stop" stream but don't clear url, etc. in case url == mInternetStreamURL
+    stop();
+
+    if (!url.empty())
+    {
+        LL_INFOS() << "Starting internet stream: " << url << LL_ENDL;
+        mCurrentInternetStreamp = new LLAudioStreamManagerFMODSTUDIO(mSystem, url);
+        mURL = url;
+    }
+    else
+    {
+        LL_INFOS() << "Set internet stream to null" << LL_ENDL;
+        mURL.clear();
+    }
+}
+
+
+void LLStreamingAudio_FMODSTUDIO::update()
+{
+    // Kill dead internet streams, if possible
+    std::list<LLAudioStreamManagerFMODSTUDIO *>::iterator iter;
+    for (iter = mDeadStreams.begin(); iter != mDeadStreams.end();)
+    {
+        LLAudioStreamManagerFMODSTUDIO *streamp = *iter;
+        if (streamp->stopStream())
+        {
+            LL_INFOS() << "Closed dead stream" << LL_ENDL;
+            delete streamp;
+            mDeadStreams.erase(iter++);
+        }
+        else
+        {
+            iter++;
+        }
+    }
+
+    // Don't do anything if there are no streams playing
+    if (!mCurrentInternetStreamp)
+    {
+        return;
+    }
+
+    unsigned int progress;
+    bool starving;
+    bool diskbusy;
+    FMOD_OPENSTATE open_state = mCurrentInternetStreamp->getOpenState(&progress, &starving, &diskbusy);
+
+    if (open_state == FMOD_OPENSTATE_READY)
+    {
+        // Stream is live
+
+        // start the stream if it's ready
+        if (!mFMODInternetStreamChannelp &&
+            (mFMODInternetStreamChannelp = mCurrentInternetStreamp->startStream()))
+        {
+            // Reset volume to previously set volume
+            setGain(getGain());
+            mFMODInternetStreamChannelp->setPaused(false);
+        }
+    }
+    else if (open_state == FMOD_OPENSTATE_ERROR)
+    {
+        stop();
+        return;
+    }
+
+    if (mFMODInternetStreamChannelp)
+    {
+        FMOD::Sound *sound = NULL;
+
+        if (mFMODInternetStreamChannelp->getCurrentSound(&sound) == FMOD_OK && sound)
+        {
+            FMOD_TAG tag;
+            S32 tagcount, dirtytagcount;
+
+            if (sound->getNumTags(&tagcount, &dirtytagcount) == FMOD_OK && dirtytagcount)
+            {
+                for (S32 i = 0; i < tagcount; ++i)
+                {
+                    if (sound->getTag(NULL, i, &tag) != FMOD_OK)
+                        continue;
+
+                    if (tag.type == FMOD_TAGTYPE_FMOD)
+                    {
+                        if (!strcmp(tag.name, "Sample Rate Change"))
+                        {
+                            LL_INFOS() << "Stream forced changing sample rate to " << *((float *)tag.data) << LL_ENDL;
+                            mFMODInternetStreamChannelp->setFrequency(*((float *)tag.data));
+                        }
+                        continue;
+                    }
+                }
+            }
+
+            if (starving)
+            {
+                bool paused = false;
+                mFMODInternetStreamChannelp->getPaused(&paused);
+                if (!paused)
+                {
+                    LL_INFOS() << "Stream starvation detected! Pausing stream until buffer nearly full." << LL_ENDL;
+                    LL_INFOS() << "  (diskbusy=" << diskbusy << ")" << LL_ENDL;
+                    LL_INFOS() << "  (progress=" << progress << ")" << LL_ENDL;
+                    mFMODInternetStreamChannelp->setPaused(true);
+                }
+            }
+            else if (progress > 80)
+            {
+                mFMODInternetStreamChannelp->setPaused(false);
+            }
+        }
+    }
+}
+
+void LLStreamingAudio_FMODSTUDIO::stop()
+{
+    if (mFMODInternetStreamChannelp)
+    {
+        mFMODInternetStreamChannelp->setPaused(true);
+        mFMODInternetStreamChannelp->setPriority(0);
+        mFMODInternetStreamChannelp = NULL;
+    }
+
+    if (mCurrentInternetStreamp)
+    {
+        LL_INFOS() << "Stopping internet stream: " << mCurrentInternetStreamp->getURL() << LL_ENDL;
+        if (mCurrentInternetStreamp->stopStream())
+        {
+            delete mCurrentInternetStreamp;
+        }
+        else
+        {
+            LL_WARNS() << "Pushing stream to dead list: " << mCurrentInternetStreamp->getURL() << LL_ENDL;
+            mDeadStreams.push_back(mCurrentInternetStreamp);
+        }
+        mCurrentInternetStreamp = NULL;
+        //mURL.clear();
+    }
+}
+
+void LLStreamingAudio_FMODSTUDIO::pause(int pauseopt)
+{
+    if (pauseopt < 0)
+    {
+        pauseopt = mCurrentInternetStreamp ? 1 : 0;
+    }
+
+    if (pauseopt)
+    {
+        if (mCurrentInternetStreamp)
+        {
+            stop();
+        }
+    }
+    else
+    {
+        start(getURL());
+    }
+}
+
+
+// A stream is "playing" if it has been requested to start.  That
+// doesn't necessarily mean audio is coming out of the speakers.
+int LLStreamingAudio_FMODSTUDIO::isPlaying()
+{
+    if (mCurrentInternetStreamp)
+    {
+        return 1; // Active and playing
+    }
+    else if (!mURL.empty())
+    {
+        return 2; // "Paused"
+    }
+    else
+    {
+        return 0;
+    }
+}
+
+
+F32 LLStreamingAudio_FMODSTUDIO::getGain()
+{
+    return mGain;
+}
+
+
+std::string LLStreamingAudio_FMODSTUDIO::getURL()
+{
+    return mURL;
+}
+
+
+void LLStreamingAudio_FMODSTUDIO::setGain(F32 vol)
+{
+    mGain = vol;
+
+    if (mFMODInternetStreamChannelp)
+    {
+        vol = llclamp(vol * vol, 0.f, 1.f);	//should vol be squared here?
+
+        mFMODInternetStreamChannelp->setVolume(vol);
+    }
+}
+
+///////////////////////////////////////////////////////
+// manager of possibly-multiple internet audio streams
+
+LLAudioStreamManagerFMODSTUDIO::LLAudioStreamManagerFMODSTUDIO(FMOD::System *system, const std::string& url) :
+mSystem(system),
+mStreamChannel(NULL),
+mInternetStream(NULL),
+mReady(false)
+{
+    mInternetStreamURL = url;
+
+    FMOD_RESULT result = mSystem->createStream(url.c_str(), FMOD_2D | FMOD_NONBLOCKING | FMOD_IGNORETAGS, 0, &mInternetStream);
+
+    if (result != FMOD_OK)
+    {
+        LL_WARNS() << "Couldn't open fmod stream, error "
+            << FMOD_ErrorString(result)
+            << LL_ENDL;
+        mReady = false;
+        return;
+    }
+
+    mReady = true;
+}
+
+FMOD::Channel *LLAudioStreamManagerFMODSTUDIO::startStream()
+{
+    // We need a live and opened stream before we try and play it.
+    if (!mInternetStream || getOpenState() != FMOD_OPENSTATE_READY)
+    {
+        LL_WARNS() << "No internet stream to start playing!" << LL_ENDL;
+        return NULL;
+    }
+
+    if (mStreamChannel)
+        return mStreamChannel;	//Already have a channel for this stream.
+
+    mSystem->playSound(mInternetStream, NULL, true, &mStreamChannel);
+    return mStreamChannel;
+}
+
+bool LLAudioStreamManagerFMODSTUDIO::stopStream()
+{
+    if (mInternetStream)
+    {
+
+
+        bool close = true;
+        switch (getOpenState())
+        {
+        case FMOD_OPENSTATE_CONNECTING:
+            close = false;
+            break;
+        default:
+            close = true;
+        }
+
+        if (close)
+        {
+            mInternetStream->release();
+            mStreamChannel = NULL;
+            mInternetStream = NULL;
+            return true;
+        }
+        else
+        {
+            return false;
+        }
+    }
+    else
+    {
+        return true;
+    }
+}
+
+FMOD_OPENSTATE LLAudioStreamManagerFMODSTUDIO::getOpenState(unsigned int* percentbuffered, bool* starving, bool* diskbusy)
+{
+    FMOD_OPENSTATE state;
+    mInternetStream->getOpenState(&state, percentbuffered, starving, diskbusy);
+    return state;
+}
+
+void LLStreamingAudio_FMODSTUDIO::setBufferSizes(U32 streambuffertime, U32 decodebuffertime)
+{
+    mSystem->setStreamBufferSize(streambuffertime / 1000 * 128 * 128, FMOD_TIMEUNIT_RAWBYTES);
+    FMOD_ADVANCEDSETTINGS settings;
+    memset(&settings, 0, sizeof(settings));
+    settings.cbSize = sizeof(settings);
+    settings.defaultDecodeBufferSize = decodebuffertime;//ms
+    mSystem->setAdvancedSettings(&settings);
+}
diff --git a/indra/llaudio/llstreamingaudio_fmodstudio.h b/indra/llaudio/llstreamingaudio_fmodstudio.h
new file mode 100644
index 0000000000000000000000000000000000000000..1fc3c54d79bc7fcb2815234b4a01951d5745103b
--- /dev/null
+++ b/indra/llaudio/llstreamingaudio_fmodstudio.h
@@ -0,0 +1,73 @@
+/** 
+ * @file streamingaudio_fmodstudio.h
+ * @brief Definition of LLStreamingAudio_FMODSTUDIO implementation
+ *
+ * $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_STREAMINGAUDIO_FMODSTUDIO_H
+#define LL_STREAMINGAUDIO_FMODSTUDIO_H
+
+#include "stdtypes.h" // from llcommon
+
+#include "llstreamingaudio.h"
+#include "lltimer.h"
+
+//Stubs
+class LLAudioStreamManagerFMODSTUDIO;
+namespace FMOD
+{
+	class System;
+	class Channel;
+}
+
+//Interfaces
+class LLStreamingAudio_FMODSTUDIO : public LLStreamingAudioInterface
+{
+public:
+    LLStreamingAudio_FMODSTUDIO(FMOD::System *system);
+    /*virtual*/ ~LLStreamingAudio_FMODSTUDIO();
+
+    /*virtual*/ void start(const std::string& url);
+    /*virtual*/ void stop();
+    /*virtual*/ void pause(S32 pause);
+    /*virtual*/ void update();
+    /*virtual*/ S32 isPlaying();
+    /*virtual*/ void setGain(F32 vol);
+    /*virtual*/ F32 getGain();
+    /*virtual*/ std::string getURL();
+
+    /*virtual*/ bool supportsAdjustableBufferSizes(){return true;}
+    /*virtual*/ void setBufferSizes(U32 streambuffertime, U32 decodebuffertime);
+private:
+    FMOD::System *mSystem;
+
+    LLAudioStreamManagerFMODSTUDIO *mCurrentInternetStreamp;
+    FMOD::Channel *mFMODInternetStreamChannelp;
+    std::list<LLAudioStreamManagerFMODSTUDIO *> mDeadStreams;
+
+    std::string mURL;
+    F32 mGain;
+};
+
+
+#endif // LL_STREAMINGAUDIO_FMODSTUDIO_H
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp
index e3bb36192c907c3b1efed33ee4442b84d6b52d06..abb043f4282b84df2cd878da7e829900ee8fce11 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 ccf4f3ddf50e66928b9a7d42cff491fa71b1bac3..80a414d00f69a993c15e2396273e1141c84b710f 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"
@@ -125,6 +126,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;
@@ -361,6 +365,7 @@ const std::string LLControlGroup::mTypeString[TYPE_COUNT] = { "U32"
                                                              ,"String"
                                                              ,"Vector3"
                                                              ,"Vector3D"
+                                                             ,"Quaternion"
                                                              ,"Rect"
                                                              ,"Color4"
                                                              ,"Color3"
@@ -523,6 +528,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);
@@ -600,6 +610,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);
@@ -677,6 +692,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);
@@ -859,6 +879,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
@@ -1201,6 +1231,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; 
@@ -1236,6 +1271,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) 
 { 
@@ -1348,6 +1387,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 de0d366492fcf475d60695955d8976e975fdd54a..f1369188963b94058bae5be9f1a06dbebf39a46c 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;
 
@@ -80,6 +81,7 @@ typedef enum e_control_type
 	TYPE_STRING,
 	TYPE_VEC3,
 	TYPE_VEC3D,
+	TYPE_QUAT,
 	TYPE_RECT,
 	TYPE_COL4,
 	TYPE_COL3,
@@ -220,6 +222,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);
@@ -234,10 +237,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);
@@ -270,6 +273,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);
@@ -436,7 +440,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>();
@@ -444,7 +449,8 @@ template <> eControlType get_control_type<LLSD>();
 
 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);
@@ -453,6 +459,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 6a27a65499ba226715f610c960127fb993129a1d..fe77d33fc420793e1f725303bd2c5b78902cab5d 100644
--- a/indra/llxml/llcontrolgroupreader.h
+++ b/indra/llxml/llcontrolgroupreader.h
@@ -65,6 +65,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 366696873051f6d87d9e9b1f10c5bf38a132b009..8208923e6fa364924b7fcb195e997833afc06e9b 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -18,7 +18,7 @@ include(DBusGlib)
 include(DirectX)
 include(DragDrop)
 include(EXPAT)
-include(FMODEX)
+include(FMODSTUDIO)
 include(GLOD)
 include(Hunspell)
 include(JsonCpp)
@@ -63,9 +63,9 @@ if (NOT HAVOK_TPV)
    add_subdirectory(${LLPHYSICSEXTENSIONS_SRC_DIR} llphysicsextensions)
 endif (NOT HAVOK_TPV)
 
-if(FMODEX)
-  include_directories(${FMODEX_INCLUDE_DIR})
-endif(FMODEX)
+if(FMODSTUDIO)
+  include_directories(${FMODSTUDIO_INCLUDE_DIR})
+endif(FMODSTUDIO)
 
 include_directories(
     ${DBUSGLIB_INCLUDE_DIRS}
@@ -228,6 +228,7 @@ set(viewer_SOURCE_FILES
     llfloaterbuycurrencyhtml.cpp
     llfloaterbuyland.cpp
     llfloatercamera.cpp
+    llfloatercamerapresets.cpp
     llfloaterchatvoicevolume.cpp
     llfloatercolorpicker.cpp
     llfloaterconversationlog.cpp
@@ -288,12 +289,14 @@ set(viewer_SOURCE_FILES
     llfloaterperms.cpp
     llfloaterpostprocess.cpp
     llfloaterpreference.cpp
+    llfloaterpreferenceviewadvanced.cpp
     llfloaterpreviewtrash.cpp
     llfloaterproperties.cpp
     llfloaterregiondebugconsole.cpp
     llfloaterregioninfo.cpp
     llfloaterreporter.cpp
     llfloaterregionrestarting.cpp
+    llfloatersavecamerapreset.cpp
     llfloatersaveprefpreset.cpp
     llfloatersceneloadstats.cpp
     llfloaterscriptdebug.cpp
@@ -481,6 +484,7 @@ set(viewer_SOURCE_FILES
     llpanelplaceprofile.cpp
     llpanelplaces.cpp
     llpanelplacestab.cpp
+    llpanelpresetscamerapulldown.cpp
     llpanelpresetspulldown.cpp
     llpanelprimmediacontrols.cpp
     llpanelprofile.cpp
@@ -494,6 +498,7 @@ set(viewer_SOURCE_FILES
     llpaneltiptoast.cpp
     llpanelvoiceeffect.cpp
     llpaneltopinfobar.cpp
+    llpanelpulldown.cpp
     llpanelvoicedevicesettings.cpp
     llpanelvolume.cpp
     llpanelvolumepulldown.cpp
@@ -852,6 +857,7 @@ set(viewer_HEADER_FILES
     llfloaterbuycurrency.h
     llfloaterbuycurrencyhtml.h
     llfloaterbuyland.h
+    llfloatercamerapresets.h
     llfloatercamera.h
     llfloaterchatvoicevolume.h
     llfloatercolorpicker.h
@@ -916,12 +922,14 @@ set(viewer_HEADER_FILES
     llfloaterperms.h
     llfloaterpostprocess.h
     llfloaterpreference.h
+    llfloaterpreferenceviewadvanced.h
     llfloaterpreviewtrash.h
     llfloaterproperties.h
     llfloaterregiondebugconsole.h
     llfloaterregioninfo.h
     llfloaterreporter.h
     llfloaterregionrestarting.h
+    llfloatersavecamerapreset.h
     llfloatersaveprefpreset.h
     llfloatersceneloadstats.h
     llfloaterscriptdebug.h
@@ -1099,12 +1107,14 @@ set(viewer_HEADER_FILES
     llpanelplaceprofile.h
     llpanelplaces.h
     llpanelplacestab.h
+    llpanelpresetscamerapulldown.h
     llpanelpresetspulldown.h
     llpanelprimmediacontrols.h
     llpanelprofile.h
     llpanelsnapshot.h
     llpanelteleporthistory.h
     llpaneltiptoast.h
+    llpanelpulldown.h
     llpanelvoicedevicesettings.h
     llpanelvoiceeffect.h
     llpaneltopinfobar.h
@@ -1712,12 +1722,13 @@ if (OPENAL)
   set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_OPENAL")
 endif (OPENAL)
 
-if (FMODEX)
-  set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_FMODEX")
-  set(FMODWRAPPER_LIBRARY ${FMODEX_LIBRARY})
-endif (FMODEX)
+if (FMODSTUDIO)
+  set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_FMODSTUDIO")
+  set(FMODWRAPPER_LIBRARY ${FMODSTUDIO_LIBRARY})
+endif (FMODSTUDIO)
 
 set_source_files_properties(llstartup.cpp PROPERTIES COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS}")
+set_source_files_properties(llprogressview.cpp PROPERTIES COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS}")
 
 list(APPEND viewer_SOURCE_FILES ${viewer_HEADER_FILES})
 
@@ -1832,13 +1843,13 @@ if (WINDOWS)
             )
     endif (ADDRESS_SIZE EQUAL 64)
 
-    if (FMODEX)
+    if (FMODSTUDIO)
       list(APPEND COPY_INPUT_DEPENDENCIES
-           ${SHARED_LIB_STAGING_DIR}/Release/fmodex.dll
-           ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/fmodex.dll
-           ${SHARED_LIB_STAGING_DIR}/Debug/fmodexL.dll
+           ${SHARED_LIB_STAGING_DIR}/Release/fmod.dll
+           ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/fmod.dll
+           ${SHARED_LIB_STAGING_DIR}/Debug/fmodL.dll
           )
-    endif (FMODEX)
+    endif (FMODSTUDIO)
 
     add_custom_command(
       OUTPUT  ${CMAKE_CFG_INTDIR}/copy_touched.bat
@@ -1849,6 +1860,7 @@ if (WINDOWS)
         --arch=${ARCH}
         --artwork=${ARTWORK_DIR}
         "--bugsplat=${BUGSPLAT_DB}"
+        "--fmodstudio=${FMODSTUDIO}"
         --build=${CMAKE_CURRENT_BINARY_DIR}
         --buildtype=${CMAKE_BUILD_TYPE}
         "--channel=${VIEWER_CHANNEL}"
@@ -1925,6 +1937,7 @@ if (WINDOWS)
           --arch=${ARCH}
           --artwork=${ARTWORK_DIR}
           "--bugsplat=${BUGSPLAT_DB}"
+          "--fmodstudio=${FMODSTUDIO}"
           --build=${CMAKE_CURRENT_BINARY_DIR}
           --buildtype=${CMAKE_BUILD_TYPE}
           "--channel=${VIEWER_CHANNEL}"
@@ -2070,6 +2083,7 @@ if (LINUX)
         --arch=${ARCH}
         --artwork=${ARTWORK_DIR}
         "--bugsplat=${BUGSPLAT_DB}"
+        "--fmodstudio=${FMODSTUDIO}"
         --build=${CMAKE_CURRENT_BINARY_DIR}
         --buildtype=${CMAKE_BUILD_TYPE}
         "--channel=${VIEWER_CHANNEL}"
@@ -2096,6 +2110,7 @@ if (LINUX)
       --arch=${ARCH}
       --artwork=${ARTWORK_DIR}
       "--bugsplat=${BUGSPLAT_DB}"
+      "--fmodstudio=${FMODSTUDIO}"
       --build=${CMAKE_CURRENT_BINARY_DIR}
       --buildtype=${CMAKE_BUILD_TYPE}
       "--channel=${VIEWER_CHANNEL}"
@@ -2172,6 +2187,7 @@ if (DARWIN)
       --arch=${ARCH}
       --artwork=${ARTWORK_DIR}
       "--bugsplat=${BUGSPLAT_DB}"
+      "--fmodstudio=${FMODSTUDIO}"
       --build=${CMAKE_CURRENT_BINARY_DIR}
       --buildtype=${CMAKE_BUILD_TYPE}
       --bundleid=${MACOSX_BUNDLE_GUI_IDENTIFIER}
@@ -2206,6 +2222,7 @@ if (DARWIN)
           --arch=${ARCH}
           --artwork=${ARTWORK_DIR}
           "--bugsplat=${BUGSPLAT_DB}"
+          "--fmodstudio=${FMODSTUDIO}"
           --build=${CMAKE_CURRENT_BINARY_DIR}
           --buildtype=${CMAKE_BUILD_TYPE}
           "--channel=${VIEWER_CHANNEL}"
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 0cb9ee3aceb4970dd40047c06738f23069e323f5..e8f01f7a6038a9ec0729d349a5caa2de70ba1b78 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -1510,6 +1510,21 @@
         <real>0.5</real>
       </array>
     </map>
+    <key>CameraOffsetCustomPreset</key>
+    <map>
+      <key>Comment</key>
+      <string>Initial camera offset from avatar for the custom camera preset</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Vector3</string>
+      <key>Value</key>
+      <array>
+        <real>-3.0</real>
+        <real>0.0</real>
+        <real>0.75</real>
+      </array>
+    </map>
     <key>CameraOffsetScale</key>
     <map>
       <key>Comment</key>
@@ -1521,6 +1536,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>
@@ -1558,7 +1584,7 @@
       <key>Value</key>
       <real>1.0</real>
     </map>
-    <key>CameraPreset</key>
+    <key>CameraPreset</key>  <!-- deprecated (see SL-12429) -->
     <map>
       <key>Comment</key>
       <string>Preset camera position - view (0 - rear, 1 - front, 2 - group)</string>
@@ -4378,6 +4404,37 @@
         <real>1.0</real>
       </array>
     </map>
+    <key>FocusOffsetCustomPreset</key>
+    <map>
+      <key>Comment</key>
+      <string>Initial focus point offset relative to avatar for the custom camera preset (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>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>FocusPosOnLogout</key>
     <map>
       <key>Comment</key>
@@ -16327,7 +16384,7 @@
   <key>FMODExProfilerEnable</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>
@@ -16338,7 +16395,7 @@
   <key>FMODExDecodeBufferSize</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>
@@ -16349,7 +16406,7 @@
   <key>FMODExStreamBufferSize</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>
@@ -16492,6 +16549,50 @@
       <key>Value</key>
       <integer>0</integer>
     </map>
+  <key>CameraOpacity</key>
+  <map>
+    <key>Comment</key>
+    <string>Opacity of the Camera Controls floater</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>F32</string>
+    <key>Value</key>
+    <real>1.0</real>
+  </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>Rear View</string>
+  </map>
+  <key>CameraPresetType</key>
+  <map>
+    <key>Comment</key>
+    <string>Preset camera position - view (0 - rear, 1 - front, 2 - group, 3 - custom)</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>U32</string>
+    <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>CefVerboseLog</key>
     <map>
       <key>Comment</key>
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/llagentcamera.cpp b/indra/newview/llagentcamera.cpp
index 85b7d7b06f3594e750cdb6e73303ac2c57a988a2..9e65409256890db2d751144f91fe990260a42caa 100644
--- a/indra/newview/llagentcamera.cpp
+++ b/indra/newview/llagentcamera.cpp
@@ -184,6 +184,9 @@ LLAgentCamera::LLAgentCamera() :
 	clearGeneralKeys();
 	clearOrbitKeys();
 	clearPanKeys();
+
+	resetPanDiff();
+	resetOrbitDiff();
 }
 
 // Requires gSavedSettings to be initialized.
@@ -205,15 +208,10 @@ void LLAgentCamera::init()
 
 	mCameraFocusOffsetTarget = LLVector4(gSavedSettings.getVector3("CameraOffsetBuild"));
 	
-	mCameraPreset = (ECameraPreset) gSavedSettings.getU32("CameraPreset");
-
-	mCameraOffsetInitial[CAMERA_PRESET_REAR_VIEW] = gSavedSettings.getControl("CameraOffsetRearView");
-	mCameraOffsetInitial[CAMERA_PRESET_FRONT_VIEW] = gSavedSettings.getControl("CameraOffsetFrontView");
-	mCameraOffsetInitial[CAMERA_PRESET_GROUP_VIEW] = gSavedSettings.getControl("CameraOffsetGroupView");
+	mCameraPreset = (ECameraPreset) gSavedSettings.getU32("CameraPresetType");
 
-	mFocusOffsetInitial[CAMERA_PRESET_REAR_VIEW] = gSavedSettings.getControl("FocusOffsetRearView");
-	mFocusOffsetInitial[CAMERA_PRESET_FRONT_VIEW] = gSavedSettings.getControl("FocusOffsetFrontView");
-	mFocusOffsetInitial[CAMERA_PRESET_GROUP_VIEW] = gSavedSettings.getControl("FocusOffsetGroupView");
+	mCameraOffsetInitial = gSavedSettings.getControl("CameraOffsetRearView");
+	mFocusOffsetInitial = gSavedSettings.getControl("FocusOffsetRearView");
 
 	mCameraCollidePlane.clearVec();
 	mCurrentCameraDistance = getCameraOffsetInitial().magVec() * gSavedSettings.getF32("CameraOffsetScale");
@@ -353,7 +351,8 @@ void LLAgentCamera::resetView(BOOL reset_camera, BOOL change_camera)
 
 		mCameraFOVZoomFactor = 0.f;
 	}
-
+	resetPanDiff();
+	resetOrbitDiff();
 	mHUDTargetZoom = 1.f;
 }
 
@@ -723,7 +722,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
@@ -733,7 +732,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);
 	}
@@ -795,13 +794,16 @@ void LLAgentCamera::setCameraZoomFraction(F32 fraction)
 
 		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;
+				}
 			}
 		}
 
@@ -835,6 +837,7 @@ void LLAgentCamera::cameraOrbitAround(const F32 radians)
 	}
 	else
 	{
+		mOrbitAroundRadians += radians;
 		mCameraFocusOffsetTarget.rotVec(radians, 0.f, 0.f, 1.f);
 		
 		cameraZoomIn(1.f);
@@ -866,12 +869,34 @@ 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);
 	}
 }
 
+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()
 //-----------------------------------------------------------------------------
@@ -1010,6 +1035,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
@@ -1026,6 +1053,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;
 
@@ -1046,6 +1075,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;
 
@@ -1058,6 +1089,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()
 //-----------------------------------------------------------------------------
@@ -1609,7 +1660,7 @@ LLVector3d LLAgentCamera::calcThirdPersonFocusOffset()
 		agent_rot *= ((LLViewerObject*)(gAgentAvatarp->getParent()))->getRenderRotation();
 	}
 
-	focus_offset = convert_from_llsd<LLVector3d>(mFocusOffsetInitial[mCameraPreset]->get(), TYPE_VEC3D, "");
+	focus_offset = convert_from_llsd<LLVector3d>(mFocusOffsetInitial->get(), TYPE_VEC3D, "");
 	return focus_offset * agent_rot;
 }
 
@@ -1684,7 +1735,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;
@@ -1758,7 +1809,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
@@ -1939,9 +1990,38 @@ LLVector3d LLAgentCamera::calcCameraPositionTargetGlobal(BOOL *hit_limit)
 }
 
 
+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 convert_from_llsd<LLVector3>(mCameraOffsetInitial[mCameraPreset]->get(), TYPE_VEC3, "");
+	return convert_from_llsd<LLVector3>(mCameraOffsetInitial->get(), TYPE_VEC3, "");
+}
+
+LLVector3d LLAgentCamera::getFocusOffsetInitial()
+{
+	return convert_from_llsd<LLVector3d>(mFocusOffsetInitial->get(), TYPE_VEC3D, "");
 }
 
 F32 LLAgentCamera::getCameraMaxZoomDistance()
@@ -1952,6 +2032,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()
 //-----------------------------------------------------------------------------
@@ -2231,15 +2317,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;
@@ -2338,7 +2416,10 @@ void LLAgentCamera::switchCameraPreset(ECameraPreset preset)
 
 	mCameraPreset = preset;
 
-	gSavedSettings.setU32("CameraPreset", mCameraPreset);
+	resetPanDiff();
+	resetOrbitDiff();
+
+	gSavedSettings.setU32("CameraPresetType", mCameraPreset);
 }
 
 
@@ -2583,7 +2664,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)
 	{
@@ -2600,22 +2681,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;
@@ -2763,6 +2836,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 a4bc8434b0c982892ff99b8710e6a4d721ed9d4f..ec1ed433d7cd9b26564b7c719041db24c7ffa3ee 100644
--- a/indra/newview/llagentcamera.h
+++ b/indra/newview/llagentcamera.h
@@ -56,7 +56,10 @@ enum ECameraPreset
 	CAMERA_PRESET_FRONT_VIEW, 
 
 	/** "Above and to the left, over the shoulder, pulled back a little on the zoom" */
-	CAMERA_PRESET_GROUP_VIEW
+	CAMERA_PRESET_GROUP_VIEW,
+
+	/** Current view when a preset is saved */
+	CAMERA_PRESET_CUSTOM
 };
 
 //------------------------------------------------------------------------
@@ -109,20 +112,32 @@ class LLAgentCamera
 	//--------------------------------------------------------------------
 public:
 	void switchCameraPreset(ECameraPreset preset);
-private:
 	/** Determines default camera offset depending on the current camera preset */
 	LLVector3 getCameraOffsetInitial();
+	/** 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();
 
 	/** Camera preset in Third Person Mode */
 	ECameraPreset mCameraPreset; 
 
-	/** Initial camera offsets */
-	std::map<ECameraPreset, LLPointer<LLControlVariable> > mCameraOffsetInitial;
+	/** Initial camera offset */
+	LLPointer<LLControlVariable> mCameraOffsetInitial;
+
+	/** Initial focus offset */
+	LLPointer<LLControlVariable> mFocusOffsetInitial;
 
-	/** Initial focus offsets */
-	std::map<ECameraPreset, LLPointer<LLControlVariable> > mFocusOffsetInitial;
+	LLQuaternion mInitSitRot;
 
 	//--------------------------------------------------------------------
 	// Position
@@ -137,6 +152,8 @@ class LLAgentCamera
 	F32				getCurrentCameraBuildOffset() 	{ return (F32)mCameraFocusOffset.length(); }
 	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
@@ -204,7 +221,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);
@@ -256,26 +273,31 @@ class LLAgentCamera
 	void			cameraOrbitAround(const F32 radians);	// Rotate camera CCW radians about build focus point
 	void			cameraOrbitOver(const F32 radians);		// Rotate camera forward radians over build focus point
 	void			cameraOrbitIn(const F32 meters);		// Move camera in toward build focus point
-
+	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
 	//--------------------------------------------------------------------
@@ -362,6 +384,9 @@ class LLAgentCamera
 	F32				mOrbitInKey;
 	F32				mOrbitOutKey;
 
+	F32				mOrbitAroundRadians;
+	F32				mOrbitOverAngle;
+
 	//--------------------------------------------------------------------
 	// Pan
 	//--------------------------------------------------------------------
@@ -389,6 +414,8 @@ class LLAgentCamera
 	F32				mPanInKey;
 	F32				mPanOutKey;
 
+	LLVector3d		mPanFocusDiff;
+
 /**                    Keys
  **                                                                            **
  *******************************************************************************/
diff --git a/indra/newview/llfloatercamera.cpp b/indra/newview/llfloatercamera.cpp
index f3406d93bb11ed388397ac50f1ad032d34d73a9b..d574f1433f764e051d6195df8b3fe0d069dc0b9c 100644
--- a/indra/newview/llfloatercamera.cpp
+++ b/indra/newview/llfloatercamera.cpp
@@ -34,6 +34,7 @@
 // Viewer includes
 #include "llagent.h"
 #include "llagentcamera.h"
+#include "llpresetsmanager.h"
 #include "lljoystickbutton.h"
 #include "llviewercontrol.h"
 #include "llviewercamera.h"
@@ -42,6 +43,8 @@
 #include "llslider.h"
 #include "llfirstuse.h"
 #include "llhints.h"
+#include "lltabcontainer.h"
+#include "llvoavatarself.h"
 
 static LLDefaultChildRegistry::Register<LLPanelCameraItem> r("panel_camera_item");
 
@@ -52,7 +55,6 @@ const F32 ORBIT_NUDGE_RATE = 0.05f; // fraction of normal speed
 #define ORBIT "cam_rotate_stick"
 #define PAN "cam_track_stick"
 #define ZOOM "zoom"
-#define PRESETS "preset_views_list"
 #define CONTROLS "controls"
 
 bool LLFloaterCamera::sFreeCamera = false;
@@ -269,13 +271,7 @@ void LLFloaterCamera::onAvatarEditingAppearance(bool editing)
 
 void LLFloaterCamera::handleAvatarEditingAppearance(bool editing)
 {
-	//camera presets (rear, front, etc.)
-	getChildView("preset_views_list")->setEnabled(!editing);
-	getChildView("presets_btn")->setEnabled(!editing);
 
-	//camera modes (object view, mouselook view)
-	getChildView("camera_modes_list")->setEnabled(!editing);
-	getChildView("avatarview_btn")->setEnabled(!editing);
 }
 
 void LLFloaterCamera::update()
@@ -322,6 +318,8 @@ void LLFloaterCamera::onOpen(const LLSD& key)
 	else
 		toPrevMode();
 	mClosed = FALSE;
+
+	populatePresetCombo();
 }
 
 void LLFloaterCamera::onClose(bool app_quitting)
@@ -353,6 +351,8 @@ LLFloaterCamera::LLFloaterCamera(const LLSD& val)
 {
 	LLHints::getInstance()->registerHintTarget("view_popup", getHandle());
 	mCommitCallbackRegistrar.add("CameraPresets.ChangeView", boost::bind(&LLFloaterCamera::onClickCameraItem, _2));
+	mCommitCallbackRegistrar.add("CameraPresets.Save", boost::bind(&LLFloaterCamera::onSavePreset, this));
+	mCommitCallbackRegistrar.add("CameraPresets.ShowPresetsList", boost::bind(&LLFloaterReg::showInstance, "camera_presets", LLSD(), FALSE));
 }
 
 // virtual
@@ -363,10 +363,14 @@ BOOL LLFloaterCamera::postBuild()
 	mRotate = getChild<LLJoystickCameraRotate>(ORBIT);
 	mZoom = findChild<LLPanelCameraZoom>(ZOOM);
 	mTrack = getChild<LLJoystickCameraTrack>(PAN);
+	mPresetCombo = getChild<LLComboBox>("preset_combo");
+
+	getChild<LLTextBox>("precise_ctrs_label")->setShowCursorHand(false);
+	getChild<LLTextBox>("precise_ctrs_label")->setSoundFlags(LLView::MOUSE_UP);
+	getChild<LLTextBox>("precise_ctrs_label")->setClickedCallback(boost::bind(&LLFloaterReg::showInstance, "prefs_view_advanced", LLSD(), FALSE));
 
-	assignButton2Mode(CAMERA_CTRL_MODE_MODES,			"avatarview_btn");
-	assignButton2Mode(CAMERA_CTRL_MODE_PAN,				"pan_btn");
-	assignButton2Mode(CAMERA_CTRL_MODE_PRESETS,		"presets_btn");
+	mPresetCombo->setCommitCallback(boost::bind(&LLFloaterCamera::onCustomPresetSelected, this));
+	LLPresetsManager::getInstance()->setPresetListChangeCameraCallback(boost::bind(&LLFloaterCamera::populatePresetCombo, this));
 
 	update();
 
@@ -376,6 +380,15 @@ BOOL LLFloaterCamera::postBuild()
 	return LLFloater::postBuild();
 }
 
+F32	LLFloaterCamera::getCurrentTransparency()
+{
+
+	static LLCachedControl<F32> camera_opacity(gSavedSettings, "CameraOpacity");
+	static LLCachedControl<F32> active_floater_transparency(gSavedSettings, "ActiveFloaterTransparency");
+	return llmin(camera_opacity(), active_floater_transparency());
+
+}
+
 void LLFloaterCamera::fillFlatlistFromPanel (LLFlatListView* list, LLPanel* panel)
 {
 	// copying child list and then iterating over a copy, because list itself
@@ -444,13 +457,6 @@ void LLFloaterCamera::switchMode(ECameraControlMode mode)
 
 	switch (mode)
 	{
-	case CAMERA_CTRL_MODE_MODES:
-		if(sFreeCamera)
-		{
-			switchMode(CAMERA_CTRL_MODE_FREE_CAMERA);
-		}
-		break;
-
 	case CAMERA_CTRL_MODE_PAN:
 		sFreeCamera = false;
 		clear_camera_tool();
@@ -474,36 +480,8 @@ void LLFloaterCamera::switchMode(ECameraControlMode mode)
 	}
 }
 
-
-void LLFloaterCamera::onClickBtn(ECameraControlMode mode)
-{
-	// check for a click on active button
-	if (mCurrMode == mode) mMode2Button[mode]->setToggleState(TRUE);
-	
-	switchMode(mode);
-
-}
-
-void LLFloaterCamera::assignButton2Mode(ECameraControlMode mode, const std::string& button_name)
-{
-	LLButton* button = getChild<LLButton>(button_name);
-	
-	button->setClickedCallback(boost::bind(&LLFloaterCamera::onClickBtn, this, mode));
-	mMode2Button[mode] = button;
-}
-
 void LLFloaterCamera::updateState()
 {
-	getChildView(ZOOM)->setVisible(CAMERA_CTRL_MODE_PAN == mCurrMode);
-	
-	bool show_presets = (CAMERA_CTRL_MODE_PRESETS == mCurrMode) || (CAMERA_CTRL_MODE_FREE_CAMERA == mCurrMode
-																	&& CAMERA_CTRL_MODE_PRESETS == mPrevMode);
-	getChildView(PRESETS)->setVisible(show_presets);
-	
-	bool show_camera_modes = CAMERA_CTRL_MODE_MODES == mCurrMode || (CAMERA_CTRL_MODE_FREE_CAMERA == mCurrMode
-																	&& CAMERA_CTRL_MODE_MODES == mPrevMode);
-	getChildView("camera_modes_list")->setVisible( show_camera_modes);
-
 	updateItemsSelection();
 
 	if (CAMERA_CTRL_MODE_FREE_CAMERA == mCurrMode)
@@ -521,13 +499,13 @@ void LLFloaterCamera::updateState()
 
 void LLFloaterCamera::updateItemsSelection()
 {
-	ECameraPreset preset = (ECameraPreset) gSavedSettings.getU32("CameraPreset");
+	ECameraPreset preset = (ECameraPreset) gSavedSettings.getU32("CameraPresetType");
 	LLSD argument;
-	argument["selected"] = preset == CAMERA_PRESET_REAR_VIEW;
+	argument["selected"] = (preset == CAMERA_PRESET_REAR_VIEW) && !sFreeCamera;
 	getChild<LLPanelCameraItem>("rear_view")->setValue(argument);
-	argument["selected"] = preset == CAMERA_PRESET_GROUP_VIEW;
+	argument["selected"] = (preset == CAMERA_PRESET_GROUP_VIEW) && !sFreeCamera;
 	getChild<LLPanelCameraItem>("group_view")->setValue(argument);
-	argument["selected"] = preset == CAMERA_PRESET_FRONT_VIEW;
+	argument["selected"] = (preset == CAMERA_PRESET_FRONT_VIEW) && !sFreeCamera;
 	getChild<LLPanelCameraItem>("front_view")->setValue(argument);
 	argument["selected"] = gAgentCamera.getCameraMode() == CAMERA_MODE_MOUSELOOK;
 	getChild<LLPanelCameraItem>("mouselook_view")->setValue(argument);
@@ -547,19 +525,19 @@ 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);
+			camera_floater->updateItemsSelection();
+			camera_floater->fromFreeToPresets();
+		}
 	}
 	else
 	{
+		LLFloaterCamera* camera_floater = LLFloaterCamera::findInstance();
+		if (camera_floater)
+			camera_floater->switchMode(CAMERA_CTRL_MODE_PAN);
 		switchToPreset(name);
 	}
-
-	LLFloaterCamera* camera_floater = LLFloaterCamera::findInstance();
-	if (camera_floater)
-	{
-		camera_floater->updateItemsSelection();
-		camera_floater->fromFreeToPresets();
-	}
 }
 
 /*static*/
@@ -567,18 +545,49 @@ void LLFloaterCamera::switchToPreset(const std::string& name)
 {
 	sFreeCamera = false;
 	clear_camera_tool();
-	if ("rear_view" == name)
+	if (PRESETS_REAR_VIEW == name)
 	{
 		gAgentCamera.switchCameraPreset(CAMERA_PRESET_REAR_VIEW);
 	}
-	else if ("group_view" == name)
+	else if (PRESETS_SIDE_VIEW == name)
 	{
 		gAgentCamera.switchCameraPreset(CAMERA_PRESET_GROUP_VIEW);
 	}
-	else if ("front_view" == name)
+	else if (PRESETS_FRONT_VIEW == name)
 	{
 		gAgentCamera.switchCameraPreset(CAMERA_PRESET_FRONT_VIEW);
 	}
+	else
+	{
+		gAgentCamera.switchCameraPreset(CAMERA_PRESET_CUSTOM);
+	}
+	
+	if (gSavedSettings.getString("PresetCameraActive") != name)
+	{
+		LLPresetsManager::getInstance()->loadPreset(PRESETS_CAMERA, name);
+	}
+
+	if (isAgentAvatarValid() && gAgentAvatarp->getParent())
+	{
+		LLQuaternion sit_rot(gSavedSettings.getLLSD("AvatarSitRotation"));
+		if (sit_rot != LLQuaternion())
+		{
+			gAgent.rotate(~gAgent.getFrameAgent().getQuaternion());
+			gAgent.rotate(sit_rot);
+		}
+		else
+		{
+			gAgentCamera.rotateToInitSitRot();
+		}
+	}
+	gAgentCamera.resetCameraZoomFraction();
+
+	LLFloaterCamera* camera_floater = LLFloaterCamera::findInstance();
+	if (camera_floater)
+	{
+		camera_floater->updateItemsSelection();
+		camera_floater->fromFreeToPresets();
+	}
 }
 
 void LLFloaterCamera::fromFreeToPresets()
@@ -588,3 +597,41 @@ void LLFloaterCamera::fromFreeToPresets()
 		switchMode(CAMERA_CTRL_MODE_PRESETS);
 	}
 }
+
+void LLFloaterCamera::populatePresetCombo()
+{
+	LLPresetsManager::getInstance()->setPresetNamesInComboBox(PRESETS_CAMERA, mPresetCombo, EDefaultOptions::DEFAULT_HIDE);
+	std::string active_preset_name = gSavedSettings.getString("PresetCameraActive");
+	if (active_preset_name.empty())
+	{
+		gSavedSettings.setU32("CameraPresetType", CAMERA_PRESET_CUSTOM);
+		updateItemsSelection();
+		mPresetCombo->setLabel(getString("inactive_combo_text"));
+	}
+	else if ((ECameraPreset)gSavedSettings.getU32("CameraPresetType") == CAMERA_PRESET_CUSTOM)
+	{
+		mPresetCombo->selectByValue(active_preset_name);
+	}
+	else
+	{
+		mPresetCombo->setLabel(getString("inactive_combo_text"));
+	}
+	updateItemsSelection();
+}
+
+void LLFloaterCamera::onSavePreset()
+{
+	LLFloaterReg::hideInstance("delete_pref_preset", PRESETS_CAMERA);
+	LLFloaterReg::hideInstance("load_pref_preset", PRESETS_CAMERA);
+	
+	LLFloaterReg::showInstance("save_camera_preset");
+}
+
+void LLFloaterCamera::onCustomPresetSelected()
+{
+	std::string selected_preset = mPresetCombo->getSelectedItemLabel();
+	if (getString("inactive_combo_text") != selected_preset)
+	{
+		switchToPreset(selected_preset);
+	}
+}
diff --git a/indra/newview/llfloatercamera.h b/indra/newview/llfloatercamera.h
index 4d6d03f22d9078e150720bf44b6ee3fdba7753c1..9440f50c3fd15b6d8787702e6273d0b102f670b7 100644
--- a/indra/newview/llfloatercamera.h
+++ b/indra/newview/llfloatercamera.h
@@ -36,10 +36,10 @@ class LLJoystickCameraRotate;
 class LLJoystickCameraTrack;
 class LLFloaterReg;
 class LLPanelCameraZoom;
+class LLComboBox;
 
 enum ECameraControlMode
 {
-	CAMERA_CTRL_MODE_MODES,
 	CAMERA_CTRL_MODE_PAN,
 	CAMERA_CTRL_MODE_FREE_CAMERA,
 	CAMERA_CTRL_MODE_PRESETS
@@ -50,7 +50,6 @@ class LLFloaterCamera : public LLFloater
 	friend class LLFloaterReg;
 	
 public:
-
 	/* whether in free camera mode */
 	static bool inFreeCameraMode();
 	/* callback for camera items selection changing */
@@ -77,6 +76,11 @@ class LLFloaterCamera : public LLFloater
 	virtual void onOpen(const LLSD& key);
 	virtual void onClose(bool app_quitting);
 
+	void onSavePreset();
+	void onCustomPresetSelected();
+
+	void populatePresetCombo();
+
 	LLJoystickCameraRotate* mRotate;
 	LLPanelCameraZoom*	mZoom;
 	LLJoystickCameraTrack*	mTrack;
@@ -91,6 +95,10 @@ class LLFloaterCamera : public LLFloater
 
 	/*virtual*/ BOOL postBuild();
 
+	F32 getCurrentTransparency();
+
+	void onViewButtonClick(const LLSD& user_data);
+
 	ECameraControlMode determineMode();
 
 	/* resets to the previous mode */
@@ -108,9 +116,6 @@ class LLFloaterCamera : public LLFloater
 	/* update camera modes items selection and camera preset items selection according to the currently selected preset */
 	void updateItemsSelection();
 
-	void onClickBtn(ECameraControlMode mode);
-	void assignButton2Mode(ECameraControlMode mode, const std::string& button_name);
-	
 	// fills flatlist with items from given panel
 	void fillFlatlistFromPanel (LLFlatListView* list, LLPanel* panel);
 
@@ -124,6 +129,8 @@ class LLFloaterCamera : public LLFloater
 	ECameraControlMode mPrevMode;
 	ECameraControlMode mCurrMode;
 	std::map<ECameraControlMode, LLButton*> mMode2Button;
+
+	LLComboBox* mPresetCombo;
 };
 
 /**
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/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/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp
index 093753c9679faea06d43622d97b5ee83315acd37..951d11bbe5461fdeaded6139eedb7566fc37515d 100644
--- a/indra/newview/llfloaterpreference.cpp
+++ b/indra/newview/llfloaterpreference.cpp
@@ -725,7 +725,7 @@ void LLFloaterPreference::cancel()
 	// hide spellchecker settings folder
 	LLFloaterReg::hideInstance("prefs_spellchecker");
 
-	// hide advancede floater
+	// hide advanced graphics floater
 	LLFloaterReg::hideInstance("prefs_graphics_advanced");
 	
 	// reverts any changes to current skin
@@ -844,7 +844,8 @@ void LLFloaterPreference::onOpen(const LLSD& key)
 	saveSettings();
 
 	// Make sure there is a default preference file
-	LLPresetsManager::getInstance()->createMissingDefault();
+	LLPresetsManager::getInstance()->createMissingDefault(PRESETS_CAMERA);
+	LLPresetsManager::getInstance()->createMissingDefault(PRESETS_GRAPHIC);
 
 	bool started = (LLStartUp::getStartupState() == STATE_STARTED);
 
@@ -853,12 +854,15 @@ void LLFloaterPreference::onOpen(const LLSD& key)
 	LLButton* delete_btn = findChild<LLButton>("PrefDeleteButton");
 	LLButton* exceptions_btn = findChild<LLButton>("RenderExceptionsButton");
 
-	load_btn->setEnabled(started);
-	save_btn->setEnabled(started);
-	delete_btn->setEnabled(started);
-	exceptions_btn->setEnabled(started);
+	if (load_btn && save_btn && delete_btn && exceptions_btn)
+	{
+		load_btn->setEnabled(started);
+		save_btn->setEnabled(started);
+		delete_btn->setEnabled(started);
+		exceptions_btn->setEnabled(started);
+	}
 
-	collectSearchableItems();
+    collectSearchableItems();
 	if (!mFilterEdit->getText().empty())
 	{
 		mFilterEdit->setText(LLStringExplicit(""));
@@ -2633,20 +2637,17 @@ void LLPanelPreference::updateMediaAutoPlayCheckbox(LLUICtrl* ctrl)
 
 void LLPanelPreference::deletePreset(const LLSD& user_data)
 {
-	std::string subdirectory = user_data.asString();
-	LLFloaterReg::showInstance("delete_pref_preset", subdirectory);
+	LLFloaterReg::showInstance("delete_pref_preset", user_data.asString());
 }
 
 void LLPanelPreference::savePreset(const LLSD& user_data)
 {
-	std::string subdirectory = user_data.asString();
-	LLFloaterReg::showInstance("save_pref_preset", subdirectory);
+	LLFloaterReg::showInstance("save_pref_preset", user_data.asString());
 }
 
 void LLPanelPreference::loadPreset(const LLSD& user_data)
 {
-	std::string subdirectory = user_data.asString();
-	LLFloaterReg::showInstance("load_pref_preset", subdirectory);
+	LLFloaterReg::showInstance("load_pref_preset", user_data.asString());
 }
 
 void LLPanelPreference::setHardwareDefaults()
@@ -2704,7 +2705,7 @@ BOOL LLPanelPreferenceGraphics::postBuild()
 
 	LLPresetsManager* presetsMgr = LLPresetsManager::getInstance();
     presetsMgr->setPresetListChangeCallback(boost::bind(&LLPanelPreferenceGraphics::onPresetsListChange, this));
-    presetsMgr->createMissingDefault(); // a no-op after the first time, but that's ok
+    presetsMgr->createMissingDefault(PRESETS_GRAPHIC); // a no-op after the first time, but that's ok
     
 	return LLPanelPreference::postBuild();
 }
@@ -2725,11 +2726,6 @@ void LLPanelPreferenceGraphics::onPresetsListChange()
 	{
 		instance->saveSettings(); //make cancel work correctly after changing the preset
 	}
-	else
-	{
-		std::string dummy;
-		instance->saveGraphicsPreset(dummy);
-	}
 }
 
 void LLPanelPreferenceGraphics::setPresetText()
diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h
index a0f43bd8848e4560e3605d3c014d499e90e6ff37..526214a617b2af0fa67e434c723aa90af28b8ec9 100644
--- a/indra/newview/llfloaterpreference.h
+++ b/indra/newview/llfloaterpreference.h
@@ -191,6 +191,7 @@ class LLFloaterPreference : public LLFloater, public LLAvatarPropertiesObserver,
 	void buildPopupLists();
 	static void refreshSkin(void* data);
 	void selectPanel(const LLSD& name);
+	void saveCameraPreset(std::string& preset);
 	void saveGraphicsPreset(std::string& preset);
 
 private:
@@ -214,6 +215,7 @@ class LLFloaterPreference : public LLFloater, public LLAvatarPropertiesObserver,
 	std::string mDirectoryVisibility;
 	
 	LLAvatarData mAvatarProperties;
+	std::string mSavedCameraPreset;
 	std::string mSavedGraphicsPreset;
 	LOG_CLASS(LLFloaterPreference);
 
@@ -288,7 +290,6 @@ class LLPanelPreferenceGraphics : public LLPanelPreference
 	bool hasDirtyChilds();
 
 private:
-
 	void onPresetsListChange();
 	LOG_CLASS(LLPanelPreferenceGraphics);
 };
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 fb202b4c406189156a8a0a86de2316aec649156e..186994c8571c118669aef4a2642e6cf55f4fd526 100644
--- a/indra/newview/llfloatersettingsdebug.cpp
+++ b/indra/newview/llfloatersettingsdebug.cpp
@@ -111,6 +111,7 @@ void LLFloaterSettingsDebug::onCommitSettings()
 
 	LLVector3 vector;
 	LLVector3d vectord;
+	LLQuaternion quat;
 	LLRect rect;
 	LLColor4 col4;
 	LLColor3 col3;
@@ -146,6 +147,13 @@ void LLFloaterSettingsDebug::onCommitSettings()
 		vectord.mdV[VZ] = getChild<LLUICtrl>("val_spinner_3")->getValue().asReal();
 		controlp->set(vectord.getValue());
 		break;
+	  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();
@@ -351,6 +359,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;
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/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 9b4dc5474a08e644ed0121935862e91ebb848958..aa5ba3f210ea5ccef5052e366ec59db0a6b6d934 100644
--- a/indra/newview/llpanelpresetspulldown.cpp
+++ b/indra/newview/llpanelpresetspulldown.cpp
@@ -40,9 +40,6 @@
 #include "llscrolllistctrl.h"
 #include "lltrans.h"
 
-/* static */ const F32 LLPanelPresetsPulldown::sAutoCloseFadeStartTimeSec = 2.0f;
-/* static */ const F32 LLPanelPresetsPulldown::sAutoCloseTotalTimeSec = 3.0f;
-
 ///----------------------------------------------------------------------------
 /// Class LLPanelPresetsPulldown
 ///----------------------------------------------------------------------------
@@ -63,17 +60,16 @@ BOOL LLPanelPresetsPulldown::postBuild()
 	LLPresetsManager* presetsMgr = LLPresetsManager::getInstance();
     presetsMgr->setPresetListChangeCallback(boost::bind(&LLPanelPresetsPulldown::populatePanel, this));
 	// Make sure there is a default preference file
-    presetsMgr->createMissingDefault();
+    presetsMgr->createMissingDefault(PRESETS_GRAPHIC);
 
 	populatePanel();
 
-	return LLPanel::postBuild();
+	return LLPanelPulldown::postBuild();
 }
 
 void LLPanelPresetsPulldown::populatePanel()
 {
-	std::string presets_dir = LLPresetsManager::getInstance()->getPresetsDir(PRESETS_GRAPHIC);
-	LLPresetsManager::getInstance()->loadPresetNamesFromDir(presets_dir, mPresetNames, DEFAULT_TOP);
+	LLPresetsManager::getInstance()->loadPresetNamesFromDir(PRESETS_GRAPHIC, mPresetNames, DEFAULT_TOP);
 
 	LLScrollListCtrl* scroll = getChild<LLScrollListCtrl>("preset_list");
 
@@ -112,61 +108,6 @@ void LLPanelPresetsPulldown::populatePanel()
 	}
 }
 
-/*virtual*/
-void LLPanelPresetsPulldown::onMouseEnter(S32 x, S32 y, MASK mask)
-{
-	mHoverTimer.stop();
-	LLPanel::onMouseEnter(x,y,mask);
-}
-
-/*virtual*/
-void LLPanelPresetsPulldown::onTopLost()
-{
-	setVisible(FALSE);
-}
-
-/*virtual*/
-BOOL LLPanelPresetsPulldown::handleMouseDown(S32 x, S32 y, MASK mask)
-{
-    LLPanel::handleMouseDown(x,y,mask);
-    return TRUE;
-}
-
-/*virtual*/
-BOOL LLPanelPresetsPulldown::handleRightMouseDown(S32 x, S32 y, MASK mask)
-{
-    LLPanel::handleRightMouseDown(x, y, mask);
-    return TRUE;
-}
-
-/*virtual*/
-BOOL LLPanelPresetsPulldown::handleDoubleClick(S32 x, S32 y, MASK mask)
-{
-    LLPanel::handleDoubleClick(x, y, mask);
-    return TRUE;
-}
-
-/*virtual*/
-void LLPanelPresetsPulldown::onMouseLeave(S32 x, S32 y, MASK mask)
-{
-	mHoverTimer.start();
-	LLPanel::onMouseLeave(x,y,mask);
-}
-
-/*virtual*/ 
-void LLPanelPresetsPulldown::onVisibilityChange ( BOOL new_visibility )
-{
-	if (new_visibility)	
-	{
-		mHoverTimer.start(); // timer will be stopped when mouse hovers over panel
-	}
-	else
-	{
-		mHoverTimer.stop();
-
-	}
-}
-
 void LLPanelPresetsPulldown::onRowClick(const LLSD& user_data)
 {
 	LLScrollListCtrl* scroll = getChild<LLScrollListCtrl>("preset_list");
@@ -213,19 +154,3 @@ void LLPanelPresetsPulldown::onGraphicsButtonClick(const LLSD& user_data)
 		}
 	}
 }
-
-//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 322bf5a58f5414bf54624677d3d6d7aae825daa3..c0d32b9b21a4df5300efc26c9cf24f19ece191fb 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/llprogressview.cpp b/indra/newview/llprogressview.cpp
index 083a913ef8bb1479d0ad248d0a2209eebeb2f367..e9feae3457bec753b914eec5d3969f3dbcf11469 100644
--- a/indra/newview/llprogressview.cpp
+++ b/indra/newview/llprogressview.cpp
@@ -229,6 +229,33 @@ void LLProgressView::drawStartTexture(F32 alpha)
 	gGL.popMatrix();
 }
 
+void LLProgressView::drawLogos(F32 alpha)
+{
+    if (mLogosList.empty())
+    {
+        return;
+    }
+
+    // logos are tied to label,
+    // due to potential resizes we have to figure offsets out on draw or resize
+    LLTextBox *logos_label = getChild<LLTextBox>("logos_lbl");
+    S32 offset_x, offset_y;
+    logos_label->localPointToScreen(0, 0, &offset_x, &offset_y);
+    std::vector<TextureData>::const_iterator iter = mLogosList.begin();
+    std::vector<TextureData>::const_iterator end = mLogosList.end();
+    for (; iter != end; iter++)
+    {
+        gl_draw_scaled_image_with_border(iter->mDrawRect.mLeft + offset_x,
+                             iter->mDrawRect.mBottom + offset_y,
+                             iter->mDrawRect.getWidth(),
+                             iter->mDrawRect.getHeight(),
+                             iter->mTexturep.get(),
+                             UI_VERTEX_COLOR % alpha,
+                             FALSE,
+                             iter->mClipRect,
+                             iter->mOffsetRect);
+    }
+}
 
 void LLProgressView::draw()
 {
@@ -245,6 +272,7 @@ void LLProgressView::draw()
 		}
 		
 		LLPanel::draw();
+		drawLogos(alpha);
 		return;
 	}
 
@@ -257,6 +285,7 @@ void LLProgressView::draw()
 				
 		drawStartTexture(alpha);
 		LLPanel::draw();
+		drawLogos(alpha);
 
 		// faded out completely - remove panel and reveal world
 		if (mFadeToWorldTimer.getElapsedTimeF32() > FADE_TO_WORLD_TIME )
@@ -283,7 +312,7 @@ void LLProgressView::draw()
 			// FIXME: this causes a crash that i haven't been able to fix
 			mMediaCtrl->unloadMediaSource();	
 
-			gStartTexture = NULL;
+            releaseTextures();
 		}
 		return;
 	}
@@ -291,6 +320,7 @@ void LLProgressView::draw()
 	drawStartTexture(1.0f);
 	// draw children
 	LLPanel::draw();
+	drawLogos(1.0f);
 }
 
 void LLProgressView::setText(const std::string& text)
@@ -309,6 +339,196 @@ void LLProgressView::setMessage(const std::string& msg)
 	getChild<LLUICtrl>("message_text")->setValue(mMessage);
 }
 
+void LLProgressView::loadLogo(const std::string &path,
+                              const U8 image_codec,
+                              const LLRect &pos_rect,
+                              const LLRectf &clip_rect,
+                              const LLRectf &offset_rect)
+{
+    // We need these images very early, so we have to force-load them, otherwise they might not load in time.
+    if (!gDirUtilp->fileExists(path))
+    {
+        return;
+    }
+
+    LLPointer<LLImageFormatted> start_image_frmted = LLImageFormatted::createFromType(image_codec);
+    if (!start_image_frmted->load(path))
+    {
+        LL_WARNS("AppInit") << "Image load failed: " << path << LL_ENDL;
+        return;
+    }
+
+    LLPointer<LLImageRaw> raw = new LLImageRaw;
+    if (!start_image_frmted->decode(raw, 0.0f))
+    {
+        LL_WARNS("AppInit") << "Image decode failed " << path << LL_ENDL;
+        return;
+    }
+    // HACK: getLocalTexture allows only power of two dimentions
+    raw->expandToPowerOfTwo();
+
+    TextureData data;
+    data.mTexturep = LLViewerTextureManager::getLocalTexture(raw.get(), FALSE);
+    data.mDrawRect = pos_rect;
+    data.mClipRect = clip_rect;
+    data.mOffsetRect = offset_rect;
+    mLogosList.push_back(data);
+}
+
+void LLProgressView::initLogos()
+{
+    mLogosList.clear();
+
+    const U8 image_codec = IMG_CODEC_PNG;
+    const LLRectf default_clip(0.f, 1.f, 1.f, 0.f);
+    const S32 default_height = 28;
+    const S32 default_pad = 15;
+
+    S32 icon_width, icon_height;
+
+    // We don't know final screen rect yet, so we can't precalculate position fully
+    LLTextBox *logos_label = getChild<LLTextBox>("logos_lbl");
+    S32 texture_start_x = logos_label->getFont()->getWidthF32(logos_label->getText()) + default_pad;
+    S32 texture_start_y = -7;
+
+    // Normally we would just preload these textures from textures.xml,
+    // and display them via icon control, but they are only needed on
+    // startup and preloaded/UI ones stay forever
+    // (and this code was done already so simply reused it)
+    std::string temp_str = gDirUtilp->getExpandedFilename(LL_PATH_DEFAULT_SKIN, "textures", "3p_icons");
+
+    temp_str += gDirUtilp->getDirDelimiter();
+
+#ifdef LL_FMODSTUDIO
+    // original image size is 264x96, it is on longer side but
+    // with no internal paddings so it gets additional padding
+    icon_width = 77;
+    icon_height = 21;
+    S32 pad_y = 4;
+    texture_start_x++;
+    loadLogo(temp_str + "fmod_logo.png",
+        image_codec,
+        LLRect(texture_start_x, texture_start_y + pad_y + icon_height, texture_start_x + icon_width, texture_start_y + pad_y),
+        default_clip,
+        default_clip);
+
+    texture_start_x += icon_width + default_pad + 1;
+#endif
+    // original image size is 342x113, central element is on a larger side
+    // plus internal padding, so it gets slightly more height than desired 32
+    icon_width = 88;
+    icon_height = 29;
+    pad_y = -1;
+    loadLogo(temp_str + "havok_logo.png",
+        image_codec,
+        LLRect(texture_start_x, texture_start_y + pad_y + icon_height, texture_start_x + icon_width, texture_start_y + pad_y),
+        default_clip,
+        default_clip);
+
+    texture_start_x += icon_width + default_pad;
+
+    // 108x41
+    icon_width = 74;
+    loadLogo(temp_str + "vivox_logo.png",
+        image_codec,
+        LLRect(texture_start_x, texture_start_y + default_height, texture_start_x + icon_width, texture_start_y),
+        default_clip,
+        default_clip);
+}
+
+void LLProgressView::initStartTexture(S32 location_id, bool is_in_production)
+{
+    if (gStartTexture.notNull())
+    {
+        gStartTexture = NULL;
+        LL_INFOS("AppInit") << "re-initializing start screen" << LL_ENDL;
+    }
+
+    LL_DEBUGS("AppInit") << "Loading startup bitmap..." << LL_ENDL;
+
+    U8 image_codec = IMG_CODEC_PNG;
+    std::string temp_str = gDirUtilp->getLindenUserDir() + gDirUtilp->getDirDelimiter();
+
+    if ((S32)START_LOCATION_ID_LAST == location_id)
+    {
+        temp_str += LLStartUp::getScreenLastFilename();
+    }
+    else
+    {
+        std::string path = temp_str + LLStartUp::getScreenHomeFilename();
+
+        if (!gDirUtilp->fileExists(path) && is_in_production)
+        {
+            // Fallback to old file, can be removed later
+            // Home image only sets when user changes home, so it will take time for users to switch to pngs
+            temp_str += "screen_home.bmp";
+            image_codec = IMG_CODEC_BMP;
+        }
+        else
+        {
+            temp_str = path;
+        }
+    }
+
+    LLPointer<LLImageFormatted> start_image_frmted = LLImageFormatted::createFromType(image_codec);
+
+    // Turn off start screen to get around the occasional readback 
+    // driver bug
+    if (!gSavedSettings.getBOOL("UseStartScreen"))
+    {
+        LL_INFOS("AppInit") << "Bitmap load disabled" << LL_ENDL;
+        return;
+    }
+    else if (!start_image_frmted->load(temp_str))
+    {
+        LL_WARNS("AppInit") << "Bitmap load failed" << LL_ENDL;
+        gStartTexture = NULL;
+    }
+    else
+    {
+        gStartImageWidth = start_image_frmted->getWidth();
+        gStartImageHeight = start_image_frmted->getHeight();
+
+        LLPointer<LLImageRaw> raw = new LLImageRaw;
+        if (!start_image_frmted->decode(raw, 0.0f))
+        {
+            LL_WARNS("AppInit") << "Bitmap decode failed" << LL_ENDL;
+            gStartTexture = NULL;
+        }
+        else
+        {
+            // HACK: getLocalTexture allows only power of two dimentions
+            raw->expandToPowerOfTwo();
+            gStartTexture = LLViewerTextureManager::getLocalTexture(raw.get(), FALSE);
+        }
+    }
+
+    if (gStartTexture.isNull())
+    {
+        gStartTexture = LLViewerTexture::sBlackImagep;
+        gStartImageWidth = gStartTexture->getWidth();
+        gStartImageHeight = gStartTexture->getHeight();
+    }
+}
+
+void LLProgressView::initTextures(S32 location_id, bool is_in_production)
+{
+    initStartTexture(location_id, is_in_production);
+    initLogos();
+
+    childSetVisible("panel_icons", mLogosList.empty() ? FALSE : TRUE);
+    childSetVisible("panel_top_spacer", mLogosList.empty() ? TRUE : FALSE);
+}
+
+void LLProgressView::releaseTextures()
+{
+    gStartTexture = NULL;
+    mLogosList.clear();
+
+    childSetVisible("panel_top_spacer", TRUE);
+    childSetVisible("panel_icons", FALSE);
+}
+
 void LLProgressView::setCancelButtonVisible(BOOL b, const std::string& label)
 {
 	mCancelBtn->setVisible( b );
diff --git a/indra/newview/llprogressview.h b/indra/newview/llprogressview.h
index 813576b21d181b196061fbe17b12ff044b7fddc6..56377a5889d235eee6973220969a1946ecd5b9c9 100644
--- a/indra/newview/llprogressview.h
+++ b/indra/newview/llprogressview.h
@@ -35,6 +35,7 @@
 class LLImageRaw;
 class LLButton;
 class LLProgressBar;
+class LLViewerTexture;
 
 class LLProgressView : 
 	public LLPanel,
@@ -51,6 +52,7 @@ class LLProgressView :
 
 	/*virtual*/ void draw();
 	void drawStartTexture(F32 alpha);
+	void drawLogos(F32 alpha);
 
 	/*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
 	/*virtual*/ BOOL handleKeyHere(KEY key, MASK mask);
@@ -70,6 +72,10 @@ class LLProgressView :
 
 	void setStartupComplete();
 
+	// we have to preload local textures to make sure they won't be grey
+	void initTextures(S32 location_id, bool is_in_production);
+	void releaseTextures();
+
 	void setCancelButtonVisible(BOOL b, const std::string& label);
 
 	static void onCancelButtonClicked( void* );
@@ -95,6 +101,25 @@ class LLProgressView :
 
 	bool handleUpdate(const LLSD& event_data);
 	static void onIdle(void* user_data);
+    void loadLogo(const std::string &path, const U8 image_codec, const LLRect &pos_rect, const LLRectf &clip_rect, const LLRectf &offset_rect);
+    // logos have unusual location and need to be preloaded to not appear grey, then deleted
+    void initLogos();
+    // Loads a bitmap to display during load
+    void initStartTexture(S32 location_id, bool is_in_production);
+
+private:
+    // We need to draw textures on login, but only once.
+    // So this vector gets filled up for textures to render and gets cleaned later
+    // Some textures have unusual requirements, so we are rendering directly
+    class TextureData
+    {
+    public:
+        LLPointer<LLViewerTexture> mTexturep;
+        LLRect mDrawRect;
+        LLRectf mClipRect;
+        LLRectf mOffsetRect;
+    };
+    std::vector<TextureData> mLogosList;
 };
 
 #endif // LL_LLPROGRESSVIEW_H
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 7673bae725aaace68d2b2a8f52d0f05d2f5fe89a..1be1c4ba9661db65d8e18ae4964e2a11be64d8b7 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -39,8 +39,8 @@
 #include "llviewermedia_streamingaudio.h"
 #include "llaudioengine.h"
 
-#ifdef LL_FMODEX
-# include "llaudioengine_fmodex.h"
+#ifdef LL_FMODSTUDIO
+# include "llaudioengine_fmodstudio.h"
 #endif
 
 #ifdef LL_OPENAL
@@ -71,6 +71,7 @@
 #include "llnotifications.h"
 #include "llnotificationsutil.h"
 #include "llpersistentnotificationstorage.h"
+#include "llpresetsmanager.h"
 #include "llteleporthistory.h"
 #include "llregionhandle.h"
 #include "llsd.h"
@@ -622,13 +623,13 @@ bool idle_startup()
 			delete gAudiop;
 			gAudiop = NULL;
 
-#ifdef LL_FMODEX		
+#ifdef LL_FMODSTUDIO
 #if !LL_WINDOWS
-			if (NULL == getenv("LL_BAD_FMODEX_DRIVER"))
+            if (NULL == getenv("LL_BAD_FMODSTUDIO_DRIVER"))
 #endif // !LL_WINDOWS
-			{
-				gAudiop = (LLAudioEngine *) new LLAudioEngine_FMODEX(gSavedSettings.getBOOL("FMODExProfilerEnable"));
-			}
+            {
+                gAudiop = (LLAudioEngine *) new LLAudioEngine_FMODSTUDIO(gSavedSettings.getBOOL("FMODExProfilerEnable"));
+            }
 #endif
 
 #ifdef LL_OPENAL
@@ -649,7 +650,7 @@ bool idle_startup()
 #else
 				void* window_handle = NULL;
 #endif
-				bool init = gAudiop->init(kAUDIO_NUM_SOURCES, window_handle);
+				bool init = gAudiop->init(kAUDIO_NUM_SOURCES, window_handle, LLAppViewer::instance()->getSecondLifeTitle());
 				if(init)
 				{
 					gAudiop->setMuted(TRUE);
@@ -993,9 +994,8 @@ bool idle_startup()
 
 		gViewerWindow->getWindow()->setCursor(UI_CURSOR_WAIT);
 
-		init_start_screen(agent_location_id);
-
 		// Display the startup progress bar.
+		gViewerWindow->initTextures(agent_location_id);
 		gViewerWindow->setShowProgress(TRUE);
 		gViewerWindow->setProgressCancelButtonVisible(TRUE, LLTrans::getString("Quit"));
 
@@ -1965,6 +1965,8 @@ bool idle_startup()
 		// JC - 7/20/2002
 		gViewerWindow->sendShapeToSim();
 
+		LLPresetsManager::getInstance()->createMissingDefault(PRESETS_CAMERA);
+
 		// The reason we show the alert is because we want to
 		// reduce confusion for when you log in and your provided
 		// location is not your expected location. So, if this is
@@ -2714,81 +2716,6 @@ std::string LLStartUp::getUserId()
     return gUserCredential->userID();
 }
 
-// Loads a bitmap to display during load
-void init_start_screen(S32 location_id)
-{
-	if (gStartTexture.notNull())
-	{
-		gStartTexture = NULL;
-		LL_INFOS("AppInit") << "re-initializing start screen" << LL_ENDL;
-	}
-
-	LL_DEBUGS("AppInit") << "Loading startup bitmap..." << LL_ENDL;
-
-	U8 image_codec = IMG_CODEC_PNG;
-	std::string temp_str = gDirUtilp->getLindenUserDir() + gDirUtilp->getDirDelimiter();
-
-	if ((S32)START_LOCATION_ID_LAST == location_id)
-	{
-		temp_str += LLStartUp::getScreenLastFilename();
-	}
-	else
-	{
-		std::string path = temp_str + LLStartUp::getScreenHomeFilename();
-		
-		if (!gDirUtilp->fileExists(path) && LLGridManager::getInstance()->isInProductionGrid())
-		{
-			// Fallback to old file, can be removed later
-			// Home image only sets when user changes home, so it will take time for users to switch to pngs
-			temp_str += "screen_home.bmp";
-			image_codec = IMG_CODEC_BMP;
-		}
-		else
-		{
-			temp_str = path;
-		}
-	}
-
-	LLPointer<LLImageFormatted> start_image_frmted = LLImageFormatted::createFromType(image_codec);
-
-	// Turn off start screen to get around the occasional readback 
-	// driver bug
-	if(!gSavedSettings.getBOOL("UseStartScreen"))
-	{
-		LL_INFOS("AppInit")  << "Bitmap load disabled" << LL_ENDL;
-		return;
-	}
-	else if(!start_image_frmted->load(temp_str) )
-	{
-		LL_WARNS("AppInit") << "Bitmap load failed" << LL_ENDL;
-		gStartTexture = NULL;
-	}
-	else
-	{
-		gStartImageWidth = start_image_frmted->getWidth();
-		gStartImageHeight = start_image_frmted->getHeight();
-
-		LLPointer<LLImageRaw> raw = new LLImageRaw;
-		if (!start_image_frmted->decode(raw, 0.0f))
-		{
-			LL_WARNS("AppInit") << "Bitmap decode failed" << LL_ENDL;
-			gStartTexture = NULL;
-		}
-		else
-		{
-			raw->expandToPowerOfTwo();
-			gStartTexture = LLViewerTextureManager::getLocalTexture(raw.get(), FALSE) ;
-		}
-	}
-
-	if(gStartTexture.isNull())
-	{
-		gStartTexture = LLViewerTexture::sBlackImagep ;
-		gStartImageWidth = gStartTexture->getWidth() ;
-		gStartImageHeight = gStartTexture->getHeight() ;
-	}
-}
-
 
 // frees the bitmap
 void release_start_screen()
diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp
index b8c227334da1ab21818d0fde9477abf3ead9c359..4d55448d78ae140182b35428a7f08e0ef87f29ad 100644
--- a/indra/newview/llstatusbar.cpp
+++ b/indra/newview/llstatusbar.cpp
@@ -38,6 +38,7 @@
 #include "llfloaterbuycurrency.h"
 #include "llbuycurrencyhtml.h"
 #include "llpanelnearbymedia.h"
+#include "llpanelpresetscamerapulldown.h"
 #include "llpanelpresetspulldown.h"
 #include "llpanelvolumepulldown.h"
 #include "llfloaterregioninfo.h"
@@ -172,8 +173,11 @@ BOOL LLStatusBar::postBuild()
 	mBoxBalance = getChild<LLTextBox>("balance");
 	mBoxBalance->setClickedCallback( &LLStatusBar::onClickBalance, this );
 
-	mIconPresets = getChild<LLIconCtrl>( "presets_icon" );
-	mIconPresets->setMouseEnterCallback(boost::bind(&LLStatusBar::onMouseEnterPresets, this));
+	mIconPresetsCamera = getChild<LLIconCtrl>( "presets_icon_camera" );
+	mIconPresetsCamera->setMouseEnterCallback(boost::bind(&LLStatusBar::onMouseEnterPresetsCamera, this));
+
+	mIconPresetsGraphic = getChild<LLIconCtrl>( "presets_icon_graphic" );
+	mIconPresetsGraphic->setMouseEnterCallback(boost::bind(&LLStatusBar::onMouseEnterPresets, this));
 
 	mBtnVolume = getChild<LLButton>( "volume_btn" );
 	mBtnVolume->setClickedCallback( onClickVolume, this );
@@ -228,6 +232,11 @@ BOOL LLStatusBar::postBuild()
 	mSGPacketLoss = LLUICtrlFactory::create<LLStatGraph>(pgp);
 	addChild(mSGPacketLoss);
 
+	mPanelPresetsCameraPulldown = new LLPanelPresetsCameraPulldown();
+	addChild(mPanelPresetsCameraPulldown);
+	mPanelPresetsCameraPulldown->setFollows(FOLLOWS_TOP|FOLLOWS_RIGHT);
+	mPanelPresetsCameraPulldown->setVisible(FALSE);
+
 	mPanelPresetsPulldown = new LLPanelPresetsPulldown();
 	addChild(mPanelPresetsPulldown);
 	mPanelPresetsPulldown->setFollows(FOLLOWS_TOP|FOLLOWS_RIGHT);
@@ -344,7 +353,8 @@ void LLStatusBar::setVisibleForMouselook(bool visible)
 	mSGPacketLoss->setVisible(visible);
 	mSearchPanel->setVisible(visible && gSavedSettings.getBOOL("MenuSearch"));
 	setBackgroundVisible(visible);
-	mIconPresets->setVisible(visible);
+	mIconPresetsCamera->setVisible(visible);
+	mIconPresetsGraphic->setVisible(visible);
 }
 
 void LLStatusBar::debitBalance(S32 debit)
@@ -485,10 +495,34 @@ void LLStatusBar::onClickBuyCurrency()
 	LLFirstUse::receiveLindens(false);
 }
 
+void LLStatusBar::onMouseEnterPresetsCamera()
+{
+	LLView* popup_holder = gViewerWindow->getRootView()->getChildView("popup_holder");
+	LLIconCtrl* icon =  getChild<LLIconCtrl>( "presets_icon_camera" );
+	LLRect icon_rect = icon->getRect();
+	LLRect pulldown_rect = mPanelPresetsCameraPulldown->getRect();
+	pulldown_rect.setLeftTopAndSize(icon_rect.mLeft -
+	     (pulldown_rect.getWidth() - icon_rect.getWidth()),
+			       icon_rect.mBottom,
+			       pulldown_rect.getWidth(),
+			       pulldown_rect.getHeight());
+
+	pulldown_rect.translate(popup_holder->getRect().getWidth() - pulldown_rect.mRight, 0);
+	mPanelPresetsCameraPulldown->setShape(pulldown_rect);
+
+	// show the master presets pull-down
+	LLUI::getInstance()->clearPopups();
+	LLUI::getInstance()->addPopup(mPanelPresetsCameraPulldown);
+	mPanelNearByMedia->setVisible(FALSE);
+	mPanelVolumePulldown->setVisible(FALSE);
+	mPanelPresetsPulldown->setVisible(FALSE);
+	mPanelPresetsCameraPulldown->setVisible(TRUE);
+}
+
 void LLStatusBar::onMouseEnterPresets()
 {
 	LLView* popup_holder = gViewerWindow->getRootView()->getChildView("popup_holder");
-	LLIconCtrl* icon =  getChild<LLIconCtrl>( "presets_icon" );
+	LLIconCtrl* icon =  getChild<LLIconCtrl>( "presets_icon_graphic" );
 	LLRect icon_rect = icon->getRect();
 	LLRect pulldown_rect = mPanelPresetsPulldown->getRect();
 	pulldown_rect.setLeftTopAndSize(icon_rect.mLeft -
@@ -527,6 +561,7 @@ void LLStatusBar::onMouseEnterVolume()
 	// show the master volume pull-down
 	LLUI::getInstance()->clearPopups();
 	LLUI::getInstance()->addPopup(mPanelVolumePulldown);
+	mPanelPresetsCameraPulldown->setVisible(FALSE);
 	mPanelPresetsPulldown->setVisible(FALSE);
 	mPanelNearByMedia->setVisible(FALSE);
 	mPanelVolumePulldown->setVisible(TRUE);
@@ -551,6 +586,7 @@ void LLStatusBar::onMouseEnterNearbyMedia()
 	LLUI::getInstance()->clearPopups();
 	LLUI::getInstance()->addPopup(mPanelNearByMedia);
 
+	mPanelPresetsCameraPulldown->setVisible(FALSE);
 	mPanelPresetsPulldown->setVisible(FALSE);
 	mPanelVolumePulldown->setVisible(FALSE);
 	mPanelNearByMedia->setVisible(TRUE);
diff --git a/indra/newview/llstatusbar.h b/indra/newview/llstatusbar.h
index cad877f7991ed7eb42730c0a9b859c8b2e148305..3002b91c107479ec544d0e33c0003232c6f47aef 100644
--- a/indra/newview/llstatusbar.h
+++ b/indra/newview/llstatusbar.h
@@ -41,6 +41,7 @@ class LLUICtrl;
 class LLUUID;
 class LLFrameTimer;
 class LLStatGraph;
+class LLPanelPresetsCameraPulldown;
 class LLPanelPresetsPulldown;
 class LLPanelVolumePulldown;
 class LLPanelNearByMedia;
@@ -99,6 +100,7 @@ class LLStatusBar
 	void onClickBuyCurrency();
 	void onVolumeChanged(const LLSD& newvalue);
 
+	void onMouseEnterPresetsCamera();
 	void onMouseEnterPresets();
 	void onMouseEnterVolume();
 	void onMouseEnterNearbyMedia();
@@ -123,7 +125,8 @@ class LLStatusBar
 	LLStatGraph *mSGBandwidth;
 	LLStatGraph *mSGPacketLoss;
 
-	LLIconCtrl	*mIconPresets;
+	LLIconCtrl	*mIconPresetsCamera;
+	LLIconCtrl	*mIconPresetsGraphic;
 	LLButton	*mBtnVolume;
 	LLTextBox	*mBoxBalance;
 	LLButton	*mMediaToggle;
@@ -135,6 +138,7 @@ class LLStatusBar
 	S32				mSquareMetersCommitted;
 	LLFrameTimer*	mBalanceTimer;
 	LLFrameTimer*	mHealthTimer;
+	LLPanelPresetsCameraPulldown* mPanelPresetsCameraPulldown;
 	LLPanelPresetsPulldown* mPanelPresetsPulldown;
 	LLPanelVolumePulldown* mPanelVolumePulldown;
 	LLPanelNearByMedia*	mPanelNearByMedia;
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index c608c589a58aa3e93493446c88a0e8928b8ccd9d..414ae1fad63688b611c735a1f1573b1e0c4f970c 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -53,6 +53,7 @@
 #include "llfloaterbuyland.h"
 #include "llfloaterbvhpreview.h"
 #include "llfloatercamera.h"
+#include "llfloatercamerapresets.h"
 #include "llfloaterchatvoicevolume.h"
 #include "llfloaterconversationlog.h"
 #include "llfloaterconversationpreview.h"
@@ -104,12 +105,14 @@
 #include "llfloaterperms.h"
 #include "llfloaterpostprocess.h"
 #include "llfloaterpreference.h"
+#include "llfloaterpreferenceviewadvanced.h"
 #include "llfloaterpreviewtrash.h"
 #include "llfloaterproperties.h"
 #include "llfloaterregiondebugconsole.h"
 #include "llfloaterregioninfo.h"
 #include "llfloaterregionrestarting.h"
 #include "llfloaterreporter.h"
+#include "llfloatersavecamerapreset.h"
 #include "llfloatersaveprefpreset.h"
 #include "llfloatersceneloadstats.h"
 #include "llfloaterscriptdebug.h"
@@ -212,6 +215,7 @@ void LLViewerFloaterReg::registerFloaters()
 	LLFloaterReg::add("bumps", "floater_bumps.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterBump>);
 
 	LLFloaterReg::add("camera", "floater_camera.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCamera>);
+	LLFloaterReg::add("camera_presets", "floater_camera_presets.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCameraPresets>);
 	LLFloaterReg::add("chat_voice", "floater_voice_chat_volume.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterChatVoiceVolume>);
 	LLFloaterReg::add("nearby_chat", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterIMNearbyChat::buildFloater);
 	LLFloaterReg::add("compile_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCompileQueue>);
@@ -299,6 +303,7 @@ void LLViewerFloaterReg::registerFloaters()
 	LLFloaterReg::add("places", "floater_places.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSidePanelContainer>);
 	LLFloaterReg::add("preferences", "floater_preferences.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterPreference>);
 	LLFloaterReg::add("prefs_graphics_advanced", "floater_preferences_graphics_advanced.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterPreferenceGraphicsAdvanced>);
+	LLFloaterReg::add("prefs_view_advanced", "floater_preferences_view_advanced.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterPreferenceViewAdvanced>);
 	LLFloaterReg::add("prefs_proxy", "floater_preferences_proxy.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterPreferenceProxy>);
 	LLFloaterReg::add("prefs_spellchecker_import", "floater_spellcheck_import.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSpellCheckerImport>);
 	LLFloaterReg::add("prefs_translation", "floater_translation_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterTranslationSettings>);
@@ -318,6 +323,7 @@ void LLViewerFloaterReg::registerFloaters()
 	LLFloaterReg::add("properties", "floater_inventory_item_properties.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterProperties>);
 	LLFloaterReg::add("publish_classified", "floater_publish_classified.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLPublishClassifiedFloater>);
 	LLFloaterReg::add("save_pref_preset", "floater_save_pref_preset.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSavePrefPreset>);
+	LLFloaterReg::add("save_camera_preset", "floater_save_camera_preset.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSaveCameraPreset>);
 	LLFloaterReg::add("script_colors", "floater_script_ed_prefs.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterScriptEdPrefs>);
 
 	LLFloaterReg::add("telehubs", "floater_telehub.xml",&LLFloaterReg::build<LLFloaterTelehub>);
diff --git a/indra/newview/llviewerkeyboard.cpp b/indra/newview/llviewerkeyboard.cpp
index a14041717f26a52528cb46539751cd7f06da966f..6914e0fc2bcd35787062fc769cf7f784190a7bba 100644
--- a/indra/newview/llviewerkeyboard.cpp
+++ b/indra/newview/llviewerkeyboard.cpp
@@ -345,6 +345,7 @@ void camera_spin_around_ccw_sitting( EKeystate s )
 	else
 	{
 		//change camera but do not send keystrokes
+		gAgentCamera.unlockView();
 		gAgentCamera.setOrbitLeftKey( get_orbit_rate() );
 	}
 }
@@ -361,6 +362,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 162acb74f18098f025e50621ca4a026759057225..b6c7be2ed3550d403fc6af79fbd7111c3b824400 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -4104,8 +4104,7 @@ void handle_reset_view()
 		// switching to outfit selector should automagically save any currently edited wearable
 		LLFloaterSidePanelContainer::showPanel("appearance", LLSD().with("type", "my_outfits"));
 	}
-
-	gAgentCamera.switchCameraPreset(CAMERA_PRESET_REAR_VIEW);
+	
 	reset_view_final( TRUE );
 	LLFloaterCamera::resetCameraMode();
 }
diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp
index e197591ef86b7f42c22018420861b4430f4ade1b..c966b7d4f93712af29cdd9e7ca5db60be82f221a 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 f64dfcf0d462f1c98e29a60cbad6cb1415e3c776..44d02b42248fdc847874ad47dcdcb14100b8f339 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -5119,6 +5119,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)
@@ -5172,7 +5180,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 385bbd57e542d9950f417a569663ebd0abbd1081..44c1fbd066a2ca4e58af80f5f41680ef4727e164 100644
--- a/indra/newview/llviewerwindow.h
+++ b/indra/newview/llviewerwindow.h
@@ -303,6 +303,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 eb461d3140c85203b81d41ebaffadc2108a16bbd..d567623ac04a64659cad560789383f2d99258af8 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -7373,7 +7373,8 @@ void LLVOAvatar::sitOnObject(LLViewerObject *sit_object)
 	mRoot->updateWorldMatrixChildren();
 
 	stopMotion(ANIM_AGENT_BODY_NOISE);
-
+	
+	gAgentCamera.setInitSitRot(gAgent.getFrameAgent().getQuaternion());
 }
 
 //-----------------------------------------------------------------------------
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.png b/indra/newview/skins/default/textures/icons/Presets_Icon.png
index 5a6628816b34fca5623902249385a0b7e836fd68..503ee892a55da5add034fe7055a8baf72ce64cc5 100644
Binary files a/indra/newview/skins/default/textures/icons/Presets_Icon.png and b/indra/newview/skins/default/textures/icons/Presets_Icon.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 624dca48d264917e9f419cb4cc51119567cbf7de..7325d836d28f105dfda4a91d9798cca917ef4a0c 100644
--- a/indra/newview/skins/default/textures/textures.xml
+++ b/indra/newview/skins/default/textures/textures.xml
@@ -98,7 +98,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" />
@@ -113,8 +113,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" />
@@ -204,7 +206,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" />
@@ -253,6 +257,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" />
@@ -492,6 +497,7 @@ with the same filename but different name
   <texture name="Play_Press" file_name="icons/Play_Press.png" preload="false" />
   
   <texture name="ProgressBar" file_name="widgets/ProgressBar.png" preload="true" scale.left="4" scale.top="11" scale.right="48" scale.bottom="3" />
+  <texture name="ProgressBarSolid" file_name="widgets/ProgressBarSolid.png" preload="true" scale.left="4" scale.top="11" scale.right="48" scale.bottom="3" />
   <texture name="ProgressTrack" file_name="widgets/ProgressTrack.png" preload="true" scale.left="4" scale.top="13" scale.right="148" scale.bottom="2" />
 
   <texture name="PushButton_Disabled" file_name="widgets/PushButton_Disabled.png" preload="true" scale.left="4" scale.top="19" scale.right="28" scale.bottom="4" />
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.xml b/indra/newview/skins/default/xui/en/floater_camera.xml
index 72a7b5540c0fd7af9700dc68d56540d2e40449b4..9deb38e3afac6397eaee2208b771c8c14badbb8e 100644
--- a/indra/newview/skins/default/xui/en/floater_camera.xml
+++ b/indra/newview/skins/default/xui/en/floater_camera.xml
@@ -7,7 +7,7 @@
  legacy_header_height="18"
  can_minimize="true"
  can_close="true"
- height="164"
+ height="135"
  layout="topleft"
  name="camera_floater"
  help_topic="camera_floater"
@@ -16,7 +16,7 @@
  title="CAMERA CONTROLS"
  chrome="true"
  save_rect="true"
- width="228">
+ width="400">
     <floater.string
      name="rotate_tooltip">
         Rotate Camera Around Focus
@@ -33,6 +33,7 @@
      name="free_mode_title">
         View Object
     </floater.string>
+    <string name="inactive_combo_text">Use preset</string>
     <panel
      border="false"
      height="123"
@@ -41,112 +42,18 @@
      top="0"
      mouse_opaque="false"
      name="controls"
-     width="226">
-       <panel
-         follows="all"
-         height="102"
-         layout="topleft"
-         left="8"
-         name="preset_views_list"
-         top="24"
-         width="212"
-         visible="false">
-        <panel_camera_item
-          name="front_view">
-          <panel_camera_item.mousedown_callback
-            function="CameraPresets.ChangeView"
-            parameter="front_view" />
-          <panel_camera_item.picture
-            image_name="Cam_Preset_Front_Off" />
-          <panel_camera_item.selected_picture
-            image_name="Cam_Preset_Front_On" />
-          <panel_camera_item.text
-            name="front_view_text">
-            Front View
-          </panel_camera_item.text>
-        </panel_camera_item>
-        <panel_camera_item
-          name="group_view"
-          top_pad="4">
-          <panel_camera_item.mousedown_callback
-            function="CameraPresets.ChangeView"
-            parameter="group_view" />
-          <panel_camera_item.picture
-            image_name="Cam_Preset_Side_Off" />
-          <panel_camera_item.selected_picture
-            image_name="Cam_Preset_Side_On" />
-          <panel_camera_item.text
-            name="side_view_text">
-            Side View
-          </panel_camera_item.text>
-        </panel_camera_item>
-        <panel_camera_item
-          name="rear_view"
-          layout="topleft"
-          top_pad="4">
-          <panel_camera_item.mousedown_callback
-            function="CameraPresets.ChangeView"
-            parameter="rear_view" />
-          <panel_camera_item.picture
-            image_name="Cam_Preset_Back_Off" />
-          <panel_camera_item.selected_picture
-            image_name="Cam_Preset_Back_On" />
-          <panel_camera_item.text
-            name="rear_view_text">
-            Rear View
-          </panel_camera_item.text>
-        </panel_camera_item>
-      </panel>
-      <panel
-          follows="all"
-          height="68"
-          layout="topleft"
-          left="8"
-          name="camera_modes_list"
-          top="24"
-          width="212"
-          visible="false">
-        <panel_camera_item
-          name="object_view">
-          <panel_camera_item.mousedown_callback
-            function="CameraPresets.ChangeView"
-            parameter="object_view" />
-          <panel_camera_item.text
-            name="object_view_text">
-            Object View
-          </panel_camera_item.text>
-          <panel_camera_item.picture
-            image_name="Object_View_Off" />
-          <panel_camera_item.selected_picture
-            image_name="Object_View_On" />
-        </panel_camera_item>
-        <panel_camera_item
-          name="mouselook_view"
-          layout="topleft">
-          <panel_camera_item.mousedown_callback
-            function="CameraPresets.ChangeView"
-            parameter="mouselook_view" />
-          <panel_camera_item.text
-            name="mouselook_view_text">
-            Mouselook View
-          </panel_camera_item.text>
-          <panel_camera_item.picture
-            image_name="MouseLook_View_Off" />
-          <panel_camera_item.selected_picture
-            image_name="MouseLook_View_On" />
-        </panel_camera_item>
-      </panel>
+     width="220">
          <!--TODO: replace + - images -->
          <panel
             border="false"
             class="camera_zoom_panel"
-            height="114"
+            height="123"
             layout="topleft"
             left="0"
             mouse_opaque="false"
             name="zoom"
-            top="20"
-            width="226">
+            top="0"
+            width="220">
            <joystick_rotate
               follows="top|left"
               height="78"
@@ -157,8 +64,8 @@
               sound_flags="3"
               visible="true"
               tool_tip="Orbit camera around focus"
-              top="20"
-              width="78" />                      
+              top="25"
+              width="78" />
            <button
               follows="top|left"
               height="18"
@@ -169,7 +76,7 @@
               left_pad="14" 
               name="zoom_plus_btn"
               width="18"
-              top="18">
+              top="23">
              <commit_callback
                 function="Zoom.plus" />
              <mouse_held_callback
@@ -214,56 +121,136 @@
          scale_image="false"
          sound_flags="3"
          tool_tip="Move camera up and down, left and right"
-         top="20"
+         top="25"
          width="78"/>
+         <text
+          type="string"
+          length="1"
+          follows="left|top"
+          height="15"
+          layout="topleft"
+          left="41"
+          top_pad="9"
+          name="precise_ctrs_label"
+          width="200">
+            Use precise controls
+        </text>
         </panel>
     </panel>
     <panel
-     border="false"
-     height="42"
+     follows="all"
+     height="102"
      layout="topleft"
-     left="2"
-     top_pad="0"
-     name="buttons"
-     width="226">
-        <button
-         height="23"
-         label=""
-         layout="topleft"
-         left="70"
-         is_toggle="true"
-         image_overlay="Cam_Avatar_Off"
-         image_selected="PushButton_Selected_Press"
-         name="presets_btn"
-         tab_stop="false"
-         tool_tip="Preset Views"
-         top="13" 
-         width="25">
-        </button>
-        <button
-         height="23"
-         label=""
-         layout="topleft"
-         left_pad="1"
-         is_toggle="true"
-         image_overlay="PanOrbit_Off"
-         image_selected="PushButton_Selected_Press"
-         name="pan_btn"
-         tab_stop="false"
-         tool_tip="Orbit Zoom Pan"
-         width="25">
-        </button>
-        <button
-         height="23"
-         label=""
-         layout="topleft"
-         left_pad="1"
-         image_overlay="Cam_FreeCam_Off"
-         image_selected="PushButton_Selected_Press"
-         name="avatarview_btn"
-         tab_stop="false"
-         tool_tip="Camera modes"
-         width="25">
-        </button>
-    </panel>
+     left_pad="2"
+     name="buttons_panel"
+     top="22"
+     width="212">
+    <panel_camera_item
+      name="front_view"
+      tool_tip="Front View"
+      width="30">
+      <panel_camera_item.mousedown_callback
+        function="CameraPresets.ChangeView"
+        parameter="Front View" />
+      <panel_camera_item.picture
+        image_name="Cam_Preset_Front_Off" />
+      <panel_camera_item.selected_picture
+        image_name="Cam_Preset_Front_On" />
+    </panel_camera_item>
+    <panel_camera_item
+      name="group_view"
+      tool_tip="Side View"
+      width="30"
+      left_pad="4">
+      <panel_camera_item.mousedown_callback
+        function="CameraPresets.ChangeView"
+        parameter="Side View" />
+      <panel_camera_item.picture
+        image_name="Cam_Preset_Side_Off" />
+      <panel_camera_item.selected_picture
+        image_name="Cam_Preset_Side_On" />
+    </panel_camera_item>
+    <panel_camera_item
+      name="rear_view"
+      tool_tip="Rear View"
+      width="30"
+      left_pad="4">
+      <panel_camera_item.mousedown_callback
+        function="CameraPresets.ChangeView"
+        tool_tip="Rear View"
+        parameter="Rear View" />
+      <panel_camera_item.picture
+        image_name="Cam_Preset_Back_Off" />
+      <panel_camera_item.selected_picture
+        image_name="Cam_Preset_Back_On" />
+    </panel_camera_item>
+    <panel_camera_item
+        name="object_view"
+        tool_tip="Object View"
+        width="30"
+        left_pad="4">
+        <panel_camera_item.mousedown_callback
+          function="CameraPresets.ChangeView"
+          parameter="object_view" />
+        <panel_camera_item.picture
+          image_name="Object_View_Off" />
+        <panel_camera_item.selected_picture
+          image_name="Object_View_On" />
+      </panel_camera_item>
+      <panel_camera_item
+        name="mouselook_view"
+        tool_tip="Mouselook View"
+        width="30"
+        left_pad="4">
+        <panel_camera_item.mousedown_callback
+          function="CameraPresets.ChangeView"
+          parameter="mouselook_view" />
+        <panel_camera_item.picture
+          image_name="MouseLook_View_Off" />
+        <panel_camera_item.selected_picture
+          image_name="MouseLook_View_On" />
+      </panel_camera_item>
+      <combo_box
+        height="23"
+        left="0"
+        mouse_opaque="true"
+        name="preset_combo"
+        top_pad="10"
+        width="136">
+        <combo_list
+        mouse_wheel_opaque="true"/>
+        <combo_box.item
+         label="Use preset"
+         name="Use preset"
+         value="default" />
+      </combo_box>
+      <button
+        height="16"
+        width="16"  
+        layout="topleft"
+        mouse_opaque="true"
+        name="gear_btn"
+        tool_tip="My Camera Presets"
+        top_delta="3"
+        left_pad="10" 
+        image_selected="Icon_Gear"
+        image_pressed="Icon_Gear"
+        image_unselected="Icon_Gear"
+        is_toggle="true">
+        <button.commit_callback
+          function="CameraPresets.ShowPresetsList"/>
+      </button>
+      <button
+        follows="top|left"
+        height="25"
+        label="Save as preset..."
+        layout="topleft"
+        left="0"
+        name="save_preset_btn"
+        top_pad="18"
+        width="150">
+        <button.commit_callback
+          function="CameraPresets.Save"/>
+      </button>
+  </panel>
 </floater>
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 52084e5f8ed07390d63097c4578346513c01fffb..3570456b444eed0b368135e2fc9707d94572378f 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,18 +4,16 @@
  legacy_header_height="225"
  can_minimize="true"
  can_close="true"
- can_resize="true"
- min_height="65"
- min_width="515"
- height="65"
+ can_resize="false"
+ height="80"
+ 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">
+ title="SET HOVER HEIGHT">
       <slider
      enabled="false"
      control_name="HoverHeightSlider"
@@ -34,4 +32,13 @@
      can_edit_text="true"
      >
     </slider>
+  <check_box
+     control_name="HoverHeightAffectsCamera"
+     follows="all"
+     height="15"
+     label="Bind Camera view"
+     layout="topleft"
+     name="BindCameraCheck"
+     top_pad="7"
+     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 a8342e723bc28d35ef6dee32b7d92e55514c7354..d783d1e23c513d060daf3955052644400b88e585 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="488"
+ height="466"
  layout="topleft"
  name="Inventory Finder"
  help_topic="inventory_finder"
@@ -202,7 +202,7 @@
      left="8"
      mouse_opaque="true"
      name="icon_settings"
-     top="262"
+     top="242"
      width="16" />
     <check_box
      height="16"
@@ -220,7 +220,7 @@
      layout="topleft"
      left="8"
      name="All"
-     top="282"
+     top="262"
      width="100" />
     <button
      height="20"
@@ -368,6 +368,6 @@
      layout="topleft"
      name="Close"
      right="-6"
-     top="454"
+     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/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 089498dfb98959b49bb16be4e1e3990381d6be27..8a91a1f721844c537d59820835926b4005d10131 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -8533,6 +8533,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"
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_preferences_graphics1.xml b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml
index 4d9152e9a5174094d690e7a42c02aa9b498a22ec..5aff7a512768ab1d69fdf897df8f5c6958598d0b 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml
@@ -293,6 +293,21 @@
     width="65">
        0
   </text>
+<text
+type="string"
+length="1"
+follows="left|top"
+height="16"
+layout="topleft"
+left_delta="68"
+name="IndirectMaxComplexityLink"
+mouse_opaque="false"
+top_delta="0"
+width="120">
+[https://community.secondlife.com/t5/Featured-News/Why-are-all-these-people-made-of-colored-jelly/ba-p/3031255 What's this?]
+</text>
+
+
   <check_box
     control_name="AlwaysRenderFriends"
     height="16"
@@ -345,7 +360,6 @@
       function="Pref.PrefLoad"
 	  parameter="graphic"/>
   </button>
-         min_val="0.125"
 
   <button
     follows="top|left"
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/panel_progress.xml b/indra/newview/skins/default/xui/en/panel_progress.xml
index 860caf2d217a91a117c9c8da05b799f90f8ea7b3..e77d097d5f6db9cbec8669a19ff03b7c44a178d0 100644
--- a/indra/newview/skins/default/xui/en/panel_progress.xml
+++ b/indra/newview/skins/default/xui/en/panel_progress.xml
@@ -44,68 +44,129 @@
                  width="670" />
                 <layout_panel
                  auto_resize="false"
-                 height="250"
+                 height="275"
                  layout="topleft"
-                 min_height="250"
+                 min_height="275"
                  name="panel4"
                  width="670">
                     <icon
                      color="LoginProgressBoxCenterColor"
                      follows="left|right|bottom|top"
-                     height="250"
+                     height="275"
                      image_name="Rounded_Square"
                      layout="topleft"
                      left="0"
                      top="0"
                      width="670" />
-                    <text
-                     follows="left|right|top"
-                     font="SansSerifHuge"
-                     font_shadow="none"
-                     halign="left"
-                     height="20"
-                     layout="topleft"
-                     left_delta="47"
-                     name="title_text"
-                     text_color="LoginProgressBoxTextColor"
-                     top_delta="50"
-                     right="-47"/>
-                    <text
-                     follows="left|right|top"
-                     font="SansSerif"
-                     font_shadow="none"
-                     halign="left"
-                     height="20"
-                     layout="topleft"
-                     left_delta="0"
-                     name="progress_text"
-                     text_color="LoginProgressBoxTextColor"
-                     top_pad="5"
-                     right="-47"
-                     word_wrap="true"/>
-                    <progress_bar
-                     bottom="115"
-                     color_bar="1 1 1 0.96"
-                     follows="left|right|top"
-                     height="16"
-                     layout="topleft"
-                     left="45"
-                     name="login_progress_bar"
-                     right="-45" />
-                    <text
+                    <layout_stack
                      follows="left|right|top|bottom"
-                     font="SansSerifLarge"
-                     font_shadow="none"
-                     halign="left"
-                     height="100"
+                     height="275"
                      layout="topleft"
-                     left="45"
-                     line_spacing.pixels="2"
-                     name="message_text"
-                     text_color="LoginProgressBoxTextColor"
-                     top="145"
-                     right="-90"
-                     word_wrap="true"/>
+                     left="0"
+                     orientation="vertical"
+                     name="vertical_centering"
+                     animate="false"
+                     top="0"
+                     width="670">
+                      <layout_panel
+                       auto_resize="false"
+                       height="30"
+                       layout="topleft"
+                       min_height="30"
+                       name="panel_top_spacer"
+                       width="670">
+                      </layout_panel>
+                      <layout_panel
+                       auto_resize="false"
+                       height="100"
+                       layout="topleft"
+                       min_height="100"
+                       name="panel_login"
+                       width="670">
+                        <text
+                         follows="left|right|top"
+                         layout="topleft"
+                         font="SansSerifHuge"
+                         font_shadow="none"
+                         halign="left"
+                         height="20"
+                         left="47"
+                         top="32"
+                         right="-47"
+                         name="title_text"
+                         text_color="LoginProgressBoxTextColor"/>
+                        <text
+                         follows="left|right|top"
+                         layout="topleft"
+                         font="SansSerif"
+                         font_shadow="none"
+                         halign="left"
+                         height="20"
+                         top_pad="5"
+                         right="-47"
+                         left_delta="0"
+                         name="progress_text"
+                         text_color="LoginProgressBoxTextColor"
+                         word_wrap="true"/>
+                        <progress_bar
+                         color_bar="0 0.67 0.9 0.96"
+                         follows="left|right|top"
+                         layout="topleft"
+                         image_fill="ProgressBarSolid"
+                         height="16"
+                         left="45"
+                         top_pad="5"
+                         name="login_progress_bar"
+                         right="-45" />
+                      </layout_panel>
+                      <layout_panel
+                       auto_resize="false"
+                       height="110"
+                       layout="topleft"
+                       min_height="110"
+                       name="panel_motd"
+                       width="670">
+                        <text
+                         follows="left|right|top|bottom"
+                         font="SansSerifLarge"
+                         font_shadow="none"
+                         halign="left"
+                         valign="center"
+                         height="100"
+                         layout="topleft"
+                         left="45"
+                         line_spacing.pixels="2"
+                         name="message_text"
+                         text_color="LoginProgressBoxTextColor"
+                         top="7"
+                         right="-90"
+                         word_wrap="true"/>
+                      </layout_panel>
+                      <layout_panel
+                       auto_resize="false"
+                       height="40"
+                       layout="topleft"
+                       min_height="40"
+                       name="panel_icons"
+                       width="670">
+                        <!--Logos are tied to following label from code-->
+                        <text
+                         follows="left|right|top"
+                         layout="topleft"
+                         font="SansSerifLarge"
+                         font_shadow="none"
+                         halign="left"
+                         height="16"
+                         width="240"
+                         left="47"
+                         top="6"
+                         line_spacing.pixels="2"
+                         name="logos_lbl"
+                         text_color="LoginProgressBoxTextColor">
+                          Second Life uses
+                        </text>
+                      </layout_panel>
+                    </layout_stack>
                 </layout_panel>
                 <layout_panel
                  height="200"
diff --git a/indra/newview/skins/default/xui/en/panel_status_bar.xml b/indra/newview/skins/default/xui/en/panel_status_bar.xml
index 52bcce01f7d7311471b2d1e3a1a7bf3ce975116d..ada980cda19616fcf60d14e6635fca3b0dc5df3f 100644
--- a/indra/newview/skins/default/xui/en/panel_status_bar.xml
+++ b/indra/newview/skins/default/xui/en/panel_status_bar.xml
@@ -75,7 +75,7 @@
   </panel>
   <panel
     height="18"
-    left="-416"
+    left="-458"
     width="185"
     top="1"
     follows="right|top" 
@@ -148,11 +148,19 @@
     <icon
      follows="right|top"
      height="16"
-     image_name="Presets_Icon"
+     image_name="Cam_FreeCam_Off"
      left_pad="8"
      top="2"
-     name="presets_icon"
+     name="presets_icon_camera"
      width="18" />
+    <icon
+     follows="right|top"
+     height="13"
+     image_name="Presets_Icon"
+     left_pad="8"
+     top="4"
+     name="presets_icon_graphic"
+     width="16" />
     <button
      follows="right|top"
      height="16"
diff --git a/indra/newview/skins/default/xui/en/widgets/panel_camera_item.xml b/indra/newview/skins/default/xui/en/widgets/panel_camera_item.xml
index 98707b84957005e3148e88319b2dcdb3ef7886d4..564f695cd05c2804a5d7cdbe35bb05fea9b1168e 100644
--- a/indra/newview/skins/default/xui/en/widgets/panel_camera_item.xml
+++ b/indra/newview/skins/default/xui/en/widgets/panel_camera_item.xml
@@ -15,7 +15,7 @@
      top="30"
      scale_image="true"
      visible="false"
-     width="212" />
+     width="30" />
   <panel_camera_item.icon_selected
    follows="top|left"
      height="30"
@@ -27,7 +27,7 @@
      top="30"
      scale_image="true"
      visible="false"
-     width="212" />
+     width="30" />
   <panel_camera_item.picture
      follows="top|left"
      height="30"
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index a403760670eeff299cf61d3b389e8fc5a054955e..4d6d75659f048e6db96a5ca06a2272c363a6a34f 100755
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -78,6 +78,9 @@ def construct(self):
                 contributor_names = self.extract_names(contributions_path)
                 self.put_in_file(contributor_names, "contributors.txt", src=contributions_path)
 
+                # ... and the default camera position settings
+                self.path("camera")
+
                 # ... and the entire windlight directory
                 self.path("windlight")
 
@@ -513,14 +516,12 @@ def construct(self):
                 print err.message
                 print "Skipping GLOD library (assumming linked statically)"
 
-            # Get fmodex dll, continue if missing
-            try:
-                if(self.address_size == 64):
-                    self.path("fmodex64.dll")
+            # Get fmodstudio dll if needed
+            if self.args['fmodstudio'] == 'ON':
+                if(self.args['configuration'].lower() == 'debug'):
+                    self.path("fmodL.dll")
                 else:
-                    self.path("fmodex.dll")
-            except:
-                print "Skipping fmodex audio library(assuming other audio engine)"
+                    self.path("fmod.dll")
 
             # For textures
             self.path("openjpeg.dll")
@@ -1046,17 +1047,18 @@ def path_optional(src, dst):
                                 ):
                     self.path2basename(relpkgdir, libfile)
 
-                # dylibs that vary based on configuration
-                if self.args['configuration'].lower() == 'debug':
-                    for libfile in (
-                                "libfmodexL.dylib",
-                                ):
-                        dylibs += path_optional(os.path.join(debpkgdir, libfile), libfile)
-                else:
-                    for libfile in (
-                                "libfmodex.dylib",
-                                ):
-                        dylibs += path_optional(os.path.join(relpkgdir, libfile), libfile)
+                # Fmod studio dylibs (vary based on configuration)
+                if self.args['fmodstudio'] == 'ON':
+                    if self.args['configuration'].lower() == 'debug':
+                        for libfile in (
+                                    "libfmodL.dylib",
+                                    ):
+                            dylibs += path_optional(os.path.join(debpkgdir, libfile), libfile)
+                    else:
+                        for libfile in (
+                                    "libfmod.dylib",
+                                    ):
+                            dylibs += path_optional(os.path.join(relpkgdir, libfile), libfile)
 
                 # our apps
                 executable_path = {}
@@ -1519,13 +1521,15 @@ def construct(self):
                 print "tcmalloc files not found, skipping"
                 pass
 
-            try:
-                self.path("libfmodex-*.so")
-                self.path("libfmodex.so")
-                pass
-            except:
-                print "Skipping libfmodex.so - not found"
-                pass
+            if self.args['fmodstudio'] == 'ON':
+                try:
+                    self.path("libfmod.so.11.7")
+                    self.path("libfmod.so.11")
+                    self.path("libfmod.so")
+                    pass
+                except:
+                    print "Skipping libfmod.so - not found"
+                    pass
 
 
         # Vivox runtimes
@@ -1555,6 +1559,7 @@ def construct(self):
     extra_arguments = [
         dict(name='bugsplat', description="""BugSplat database to which to post crashes,
              if BugSplat crash reporting is desired""", default=''),
+        dict(name='fmodstudio', description="""Indication if fmod studio libraries are needed""", default='OFF'),
         ]
     try:
         main(extra=extra_arguments)