diff --git a/doc/contributions.txt b/doc/contributions.txt
index 2c1e5487ce73a53a2590c3ac657d7ee13e63eaf6..a693c22abcfe0a48bbf0c35e36f039b3890d6645 100755
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -401,6 +401,7 @@ Cinder Roxley
     STORM-2127
     STORM-2144
     SL-3404
+    SL-17634
 Clara Young
 Coaldust Numbers
     VWR-1095
@@ -1122,6 +1123,7 @@ Nicky Dasmijn
 	SL-14541
 	SL-16438
 	SL-17218
+	SL-17585
 Nicky Perian
 	OPEN-1
 	STORM-1087
diff --git a/indra/llappearance/llavatarappearance.cpp b/indra/llappearance/llavatarappearance.cpp
index 0c371892fe40dd002934b750f6349d3a1e600a92..be0a875848903cee851e3e2dac7a57f5f1b69dd7 100644
--- a/indra/llappearance/llavatarappearance.cpp
+++ b/indra/llappearance/llavatarappearance.cpp
@@ -924,6 +924,9 @@ BOOL LLAvatarAppearance::loadAvatar()
 		return FALSE;
 	}
 
+	// initialize mJointAliasMap
+	getJointAliases();
+
 	// avatar_lad.xml : <skeleton>
 	if( !loadSkeletonNode() )
 	{
@@ -1044,7 +1047,6 @@ BOOL LLAvatarAppearance::loadAvatar()
 			return FALSE;
 		}
 	}
-
 	
 	return TRUE;
 }
diff --git a/indra/llappearance/llpolymesh.cpp b/indra/llappearance/llpolymesh.cpp
index 88df607599ae942100b10a171ac54e08b9ef6b31..48e18176b505e0e418b614911330c6ec263bc6ef 100644
--- a/indra/llappearance/llpolymesh.cpp
+++ b/indra/llappearance/llpolymesh.cpp
@@ -616,14 +616,16 @@ BOOL LLPolyMeshSharedData::loadMesh( const std::string& fileName )
                                         // we reached the end of the morphs
                                         break;
                                 }
-                                LLPolyMorphData* morph_data = new LLPolyMorphData(std::string(morphName));
+                                std::string morph_name(morphName);
+                                LLPolyMorphData* morph_data = new LLPolyMorphData(morph_name);
 
                                 BOOL result = morph_data->loadBinary(fp, this);
 
                                 if (!result)
                                 {
-                                        delete morph_data;
-                                        continue;
+                                    LL_WARNS() << "Failure loading " << morph_name << " from " << fileName << LL_ENDL;
+                                    delete morph_data;
+                                    continue;
                                 }
 
                                 mMorphData.insert(morph_data);
diff --git a/indra/llappearance/llpolymorph.cpp b/indra/llappearance/llpolymorph.cpp
index 4208f06f109ac8135f64835b3a103eb4be9d98f9..651d0001d6f200e255bddd6ffaf9c917db5807e7 100644
--- a/indra/llappearance/llpolymorph.cpp
+++ b/indra/llappearance/llpolymorph.cpp
@@ -156,7 +156,9 @@ BOOL LLPolyMorphData::loadBinary(LLFILE *fp, LLPolyMeshSharedData *mesh)
 
 		if (mVertexIndices[v] > 10000)
 		{
-			LL_ERRS() << "Bad morph index: " << mVertexIndices[v] << LL_ENDL;
+            // Bad install? These are usually .llm files from 'character' fodler
+			LL_WARNS() << "Bad morph index " << v << ": " << mVertexIndices[v] << LL_ENDL;
+            return FALSE;
 		}
 
 
diff --git a/indra/llaudio/llaudiodecodemgr.cpp b/indra/llaudio/llaudiodecodemgr.cpp
index d2d13a70e40a4a5b36158ba2e436ac9d091313cf..c5ab0028293adbeeb380ed5743bb9982314083d7 100644
--- a/indra/llaudio/llaudiodecodemgr.cpp
+++ b/indra/llaudio/llaudiodecodemgr.cpp
@@ -35,6 +35,8 @@
 #include "llendianswizzle.h"
 #include "llassetstorage.h"
 #include "llrefcount.h"
+#include "threadpool.h"
+#include "workqueue.h"
 
 #include "llvorbisencode.h"
 
@@ -53,15 +55,13 @@
 
 extern LLAudioEngine *gAudiop;
 
-LLAudioDecodeMgr *gAudioDecodeMgrp = NULL;
-
 static const S32 WAV_HEADER_SIZE = 44;
 
 
 //////////////////////////////////////////////////////////////////////////////
 
 
-class LLVorbisDecodeState final : public LLRefCount
+class LLVorbisDecodeState final : public LLThreadSafeRefCount
 {
 public:
 	class WriteResponder : public LLLFSThread::Responder
@@ -540,137 +540,254 @@ void LLVorbisDecodeState::flushBadFile()
 
 class LLAudioDecodeMgr::Impl
 {
-	friend class LLAudioDecodeMgr;
-public:
-	Impl() = default;
-	~Impl() = default;
+    friend class LLAudioDecodeMgr;
+    Impl();
+  public:
 
-	void processQueue(const F32 num_secs = 0.005);
+    void processQueue();
 
-protected:
-	std::deque<LLUUID> mDecodeQueue;
-	LLPointer<LLVorbisDecodeState> mCurrentDecodep;
+    void startMoreDecodes();
+    void enqueueFinishAudio(const LLUUID &decode_id, LLPointer<LLVorbisDecodeState>& decode_state);
+    void checkDecodesFinished();
+
+  protected:
+    std::deque<LLUUID> mDecodeQueue;
+    std::map<LLUUID, LLPointer<LLVorbisDecodeState>> mDecodes;
 };
 
+LLAudioDecodeMgr::Impl::Impl()
+{
+}
+
+// Returns the in-progress decode_state, which may be an empty LLPointer if
+// there was an error and there is no more work to be done.
+LLPointer<LLVorbisDecodeState> beginDecodingAndWritingAudio(const LLUUID &decode_id);
+
+// Return true if finished
+bool tryFinishAudio(const LLUUID &decode_id, LLPointer<LLVorbisDecodeState> decode_state);
 
-void LLAudioDecodeMgr::Impl::processQueue(const F32 num_secs)
+void LLAudioDecodeMgr::Impl::processQueue()
 {
-	LLTimer decode_timer;
+    // First, check if any audio from in-progress decodes are ready to play. If
+    // so, mark them ready for playback (or errored, in case of error).
+    checkDecodesFinished();
 
-	BOOL done = FALSE;
-	while (!done)
-	{
-		if (mCurrentDecodep)
-		{
-			BOOL res;
+    // Second, start as many decodes from the queue as permitted
+    startMoreDecodes();
+}
 
-			// Decode in a loop until we're done or have run out of time.
-			while(!(res = mCurrentDecodep->decodeSection()) && (decode_timer.getElapsedTimeF32() < num_secs))
-			{
-				// decodeSection does all of the work above
-			}
+void LLAudioDecodeMgr::Impl::startMoreDecodes()
+{
+    llassert_always(gAudiop);
+
+    LL::WorkQueue::ptr_t main_queue = LL::WorkQueue::getInstance("mainloop");
+    // *NOTE: main_queue->postTo casts this refcounted smart pointer to a weak
+    // pointer
+    LL::WorkQueue::ptr_t general_queue = LL::WorkQueue::getInstance("General");
+    const LL::ThreadPool::ptr_t general_thread_pool = LL::ThreadPool::getInstance("General");
+    llassert_always(main_queue);
+    llassert_always(general_queue);
+    llassert_always(general_thread_pool);
+    // Set max decodes to double the thread count of the general work queue.
+    // This ensures the general work queue is full, but prevents theoretical
+    // buildup of buffers in memory due to disk writes once the
+    // LLVorbisDecodeState leaves the worker thread (see
+    // LLLFSThread::sLocal->write). This is probably as fast as we can get it
+    // without modifying/removing LLVorbisDecodeState, at which point we should
+    // consider decoding the audio during the asset download process.
+    // -Cosmic,2022-05-11
+    const size_t max_decodes = general_thread_pool->getWidth() * 2;
+
+    while (!mDecodeQueue.empty() && mDecodes.size() < max_decodes)
+    {
+        const LLUUID decode_id = mDecodeQueue.front();
+        mDecodeQueue.pop_front();
+
+        // Don't decode the same file twice
+        if (mDecodes.find(decode_id) != mDecodes.end())
+        {
+            continue;
+        }
+        if (gAudiop->hasDecodedFile(decode_id))
+        {
+            continue;
+        }
+
+        // Kick off a decode
+        mDecodes[decode_id] = LLPointer<LLVorbisDecodeState>(NULL);
+        try
+        {
+            main_queue->postTo(
+                general_queue,
+                [decode_id]() // Work done on general queue
+                {
+                    LLPointer<LLVorbisDecodeState> decode_state = beginDecodingAndWritingAudio(decode_id);
+
+                    if (!decode_state)
+                    {
+                        // Audio decode has errored
+                        return decode_state;
+                    }
+
+                    // Disk write of decoded audio is now in progress off-thread
+                    return decode_state;
+                },
+                [decode_id, this](LLPointer<LLVorbisDecodeState> decode_state) // Callback to main thread
+                mutable {
+                    if (!gAudiop)
+                    {
+                        // There is no LLAudioEngine anymore. This might happen if
+                        // an audio decode is enqueued just before shutdown.
+                        return;
+                    }
+
+                    // At this point, we can be certain that the pointer to "this"
+                    // is valid because the lifetime of "this" is dependent upon
+                    // the lifetime of gAudiop.
+
+                    enqueueFinishAudio(decode_id, decode_state);
+                });
+        }
+        catch (const LLThreadSafeQueueInterrupt&)
+        {
+            // Shutdown
+            // Consider making processQueue() do a cleanup instead
+            // of starting more decodes
+            LL_WARNS() << "Tried to start decoding on shutdown" << LL_ENDL;
+        }
+    }
+}
 
-			if (mCurrentDecodep->isDone() && !mCurrentDecodep->isValid())
-			{
-				// We had an error when decoding, abort.
-				LL_WARNS("AudioEngine") << mCurrentDecodep->getUUID() << " has invalid vorbis data, aborting decode" << LL_ENDL;
-				mCurrentDecodep->flushBadFile();
-
-				if (gAudiop)
-				{
-					LLAudioData *adp = gAudiop->getAudioData(mCurrentDecodep->getUUID());
-					adp->setHasValidData(false);
-					adp->setHasCompletedDecode(true);
-				}
-
-				mCurrentDecodep = NULL;
-				done = TRUE;
-			}
+LLPointer<LLVorbisDecodeState> beginDecodingAndWritingAudio(const LLUUID &decode_id)
+{
+    LL_PROFILE_ZONE_SCOPED_CATEGORY_MEDIA;
+
+    LL_DEBUGS() << "Decoding " << decode_id << " from audio queue!" << LL_ENDL;
+
+    std::string                    d_path       = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, decode_id.asString()) + ".dsf";
+    LLPointer<LLVorbisDecodeState> decode_state = new LLVorbisDecodeState(decode_id, d_path);
+
+    if (!decode_state->initDecode())
+    {
+        return NULL;
+    }
+
+    // Decode in a loop until we're done
+    while (!decode_state->decodeSection())
+    {
+        // decodeSection does all of the work above
+    }
+
+    if (!decode_state->isDone())
+    {
+        // Decode stopped early, or something bad happened to the file
+        // during decoding.
+        LL_WARNS("AudioEngine") << decode_id << " has invalid vorbis data or decode has been canceled, aborting decode" << LL_ENDL;
+        decode_state->flushBadFile();
+        return NULL;
+    }
+
+    if (!decode_state->isValid())
+    {
+        // We had an error when decoding, abort.
+        LL_WARNS("AudioEngine") << decode_id << " has invalid vorbis data, aborting decode" << LL_ENDL;
+        decode_state->flushBadFile();
+        return NULL;
+    }
+
+    // Kick off the writing of the decoded audio to the disk cache.
+    // The receiving thread can then cheaply call finishDecode() again to check
+    // if writing has finished. Someone has to hold on to the refcounted
+    // decode_state to prevent it from getting destroyed during write.
+    decode_state->finishDecode();
+
+    return decode_state;
+}
 
-			if (!res)
-			{
-				// We've used up out time slice, bail...
-				done = TRUE;
-			}
-			else if (mCurrentDecodep)
-			{
-				if (gAudiop && mCurrentDecodep->finishDecode())
-				{
-					// We finished!
-					LLAudioData *adp = gAudiop->getAudioData(mCurrentDecodep->getUUID());
-					if (!adp)
-					{
-						LL_WARNS("AudioEngine") << "Missing LLAudioData for decode of " << mCurrentDecodep->getUUID() << LL_ENDL;
-					}
-					else if (mCurrentDecodep->isValid() && mCurrentDecodep->isDone())
-					{
-						adp->setHasCompletedDecode(true);
-						adp->setHasDecodedData(true);
-						adp->setHasValidData(true);
-
-						// At this point, we could see if anyone needs this sound immediately, but
-						// I'm not sure that there's a reason to - we need to poll all of the playing
-						// sounds anyway.
-						//LL_INFOS("AudioEngine") << "Finished the vorbis decode, now what?" << LL_ENDL;
-					}
-					else
-					{
-						adp->setHasCompletedDecode(true);
-						LL_INFOS("AudioEngine") << "Vorbis decode failed for " << mCurrentDecodep->getUUID() << LL_ENDL;
-					}
-					mCurrentDecodep = NULL;
-				}
-				done = TRUE; // done for now
-			}
-		}
+void LLAudioDecodeMgr::Impl::enqueueFinishAudio(const LLUUID &decode_id, LLPointer<LLVorbisDecodeState>& decode_state)
+{
+    // Assumed fast
+    if (tryFinishAudio(decode_id, decode_state))
+    {
+        // Done early!
+        auto decode_iter = mDecodes.find(decode_id);
+        llassert(decode_iter != mDecodes.end());
+        mDecodes.erase(decode_iter);
+        return;
+    }
+
+    // Not done yet... enqueue it
+    mDecodes[decode_id] = decode_state;
+}
 
-		if (!done)
-		{
-			if (mDecodeQueue.empty())
-			{
-				// Nothing else on the queue.
-				done = TRUE;
-			}
-			else
-			{
-				LLUUID uuid;
-				uuid = mDecodeQueue.front();
-				mDecodeQueue.pop_front();
-				if (!gAudiop || gAudiop->hasDecodedFile(uuid))
-				{
-					// This file has already been decoded, don't decode it again.
-					continue;
-				}
-
-				LL_DEBUGS() << "Decoding " << uuid << " from audio queue!" << LL_ENDL;
-
-				std::string d_path = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, uuid.asString()) + ".dsf";
-
-				mCurrentDecodep = new LLVorbisDecodeState(uuid, d_path);
-				if (!mCurrentDecodep->initDecode())
-				{
-					mCurrentDecodep = NULL;
-				}
-			}
-		}
-	}
+void LLAudioDecodeMgr::Impl::checkDecodesFinished()
+{
+    auto decode_iter = mDecodes.begin();
+    while (decode_iter != mDecodes.end())
+    {
+        const LLUUID& decode_id = decode_iter->first;
+        const LLPointer<LLVorbisDecodeState>& decode_state = decode_iter->second;
+        if (tryFinishAudio(decode_id, decode_state))
+        {
+            decode_iter = mDecodes.erase(decode_iter);
+        }
+        else
+        {
+            ++decode_iter;
+        }
+    }
+}
+
+bool tryFinishAudio(const LLUUID &decode_id, LLPointer<LLVorbisDecodeState> decode_state)
+{
+    // decode_state is a file write in progress unless finished is true
+    bool finished = decode_state && decode_state->finishDecode();
+    if (!finished)
+    {
+        return false;
+    }
+
+    llassert_always(gAudiop);
+
+    LLAudioData *adp = gAudiop->getAudioData(decode_id);
+    if (!adp)
+    {
+        LL_WARNS("AudioEngine") << "Missing LLAudioData for decode of " << decode_id << LL_ENDL;
+        return true;
+    }
+
+    bool valid = decode_state && decode_state->isValid();
+    // Mark current decode finished regardless of success or failure
+    adp->setHasCompletedDecode(true);
+    // Flip flags for decoded data
+    adp->setHasDecodeFailed(!valid);
+    adp->setHasDecodedData(valid);
+    // When finished decoding, there will also be a decoded wav file cached on
+    // disk with the .dsf extension
+    if (valid)
+    {
+        adp->setHasWAVLoadFailed(false);
+    }
+
+    return true;
 }
 
 //////////////////////////////////////////////////////////////////////////////
 
 LLAudioDecodeMgr::LLAudioDecodeMgr()
 {
-	mImpl = new Impl;
+    mImpl = new Impl();
 }
 
 LLAudioDecodeMgr::~LLAudioDecodeMgr()
 {
-	delete mImpl;
+    delete mImpl;
+    mImpl = nullptr;
 }
 
-void LLAudioDecodeMgr::processQueue(const F32 num_secs)
+void LLAudioDecodeMgr::processQueue()
 {
-	mImpl->processQueue(num_secs);
+    mImpl->processQueue();
 }
 
 BOOL LLAudioDecodeMgr::addDecodeRequest(const LLUUID &uuid)
@@ -686,7 +803,7 @@ BOOL LLAudioDecodeMgr::addDecodeRequest(const LLUUID &uuid)
 	{
 		// Just put it on the decode queue.
 		LL_DEBUGS("AudioEngine") << "addDecodeRequest for " << uuid << " has local asset file already" << LL_ENDL;
-		mImpl->mDecodeQueue.push_back(uuid);
+        mImpl->mDecodeQueue.push_back(uuid);
 		return TRUE;
 	}
 
diff --git a/indra/llaudio/llaudiodecodemgr.h b/indra/llaudio/llaudiodecodemgr.h
index ceaff3f2d8254a97cf2f865a67fb2dc02cbb15c6..4c17b46156237557ae461b47b63a4e0a8dd7ca51 100644
--- a/indra/llaudio/llaudiodecodemgr.h
+++ b/indra/llaudio/llaudiodecodemgr.h
@@ -32,24 +32,23 @@
 
 #include "llassettype.h"
 #include "llframetimer.h"
+#include "llsingleton.h"
 
+template<class T> class LLPointer;
 class LLVorbisDecodeState;
 
-class LLAudioDecodeMgr
+class LLAudioDecodeMgr : public LLSingleton<LLAudioDecodeMgr>
 {
+    LLSINGLETON(LLAudioDecodeMgr);
+    ~LLAudioDecodeMgr();
 public:
-	LLAudioDecodeMgr();
-	~LLAudioDecodeMgr();
-
-	void processQueue(const F32 num_secs = 0.005);
+	void processQueue();
 	BOOL addDecodeRequest(const LLUUID &uuid);
 	void addAudioRequest(const LLUUID &uuid);
 	
 protected:
 	class Impl;
-	Impl* mImpl;
+    Impl* mImpl;
 };
 
-extern LLAudioDecodeMgr *gAudioDecodeMgrp;
-
 #endif
diff --git a/indra/llaudio/llaudioengine.cpp b/indra/llaudio/llaudioengine.cpp
index df934a5618e502329b8f41ed014f754019c28122..314b640e0a2aa04f97cd20e22011465a5eb67a41 100644
--- a/indra/llaudio/llaudioengine.cpp
+++ b/indra/llaudio/llaudioengine.cpp
@@ -78,18 +78,10 @@ void LLAudioEngine::setDefaults()
 
 	mLastStatus = 0;
 
-	mNumChannels = 0;
 	mEnableWind = false;
 
-	S32 i;
-	for (i = 0; i < MAX_CHANNELS; i++)
-	{
-		mChannels[i] = NULL;
-	}
-	for (i = 0; i < MAX_BUFFERS; i++)
-	{
-		mBuffers[i] = NULL;
-	}
+	mChannels.fill(nullptr);
+	mBuffers.fill(nullptr);
 
 	mMasterGain = 1.f;
 	// Setting mInternalGain to an out of range value fixes the issue reported in STORM-830.
@@ -106,18 +98,14 @@ void LLAudioEngine::setDefaults()
 }
 
 
-bool LLAudioEngine::init(const S32 num_channels, void* userdata, const std::string &app_title)
+bool LLAudioEngine::init(void* userdata, const std::string &app_title)
 {
 	setDefaults();
 
-	mNumChannels = num_channels;
 	mUserData = userdata;
 	
 	allocateListener();
 
-	// Initialize the decode manager
-	gAudioDecodeMgrp = new LLAudioDecodeMgr;
-
 	LL_INFOS("AudioEngine") << "LLAudioEngine::init() AudioEngine successfully initialized" << LL_ENDL;
 
 	return true;
@@ -126,10 +114,6 @@ bool LLAudioEngine::init(const S32 num_channels, void* userdata, const std::stri
 
 void LLAudioEngine::shutdown()
 {
-	// Clean up decode manager
-	delete gAudioDecodeMgrp;
-	gAudioDecodeMgrp = nullptr;
-
 	// Clean up wind source
 	cleanupWind();
 
@@ -155,14 +139,14 @@ void LLAudioEngine::shutdown()
 
 	// Clean up channels
 	S32 i;
-	for (i = 0; i < MAX_CHANNELS; i++)
+	for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++)
 	{
 		delete mChannels[i];
 		mChannels[i] = NULL;
 	}
 
 	// Clean up buffers
-	for (i = 0; i < MAX_BUFFERS; i++)
+	for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++)
 	{
 		delete mBuffers[i];
 		mBuffers[i] = NULL;
@@ -228,7 +212,7 @@ std::string LLAudioEngine::getInternetStreamURL()
 void LLAudioEngine::updateChannels()
 {
 	S32 i;
-	for (i = 0; i < MAX_CHANNELS; i++)
+	for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++)
 	{
 		if (mChannels[i])
 		{
@@ -239,20 +223,14 @@ void LLAudioEngine::updateChannels()
 	}
 }
 
-static const F32 default_max_decode_time = .002f; // 2 ms
-void LLAudioEngine::idle(F32 max_decode_time)
+void LLAudioEngine::idle()
 {
-	if (max_decode_time <= 0.f)
-	{
-		max_decode_time = default_max_decode_time;
-	}
-	
 	// "Update" all of our audio sources, clean up dead ones.
 	// Primarily does position updating, cleanup of unused audio sources.
 	// Also does regeneration of the current priority of each audio source.
 
 	S32 i;
-	for (i = 0; i < MAX_BUFFERS; i++)
+	for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++)
 	{
 		if (mBuffers[i])
 		{
@@ -469,7 +447,7 @@ void LLAudioEngine::idle(F32 max_decode_time)
 	commitDeferredChanges();
 	
 	// Flush unused buffers that are stale enough
-	for (i = 0; i < MAX_BUFFERS; i++)
+	for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++)
 	{
 		if (mBuffers[i])
 		{
@@ -485,7 +463,7 @@ void LLAudioEngine::idle(F32 max_decode_time)
 
 
 	// Clear all of the looped flags for the channels
-	for (i = 0; i < MAX_CHANNELS; i++)
+	for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++)
 	{
 		if (mChannels[i])
 		{
@@ -494,7 +472,7 @@ void LLAudioEngine::idle(F32 max_decode_time)
 	}
 
 	// Decode audio files
-	gAudioDecodeMgrp->processQueue(max_decode_time);
+    LLAudioDecodeMgr::getInstance()->processQueue();
 	
 	// Call this every frame, just in case we somehow
 	// missed picking it up in all the places that can add
@@ -528,7 +506,7 @@ bool LLAudioEngine::updateBufferForData(LLAudioData *adp, const LLUUID &audio_uu
 		{
 			if (audio_uuid.notNull())
 			{
-				gAudioDecodeMgrp->addDecodeRequest(audio_uuid);
+                LLAudioDecodeMgr::getInstance()->addDecodeRequest(audio_uuid);
 			}
 		}
 		else
@@ -557,7 +535,7 @@ void LLAudioEngine::enableWind(bool enable)
 LLAudioBuffer * LLAudioEngine::getFreeBuffer()
 {
 	S32 i;
-	for (i = 0; i < MAX_BUFFERS; i++)
+	for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++)
 	{
 		if (!mBuffers[i])
 		{
@@ -570,7 +548,7 @@ LLAudioBuffer * LLAudioEngine::getFreeBuffer()
 	// Grab the oldest unused buffer
 	F32 max_age = -1.f;
 	S32 buffer_id = -1;
-	for (i = 0; i < MAX_BUFFERS; i++)
+	for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++)
 	{
 		if (mBuffers[i])
 		{
@@ -601,7 +579,7 @@ LLAudioBuffer * LLAudioEngine::getFreeBuffer()
 LLAudioChannel * LLAudioEngine::getFreeChannel(const F32 priority)
 {
 	S32 i;
-	for (i = 0; i < mNumChannels; i++)
+    for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++)
 	{
 		if (!mChannels[i])
 		{
@@ -629,7 +607,7 @@ LLAudioChannel * LLAudioEngine::getFreeChannel(const F32 priority)
 	F32 min_priority = 10000.f;
 	LLAudioChannel *min_channelp = NULL;
 
-	for (i = 0; i < mNumChannels; i++)
+    for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++)
 	{
 		LLAudioChannel *channelp = mChannels[i];
 		LLAudioSource *sourcep = channelp->getSource();
@@ -656,7 +634,7 @@ LLAudioChannel * LLAudioEngine::getFreeChannel(const F32 priority)
 void LLAudioEngine::cleanupBuffer(LLAudioBuffer *bufferp)
 {
 	S32 i;
-	for (i = 0; i < MAX_BUFFERS; i++)
+	for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++)
 	{
 		if (mBuffers[i] == bufferp)
 		{
@@ -674,7 +652,7 @@ bool LLAudioEngine::preloadSound(const LLUUID &uuid)
 	getAudioData(uuid);	// We don't care about the return value, this is just to make sure
 									// that we have an entry, which will mean that the audio engine knows about this
 
-	if (gAudioDecodeMgrp->addDecodeRequest(uuid))
+    if (LLAudioDecodeMgr::getInstance()->addDecodeRequest(uuid))
 	{
 		// This means that we do have a local copy, and we're working on decoding it.
 		return true;
@@ -968,6 +946,7 @@ LLAudioSource * LLAudioEngine::findAudioSource(const LLUUID &source_id)
 
 LLAudioData * LLAudioEngine::getAudioData(const LLUUID &audio_uuid)
 {
+	LL_PROFILE_ZONE_SCOPED_CATEGORY_MEDIA;
 	auto iter = mAllData.find(audio_uuid);
 	if (iter == mAllData.end())
 	{
@@ -1051,7 +1030,7 @@ void LLAudioEngine::startNextTransfer()
 
 	// Check all channels for currently playing sounds.
 	F32 max_pri = -1.f;
-	for (i = 0; i < MAX_CHANNELS; i++)
+	for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++)
 	{
 		if (!mChannels[i])
 		{
@@ -1079,7 +1058,7 @@ void LLAudioEngine::startNextTransfer()
 			continue;
 		}
 
-		if (!adp->hasLocalData() && adp->hasValidData())
+        if (!adp->hasLocalData() && !adp->hasDecodeFailed())
 		{
 			asset_id = adp->getID();
 			max_pri = asp->getPriority();
@@ -1090,7 +1069,7 @@ void LLAudioEngine::startNextTransfer()
 	if (asset_id.isNull())
 	{
 		max_pri = -1.f;
-		for (i = 0; i < MAX_CHANNELS; i++)
+		for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++)
 		{
 			if (!mChannels[i])
 			{
@@ -1115,7 +1094,7 @@ void LLAudioEngine::startNextTransfer()
 				continue;
 			}
 
-			if (!adp->hasLocalData() && adp->hasValidData())
+            if (!adp->hasLocalData() && !adp->hasDecodeFailed())
 			{
 				asset_id = adp->getID();
 				max_pri = asp->getPriority();
@@ -1127,7 +1106,7 @@ void LLAudioEngine::startNextTransfer()
 	if (asset_id.isNull())
 	{
 		max_pri = -1.f;
-		for (i = 0; i < MAX_CHANNELS; i++)
+		for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++)
 		{
 			if (!mChannels[i])
 			{
@@ -1155,7 +1134,7 @@ void LLAudioEngine::startNextTransfer()
 					continue;
 				}
 
-				if (!adp->hasLocalData() && adp->hasValidData())
+                if (!adp->hasLocalData() && !adp->hasDecodeFailed())
 				{
 					asset_id = adp->getID();
 					max_pri = asp->getPriority();
@@ -1182,7 +1161,7 @@ void LLAudioEngine::startNextTransfer()
 			}
 
 			adp = asp->getCurrentData();
-			if (adp && !adp->hasLocalData() && adp->hasValidData())
+            if (adp && !adp->hasLocalData() && !adp->hasDecodeFailed())
 			{
 				asset_id = adp->getID();
 				max_pri = asp->getPriority();
@@ -1190,7 +1169,7 @@ void LLAudioEngine::startNextTransfer()
 			}
 
 			adp = asp->getQueuedData();
-			if (adp && !adp->hasLocalData() && adp->hasValidData())
+            if (adp && !adp->hasLocalData() && !adp->hasDecodeFailed())
 			{
 				asset_id = adp->getID();
 				max_pri = asp->getPriority();
@@ -1205,7 +1184,7 @@ void LLAudioEngine::startNextTransfer()
 					continue;
 				}
 
-				if (!adp->hasLocalData() && adp->hasValidData())
+                if (!adp->hasLocalData() && !adp->hasDecodeFailed())
 				{
 					asset_id = adp->getID();
 					max_pri = asp->getPriority();
@@ -1246,7 +1225,7 @@ void LLAudioEngine::assetCallback(const LLUUID &uuid, LLAssetType::EType type, v
 		LLAudioData *adp = gAudiop->getAudioData(uuid);
 		if (adp)
         {	// Make sure everything is cleared
-			adp->setHasValidData(false);
+            adp->setHasDecodeFailed(true);
 			adp->setHasLocalData(false);
 			adp->setHasDecodedData(false);
 			adp->setHasCompletedDecode(true);
@@ -1263,9 +1242,9 @@ void LLAudioEngine::assetCallback(const LLUUID &uuid, LLAssetType::EType type, v
 		else
 		{
 			// LL_INFOS() << "Got asset callback with good audio data for " << uuid << ", making decode request" << LL_ENDL;
-			adp->setHasValidData(true);
+            adp->setHasDecodeFailed(false);
 		    adp->setHasLocalData(true);
-		    gAudioDecodeMgrp->addDecodeRequest(uuid);
+            LLAudioDecodeMgr::getInstance()->addDecodeRequest(uuid);
 		}
 	}
 	gAudiop->mCurrentTransfer.setNull();
@@ -1402,11 +1381,15 @@ void LLAudioSource::update()
 		{
 			// Hack - try and load the sound.  Will do this as a callback
 			// on decode later.
-			if (adp->load() && adp->getBuffer())
+            if (adp->getBuffer())
 			{
 				play(adp->getID());
 			}
-			else if (adp->hasCompletedDecode())		// Only mark corrupted after decode is done
+            else if (adp->hasDecodedData() && !adp->hasWAVLoadFailed())
+            {
+                adp->load();
+            }
+            else if (adp->hasCompletedDecode() && adp->hasDecodeFailed()) // Only mark corrupted after decode is done
 			{
 				LL_WARNS() << "Marking LLAudioSource corrupted for " << adp->getID() << LL_ENDL;
 				mCorrupted = true ;
@@ -1709,12 +1692,12 @@ bool LLAudioSource::hasPendingPreloads() const
 	{
 		LLAudioData *adp = preload_pair.second;
 		// note: a bad UUID will forever be !hasDecodedData()
-		// but also !hasValidData(), hence the check for hasValidData()
+        // but also hasDecodeFailed(), hence the check for hasDecodeFailed()
 		if (!adp)
 		{
 			continue;
 		}
-		if (!adp->hasDecodedData() && adp->hasValidData())
+        if (!adp->hasDecodedData() && !adp->hasDecodeFailed())
 		{
 			// This source is still waiting for a preload
 			return true;
@@ -1871,7 +1854,8 @@ LLAudioData::LLAudioData(const LLUUID &uuid) :
 	mHasLocalData(false),
 	mHasDecodedData(false),
 	mHasCompletedDecode(false),
-	mHasValidData(true)
+    mHasDecodeFailed(false),
+    mHasWAVLoadFailed(false)
 {
 	if (uuid.isNull())
 	{
@@ -1906,12 +1890,14 @@ bool LLAudioData::load()
 	{
 		// We already have this sound in a buffer, don't do anything.
 		LL_INFOS() << "Already have a buffer for this sound, don't bother loading!" << LL_ENDL;
+        mHasWAVLoadFailed = false;
 		return true;
 	}
 
 	if (!gAudiop)
 	{
 		LL_WARNS("AudioEngine") << "LLAudioEngine instance doesn't exist!" << LL_ENDL;
+        mHasWAVLoadFailed = true;
 		return false;
 	}
 	
@@ -1920,11 +1906,14 @@ bool LLAudioData::load()
 	{
 		// No free buffers, abort.
 		LL_INFOS() << "Not able to allocate a new audio buffer, aborting." << LL_ENDL;
+        // *TODO: Mark this failure differently so the audio engine could retry loading this buffer in the future
+        mHasWAVLoadFailed = true;
 		return true;
 	}
 
 	std::string wav_path = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, mID.asString()) + ".dsf";
-	if (!mBufferp->loadWAV(wav_path))
+    mHasWAVLoadFailed = !mBufferp->loadWAV(wav_path);
+    if (mHasWAVLoadFailed)
 	{
 		// Hrm.  Right now, let's unset the buffer, since it's empty.
 		gAudiop->cleanupBuffer(mBufferp);
diff --git a/indra/llaudio/llaudioengine.h b/indra/llaudio/llaudioengine.h
index 75ddb0f95ff6fb1f66b6716883f79fa32ca234c6..95cc3870d764221dc3992065d5095b8d083e38df 100644
--- a/indra/llaudio/llaudioengine.h
+++ b/indra/llaudio/llaudioengine.h
@@ -49,8 +49,8 @@ const F32 LL_WIND_UNDERWATER_CENTER_FREQ = 20.f;
 const F32 ATTACHED_OBJECT_TIMEOUT = 5.0f;
 const F32 DEFAULT_MIN_DISTANCE = 2.0f;
 
-#define MAX_CHANNELS 30
-#define MAX_BUFFERS 60	// Some extra for preloading, maybe?
+#define LL_MAX_AUDIO_CHANNELS 30
+#define LL_MAX_AUDIO_BUFFERS 40 // Some extra for preloading, maybe?
 
 class LLAudioSource;
 class LLAudioData;
@@ -91,7 +91,7 @@ class LLAudioEngine
 	virtual ~LLAudioEngine() = default;
 
 	// initialization/startup/shutdown
-	virtual bool init(const S32 num_channels, void *userdata, const std::string &app_title);
+	virtual bool init(void *userdata, const std::string &app_title);
 	virtual std::string getDriverName(bool verbose) = 0;
 	virtual void shutdown();
 
@@ -99,7 +99,7 @@ class LLAudioEngine
 	//virtual void processQueue(const LLUUID &sound_guid);
 	virtual void setListener(const LLVector3& pos, const LLVector3& vel, const LLVector3& up, const LLVector3& at);
 	virtual void updateWind(LLVector3 direction, F32 camera_height_above_water) = 0;
-	virtual void idle(F32 max_decode_time = 0.f);
+	virtual void idle();
 	virtual void updateChannels();
 
 	//
@@ -214,7 +214,6 @@ class LLAudioEngine
 
 	S32 mLastStatus;
 	
-	S32 mNumChannels;
 	bool mEnableWind;
 
 	LLUUID mCurrentTransfer; // Audio file currently being transferred by the system
@@ -229,11 +228,11 @@ class LLAudioEngine
 	source_map mAllSources;
 	data_map mAllData;
 
-	LLAudioChannel *mChannels[MAX_CHANNELS];
+    std::array<LLAudioChannel*, LL_MAX_AUDIO_CHANNELS> mChannels;
 
 	// Buffers needs to change into a different data structure, as the number of buffers
 	// that we have active should be limited by RAM usage, not count.
-	LLAudioBuffer *mBuffers[MAX_BUFFERS];
+    std::array<LLAudioBuffer*, LL_MAX_AUDIO_BUFFERS> mBuffers;
 	
 	F32 mMasterGain;
 	F32 mInternalGain;			// Actual gain set; either mMasterGain or 0 when mMuted is true.
@@ -381,32 +380,36 @@ class LLAudioSource
 
 class LLAudioData
 {
-public:
-	LLAudioData(const LLUUID &uuid);
-	bool load();
-
-	LLUUID getID() const				{ return mID; }
-	LLAudioBuffer *getBuffer() const	{ return mBufferp; }
-
-	bool	hasLocalData() const		{ return mHasLocalData; }
-	bool	hasDecodedData() const		{ return mHasDecodedData; }
-	bool	hasCompletedDecode() const	{ return mHasCompletedDecode; }
-	bool	hasValidData() const		{ return mHasValidData; }
-
-	void	setHasLocalData(const bool hld)		{ mHasLocalData = hld; }
-	void	setHasDecodedData(const bool hdd)	{ mHasDecodedData = hdd; }
-	void	setHasCompletedDecode(const bool hcd)	{ mHasCompletedDecode = hcd; }
-	void	setHasValidData(const bool hvd)		{ mHasValidData = hvd; }
-
-	friend class LLAudioEngine; // Severe laziness, bad.
-
-protected:
-	LLUUID mID;
-	LLAudioBuffer *mBufferp;	// If this data is being used by the audio system, a pointer to the buffer will be set here.
-	bool mHasLocalData;			// Set true if the sound asset file is available locally
-	bool mHasDecodedData;		// Set true if the sound file has been decoded
-	bool mHasCompletedDecode;	// Set true when the sound is decoded
-	bool mHasValidData;			// Set false if decoding failed, meaning the sound asset is bad
+  public:
+    LLAudioData(const LLUUID &uuid);
+    bool load();
+
+    LLUUID         getID() const { return mID; }
+    LLAudioBuffer *getBuffer() const { return mBufferp; }
+
+    bool hasLocalData() const { return mHasLocalData; }
+    bool hasDecodedData() const { return mHasDecodedData; }
+    bool hasCompletedDecode() const { return mHasCompletedDecode; }
+    bool hasDecodeFailed() const { return mHasDecodeFailed; }
+    bool hasWAVLoadFailed() const { return mHasWAVLoadFailed; }
+
+    void setHasLocalData(const bool hld) { mHasLocalData = hld; }
+    void setHasDecodedData(const bool hdd) { mHasDecodedData = hdd; }
+    void setHasCompletedDecode(const bool hcd) { mHasCompletedDecode = hcd; }
+    void setHasDecodeFailed(const bool hdf) { mHasDecodeFailed = hdf; }
+    void setHasWAVLoadFailed(const bool hwlf) { mHasWAVLoadFailed = hwlf; }
+
+    friend class LLAudioEngine;  // Severe laziness, bad.
+
+  protected:
+    LLUUID         mID;
+    LLAudioBuffer *mBufferp;             // If this data is being used by the audio system, a pointer to the buffer will be set here.
+    bool           mHasLocalData;        // Set true if the encoded sound asset file is available locally
+    bool           mHasDecodedData;      // Set true if the decoded sound file is available on disk
+    bool           mHasCompletedDecode;  // Set true when the sound is decoded
+    bool           mHasDecodeFailed;     // Set true if decoding failed, meaning the sound asset is bad
+    bool mHasWAVLoadFailed;  // Set true if loading the decoded WAV file failed, meaning the sound asset should be decoded instead if
+                             // possible
 };
 
 
diff --git a/indra/llaudio/llaudioengine_fmodstudio.cpp b/indra/llaudio/llaudioengine_fmodstudio.cpp
index 02f01c3140463541490adc75f1fb78195765a05c..595122ce175c1fd05546db6f1b2aa535d5b9af4d 100644
--- a/indra/llaudio/llaudioengine_fmodstudio.cpp
+++ b/indra/llaudio/llaudioengine_fmodstudio.cpp
@@ -69,7 +69,7 @@ inline bool Check_FMOD_Error(FMOD_RESULT result, const char *string)
 	return true;
 }
 
-bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, const std::string& app_title)
+bool LLAudioEngine_FMODSTUDIO::init(void* userdata, const std::string &app_title)
 {
 	U32 version;
 	FMOD_RESULT result;
@@ -81,7 +81,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons
 		return false;
 
 	//will call LLAudioEngine_FMODSTUDIO::allocateListener, which needs a valid mSystem pointer.
-	LLAudioEngine::init(num_channels, userdata, app_title);
+    LLAudioEngine::init(userdata, app_title);
 	
 	result = mSystem->getVersion(&version);
 	Check_FMOD_Error(result, "FMOD::System::getVersion");
@@ -93,7 +93,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons
 	}
 
 	// In this case, all sounds, PLUS wind and stream will be software.
-	result = mSystem->setSoftwareChannels(num_channels + EXTRA_SOUND_CHANNELS);
+    result = mSystem->setSoftwareChannels(LL_MAX_AUDIO_CHANNELS + EXTRA_SOUND_CHANNELS);
 	Check_FMOD_Error(result,"FMOD::System::setSoftwareChannels");
 
 	FMOD_ADVANCEDSETTINGS adv_settings = { };
@@ -130,7 +130,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons
 		{
 			LL_DEBUGS("AppInit") << "Trying PulseAudio audio output..." << LL_ENDL;
 			if((result = mSystem->setOutput(FMOD_OUTPUTTYPE_PULSEAUDIO)) == FMOD_OK &&
-				(result = mSystem->init(num_channels + EXTRA_SOUND_CHANNELS, fmod_flags, const_cast<char*>(app_title.c_str()))) == FMOD_OK)
+                (result = mSystem->init(LL_MAX_AUDIO_CHANNELS + EXTRA_SOUND_CHANNELS, fmod_flags, const_cast<char*>(app_title.c_str()))) == FMOD_OK)
 			{
 				LL_DEBUGS("AppInit") << "PulseAudio output initialized OKAY"	<< LL_ENDL;
 				audio_ok = true;
@@ -151,7 +151,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons
 		{
 			LL_DEBUGS("AppInit") << "Trying ALSA audio output..." << LL_ENDL;
 			if((result = mSystem->setOutput(FMOD_OUTPUTTYPE_ALSA)) == FMOD_OK &&
-			    (result = mSystem->init(num_channels + EXTRA_SOUND_CHANNELS, fmod_flags, 0)) == FMOD_OK)
+                (result = mSystem->init(LL_MAX_AUDIO_CHANNELS + EXTRA_SOUND_CHANNELS, fmod_flags, 0)) == FMOD_OK)
 			{
 				LL_DEBUGS("AppInit") << "ALSA audio output initialized OKAY" << LL_ENDL;
 				audio_ok = true;
@@ -192,7 +192,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons
 #else // LL_LINUX
 
 	// initialize the FMOD engine
-	result = mSystem->init( num_channels + EXTRA_SOUND_CHANNELS, fmod_flags, nullptr);
+    result = mSystem->init(LL_MAX_AUDIO_CHANNELS + EXTRA_SOUND_CHANNELS, fmod_flags, 0);
 	if (result == FMOD_ERR_OUTPUT_CREATEBUFFER)
 	{
 		/*
@@ -204,7 +204,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons
 		/*
 		... and re-init.
 		*/
-		result = mSystem->init( num_channels + EXTRA_SOUND_CHANNELS, fmod_flags, nullptr);
+        result = mSystem->init(LL_MAX_AUDIO_CHANNELS + EXTRA_SOUND_CHANNELS, fmod_flags, 0);
 	}
 	if(Check_FMOD_Error(result, "Error initializing FMOD Studio"))
 		return false;
diff --git a/indra/llaudio/llaudioengine_fmodstudio.h b/indra/llaudio/llaudioengine_fmodstudio.h
index 9f48ec6aab3cba59f1c7b16a371b7f5dbaf00768..89a58d3346b1a4fe41c83e6e4d72781319848cc9 100644
--- a/indra/llaudio/llaudioengine_fmodstudio.h
+++ b/indra/llaudio/llaudioengine_fmodstudio.h
@@ -57,7 +57,7 @@ class LLAudioEngine_FMODSTUDIO final : public LLAudioEngine
 	virtual ~LLAudioEngine_FMODSTUDIO() = default;
 
 	// initialization/startup/shutdown
-	bool init(const S32 num_channels, void *user_data, const std::string& app_title) final override;
+	virtual bool init(void *user_data, const std::string &app_title) final;
 	std::string getDriverName(bool verbose) final override;
 	void allocateListener() final override;
 
diff --git a/indra/llaudio/llaudioengine_openal.cpp b/indra/llaudio/llaudioengine_openal.cpp
index 402066f14d382a9440a8c5ec9a199022a89bdfdf..0e1fb59a03e780bc6d714c544ea7f3a2a716ca2b 100644
--- a/indra/llaudio/llaudioengine_openal.cpp
+++ b/indra/llaudio/llaudioengine_openal.cpp
@@ -47,10 +47,10 @@ LLAudioEngine_OpenAL::LLAudioEngine_OpenAL()
 }
 
 // virtual
-bool LLAudioEngine_OpenAL::init(const S32 num_channels, void* userdata, const std::string &app_title)
+bool LLAudioEngine_OpenAL::init(void* userdata, const std::string &app_title)
 {
 	mWindGen = NULL;
-	LLAudioEngine::init(num_channels, userdata, app_title);
+	LLAudioEngine::init(userdata, app_title);
 
 	if(!alutInit(NULL, NULL))
 	{
diff --git a/indra/llaudio/llaudioengine_openal.h b/indra/llaudio/llaudioengine_openal.h
index 29108d7a0f3b1c09e499aeb26dd91265426249c8..0a4c837315f54420a71b7d8fa41f6f26b5490de7 100644
--- a/indra/llaudio/llaudioengine_openal.h
+++ b/indra/llaudio/llaudioengine_openal.h
@@ -40,7 +40,7 @@ class LLAudioEngine_OpenAL final : public LLAudioEngine
 		LLAudioEngine_OpenAL();
 		virtual ~LLAudioEngine_OpenAL() = default;
 
-        virtual bool init(const S32 num_channels, void *user_data, const std::string &app_title);
+        virtual bool init(void *user_data, const std::string &app_title);
         virtual std::string getDriverName(bool verbose);
 		virtual void allocateListener();
 
diff --git a/indra/llcharacter/llkeyframemotion.cpp b/indra/llcharacter/llkeyframemotion.cpp
index 90671f08151e1334514ad8f10be28a5cb661088e..e0a8eade8970279642580dfb9d270b6c868ffe6a 100644
--- a/indra/llcharacter/llkeyframemotion.cpp
+++ b/indra/llcharacter/llkeyframemotion.cpp
@@ -979,8 +979,11 @@ void LLKeyframeMotion::applyConstraint(JointConstraint* constraint, F32 time, U8
 
 //-----------------------------------------------------------------------------
 // deserialize()
+//
+// allow_invalid_joints should be true when handling existing content, to avoid breakage.
+// During upload, we should be more restrictive and reject such animations.
 //-----------------------------------------------------------------------------
-BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id)
+BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, bool allow_invalid_joints)
 {
 	BOOL old_version = FALSE;
     auto joint_motion_list = std::make_unique<LLKeyframeMotion::JointMotionList>();
@@ -1101,6 +1104,15 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id)
 		return FALSE;
 	}
 
+	//SL-17206 hack to alter Female_land loop setting, while current behavior won't be changed serverside
+	LLUUID const female_land_anim("ca1baf4d-0a18-5a1f-0330-e4bd1e71f09e");
+	LLUUID const formal_female_land_anim("6a9a173b-61fa-3ad5-01fa-a851cfc5f66a");
+	if (female_land_anim == asset_id || formal_female_land_anim == asset_id)
+	{
+		LL_WARNS() << "Animation(" << asset_id << ") won't be looped." << LL_ENDL;
+		mJointMotionList->mLoop = FALSE;
+	}
+
 	//-------------------------------------------------------------------------
 	// get easeIn and easeOut
 	//-------------------------------------------------------------------------
@@ -1200,6 +1212,7 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id)
 		if (joint)
 		{
             S32 joint_num = joint->getJointNum();
+			joint_name = joint->getName(); // canonical name in case this is an alias.
 //			LL_INFOS() << "  joint: " << joint_name << LL_ENDL;
             if ((joint_num >= (S32)LL_CHARACTER_MAX_ANIMATED_JOINTS) || (joint_num < 0))
             {
@@ -1214,7 +1227,10 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id)
 		{
 			LL_WARNS() << "invalid joint name: " << joint_name
                        << " for animation " << asset_id << LL_ENDL;
-			//return FALSE;
+			if (!allow_invalid_joints)
+			{
+				return FALSE;
+			}
 		}
 
 		joint_motion->mJointName = std::move(joint_name); // Do not use joint_name after this point.
@@ -1865,8 +1881,9 @@ U32	LLKeyframeMotion::getFileSize()
 //-----------------------------------------------------------------------------
 // dumpToFile()
 //-----------------------------------------------------------------------------
-void LLKeyframeMotion::dumpToFile(const std::string& name)
+bool LLKeyframeMotion::dumpToFile(const std::string& name)
 {
+	bool succ = false;
     if (isLoaded())
     {
         std::string outfile_base;
@@ -1883,10 +1900,24 @@ void LLKeyframeMotion::dumpToFile(const std::string& name)
             const LLUUID& id = getID();
             outfile_base = id.asString();
         }
-        std::string outfilename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,outfile_base + ".anim");
+
+		if (gDirUtilp->getExtension(outfile_base).empty())
+		{
+			outfile_base += ".anim";
+		}
+		std::string outfilename;
+		if (gDirUtilp->getDirName(outfile_base).empty())
+		{
+			outfilename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,outfile_base);
+		}
+		else
+		{
+			outfilename = outfile_base;
+		}
         if (LLFile::isfile(outfilename))
         {
-            return;
+			LL_WARNS() << outfilename << " already exists, write failed" << LL_ENDL;
+            return false;
         }
 
         S32 file_size = getFileSize();
@@ -1900,11 +1931,13 @@ void LLKeyframeMotion::dumpToFile(const std::string& name)
             outfile.open(outfilename, LL_APR_WPB);
             if (outfile.getFileHandle())
             {
-                outfile.write(buffer, file_size);
+                S32 wrote_bytes = outfile.write(buffer, file_size);
+				succ = (wrote_bytes == file_size);
             }
         }
         delete [] buffer;
     }
+	return succ;
 }
 
 //-----------------------------------------------------------------------------
diff --git a/indra/llcharacter/llkeyframemotion.h b/indra/llcharacter/llkeyframemotion.h
index 7a6c0366cbbccf1f01e5ea102bb102f77d2e0255..23a6cdff7ec82ad39e1615fb22217672e2108de8 100644
--- a/indra/llcharacter/llkeyframemotion.h
+++ b/indra/llcharacter/llkeyframemotion.h
@@ -174,9 +174,9 @@ class LLKeyframeMotion :
 public:
 	U32		getFileSize();
 	BOOL	serialize(LLDataPacker& dp) const;
-	BOOL	deserialize(LLDataPacker& dp, const LLUUID& asset_id);
+	BOOL	deserialize(LLDataPacker& dp, const LLUUID& asset_id, bool allow_invalid_joints = true);
 	BOOL	isLoaded() { return mJointMotionList != NULL; }
-    void	dumpToFile(const std::string& name);
+    bool	dumpToFile(const std::string& name);
 
 
 	// setters for modifying a keyframe animation
@@ -433,6 +433,9 @@ class LLKeyframeMotion :
 	F32								mLastUpdateTime;
 	F32								mLastLoopedTime;
 	AssetStatus						mAssetStatus;
+
+public:
+	void setCharacter(LLCharacter* character) { mCharacter = character; }
 };
 
 class LLKeyframeDataCache
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index e288fae96d409ac103826d42394af4157e64c8fd..1c0f61ab470324e48c06e2fa6ca43e35b36ff533 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -268,6 +268,11 @@ set(llcommon_HEADER_FILES
     workqueue.h
     StackWalker.h
     )
+    
+if (DARWIN)
+  list(APPEND llcommon_HEADER_FILES llsys_objc.h)
+  list(APPEND llcommon_SOURCE_FILES llsys_objc.mm)
+endif (DARWIN)
 
 if (DARWIN)
   list(APPEND llcommon_HEADER_FILES llsys_objc.h)
diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp
index 53c5d2b75031b119e2205c87d571eb4c105db19a..33825283893905a1f038c35a9e332ca787902d4b 100644
--- a/indra/llcommon/llmemory.cpp
+++ b/indra/llcommon/llmemory.cpp
@@ -254,11 +254,9 @@ U64 LLMemory::getCurrentRSS()
 	mach_msg_type_number_t  basicInfoCount = MACH_TASK_BASIC_INFO_COUNT;
 	if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&basicInfo, &basicInfoCount) == KERN_SUCCESS)
 	{
-//		residentSize = basicInfo.resident_size;
-		// Although this method is defined to return the "resident set size,"
-		// in fact what callers want from it is the total virtual memory
-		// consumed by the application.
-		residentSize = basicInfo.virtual_size;
+        residentSize = basicInfo.resident_size;
+        // 64-bit macos apps allocate 32 GB or more at startup, and this is reflected in virtual_size.
+        // basicInfo.virtual_size is not what we want.
 	}
 	else
 	{
diff --git a/indra/llcommon/llrefcount.cpp b/indra/llcommon/llrefcount.cpp
index 5cbd3464115ea784952d572bab513d3c8045ea31..6852b5536aecdfebb1627ae218cfcfed26bacc78 100644
--- a/indra/llcommon/llrefcount.cpp
+++ b/indra/llcommon/llrefcount.cpp
@@ -30,7 +30,7 @@
 #include "llerror.h"
 
 // maximum reference count before sounding memory leak alarm
-const S32 gMaxRefCount = 65536;
+const S32 gMaxRefCount = S32_MAX;
 
 LLRefCount::LLRefCount(const LLRefCount& other)
 :	mRef(0)
diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp
index 5e0f1b152afc4645c3c4e4dab99007b3cf4efc79..654226646d84f0dde939487e4ee54778c051e2e0 100644
--- a/indra/llcommon/llsys.cpp
+++ b/indra/llcommon/llsys.cpp
@@ -292,7 +292,7 @@ LLOSInfo::LLOSInfo() :
 	{
 		S32 major_version, minor_version, bugfix_version = 0;
 
-		if (LLSysDarwin::getOperatingSystemInfo(major_version, minor_version, bugfix_version))
+		if (LLGetDarwinOSInfo(major_version, minor_version, bugfix_version))
 		{
 			mMajorVer = major_version;
 			mMinorVer = minor_version;
diff --git a/indra/llcommon/llsys_objc.h b/indra/llcommon/llsys_objc.h
index 2d12ca7f2b276c6322e23b327e2f87fa0fe1949a..35599a574b7212c9eb00e3be9522283a0a760973 100644
--- a/indra/llcommon/llsys_objc.h
+++ b/indra/llcommon/llsys_objc.h
@@ -1,46 +1,33 @@
-/*
+/**
  * @file llsys_objc.h
- * @brief Some objective-c crap for llcommon
+ * @brief Header file for llsys_objc.mm
  *
- * (C) 2014 Cinder Roxley @ Second Life <cinder@alchemyviewer.org>
+ * $LicenseInfo:firstyear=2022&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2022, Linden Research, Inc.
  *
- * Permission is hereby granted, free of charge, to any person or organization
- * obtaining a copy of the software and accompanying documentation covered by
- * this license (the "Software") to use, reproduce, display, distribute,
- * execute, and transmit the Software, and to prepare derivative works of the
- * Software, and to permit third-parties to whom the Software is furnished to
- * do so, all subject to the following:
+ * 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.
  *
- * The copyright notices in the Software and this entire statement, including
- * the above license grant, this restriction and the following disclaimer,
- * must be included in all copies of the Software, in whole or in part, and
- * all derivative works of the Software, unless such copies or derivative
- * works are solely in the form of machine-executable object code generated by
- * a source language processor.
+ * 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.
  *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
- * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
- * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+ * 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_SYS_OBJC_H
-#define LL_SYS_OBJC_H
-
-#ifndef LL_DARWIN
-#  error "This file should only be included when building on mac!"
-#else
+#ifndef LL_LLSYS_OBJC_H
+#define LL_LLSYS_OBJC_H
 
-namespace LLSysDarwin
-{
+bool LLGetDarwinOSInfo(int &major, int &minor, int &patch);
 
-bool getOperatingSystemInfo(int &major, int &minor, int &patch);
-const char* getPreferredLanguage();
-	
-}
 
-#endif // !LL_DARWIN
-#endif // LL_SYS_OBJC_H
+#endif // LL_LLSYS_OBJC_H
diff --git a/indra/llcommon/llsys_objc.mm b/indra/llcommon/llsys_objc.mm
index 46b3337e7fb85f97a8b5d58d115e0861cdb68e56..1caf12c2d12a2865da425b62b76498e83b2d355c 100644
--- a/indra/llcommon/llsys_objc.mm
+++ b/indra/llcommon/llsys_objc.mm
@@ -1,54 +1,65 @@
-/*
+/**
  * @file llsys_objc.mm
- * @brief Some objective-c crap for llcommon
+ * @brief obj-c implementation of the system information functions
  *
- * (C) 2014 Cinder Roxley @ Second Life <cinder@alchemyviewer.org>
+ * $LicenseInfo:firstyear=2022&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2022, Linden Research, Inc.
  *
- * Permission is hereby granted, free of charge, to any person or organization
- * obtaining a copy of the software and accompanying documentation covered by
- * this license (the "Software") to use, reproduce, display, distribute,
- * execute, and transmit the Software, and to prepare derivative works of the
- * Software, and to permit third-parties to whom the Software is furnished to
- * do so, all subject to the following:
+ * 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.
  *
- * The copyright notices in the Software and this entire statement, including
- * the above license grant, this restriction and the following disclaimer,
- * must be included in all copies of the Software, in whole or in part, and
- * all derivative works of the Software, unless such copies or derivative
- * works are solely in the form of machine-executable object code generated by
- * a source language processor.
+ * 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.
  *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
- * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
- * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+ * 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_DARWIN
-#  error "This file should only be included when building on mac!"
-#else
-
 #import "llsys_objc.h"
-#import <Foundation/Foundation.h>
-#import <AppKit/NSApplication.h>
+#import <AppKit/AppKit.h>
+
+static int intAtStringIndex(NSArray *array, int index)
+{
+    return [(NSString *)[array objectAtIndex:index] integerValue];
+}
 
-bool LLSysDarwin::getOperatingSystemInfo(int &major, int &minor, int &patch)
+bool LLGetDarwinOSInfo(int &major, int &minor, int &patch)
 {
     NSOperatingSystemVersion osVersion = [[NSProcessInfo processInfo] operatingSystemVersion];
     major = osVersion.majorVersion;
     minor = osVersion.minorVersion;
     patch = osVersion.patchVersion;
-	return true;
+    return true;
 }
 
-const char* LLSysDarwin::getPreferredLanguage()
+const char* LLGetDarwinPreferredLanguage()
 {
 	NSString* lang = [[NSLocale preferredLanguages] objectAtIndex:0];
 	const char* ret = [lang cStringUsingEncoding:NSASCIIStringEncoding];
+                                    @"/System/Library/CoreServices/SystemVersion.plist"] objectForKey:@"ProductVersion"];
+        NSArray* versions = [versionString componentsSeparatedByString:@"."];
+        NSUInteger count = [versions count];
+        if (count > 0)
+        {
+            major = intAtStringIndex(versions, 0);
+            if (count > 1)
+            {
+                minor = intAtStringIndex(versions, 1);
+                if (count > 2)
+                {
+                    patch = intAtStringIndex(versions, 2);
+                }
+            }
+        }
+    }
 	return ret;
 }
-
-#endif // !LL_DARWIN
diff --git a/indra/llcommon/threadpool.cpp b/indra/llcommon/threadpool.cpp
index ba914035e200a9ecfaee13c010e2f6fe32863af1..d5adf11264cfc25f8de690f545a2a80807355c50 100644
--- a/indra/llcommon/threadpool.cpp
+++ b/indra/llcommon/threadpool.cpp
@@ -22,6 +22,7 @@
 #include "stringize.h"
 
 LL::ThreadPool::ThreadPool(const std::string& name, size_t threads, size_t capacity):
+    super(name),
     mQueue(name, capacity),
     mName("ThreadPool:" + name),
     mThreadCount(threads)
diff --git a/indra/llcommon/threadpool.h b/indra/llcommon/threadpool.h
index b79c9b90903e6cec030608f5dc097bf5cba00575..f8eec3b45744d8a18532811a24901fe84271f62a 100644
--- a/indra/llcommon/threadpool.h
+++ b/indra/llcommon/threadpool.h
@@ -22,8 +22,10 @@
 namespace LL
 {
 
-    class ThreadPool
+    class ThreadPool: public LLInstanceTracker<ThreadPool, std::string>
     {
+    private:
+        using super = LLInstanceTracker<ThreadPool, std::string>;
     public:
         /**
          * Pass ThreadPool a string name. This can be used to look up the
diff --git a/indra/llcommon/workqueue.h b/indra/llcommon/workqueue.h
index 96574a18b92f137ef5d3a02b70412928abee6653..70fd65bd0cd71c1f6e5c9dd86639f45ef7c4cb5f 100644
--- a/indra/llcommon/workqueue.h
+++ b/indra/llcommon/workqueue.h
@@ -403,7 +403,7 @@ namespace LL
                 [result = std::forward<CALLABLE>(callable)(),
                  callback = std::move(callback)]
                 ()
-                { callback(std::move(result)); };
+                mutable { callback(std::move(result)); };
         }
     };
 
@@ -449,7 +449,7 @@ namespace LL
              callable = std::move(callable),
              callback = std::move(callback)]
             ()
-            {
+            mutable {
                 // Use postMaybe() below in case this originating WorkQueue
                 // has been closed or destroyed. Remember, the outer lambda is
                 // now running on a thread servicing the target WorkQueue, and
@@ -513,7 +513,7 @@ namespace LL
                 // We dare to bind a reference to Promise because it's
                 // specifically designed for cross-thread communication.
                 [&promise, callable = std::move(callable)]()
-                {
+                mutable {
                     try
                     {
                         // call the caller's callable and trigger promise with result
@@ -542,7 +542,7 @@ namespace LL
                 time,
                 // &promise is designed for cross-thread access
                 [&promise, callable = std::move(callable)]()
-                {
+                mutable {
                     try
                     {
                         callable();
diff --git a/indra/llfilesystem/lldiskcache.cpp b/indra/llfilesystem/lldiskcache.cpp
index fa071b495c80ca63d82c98a0a8e87ca55d4e6f29..bb7b62d872e5286a61cef1ab2b91ac3264ebca7d 100644
--- a/indra/llfilesystem/lldiskcache.cpp
+++ b/indra/llfilesystem/lldiskcache.cpp
@@ -159,9 +159,16 @@ void LLDiskCache::purge()
     {
         file_size_total += entry.second.first;
 
+        bool should_remove = file_size_total > mMaxSizeBytes;
+        if (mEnableCacheDebugInfo)
+        {
+            file_removed.push_back(should_remove);
+        }
+
         std::string action = "";
-        if (file_size_total > mMaxSizeBytes)
+        if (should_remove)
         {
+
             action = "DELETE:";
             auto uuid_as_string = LLUUID(gDirUtilp->getBaseFileName(entry.second.second.string(), true));
             // LL_INFOS() << "checking UUID=" <<uuid_as_string<< LL_ENDL;
@@ -187,11 +194,22 @@ void LLDiskCache::purge()
         else
         {
             keep++;
-            action = "  KEEP:";
         }
+    }
 
-        if (mEnableCacheDebugInfo)
+    if (mEnableCacheDebugInfo)
+    {
+        auto end_time = std::chrono::high_resolution_clock::now();
+        auto execute_time = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count();
+
+        // Log afterward so it doesn't affect the time measurement
+        // Logging thousands of file results can take hundreds of milliseconds
+        for (size_t i = 0; i < file_info.size(); ++i)
         {
+            const file_info_t& entry = file_info[i];
+            const bool removed = file_removed[i];
+            const std::string action = removed ? "DELETE:" : "KEEP:";
+
             // have to do this because of LL_INFO/LL_END weirdness
             std::ostringstream line;
 
@@ -202,12 +220,7 @@ void LLDiskCache::purge()
             line << " (" << file_size_total << "/" << mMaxSizeBytes << ")";
             LL_INFOS() << line.str() << LL_ENDL;
         }
-    }
 
-    if (mEnableCacheDebugInfo)
-    {
-        auto end_time = std::chrono::high_resolution_clock::now();
-        auto execute_time = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count();
         LL_INFOS() << "Total dir size after purge is " << dirFileSize(sCacheDir) << LL_ENDL;
         LL_INFOS() << "Cache purge took " << execute_time << " ms to execute for " << file_info.size() << " files" << LL_ENDL;
         LL_INFOS() << "Deleted: " << del << " Skipped: " << skip << " Kept: " << keep << LL_ENDL;
diff --git a/indra/llmath/lloctree.h b/indra/llmath/lloctree.h
index 75b2c04d5e392e436eee1d98f87e98956c347d4e..ef83838b10c14c222d6cef478701fdec56e8841f 100644
--- a/indra/llmath/lloctree.h
+++ b/indra/llmath/lloctree.h
@@ -50,58 +50,59 @@ extern float gOctreeMinSize;
 #define LL_OCTREE_MAX_CAPACITY 128
 #endif*/
 
-#if !LL_DEBUG
-#define LL_OCTREE_POOLS 1
-#endif
-
-template <class T> class LLOctreeNode;
-
-template <class T>
+// T is the type of the element referenced by the octree node.
+// T_PTR determines how pointers to elements are stored internally.
+// LLOctreeNode<T, LLPointer<T>> assumes ownership of inserted elements and
+// deletes elements removed from the tree.
+// LLOctreeNode<T, T*> doesn't take ownership of inserted elements, so the API
+// user is responsible for managing the storage lifecycle of elements added to
+// the tree.
+template <class T, typename T_PTR> class LLOctreeNode;
+
+template <class T, typename T_PTR>
 class LLOctreeListener: public LLTreeListener<T>
 {
 public:
 	typedef LLTreeListener<T> BaseType;
-	typedef LLOctreeNode<T> oct_node;
+    typedef LLOctreeNode<T, T_PTR> oct_node;
 
 	virtual void handleChildAddition(const oct_node* parent, oct_node* child) = 0;
 	virtual void handleChildRemoval(const oct_node* parent, const oct_node* child) = 0;
 };
 
-template <class T>
+template <class T, typename T_PTR>
 class LLOctreeTraveler
 {
 public:
-	virtual ~LLOctreeTraveler() = default;
-
-	virtual void traverse(const LLOctreeNode<T>* node);
-	virtual void visit(const LLOctreeNode<T>* branch) = 0;
+    virtual void traverse(const LLOctreeNode<T, T_PTR>* node);
+    virtual void visit(const LLOctreeNode<T, T_PTR>* branch) = 0;
 };
 
-template <class T>
-class LLOctreeTravelerDepthFirst : public LLOctreeTraveler<T>
+template <class T, typename T_PTR>
+class LLOctreeTravelerDepthFirst : public LLOctreeTraveler<T, T_PTR>
 {
 public:
-	virtual void traverse(const LLOctreeNode<T>* node) override;
+	virtual void traverse(const LLOctreeNode<T, T_PTR>* node) override;
 };
 
-template <class T>
+template <class T, typename T_PTR>
 class alignas(16) LLOctreeNode : public LLTreeNode<T>
 {
     LL_ALIGN_NEW
 public:
 
-	typedef LLOctreeTraveler<T>									oct_traveler;
-	typedef LLTreeTraveler<T>									tree_traveler;
-	typedef std::vector< LLPointer<T> >							element_list;		// note:  don't remove the whitespace between "> >"
-	typedef LLPointer<T>*										element_iter;
-	typedef const LLPointer<T>*									const_element_iter;
+    typedef LLOctreeTraveler<T, T_PTR>                          oct_traveler;
+    typedef LLTreeTraveler<T>                                   tree_traveler;
+    typedef std::vector<T_PTR>                                  element_list;
+    typedef typename element_list::iterator                     element_iter;
+    typedef typename element_list::const_iterator               const_element_iter;
 	typedef typename std::vector<LLTreeListener<T>*>::iterator	tree_listener_iter;
-	typedef LLOctreeNode<T>**									child_list;
-	typedef LLOctreeNode<T>**									child_iter;
+    typedef LLOctreeNode<T, T_PTR>**                            child_list;
+    typedef LLOctreeNode<T, T_PTR>**                            child_iter;
 
-	typedef LLTreeNode<T>		BaseType;
-	typedef LLOctreeNode<T>		oct_node;
-	typedef LLOctreeListener<T>	oct_listener;
+    typedef LLTreeNode<T>               BaseType;
+    typedef LLOctreeNode<T, T_PTR>      oct_node;
+    typedef LLOctreeListener<T, T_PTR>  oct_listener;
 
     enum
     {
@@ -112,15 +113,13 @@ class alignas(16) LLOctreeNode : public LLTreeNode<T>
 					const LLVector4a& size, 
 					BaseType* parent, 
 					U8 octant = NO_CHILD_NODES)
-	:	mCenter(center),
-		mSize(size),
-		mParent((oct_node*)parent), 
+	:	mParent((oct_node*)parent), 
 		mOctant(octant) 
 	{ 
 		llassert(size[0] >= gOctreeMinSize*0.5f);
-		//always keep a NULL terminated list to avoid out of bounds exceptions in debug builds
-		mData.push_back(NULL);
-		mDataEnd = &mData[0];
+
+		mCenter = center;
+		mSize = size;
 
 		updateMinMax();
 		if ((mOctant == NO_CHILD_NODES) && mParent)
@@ -128,8 +127,6 @@ class alignas(16) LLOctreeNode : public LLTreeNode<T>
 			mOctant = ((oct_node*) mParent)->getOctant(mCenter);
 		}
 
-		mElementCount = 0;
-
 		clearChildren();
 	}
 
@@ -137,15 +134,14 @@ class alignas(16) LLOctreeNode : public LLTreeNode<T>
 	{ 
 		BaseType::destroyListeners();
 		
-		for (U32 i = 0; i < mElementCount; ++i)
+        const U32 element_count = getElementCount();
+        for (U32 i = 0; i < element_count; ++i)
 		{
 			mData[i]->setBinIndex(-1);
 			mData[i] = NULL;
 		}
 
 		mData.clear();
-		mData.push_back(NULL);
-		mDataEnd = &mData[0];
 
 		for (U32 i = 0; i < getChildCount(); i++)
 		{
@@ -245,14 +241,12 @@ class alignas(16) LLOctreeNode : public LLTreeNode<T>
 	void accept(oct_traveler* visitor)				{ visitor->visit(this); }
 	virtual bool isLeaf() const						{ return mChildCount == 0; }
 	
-	U32 getElementCount() const						{ return mElementCount; }
-	bool isEmpty() const							{ return mElementCount == 0; }
-	element_list& getData()							{ return mData; }
-	const element_list& getData() const				{ return mData; }
-	element_iter getDataBegin()						{ return &mData[0]; }
-	element_iter getDataEnd()						{ return mDataEnd; }
-	const_element_iter getDataBegin() const			{ return &mData[0]; }
-	const_element_iter getDataEnd() const			{ return mDataEnd; }
+    U32 getElementCount() const                     { return (U32)mData.size(); }
+    bool isEmpty() const                            { return mData.empty(); }
+    element_iter getDataBegin()                     { return mData.begin(); }
+    element_iter getDataEnd()                       { return mData.end(); }
+    const_element_iter getDataBegin() const         { return mData.cbegin(); }
+    const_element_iter getDataEnd() const           { return mData.cend(); }
 		
 	U32 getChildCount()	const						{ return mChildCount; }
 	oct_node* getChild(U32 index)					{ return mChild[index]; }
@@ -270,7 +264,7 @@ class alignas(16) LLOctreeNode : public LLTreeNode<T>
 			U8 idx = mChildMap[i];
 			if (idx != NO_CHILD_NODES)
 			{
-				LLOctreeNode<T>* child = mChild[idx];
+                oct_node* child = mChild[idx];
 
 				if (child->getOctant() != i)
 				{
@@ -288,7 +282,7 @@ class alignas(16) LLOctreeNode : public LLTreeNode<T>
 
 	oct_node* getNodeAt(const LLVector4a& pos, const F32& rad)
 	{ 
-		LLOctreeNode<T>* node = this;
+        oct_node* node = this;
 
 		if (node->isInside(pos, rad))
 		{
@@ -310,7 +304,7 @@ class alignas(16) LLOctreeNode : public LLTreeNode<T>
 		}
 		else if (!node->contains(rad) && node->getParent())
 		{ //if we got here, data does not exist in this node
-			return ((LLOctreeNode<T>*) node->getParent())->getNodeAt(pos, rad);
+            return ((oct_node*) node->getParent())->getNodeAt(pos, rad);
 		}
 
 		return node;
@@ -325,7 +319,7 @@ class alignas(16) LLOctreeNode : public LLTreeNode<T>
 			OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE BRANCH !!!" << LL_ENDL;
 			return false;
 		}
-		LLOctreeNode<T>* parent = getOctParent();
+        oct_node* parent = getOctParent();
 
 		//is it here?
 		if (isInside(data->getPositionGroup()))
@@ -333,11 +327,8 @@ class alignas(16) LLOctreeNode : public LLTreeNode<T>
 			if ((((getElementCount() < gOctreeMaxCapacity || getSize()[0] <= gOctreeMinSize) && contains(data->getBinRadius())) ||
 				(data->getBinRadius() > getSize()[0] &&	parent && parent->getElementCount() >= gOctreeMaxCapacity))) 
 			{ //it belongs here
-				mData.push_back(NULL);
-				mData[mElementCount] = data;
-				mElementCount++;
-				mDataEnd = &mData[mElementCount];
-				data->setBinIndex(mElementCount-1);
+                mData.push_back(data);
+                data->setBinIndex(getElementCount() - 1);
 				BaseType::insert(data);
 				return true;
 			}
@@ -361,7 +352,7 @@ class alignas(16) LLOctreeNode : public LLTreeNode<T>
 				size.mul(0.5f);
 		        		
 				//push center in direction of data
-				LLOctreeNode<T>::pushCenter(center, size, data);
+                oct_node::pushCenter(center, size, data);
 
 				// handle case where floating point number gets too small
 				LLVector4a val;
@@ -373,11 +364,8 @@ class alignas(16) LLOctreeNode : public LLTreeNode<T>
 
 				if( lt == 0x7 )
 				{
-					mData.push_back(NULL);
-					mData[mElementCount] = data;
-					mElementCount++;
-					mDataEnd = &mData[mElementCount];
-					data->setBinIndex(mElementCount-1);
+                    mData.push_back(data);
+                    data->setBinIndex(getElementCount() - 1);
 					BaseType::insert(data);
 					return true;
 				}
@@ -403,7 +391,7 @@ class alignas(16) LLOctreeNode : public LLTreeNode<T>
 
 				llassert(size[0] >= gOctreeMinSize*0.5f);
 				//make the new kid
-				child = new LLOctreeNode<T>(center, size, this);
+                child = new oct_node(center, size, this);
 				addChild(child);
 								
 				child->insert(data);
@@ -414,28 +402,25 @@ class alignas(16) LLOctreeNode : public LLTreeNode<T>
 	}
 
 	void _remove(T* data, S32 i)
-	{ //precondition -- mElementCount > 0, idx is in range [0, mElementCount)
+    { //precondition -- getElementCount() > 0, idx is in range [0, getElementCount())
 
-		mElementCount--;
 		data->setBinIndex(-1); 
 		
-		if (mElementCount > 0)
+        const U32 new_element_count = getElementCount() - 1;
+		if (new_element_count > 0)
 		{
-			if (mElementCount != i)
+			if (new_element_count != i)
 			{
-				mData[i] = mData[mElementCount]; //might unref data, do not access data after this point
+				mData[i] = mData[new_element_count]; //might unref data, do not access data after this point
 				mData[i]->setBinIndex(i);
 			}
 
-			mData[mElementCount] = NULL;
+			mData[new_element_count] = NULL;
 			mData.pop_back();
-			mDataEnd = &mData[mElementCount];
 		}
 		else
 		{
 			mData.clear();
-			mData.push_back(NULL);
-			mDataEnd = &mData[0];
 		}
 
 		this->notifyRemoval(data);
@@ -448,7 +433,7 @@ class alignas(16) LLOctreeNode : public LLTreeNode<T>
 
 		S32 i = data->getBinIndex();
 
-		if (i >= 0 && i < mElementCount)
+        if (i >= 0 && i < getElementCount())
 		{
 			if (mData[i] == data)
 			{ //found it
@@ -491,7 +476,8 @@ class alignas(16) LLOctreeNode : public LLTreeNode<T>
 
 	void removeByAddress(T* data)
 	{
-        for (U32 i = 0; i < mElementCount; ++i)
+        const U32 element_count = getElementCount();
+        for (U32 i = 0; i < element_count; ++i)
 		{
 			if (mData[i] == data)
 			{ //we have data
@@ -503,7 +489,7 @@ class alignas(16) LLOctreeNode : public LLTreeNode<T>
 		
 		for (U32 i = 0; i < getChildCount(); i++)
 		{	//we don't contain data, so pass this guy down
-			LLOctreeNode<T>* child = (LLOctreeNode<T>*) getChild(i);
+            oct_node* child = (oct_node*) getChild(i);
 			child->removeByAddress(data);
 		}
 	}
@@ -657,22 +643,20 @@ class alignas(16) LLOctreeNode : public LLTreeNode<T>
 	oct_node* mParent;
 	U8 mOctant;
 
-	LLOctreeNode<T>* mChild[8];
+    oct_node* mChild[8];
 	U8 mChildMap[8];
 	U32 mChildCount;
 
 	element_list mData;
-	element_iter mDataEnd;
-	U32 mElementCount;
 }; 
 
 //just like a regular node, except it might expand on insert and compress on balance
-template <class T>
-class LLOctreeRoot : public LLOctreeNode<T>
+template <class T, typename T_PTR>
+class LLOctreeRoot : public LLOctreeNode<T, T_PTR>
 {
 public:
-	typedef LLOctreeNode<T>	BaseType;
-	typedef LLOctreeNode<T>	oct_node;
+    typedef LLOctreeNode<T, T_PTR> BaseType;
+    typedef LLOctreeNode<T, T_PTR> oct_node;
 
 	LLOctreeRoot(const LLVector4a& center, 
 				 const LLVector4a& size, 
@@ -753,7 +737,7 @@ class LLOctreeRoot : public LLOctreeNode<T>
 			oct_node* node = this->getNodeAt(data);
 			if (node == this)
 			{
-				LLOctreeNode<T>::insert(data);
+                oct_node::insert(data);
 			}
 			else if (node->isInside(data->getPositionGroup()))
 			{
@@ -773,13 +757,13 @@ class LLOctreeRoot : public LLOctreeNode<T>
 				LLVector4a center, size;
 				center = this->getCenter();
 				size = this->getSize();
-				LLOctreeNode<T>::pushCenter(center, size, data);
+                oct_node::pushCenter(center, size, data);
 				this->setCenter(center);
 				size.mul(2.f);
 				this->setSize(size);
 				this->updateMinMax();
 			}
-			LLOctreeNode<T>::insert(data);
+            oct_node::insert(data);
 		}
 		else
 		{
@@ -791,7 +775,7 @@ class LLOctreeRoot : public LLOctreeNode<T>
 
 				//expand this node
 				LLVector4a newcenter(center);
-				LLOctreeNode<T>::pushCenter(newcenter, size, data);
+                oct_node::pushCenter(newcenter, size, data);
 				this->setCenter(newcenter);
 				LLVector4a size2 = size;
 				size2.mul(2.f);
@@ -801,11 +785,11 @@ class LLOctreeRoot : public LLOctreeNode<T>
 				llassert(size[0] >= gOctreeMinSize);
 
 				//copy our children to a new branch
-				LLOctreeNode<T>* newnode = new LLOctreeNode<T>(center, size, this);
+                oct_node* newnode = new oct_node(center, size, this);
 				
 				for (U32 i = 0; i < this->getChildCount(); i++)
 				{
-					LLOctreeNode<T>* child = this->getChild(i);
+                    oct_node* child = this->getChild(i);
 					newnode->addChild(child);
 				}
 
@@ -831,8 +815,8 @@ class LLOctreeRoot : public LLOctreeNode<T>
 //========================
 //		LLOctreeTraveler
 //========================
-template <class T>
-void LLOctreeTraveler<T>::traverse(const LLOctreeNode<T>* node)
+template <class T, typename T_PTR>
+void LLOctreeTraveler<T, T_PTR>::traverse(const LLOctreeNode<T, T_PTR>* node)
 {
 	node->accept(this);
 	for (U32 i = 0; i < node->getChildCount(); i++)
@@ -841,8 +825,8 @@ void LLOctreeTraveler<T>::traverse(const LLOctreeNode<T>* node)
 	}
 }
 
-template <class T>
-void LLOctreeTravelerDepthFirst<T>::traverse(const LLOctreeNode<T>* node)
+template <class T, typename T_PTR>
+void LLOctreeTravelerDepthFirst<T, T_PTR>::traverse(const LLOctreeNode<T, T_PTR>* node)
 {
 	for (U32 i = 0; i < node->getChildCount(); i++)
 	{
diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
index 9c907803b78e2f35bd0f849ed4b3bb146079aa8f..4a713059a20d6087c16e8476f29b7768c84381ea 100644
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -373,7 +373,7 @@ BOOL LLTriangleRayIntersect(const LLVector3& vert0, const LLVector3& vert1, cons
 	}
 }
 
-class LLVolumeOctreeRebound : public LLOctreeTravelerDepthFirst<LLVolumeTriangle>
+class LLVolumeOctreeRebound : public LLOctreeTravelerDepthFirst<LLVolumeTriangle, LLVolumeTriangle*>
 {
 public:
 	const LLVolumeFace* mFace;
@@ -383,7 +383,7 @@ class LLVolumeOctreeRebound : public LLOctreeTravelerDepthFirst<LLVolumeTriangle
 		mFace = face;
 	}
 
-	virtual void visit(const LLOctreeNode<LLVolumeTriangle>* branch)
+    virtual void visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* branch)
 	{ //this is a depth first traversal, so it's safe to assum all children have complete
 		//bounding data
 	LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
@@ -2436,6 +2436,13 @@ bool LLVolume::unpackVolumeFacesInternal(const LLSD& mdl)
 
 			//copy out indices
             S32 num_indices = idx.size() / 2;
+            const S32 indices_to_discard = num_indices % 3;
+            if (indices_to_discard > 0)
+            {
+                // Invalid number of triangle indices
+                LL_WARNS() << "Incomplete triangle discarded from face! Indices count " << num_indices << " was not divisible by 3. face index: " << i << " Total: " << face_count << LL_ENDL;
+                num_indices -= indices_to_discard;
+            }
             face.resizeIndices(num_indices);
 
             if (num_indices > 2 && !face.mIndices)
@@ -2451,8 +2458,7 @@ bool LLVolume::unpackVolumeFacesInternal(const LLSD& mdl)
 			}
 
 			U16* indices = (U16*) &(idx[0]);
-			U32 count = idx.size()/2;
-			for (U32 j = 0; j < count; ++j)
+            for (U32 j = 0; j < num_indices; ++j)
 			{
 				face.mIndices[j] = indices[j];
 			}
@@ -3860,8 +3866,8 @@ void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices,
 #if DEBUG_SILHOUETTE_EDGE_MAP
 
 			//for each triangle
-			U32 count = face.mNumIndices;
-			for (U32 j = 0; j < count/3; j++) {
+            U32 tri_count = face.mNumIndices / 3;
+            for (U32 j = 0; j < tri_count; j++) {
 				//get vertices
 				S32 v1 = face.mIndices[j*3+0];
 				S32 v2 = face.mIndices[j*3+1];
@@ -3879,7 +3885,7 @@ void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices,
 						continue;
 					}
 
-					if (nIndex >= (S32) count/3) {
+                    if (nIndex >= (S32)tri_count) {
 						continue;
 					}
 					//get neighbor vertices
@@ -4171,13 +4177,13 @@ S32 LLVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& en
 			}
 			else
 			{
-				if (!face.mOctree)
+                if (!face.getOctree())
 				{
 					face.createOctree();
 				}
 			
 				LLOctreeTriangleRayIntersect intersect(start, dir, &face, &closest_t, intersection, tex_coord, normal, tangent_out);
-				intersect.traverse(face.mOctree);
+                intersect.traverse(face.getOctree());
 				if (intersect.mHitFace)
 				{
 					hit_face = i;
@@ -4726,6 +4732,7 @@ LLVolumeFace::LLVolumeFace() :
 	mWeights(NULL),
     mWeightsScrubbed(FALSE),
 	mOctree(NULL),
+    mOctreeTriangles(NULL),
 	mOptimized(FALSE)
 {
 	mExtents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*3);
@@ -4752,8 +4759,9 @@ LLVolumeFace::LLVolumeFace(const LLVolumeFace& src)
 	mWeights(NULL),
     mWeightsScrubbed(FALSE),
 	mOctree(NULL),
+    mOctreeTriangles(NULL),
 	mOptimized(FALSE)
-{ 
+{
 	mExtents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*3);
 	mCenter = mExtents+2;
 	*this = src;
@@ -4849,8 +4857,7 @@ void LLVolumeFace::freeData()
 	allocateWeights(0);
 	allocateIndices(0);
 
-	delete mOctree;
-	mOctree = NULL;
+    destroyOctree();
 }
 
 BOOL LLVolumeFace::create(LLVolume* volume, BOOL partial_build)
@@ -4858,8 +4865,7 @@ BOOL LLVolumeFace::create(LLVolume* volume, BOOL partial_build)
 	LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
 
 	//tree for this face is no longer valid
-	delete mOctree;
-	mOctree = NULL;
+    destroyOctree();
 
 	LL_CHECK_MEMORY
 	BOOL ret = FALSE ;
@@ -5464,21 +5470,27 @@ void LLVolumeFace::createOctree(F32 scaler, const LLVector4a& center, const LLVe
 {
 	LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
 
-	if (mOctree)
+    if (getOctree())
 	{
 		return;
 	}
 
-	mOctree = new LLOctreeRoot<LLVolumeTriangle>(center, size, NULL);
+    llassert(mNumIndices % 3 == 0);
+
+    mOctree = new LLOctreeRoot<LLVolumeTriangle, LLVolumeTriangle*>(center, size, NULL);
 	new LLVolumeOctreeListener(mOctree);
+    const U32 num_triangles = mNumIndices / 3;
+    // Initialize all the triangles we need
+    mOctreeTriangles = new LLVolumeTriangle[num_triangles];
 
-	for (U32 i = 0; i < mNumIndices; i+= 3)
+    for (U32 triangle_index = 0; triangle_index < num_triangles; ++triangle_index)
 	{ //for each triangle
-		LLPointer<LLVolumeTriangle> tri = new LLVolumeTriangle();
+        const U32 index = triangle_index * 3;
+        LLVolumeTriangle* tri = &mOctreeTriangles[triangle_index];
 				
-		const LLVector4a& v0 = mPositions[mIndices[i]];
-		const LLVector4a& v1 = mPositions[mIndices[i+1]];
-		const LLVector4a& v2 = mPositions[mIndices[i+2]];
+		const LLVector4a& v0 = mPositions[mIndices[index]];
+		const LLVector4a& v1 = mPositions[mIndices[index + 1]];
+		const LLVector4a& v2 = mPositions[mIndices[index + 2]];
 
 		//store pointers to vertex data
 		tri->mV[0] = &v0;
@@ -5486,9 +5498,9 @@ void LLVolumeFace::createOctree(F32 scaler, const LLVector4a& center, const LLVe
 		tri->mV[2] = &v2;
 
 		//store indices
-		tri->mIndex[0] = mIndices[i];
-		tri->mIndex[1] = mIndices[i+1];
-		tri->mIndex[2] = mIndices[i+2];
+		tri->mIndex[0] = mIndices[index];
+		tri->mIndex[1] = mIndices[index + 1];
+		tri->mIndex[2] = mIndices[index + 2];
 
 		//get minimum point
 		LLVector4a min = v0;
@@ -5531,6 +5543,19 @@ void LLVolumeFace::createOctree(F32 scaler, const LLVector4a& center, const LLVe
 	}
 }
 
+void LLVolumeFace::destroyOctree()
+{
+    delete mOctree;
+    mOctree = NULL;
+    delete[] mOctreeTriangles;
+    mOctreeTriangles = NULL;
+}
+
+const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* LLVolumeFace::getOctree() const
+{
+    return mOctree;
+}
+
 
 void LLVolumeFace::swapData(LLVolumeFace& rhs)
 {
@@ -6410,6 +6435,7 @@ bool LLVolumeFace::allocateVertices(S32 num_verts, bool copy)
 
 bool LLVolumeFace::allocateIndices(S32 num_indices, bool copy)
 {
+	llassert(num_indices % 3 == 0);
 	if (num_indices == mNumIndices)
 	{
 		return true;
diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h
index e12911187f8b65107fa2d14bd7e86ade89e03ba5..cd2547e943b71bea3413abf4f6082bd12ea0a32a 100644
--- a/indra/llmath/llvolume.h
+++ b/indra/llmath/llvolume.h
@@ -35,7 +35,8 @@ class LLVolumeParams;
 class LLProfile;
 class LLPath;
 
-template <class T> class LLOctreeNode;
+template<class T> class LLPointer;
+template <class T, typename T_PTR> class LLOctreeNode;
 
 class LLVolumeFace;
 class LLVolume;
@@ -914,6 +915,9 @@ class LLVolumeFace
 	bool cacheOptimize();
 
 	void createOctree(F32 scaler = 0.25f, const LLVector4a& center = LLVector4a(0,0,0), const LLVector4a& size = LLVector4a(0.5f,0.5f,0.5f));
+    void destroyOctree();
+    // Get a reference to the octree, which may be null
+    const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* getOctree() const;
 
 	enum
 	{
@@ -976,13 +980,14 @@ class LLVolumeFace
     // Which joints are rigged to, and the bounding box of any rigged
     // vertices per joint.
     LLJointRiggingInfoTab mJointRiggingInfoTab;
-    
-	LLOctreeNode<LLVolumeTriangle>* mOctree;
 
 	//whether or not face has been cache optimized
 	BOOL mOptimized;
 
 private:
+    LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* mOctree;
+    LLVolumeTriangle* mOctreeTriangles;
+
 	BOOL createUnCutCubeCap(LLVolume* volume, BOOL partial_build = FALSE);
 	BOOL createCap(LLVolume* volume, BOOL partial_build = FALSE);
 	BOOL createSide(LLVolume* volume, BOOL partial_build = FALSE);
diff --git a/indra/llmath/llvolumeoctree.cpp b/indra/llmath/llvolumeoctree.cpp
index 0c83cb6e4f1df3c2d0a1b64029191851e297e51c..e753cc688c33dae45f1fd53ca4e9ce57c9743423 100644
--- a/indra/llmath/llvolumeoctree.cpp
+++ b/indra/llmath/llvolumeoctree.cpp
@@ -75,18 +75,17 @@ BOOL LLLineSegmentBoxIntersect(const LLVector4a& start, const LLVector4a& end, c
 }
 
 
-LLVolumeOctreeListener::LLVolumeOctreeListener(LLOctreeNode<LLVolumeTriangle>* node)
+LLVolumeOctreeListener::LLVolumeOctreeListener(LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* node)
 {
 	node->addListener(this);
 }
 
-void LLVolumeOctreeListener::handleChildAddition(const LLOctreeNode<LLVolumeTriangle>* parent, 
-	LLOctreeNode<LLVolumeTriangle>* child)
+void LLVolumeOctreeListener::handleChildAddition(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* parent, 
+    LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* child)
 {
 	new LLVolumeOctreeListener(child);
 }
 
-
 LLOctreeTriangleRayIntersect::LLOctreeTriangleRayIntersect(const LLVector4a& start, const LLVector4a& dir, 
 							   const LLVolumeFace* face, F32* closest_t,
 							   LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent)
@@ -103,7 +102,7 @@ LLOctreeTriangleRayIntersect::LLOctreeTriangleRayIntersect(const LLVector4a& sta
 	mEnd.setAdd(mStart, mDir);
 }
 
-void LLOctreeTriangleRayIntersect::traverse(const LLOctreeNode<LLVolumeTriangle>* node)
+void LLOctreeTriangleRayIntersect::traverse(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* node)
 {
 	LLVolumeOctreeListener* vl = (LLVolumeOctreeListener*) node->getListener(0);
 
@@ -117,9 +116,9 @@ void LLOctreeTriangleRayIntersect::traverse(const LLOctreeNode<LLVolumeTriangle>
 	}
 }
 
-void LLOctreeTriangleRayIntersect::visit(const LLOctreeNode<LLVolumeTriangle>* node)
+void LLOctreeTriangleRayIntersect::visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* node)
 {
-	for (LLOctreeNode<LLVolumeTriangle>::const_element_iter iter = 
+    for (typename LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>::const_element_iter iter =
 			node->getDataBegin(), iter_end = node->getDataEnd(); iter != iter_end; ++iter)
 	{
 		const LLVolumeTriangle* tri = *iter;
@@ -214,7 +213,7 @@ const F32& LLVolumeTriangle::getBinRadius() const
 
 //TEST CODE
 
-void LLVolumeOctreeValidate::visit(const LLOctreeNode<LLVolumeTriangle>* branch)
+void LLVolumeOctreeValidate::visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* branch)
 {
 	LLVolumeOctreeListener* node = (LLVolumeOctreeListener*) branch->getListener(0);
 
@@ -251,7 +250,7 @@ void LLVolumeOctreeValidate::visit(const LLOctreeNode<LLVolumeTriangle>* branch)
 	}
 
 	//children fit, check data
-	for (LLOctreeNode<LLVolumeTriangle>::const_element_iter iter = branch->getDataBegin(), iter_end = branch->getDataEnd();
+    for (typename LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>::const_element_iter iter = branch->getDataBegin(), iter_end = branch->getDataEnd();
 			iter != iter_end; ++iter)
 	{
 		const LLVolumeTriangle* tri = *iter;
@@ -268,4 +267,3 @@ void LLVolumeOctreeValidate::visit(const LLOctreeNode<LLVolumeTriangle>* branch)
 	}
 }
 
-
diff --git a/indra/llmath/llvolumeoctree.h b/indra/llmath/llvolumeoctree.h
index 47407ccecc03f71486c4112bc1c24fd4c6c2ac9d..7bb158a895f69fec75cb6a5998f190f489e4ff82 100644
--- a/indra/llmath/llvolumeoctree.h
+++ b/indra/llmath/llvolumeoctree.h
@@ -66,22 +66,20 @@ class alignas(16) LLVolumeTriangle final : public LLRefCount
 
 };
 
-class alignas(16) LLVolumeOctreeListener final : public LLOctreeListener<LLVolumeTriangle>
+class alignas(16) LLVolumeOctreeListener final : public LLOctreeListener<LLVolumeTriangle, LLVolumeTriangle*>
 {
     LL_ALIGN_NEW
 public:
-	LLVolumeOctreeListener(LLOctreeNode<LLVolumeTriangle>* node);
+    LLVolumeOctreeListener(LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* node);
 	~LLVolumeOctreeListener() = default;
 	
 	LLVolumeOctreeListener(const LLVolumeOctreeListener& rhs) = delete;
     LLVolumeOctreeListener& operator=(const LLVolumeOctreeListener& rhs) = delete;
 
 	 //LISTENER FUNCTIONS
-	virtual void handleChildAddition(const LLOctreeNode<LLVolumeTriangle>* parent, 
-		LLOctreeNode<LLVolumeTriangle>* child);
+    virtual void handleChildAddition(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* parent, LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* child);
 	virtual void handleStateChange(const LLTreeNode<LLVolumeTriangle>* node) { }
-	virtual void handleChildRemoval(const LLOctreeNode<LLVolumeTriangle>* parent, 
-			const LLOctreeNode<LLVolumeTriangle>* child) {	}
+    virtual void handleChildRemoval(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* parent, const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* child) { }
 	virtual void handleInsertion(const LLTreeNode<LLVolumeTriangle>* node, LLVolumeTriangle* tri) { }
 	virtual void handleRemoval(const LLTreeNode<LLVolumeTriangle>* node, LLVolumeTriangle* tri) { }
 	virtual void handleDestruction(const LLTreeNode<LLVolumeTriangle>* node) { }
@@ -92,7 +90,7 @@ class alignas(16) LLVolumeOctreeListener final : public LLOctreeListener<LLVolum
 	LL_ALIGN_16(LLVector4a mExtents[2]); // extents (min, max) of this node and all its children
 };
 
-class LLOctreeTriangleRayIntersect : public LLOctreeTraveler<LLVolumeTriangle>
+class LLOctreeTriangleRayIntersect : public LLOctreeTraveler<LLVolumeTriangle, LLVolumeTriangle*>
 {
 public:
 	const LLVolumeFace* mFace;
@@ -110,14 +108,14 @@ class LLOctreeTriangleRayIntersect : public LLOctreeTraveler<LLVolumeTriangle>
 								   const LLVolumeFace* face, F32* closest_t,
 								   LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent);
 
-	void traverse(const LLOctreeNode<LLVolumeTriangle>* node);
+    void traverse(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* node);
 
-	virtual void visit(const LLOctreeNode<LLVolumeTriangle>* node);
+    virtual void visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* node);
 };
 
-class LLVolumeOctreeValidate final : public LLOctreeTraveler<LLVolumeTriangle>
+class LLVolumeOctreeValidate final : public LLOctreeTraveler<LLVolumeTriangle, LLVolumeTriangle*>
 {
-	virtual void visit(const LLOctreeNode<LLVolumeTriangle>* branch);
+    virtual void visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* branch);
 };
 
 #endif
diff --git a/indra/llmessage/lldatapacker.cpp b/indra/llmessage/lldatapacker.cpp
index bf05b8b33be7fb8fcdd8c62d1bf3046e67afcdd5..64cd1c2ad0a5259190faf420c3c17d76863d5a05 100644
--- a/indra/llmessage/lldatapacker.cpp
+++ b/indra/llmessage/lldatapacker.cpp
@@ -126,13 +126,7 @@ BOOL LLDataPacker::unpackFixed(F32 &value, const char *name,
 		total_bits++;
 	}
 
-	S32 min_val;
 	U32 max_val;
-	if (is_signed)
-	{
-		min_val = 1 << int_bits;
-		min_val *= -1;
-	}
 	max_val = 1 << int_bits;
 
 	F32 fixed_val;
diff --git a/indra/llmessage/llpartdata.cpp b/indra/llmessage/llpartdata.cpp
index 4816daa8cb7f436b33c98cd1da327b91bf498057..20a43a91194f1f6ff806911f5325869a7ec0c100 100644
--- a/indra/llmessage/llpartdata.cpp
+++ b/indra/llmessage/llpartdata.cpp
@@ -311,8 +311,9 @@ BOOL LLPartSysData::unpack(LLDataPacker &dp)
 std::ostream& operator<<(std::ostream& s, const LLPartSysData &data)
 {
 	s << "Flags: " << std::hex << data.mFlags;
-	s << " Pattern: " << std::hex << (U32) data.mPattern << "\n";
-	s << "Age: [" << data.mStartAge << ", " << data.mMaxAge << "]\n";
+	s << "Pattern: " << std::hex << (U32) data.mPattern << "\n";
+	s << "Source Age: [" << data.mStartAge << ", " << data.mMaxAge << "]\n";
+    s << "Particle Age: " << data.mPartData.mMaxAge << "\n";
 	s << "Angle: [" << data.mInnerAngle << ", " << data.mOuterAngle << "]\n";
 	s << "Burst Rate: " << data.mBurstRate << "\n";
 	s << "Burst Radius: " << data.mBurstRadius << "\n";
diff --git a/indra/llmessage/llthrottle.cpp b/indra/llmessage/llthrottle.cpp
index 7605da4d3fa9b06c0f5700eb351bf8d1f56226be..935af2aa5a551b04c45b54cc6d8bc637c296d664 100644
--- a/indra/llmessage/llthrottle.cpp
+++ b/indra/llmessage/llthrottle.cpp
@@ -374,7 +374,6 @@ BOOL LLThrottleGroup::dynamicAdjust()
 	}
 	mDynamicAdjustTime = mt_sec;
 
-	S32 total = 0;
 	// Update historical information
 	for (i = 0; i < TC_EOF; i++)
 	{
@@ -391,7 +390,6 @@ BOOL LLThrottleGroup::dynamicAdjust()
 		}
 
 		mBitsSentThisPeriod[i] = 0;
-		total += ll_round(mBitsSentHistory[i]);
 	}
 
 	// Look for busy channels
diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp
index 5ca493b2e1b95fdc9b8ffabcd5005f798978002d..e4e107cd7ed5d0f759d110a704bf761fc864cf9c 100644
--- a/indra/llprimitive/llmodel.cpp
+++ b/indra/llprimitive/llmodel.cpp
@@ -379,6 +379,8 @@ void LLModel::setVolumeFaceData(
 	U32 num_verts, 
 	U32 num_indices)
 {
+    llassert(num_indices % 3 == 0);
+
 	LLVolumeFace& face = mVolumeFaces[f];
 
 	face.resizeVertices(num_verts);
diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp
index b8ad8c4cfb5207ad59a181fbe3385ae0d36760b6..efbbeff646137495e4d9f83e1790153728976739 100644
--- a/indra/llrender/llgl.cpp
+++ b/indra/llrender/llgl.cpp
@@ -467,6 +467,14 @@ bool LLGLManager::initGL()
 		LL_WARNS("RenderInit") << "GL Drivers do not support GL_ARB_multitexture" << LL_ENDL;
 		return false;
 	}
+
+    if (!mHasFramebufferObject)
+    {
+        mHasRequirements = FALSE;
+
+        LL_WARNS("RenderInit") << "GL Drivers do not support GL_ARB_framebuffer_object" << LL_ENDL;
+        return false;
+    }
 	
 	stop_glerror();
 
@@ -492,12 +500,6 @@ bool LLGLManager::initGL()
 
 	//HACK always disable texture multisample, use FXAA instead
 	mHasTextureMultisample = FALSE;
-#if LL_WINDOWS
-	if (mIsIntel && mGLVersion <= 3.f)
-	{ //never try to use framebuffer objects on older intel drivers (crashy)
-		mHasFramebufferObject = FALSE;
-	}
-#endif
 
 	if (mHasFramebufferObject)
 	{
diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp
index c6a6d6ab5d936de29fa39715466ff1fdd88c820e..c330dc2661e79ce50b66eed252c0c3163dfd7a21 100644
--- a/indra/llrender/llimagegl.cpp
+++ b/indra/llrender/llimagegl.cpp
@@ -2539,7 +2539,7 @@ void LLImageGLThread::run()
     // We must perform setup on this thread before actually servicing our
     // WorkQueue, likewise cleanup afterwards.
     mWindow->makeContextCurrent(mContext);
-    gGL.init();
+    gGL.init(false);
     ThreadPool::run();
     gGL.shutdown();
     mWindow->destroySharedContext(mContext);
diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp
index 1b1be03df8c4b525b22bb475ec1a17d658770d54..57a2c9d1ba1f2b4ecd97aba9e44599d83473a990 100644
--- a/indra/llrender/llrender.cpp
+++ b/indra/llrender/llrender.cpp
@@ -871,7 +871,7 @@ LLRender::~LLRender()
 	shutdown();
 }
 
-void LLRender::init()
+void LLRender::init(bool needs_vertex_buffer)
 {
 #if 1
     if (gGLManager.mHasDebugOutput && gDebugGL)
@@ -890,7 +890,31 @@ void LLRender::init()
 
     glCullFace(GL_BACK);
 
-	restoreVertexBuffers();
+    if (needs_vertex_buffer)
+    {
+        initVertexBuffer();
+    }
+}
+
+void LLRender::initVertexBuffer()
+{
+	llassert_always(mBuffer.isNull());
+	stop_glerror();
+	mBuffer = new LLVertexBuffer(immediate_mask, 0);
+	stop_glerror();
+	mBuffer->allocateBuffer(4096, 0, TRUE);
+	stop_glerror();
+	mBuffer->getVertexStrider(mVerticesp);
+	stop_glerror();
+	mBuffer->getTexCoord0Strider(mTexcoordsp);
+	stop_glerror();
+	mBuffer->getColorStrider(mColorsp);
+	stop_glerror();
+}
+
+void LLRender::resetVertexBuffer()
+{
+    mBuffer = NULL;
 }
 
 void LLRender::shutdown()
@@ -908,8 +932,7 @@ void LLRender::shutdown()
 		delete mLightState[i];
 	}
 	mLightState.clear();
-
-	mBuffer = nullptr;
+    resetVertexBuffer();
 }
 
 void LLRender::refreshState(void)
@@ -932,27 +955,6 @@ void LLRender::refreshState(void)
 	mDirty = false;
 }
 
-void LLRender::resetVertexBuffers()
-{
-	mBuffer = nullptr;
-}
-
-void LLRender::restoreVertexBuffers()
-{
-	llassert_always(mBuffer.isNull());
-	stop_glerror();
-	mBuffer = new LLVertexBuffer(immediate_mask, 0);
-	stop_glerror();
-	mBuffer->allocateBuffer(4096, 0, TRUE);
-	stop_glerror();
-	mBuffer->getVertexStrider(mVerticesp);
-	stop_glerror();
-	mBuffer->getTexCoord0Strider(mTexcoordsp);
-	stop_glerror();
-	mBuffer->getColorStrider(mColorsp);
-	stop_glerror();
-}
-
 void LLRender::syncLightState()
 {
     LLGLSLShader *shader = LLGLSLShader::sCurBoundShaderPtr;
@@ -1656,16 +1658,25 @@ void LLRender::flush()
 
 		mCount = 0;
 
-		if (mBuffer->useVBOs() && !mBuffer->isLocked())
-		{ //hack to only flush the part of the buffer that was updated (relies on stream draw using buffersubdata)
-			mBuffer->getVertexStrider(mVerticesp, 0, count);
-			mBuffer->getTexCoord0Strider(mTexcoordsp, 0, count);
-			mBuffer->getColorStrider(mColorsp, 0, count);
-		}
-		
-		mBuffer->flush();
-		mBuffer->setBuffer(immediate_mask);
-		mBuffer->drawArrays(mMode, 0, count);
+        if (mBuffer)
+        {
+            if (mBuffer->useVBOs() && !mBuffer->isLocked())
+            { //hack to only flush the part of the buffer that was updated (relies on stream draw using buffersubdata)
+                mBuffer->getVertexStrider(mVerticesp, 0, count);
+                mBuffer->getTexCoord0Strider(mTexcoordsp, 0, count);
+                mBuffer->getColorStrider(mColorsp, 0, count);
+            }
+
+            mBuffer->flush();
+            mBuffer->setBuffer(immediate_mask);
+            mBuffer->drawArrays(mMode, 0, count);
+        }
+        else
+        {
+            // mBuffer is present in main thread and not present in an image thread
+            LL_ERRS() << "A flush call from outside main rendering thread" << LL_ENDL;
+        }
+
 		
 		mVerticesp[0] = mVerticesp[count];
 		mTexcoordsp[0] = mTexcoordsp[count];
diff --git a/indra/llrender/llrender.h b/indra/llrender/llrender.h
index 9a3e0f2acc9fa094da0781466fb656dd6a32c8a8..6186a46b005c537001549e90ccd9636cd431e921 100644
--- a/indra/llrender/llrender.h
+++ b/indra/llrender/llrender.h
@@ -356,7 +356,9 @@ class LLRender
 
 	LLRender();
 	~LLRender();
-	void init() ;
+    void init(bool needs_vertex_buffer);
+    void initVertexBuffer();
+    void resetVertexBuffer();
 	void shutdown();
 	
 	// Refreshes renderer state to the cached values
diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp
index 4a527967c7be3e1af136855f57d16babd05c3029..fe6dbb981283b7a48a877ddb518f85c406b315e7 100644
--- a/indra/llrender/llrendertarget.cpp
+++ b/indra/llrender/llrendertarget.cpp
@@ -135,7 +135,7 @@ bool LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, boo
 	mUsage = usage;
 	mUseDepth = depth;
 
-	if ((sUseFBO || use_fbo) && gGLManager.mHasFramebufferObject)
+	if (sUseFBO || use_fbo)
 	{
 		if (depth)
 		{
diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp
index eb218c537282c83d655b71166071309dd9068de3..e7b94245a5746dd73922c7bdd8b55f888df4d65b 100644
--- a/indra/llrender/llshadermgr.cpp
+++ b/indra/llrender/llshadermgr.cpp
@@ -662,7 +662,7 @@ GLuint LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shader_lev
 	
 	if (file == NULL)
 	{
-		LL_SHADER_LOADING_WARNS() << "GLSL Shader file not found: " << open_file_name << LL_ENDL;
+		LL_WARNS("ShaderLoading") << "GLSL Shader file not found: " << open_file_name << LL_ENDL;
 		return 0;
 	}
 
diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp
index 2fce4776629268387292285ac8c83e023d9cb484..7897b7393de1f712b4f33248d35a78f2e6296043 100644
--- a/indra/llrender/llvertexbuffer.cpp
+++ b/indra/llrender/llvertexbuffer.cpp
@@ -1210,7 +1210,7 @@ void LLVertexBuffer::releaseIndices()
 
 bool LLVertexBuffer::createGLBuffer(U32 size)
 {
-	if (mGLBuffer)
+	if (mGLBuffer || mMappedData)
 	{
 		destroyGLBuffer();
 	}
diff --git a/indra/llrender/llvertexbuffer.h b/indra/llrender/llvertexbuffer.h
index af4804fa62ba7d4f985551831902a47c0eb7293b..2937b25dc0ffb36c19f41e291eae52b9f5ad8a92 100644
--- a/indra/llrender/llvertexbuffer.h
+++ b/indra/llrender/llvertexbuffer.h
@@ -259,7 +259,6 @@ class LLVertexBuffer final : public LLRefCount
 	bool getTangentStrider(LLStrider<LLVector3>& strider, S32 index=0, S32 count = -1, bool map_range = false);
 	bool getTangentStrider(LLStrider<LLVector4a>& strider, S32 index=0, S32 count = -1, bool map_range = false);
 	bool getColorStrider(LLStrider<LLColor4U>& strider, S32 index=0, S32 count = -1, bool map_range = false);
-	bool getTextureIndexStrider(LLStrider<LLColor4U>& strider, S32 index=0, S32 count = -1, bool map_range = false);
 	bool getEmissiveStrider(LLStrider<LLColor4U>& strider, S32 index=0, S32 count = -1, bool map_range = false);
 	bool getWeightStrider(LLStrider<F32>& strider, S32 index=0, S32 count = -1, bool map_range = false);
 	bool getWeight4Strider(LLStrider<LLVector4a>& strider, S32 index=0, S32 count = -1, bool map_range = false);
diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp
index 92e25991fa4081aab7c2c65a5da09ba8d7ddda59..dad94168f1d3aeca23c7b9938601609018b772e7 100644
--- a/indra/llui/lllayoutstack.cpp
+++ b/indra/llui/lllayoutstack.cpp
@@ -397,7 +397,6 @@ void LLLayoutStack::updateLayout()
 	space_to_distribute += panelp ? ll_round((F32)mPanelSpacing * panelp->getVisibleAmount()) : 0;
 
 	S32 remaining_space = space_to_distribute;
-//	F32 fraction_distributed = 0.f;
 	if (space_to_distribute > 0 && total_visible_fraction > 0.f)
 	{	// give space proportionally to visible auto resize panels
 		for (LLLayoutPanel* panelp : mPanels)
@@ -406,7 +405,6 @@ void LLLayoutStack::updateLayout()
 			{
 				F32 fraction_to_distribute = (panelp->mFractionalSize * panelp->getAutoResizeFactor()) / (total_visible_fraction);
 				S32 delta = ll_round((F32)space_to_distribute * fraction_to_distribute);
-//				fraction_distributed += fraction_to_distribute;
 				panelp->mTargetDim += delta;
 				remaining_space -= delta;
 			}
diff --git a/indra/llwindow/CMakeLists.txt b/indra/llwindow/CMakeLists.txt
index 1391a1cf158cac7404324681de2f34926579409c..56bf8302e14c43533d29d3295c0661799f3537ec 100644
--- a/indra/llwindow/CMakeLists.txt
+++ b/indra/llwindow/CMakeLists.txt
@@ -195,6 +195,12 @@ if (SDL_FOUND)
 endif (SDL_FOUND)
 
 target_link_libraries (llwindow llcommon ${llwindow_LINK_LIBRARIES} readerwriterqueue)
+  
+if (DARWIN)
+  include(CMakeFindFrameworks)
+  find_library(CARBON_LIBRARY Carbon)
+  target_link_libraries(llwindow ${CARBON_LIBRARY})
+endif (DARWIN)
 
 if(USE_PRECOMPILED_HEADERS AND ${CMAKE_VERSION} VERSION_GREATER "3.15.0") 
   target_precompile_headers(llwindow
diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp
index 152db40255bbdcc7db7ca824981e2bbb427e677c..61dd48d2cb98edd224012cf79b91f9f7e78529db 100644
--- a/indra/llwindow/llwindowmacosx.cpp
+++ b/indra/llwindow/llwindowmacosx.cpp
@@ -621,7 +621,6 @@ BOOL LLWindowMacOSX::createContext(int x, int y, int width, int height, int bits
 		// Get the view instead.
 		mGLView = createOpenGLView(mWindow, mFSAASamples, enable_vsync);
 		mContext = getCGLContextObj(mGLView);
-
 		gGLManager.mVRAM = getVramSize(mGLView);
 	}
 	
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
index 1327d7ea874f28153eeb39960b0df341fbcc8b79..6dc4206d196fe85f78d5c1ac6966734d67b7aeb5 100644
--- a/indra/llwindow/llwindowwin32.cpp
+++ b/indra/llwindow/llwindowwin32.cpp
@@ -971,21 +971,20 @@ void LLWindowWin32::close()
 	// Restore gamma to the system values.
 	restoreGamma();
 
-	if (mhDC)
-	{
-		if (!ReleaseDC(mWindowHandle, mhDC))
-		{
-			LL_WARNS("Window") << "Release of mhDC failed" << LL_ENDL;
-		}
-		mhDC = NULL;
-	}
-
 	LL_DEBUGS("Window") << "Destroying Window" << LL_ENDL;
 
     mWindowThread->post([=]()
         {
             if (IsWindow(mWindowHandle))
             {
+                if (mhDC)
+                {
+                    if (!ReleaseDC(mWindowHandle, mhDC))
+                    {
+                        LL_WARNS("Window") << "Release of ghDC failed!" << LL_ENDL;
+                    }
+                }
+
                 // Make sure we don't leave a blank toolbar button.
                 ShowWindow(mWindowHandle, SW_HIDE);
 
@@ -1011,6 +1010,7 @@ void LLWindowWin32::close()
     // Even though the above lambda might not yet have run, we've already
     // bound mWindowHandle into it by value, which should suffice for the
     // operations we're asking. That's the last time WE should touch it.
+    mhDC = NULL;
     mWindowHandle = NULL;
     mWindowThread->close();
 }
@@ -1450,9 +1450,6 @@ BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen& size, BO
 	
 	gGLManager.initWGL(mhDC);
 
-	HWND oldWND = nullptr;
-	HDC oldDC = nullptr;
-	HGLRC oldRC = nullptr;
 	if (epoxy_has_wgl_extension(mhDC, "WGL_ARB_pixel_format"))
 	{
 		// OK, at this point, use the ARB wglChoosePixelFormatsARB function to see if we
@@ -1615,22 +1612,17 @@ const	S32   max_format  = (S32)num_formats - 1;
 		
 		pixel_format = pixel_formats[cur_format];
 		
-		if (mWindowHandle != nullptr)
+		if (mhDC != 0)											// Does The Window Have A Device Context?
 		{
-			if (mhDC != nullptr)											// Does The Window Have A Device Context?
+			wglMakeCurrent(mhDC, 0);							// Set The Current Active Rendering Context To Zero
+			if (mhRC != 0)										// Does The Window Have A Rendering Context?
 			{
-				if (mhRC != nullptr)										// Does The Window Have A Rendering Context?
-				{
-					oldRC = mhRC;
-					mhRC = nullptr;										// Zero The Rendering Context
-				}
-				oldDC = mhDC;
-				mhDC = nullptr;											// Zero The Device Context
+				wglDeleteContext (mhRC);							// Release The Rendering Context
+				mhRC = 0;										// Zero The Rendering Context
 			}
-
-			oldWND = mWindowHandle;
 		}
 
+        // will release and recreate mhDC, mWindowHandle
 		recreateWindow(window_rect, dw_ex_style, dw_style);
         
         RECT rect;
@@ -1742,23 +1734,6 @@ const	S32   max_format  = (S32)num_formats - 1;
 		return FALSE;
 	}
 
-	if (oldWND != nullptr)
-	{
-		if (oldDC != nullptr)											// Does The Window Have A Device Context?
-		{
-			if (oldRC != nullptr)										// Does The Window Have A Rendering Context?
-			{
-				wglDeleteContext(oldRC);							// Release The Rendering Context
-				oldRC = nullptr;										// Zero The Rendering Context
-
-			}
-			ReleaseDC(oldWND, oldDC);						// Release The Device Context
-			oldDC = nullptr;											// Zero The Device Context
-		}
-		destroy_window_handler(oldWND);									// Destroy The Window
-		oldWND = nullptr;
-	}
-
 	LL_PROFILER_GPU_CONTEXT
 
 	if (!gGLManager.initGL())
@@ -1797,7 +1772,8 @@ const	S32   max_format  = (S32)num_formats - 1;
 
 void LLWindowWin32::recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw_style)
 {
-    auto oldHandle = mWindowHandle;
+    auto oldWindowHandle = mWindowHandle;
+    auto oldDCHandle = mhDC;
 
     // zero out mWindowHandle and mhDC before destroying window so window
     // thread falls back to peekmessage
@@ -1809,7 +1785,8 @@ void LLWindowWin32::recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw
     auto window_work =
         [this,
          self=mWindowThread,
-         oldHandle,
+         oldWindowHandle,
+         oldDCHandle,
          // bind CreateWindowEx() parameters by value instead of
          // back-referencing LLWindowWin32 members
          windowClassName=mWindowClassName,
@@ -1825,11 +1802,20 @@ void LLWindowWin32::recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw
             self->mWindowHandle = 0;
             self->mhDC = 0;
 
-            // important to call DestroyWindow() from the window thread
-            if (oldHandle && !destroy_window_handler(oldHandle))
+            if (oldWindowHandle)
             {
-                LL_WARNS("Window") << "Failed to properly close window before recreating it!"
-                                   << LL_ENDL;
+                if (oldDCHandle && !ReleaseDC(oldWindowHandle, oldDCHandle))
+                {
+                    LL_WARNS("Window") << "Failed to ReleaseDC" << LL_ENDL;
+                }
+
+                // important to call DestroyWindow() from the window thread
+                if (!destroy_window_handler(oldWindowHandle))
+                {
+
+                    LL_WARNS("Window") << "Failed to properly close window before recreating it!"
+                        << LL_ENDL;
+                }
             }
 
             auto handle = CreateWindowEx(dw_ex_style,
@@ -1867,7 +1853,7 @@ void LLWindowWin32::recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw
         };
     // But how we pass window_work to the window thread depends on whether we
     // already have a window handle.
-    if (! oldHandle)
+    if (!oldWindowHandle)
     {
         // Pass window_work using the WorkQueue: without an existing window
         // handle, the window thread can't call GetMessage().
@@ -1880,7 +1866,7 @@ void LLWindowWin32::recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw
         // PostMessage(oldHandle) because oldHandle won't be destroyed until
         // the window thread has retrieved and executed window_work.
         LL_DEBUGS("Window") << "posting window_work to message queue" << LL_ENDL;
-        mWindowThread->Post(oldHandle, window_work);
+        mWindowThread->Post(oldWindowHandle, window_work);
     }
 
     auto future = promise.get_future();
diff --git a/indra/newview/app_settings/key_bindings.xml b/indra/newview/app_settings/key_bindings.xml
index 3ff9762767bc577f5418680a205f1d1b7e2dc2ca..4a1abff711e9f667b2f265103ff4ad1341a8e6ae 100644
--- a/indra/newview/app_settings/key_bindings.xml
+++ b/indra/newview/app_settings/key_bindings.xml
@@ -87,7 +87,6 @@
     <binding key="DOWN" mask="CTL_ALT_SHIFT" command="pan_down"/>
 
     <binding key="" mask="NONE" mouse="MMB" command="toggle_voice"/>
-    <binding key="" mask="NONE" mouse="LMB" command="walk_to"/>
 
     <binding key="" mask="NONE" mouse="LMB" command="script_trigger_lbutton"/>
   </third_person>
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 0fe99bb0096136effd95f83bf85bbb9f52bb34cc..70bc2529bb9b49e7b4b978dff46b524e04cdbc0a 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -1694,7 +1694,7 @@
       <key>Type</key>
       <string>F32</string>
       <key>Value</key>
-      <real>20.0</real>
+      <real>40.0</real>
     </map>
     <key>DiskCacheDirName</key>
     <map>
@@ -7230,6 +7230,9 @@
       <key>Type</key>
       <string>Boolean</string>
       <key>Value</key>
+      <!-- *HACK: On first run, set this to 0 for new users,
+           otherwise the default is 1 to maintain consistent experience
+           for existing users. Hardcoded in llnetmap.cpp -->
       <integer>1</integer>
     </map>
     <key>MiniMapScale</key>
@@ -7243,6 +7246,17 @@
       <key>Value</key>
       <real>128.0</real>
     </map>
+    <key>MiniMapShowPropertyLines</key>
+    <map>
+      <key>Comment</key>
+      <string>Whether or not to show parcel borders on the MiniMap.</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <real>1</real>
+    </map>
     <key>MouseSensitivity</key>
     <map>
       <key>Comment</key>
@@ -7521,13 +7535,13 @@
     <key>NonvisibleObjectsInMemoryTime</key>
     <map>
       <key>Comment</key>
-      <string>Number of frames non-visible objects stay in memory before being removed. 0 means never to remove.</string>
+      <string>Number of frames non-visible objects stay in memory before being removed. 0 means max.</string>
       <key>Persist</key>
       <integer>1</integer>
       <key>Type</key>
       <string>U32</string>
       <key>Value</key>
-			<integer>300</integer>
+			<integer>64</integer>
     </map>
     <key>NoPreload</key>
     <map>
@@ -11466,7 +11480,7 @@
       <key>Type</key>
       <string>U32</string>
       <key>Value</key>
-      <integer>1024</integer>
+      <integer>2048</integer>
     </map>
     <key>SceneLoadLowMemoryBound</key>
     <map>
diff --git a/indra/newview/app_settings/shaders/class1/deferred/materialF.glsl b/indra/newview/app_settings/shaders/class1/deferred/materialF.glsl
index 7ba8ce465c862974b90c8337fb1020477f412f1b..9085a491a57ce80df5defccb245b250f64df94e4 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/materialF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/materialF.glsl
@@ -203,7 +203,7 @@ VARYING vec2 vary_texcoord2;
 uniform float env_intensity;
 uniform vec4 specular_color;  // specular color RGB and specular exponent (glossiness) in alpha
 
-#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_MASK)
+#ifdef HAS_ALPHA_MASK
 uniform float minimum_alpha;
 #endif
 
@@ -228,11 +228,12 @@ void main()
     vec4 diffcol = texture2D(diffuseMap, vary_texcoord0.xy);
 	diffcol.rgb *= vertex_color.rgb;
 
-#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_MASK)
-
-    // Comparing floats cast from 8-bit values, produces acne right at the 8-bit transition points
-    float bias = 0.001953125; // 1/512, or half an 8-bit quantization
-    if (diffcol.a < minimum_alpha-bias)
+#ifdef HAS_ALPHA_MASK
+#if DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_BLEND
+    if (diffcol.a*vertex_color.a < minimum_alpha)
+#else
+    if (diffcol.a < minimum_alpha)
+#endif
     {
         discard;
     }
diff --git a/indra/newview/character/avatar_skeleton.xml b/indra/newview/character/avatar_skeleton.xml
index 2241a12545ffd942b86c3ed87422cdceb0ac0798..6cfc0b0be284682bd42dc83bcee42acc60c74cf5 100644
--- a/indra/newview/character/avatar_skeleton.xml
+++ b/indra/newview/character/avatar_skeleton.xml
@@ -2,15 +2,15 @@
    <bone aliases="hip avatar_mPelvis" connected="false" end="0.000 0.000 0.084" group="Torso" name="mPelvis" pivot="0.000000 0.000000 1.067015" pos="0.000 0.000 1.067" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base">
       <collision_volume end="0.030 0.000 0.095" group="Collision" name="PELVIS" pos="-0.01 0 -0.02" rot="0.000000 8.00000 0.000000" scale="0.12 0.16 0.17" support="base"/>
       <collision_volume end="-0.100 0.000 0.000" group="Collision" name="BUTT" pos="-0.06 0 -0.1" rot="0.000000 0.00000 0.000000" scale="0.1 0.1 0.1" support="base"/>
-      <bone connected="true" end="0.000 0.000 -0.084" group="Spine" name="mSpine1" pivot="0.000000 0.000000 0.084073" pos="0.000 0.000 0.084" rot="0.000000 0.000000 0.000000" scale="1.00 1.00 1.00" support="extended">
-         <bone connected="true" end="0.000 0.000 0.084" group="Spine" name="mSpine2" pivot="0.000000 0.000000 -0.084073" pos="0.000 0.000 -0.084" rot="0.000000 0.000000 0.000000" scale="1.00 1.00 1.00" support="extended">
+      <bone aliases="avatar_mSpine1" connected="true" end="0.000 0.000 -0.084" group="Spine" name="mSpine1" pivot="0.000000 0.000000 0.084073" pos="0.000 0.000 0.084" rot="0.000000 0.000000 0.000000" scale="1.00 1.00 1.00" support="extended">
+         <bone aliases="avatar_mSpine2" connected="true" end="0.000 0.000 0.084" group="Spine" name="mSpine2" pivot="0.000000 0.000000 -0.084073" pos="0.000 0.000 -0.084" rot="0.000000 0.000000 0.000000" scale="1.00 1.00 1.00" support="extended">
             <bone aliases="abdomen avatar_mTorso" connected="true" end="-0.015 0.000 0.205" group="Torso" name="mTorso" pivot="0.000000 0.000000 0.084073" pos="0.000 0.000 0.084" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base">
                <collision_volume end="0.028 0.000 0.094" group="Collision" name="BELLY" pos="0.028 0 0.04" rot="0.000000 8.00000 0.000000" scale="0.09 0.13 0.15" support="base"/>
                <collision_volume end="0.000 0.100 0.000" group="Collision" name="LEFT_HANDLE" pos="0.0 0.10 0.058" rot="0.000000 0.00000 0.000000" scale="0.05 0.05 0.05" support="base"/>
                <collision_volume end="0.000 -0.100 0.000" group="Collision" name="RIGHT_HANDLE" pos="0.0 -0.10 0.058" rot="0.000000 0.00000 0.000000" scale="0.05 0.05 0.05" support="base"/>
                <collision_volume end="-0.100 0.000 0.000" group="Collision" name="LOWER_BACK" pos="0.0 0.0 0.023" rot="0.000000 0.00000 0.000000" scale="0.09 0.13 0.15" support="base"/>
-               <bone connected="true" end="0.015 0.000 -0.205" group="Spine" name="mSpine3" pivot="-0.015368 0.000000 0.204877" pos="-0.015 0.000 0.205" rot="0.000000 0.000000 0.000000" scale="1.00 1.00 1.00" support="extended">
-                  <bone connected="true" end="-0.015 0.000 0.205" group="Spine" name="mSpine4" pivot="0.015368 0.000000 -0.204877" pos="0.015 0.000 -0.205" rot="0.000000 0.000000 0.000000" scale="1.00 1.00 1.00" support="extended">
+               <bone aliases="avatar_mSpine3" connected="true" end="0.015 0.000 -0.205" group="Spine" name="mSpine3" pivot="-0.015368 0.000000 0.204877" pos="-0.015 0.000 0.205" rot="0.000000 0.000000 0.000000" scale="1.00 1.00 1.00" support="extended">
+                  <bone aliases="avatar_mSpine4" connected="true" end="-0.015 0.000 0.205" group="Spine" name="mSpine4" pivot="0.015368 0.000000 -0.204877" pos="0.015 0.000 -0.205" rot="0.000000 0.000000 0.000000" scale="1.00 1.00 1.00" support="extended">
                      <bone aliases="chest avatar_mChest" connected="true" end="-0.010 0.000 0.250" group="Torso" name="mChest" pivot="-0.015368 0.000000 0.204877" pos="-0.015 0.000 0.205" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base">
                         <collision_volume end="-0.096 0.000 0.152" group="Collision" name="CHEST" pos="0.028 0 0.07" rot="0.000000 -10.00000 0.000000" scale="0.11 0.15 0.2" support="base"/>
                         <collision_volume end="0.080 0.000 -0.006" group="Collision" name="LEFT_PEC" pos="0.119 0.082 0.042" rot="0.000000 4.29000 0.000000" scale="0.05 0.05 0.05" support="base"/>
@@ -23,58 +23,58 @@
                               <bone aliases="figureHair avatar_mSkull" connected="false" end="0.000 0.000 0.033" group="Extra" name="mSkull" pivot="0.000000 0.000000 0.079000" pos="0.000 0.000 0.079" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base"/>
                               <bone aliases="avatar_mEyeRight" connected="false" end="0.025 0.000 0.000" group="Extra" name="mEyeRight" pivot="0.098466 -0.036000 0.079000" pos="0.098 -0.036 0.079" rot="0.000000 0.000000 -0.000000" scale="1.000 1.000 1.000" support="base"/>
                               <bone aliases="avatar_mEyeLeft" connected="false" end="0.025 0.000 0.000" group="Extra" name="mEyeLeft" pivot="0.098461 0.036000 0.079000" pos="0.098 0.036 0.079" rot="0.000000 -0.000000 0.000000" scale="1.000 1.000 1.000" support="base"/>
-                              <bone connected="false" end="0.020 0.000 0.000" group="Face" name="mFaceRoot" pivot="0.025000 0.000000 0.045000" pos="0.025 0.000 0.045" rot="0.000000 0.000000 0.000000" scale="1.00 1.00 1.00" support="extended">
-                                 <bone connected="false" end="0.025 0.000 0.000" group="Face" name="mFaceEyeAltRight" pivot="0.073466 -0.036000 0.0339300" pos="0.073 -0.036 0.034" rot="0.000000 0.000000 0.000000" scale="1.00 1.00 1.00" support="extended"/>
-                                 <bone connected="false" end="0.025 0.000 0.000" group="Face" name="mFaceEyeAltLeft" pivot="0.073461 0.036000 0.0339300" pos="0.073 0.036 0.034" rot="0.000000 0.000000 0.000000" scale="1.00 1.00 1.00" support="extended"/>
-                                 <bone connected="false" end="0.024 0.004 0.018" group="Face" name="mFaceForeheadLeft" pivot="0.061 0.035 0.083" pos="0.061 0.035 0.083" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                 <bone connected="false" end="0.024 -0.004 0.018" group="Face" name="mFaceForeheadRight" pivot="0.061 -0.035 0.083" pos="0.061 -0.035 0.083" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                 <bone connected="false" end="0.023 0.013 0.000" group="Eyes" name="mFaceEyebrowOuterLeft" pivot="0.064 0.051 0.048" pos="0.064 0.051 0.048" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                 <bone connected="false" end="0.027 0.000 0.000" group="Eyes" name="mFaceEyebrowCenterLeft" pivot="0.070 0.043 0.056" pos="0.070 0.043 0.056" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                 <bone connected="false" end="0.026 0.000 0.000" group="Eyes" name="mFaceEyebrowInnerLeft" pivot="0.075 0.022 0.051" pos="0.075 0.022 0.051" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                 <bone connected="false" end="0.023 -0.013 0.000" group="Eyes" name="mFaceEyebrowOuterRight" pivot="0.064 -0.051 0.048" pos="0.064 -0.051 0.048" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                 <bone connected="false" end="0.027 0.000 0.000" group="Eyes" name="mFaceEyebrowCenterRight" pivot="0.070 -0.043 0.056" pos="0.070 -0.043 0.056" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                 <bone connected="false" end="0.026 0.000 0.000" group="Eyes" name="mFaceEyebrowInnerRight" pivot="0.075 -0.022 0.051" pos="0.075 -0.022 0.051" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                 <bone connected="false" end="0.027 0.000 0.005" group="Eyes" name="mFaceEyeLidUpperLeft" pivot="0.073 0.036 0.034" pos="0.073 0.036 0.034" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                 <bone connected="false" end="0.024 0.000 -0.007" group="Eyes" name="mFaceEyeLidLowerLeft" pivot="0.073 0.036 0.034" pos="0.073 0.036 0.034" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                 <bone connected="false" end="0.027 0.000 0.005" group="Eyes" name="mFaceEyeLidUpperRight" pivot="0.073 -0.036 0.034" pos="0.073 -0.036 0.034" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                 <bone connected="false" end="0.024 0.000 -0.007" group="Eyes" name="mFaceEyeLidLowerRight" pivot="0.073 -0.036 0.034" pos="0.073 -0.036 0.034" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                 <bone connected="false" end="-0.019 0.018 0.025" group="Ears" name="mFaceEar1Left" pivot="0.000 0.080 0.002" pos="0.000 0.080 0.002" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                    <bone connected="true" end="0.000 0.000 0.033" group="Ears" name="mFaceEar2Left" pivot="-0.019 0.018 0.025" pos="-0.019 0.018 0.025" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                              <bone aliases="avatar_mFaceRoot" connected="false" end="0.020 0.000 0.000" group="Face" name="mFaceRoot" pivot="0.025000 0.000000 0.045000" pos="0.025 0.000 0.045" rot="0.000000 0.000000 0.000000" scale="1.00 1.00 1.00" support="extended">
+                                 <bone aliases="avatar_mFaceEyeAltRight" connected="false" end="0.025 0.000 0.000" group="Face" name="mFaceEyeAltRight" pivot="0.073466 -0.036000 0.0339300" pos="0.073 -0.036 0.034" rot="0.000000 0.000000 0.000000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceEyeAltLeft" connected="false" end="0.025 0.000 0.000" group="Face" name="mFaceEyeAltLeft" pivot="0.073461 0.036000 0.0339300" pos="0.073 0.036 0.034" rot="0.000000 0.000000 0.000000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceForeheadLeft" connected="false" end="0.024 0.004 0.018" group="Face" name="mFaceForeheadLeft" pivot="0.061 0.035 0.083" pos="0.061 0.035 0.083" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceForeheadRight" connected="false" end="0.024 -0.004 0.018" group="Face" name="mFaceForeheadRight" pivot="0.061 -0.035 0.083" pos="0.061 -0.035 0.083" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceEyebrowOuterLeft" connected="false" end="0.023 0.013 0.000" group="Eyes" name="mFaceEyebrowOuterLeft" pivot="0.064 0.051 0.048" pos="0.064 0.051 0.048" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceEyebrowCenterLeft" connected="false" end="0.027 0.000 0.000" group="Eyes" name="mFaceEyebrowCenterLeft" pivot="0.070 0.043 0.056" pos="0.070 0.043 0.056" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceEyebrowInnerLeft" connected="false" end="0.026 0.000 0.000" group="Eyes" name="mFaceEyebrowInnerLeft" pivot="0.075 0.022 0.051" pos="0.075 0.022 0.051" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceEyebrowOuterRight" connected="false" end="0.023 -0.013 0.000" group="Eyes" name="mFaceEyebrowOuterRight" pivot="0.064 -0.051 0.048" pos="0.064 -0.051 0.048" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceEyebrowCenterRight" connected="false" end="0.027 0.000 0.000" group="Eyes" name="mFaceEyebrowCenterRight" pivot="0.070 -0.043 0.056" pos="0.070 -0.043 0.056" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceEyebrowInnerRight" connected="false" end="0.026 0.000 0.000" group="Eyes" name="mFaceEyebrowInnerRight" pivot="0.075 -0.022 0.051" pos="0.075 -0.022 0.051" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceEyeLidUpperLeft" connected="false" end="0.027 0.000 0.005" group="Eyes" name="mFaceEyeLidUpperLeft" pivot="0.073 0.036 0.034" pos="0.073 0.036 0.034" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceEyeLidLowerLeft" connected="false" end="0.024 0.000 -0.007" group="Eyes" name="mFaceEyeLidLowerLeft" pivot="0.073 0.036 0.034" pos="0.073 0.036 0.034" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceEyeLidUpperRight" connected="false" end="0.027 0.000 0.005" group="Eyes" name="mFaceEyeLidUpperRight" pivot="0.073 -0.036 0.034" pos="0.073 -0.036 0.034" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceEyeLidLowerRight" connected="false" end="0.024 0.000 -0.007" group="Eyes" name="mFaceEyeLidLowerRight" pivot="0.073 -0.036 0.034" pos="0.073 -0.036 0.034" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceEar1Left" connected="false" end="-0.019 0.018 0.025" group="Ears" name="mFaceEar1Left" pivot="0.000 0.080 0.002" pos="0.000 0.080 0.002" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                    <bone aliases="avatar_mFaceEar2Left" connected="true" end="0.000 0.000 0.033" group="Ears" name="mFaceEar2Left" pivot="-0.019 0.018 0.025" pos="-0.019 0.018 0.025" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
                                  </bone>
-                                 <bone connected="false" end="-0.019 -0.018 0.025" group="Ears" name="mFaceEar1Right" pivot="0.000 -0.080 0.002" pos="0.000 -0.080 0.002" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                    <bone connected="true" end="0.000 0.000 0.033" group="Ears" name="mFaceEar2Right" pivot="-0.019 -0.018 0.025" pos="-0.019 -0.018 0.025" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceEar1Right" connected="false" end="-0.019 -0.018 0.025" group="Ears" name="mFaceEar1Right" pivot="0.000 -0.080 0.002" pos="0.000 -0.080 0.002" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                    <bone aliases="avatar_mFaceEar2Right" connected="true" end="0.000 0.000 0.033" group="Ears" name="mFaceEar2Right" pivot="-0.019 -0.018 0.025" pos="-0.019 -0.018 0.025" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
                                  </bone>
-                                 <bone connected="false" end="0.015 0.004 0.000" group="Face" name="mFaceNoseLeft" pivot="0.086 0.015 -0.004" pos="0.086 0.015 -0.004" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                 <bone connected="false" end="0.025 0.000 0.000" group="Face" name="mFaceNoseCenter" pivot="0.102 0.000 0.000" pos="0.102 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                 <bone connected="false" end="0.015 -0.004 0.000" group="Face" name="mFaceNoseRight" pivot="0.086 -0.015 -0.004" pos="0.086 -0.015 -0.004" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                 <bone connected="false" end="0.013 0.030 0.000" group="Face" name="mFaceCheekLowerLeft" pivot="0.050 0.034 -0.031" pos="0.050 0.034 -0.031" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                 <bone connected="false" end="0.022 0.015 0.000" group="Face" name="mFaceCheekUpperLeft" pivot="0.070 0.034 -0.005" pos="0.070 0.034 -0.005" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                 <bone connected="false" end="0.013 -0.030 0.000" group="Face" name="mFaceCheekLowerRight" pivot="0.050 -0.034 -0.031" pos="0.050 -0.034 -0.031" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                 <bone connected="false" end="0.022 -0.015 0.000" group="Face" name="mFaceCheekUpperRight" pivot="0.070 -0.034 -0.005" pos="0.070 -0.034 -0.005" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                 <bone connected="false" end="0.059 0.000 -0.039" group="Mouth" name="mFaceJaw" pivot="-0.001 0.000 -0.015" pos="-0.001 0.000 -0.015" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                    <bone connected="false" end="0.021 0.000 -0.018" group="Mouth" name="mFaceChin" pivot="0.074 0.000 -0.054" pos="0.074 0.000 -0.054" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                    <bone connected="false" end="0.035 0.000 0.000" group="Mouth" name="mFaceTeethLower" pivot="0.021 0.000 -0.039" pos="0.021 0.000 -0.039" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                       <bone connected="false" end="0.034 0.017 0.005" group="Lips" name="mFaceLipLowerLeft" pivot="0.045 0.000 0.000" pos="0.045 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                       <bone connected="false" end="0.034 -0.017 0.005" group="Lips" name="mFaceLipLowerRight" pivot="0.045 0.000 0.000" pos="0.045 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                       <bone connected="false" end="0.040 0.000 0.002" group="Lips" name="mFaceLipLowerCenter" pivot="0.045 0.000 0.000" pos="0.045 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                       <bone connected="false" end="0.022 0.000 0.007" group="Mouth" name="mFaceTongueBase" pivot="0.039 0.000 0.005" pos="0.039 0.000 0.005" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                          <bone connected="true" end="0.010 0.000 0.000" group="Mouth" name="mFaceTongueTip" pivot="0.022 0.000 0.007" pos="0.022 0.000 0.007" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceNoseLeft" connected="false" end="0.015 0.004 0.000" group="Face" name="mFaceNoseLeft" pivot="0.086 0.015 -0.004" pos="0.086 0.015 -0.004" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceNoseCenter" connected="false" end="0.025 0.000 0.000" group="Face" name="mFaceNoseCenter" pivot="0.102 0.000 0.000" pos="0.102 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceNoseRight" connected="false" end="0.015 -0.004 0.000" group="Face" name="mFaceNoseRight" pivot="0.086 -0.015 -0.004" pos="0.086 -0.015 -0.004" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceCheekLowerLeft" connected="false" end="0.013 0.030 0.000" group="Face" name="mFaceCheekLowerLeft" pivot="0.050 0.034 -0.031" pos="0.050 0.034 -0.031" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceCheekUpperLeft" connected="false" end="0.022 0.015 0.000" group="Face" name="mFaceCheekUpperLeft" pivot="0.070 0.034 -0.005" pos="0.070 0.034 -0.005" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceCheekLowerRight" connected="false" end="0.013 -0.030 0.000" group="Face" name="mFaceCheekLowerRight" pivot="0.050 -0.034 -0.031" pos="0.050 -0.034 -0.031" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceCheekUpperRight" connected="false" end="0.022 -0.015 0.000" group="Face" name="mFaceCheekUpperRight" pivot="0.070 -0.034 -0.005" pos="0.070 -0.034 -0.005" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceJaw" connected="false" end="0.059 0.000 -0.039" group="Mouth" name="mFaceJaw" pivot="-0.001 0.000 -0.015" pos="-0.001 0.000 -0.015" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                    <bone aliases="avatar_mFaceChin" connected="false" end="0.021 0.000 -0.018" group="Mouth" name="mFaceChin" pivot="0.074 0.000 -0.054" pos="0.074 0.000 -0.054" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                    <bone aliases="avatar_mFaceTeethLower" connected="false" end="0.035 0.000 0.000" group="Mouth" name="mFaceTeethLower" pivot="0.021 0.000 -0.039" pos="0.021 0.000 -0.039" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                       <bone aliases="avatar_mFaceLipLowerLeft" connected="false" end="0.034 0.017 0.005" group="Lips" name="mFaceLipLowerLeft" pivot="0.045 0.000 0.000" pos="0.045 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                       <bone aliases="avatar_mFaceLipLowerRight" connected="false" end="0.034 -0.017 0.005" group="Lips" name="mFaceLipLowerRight" pivot="0.045 0.000 0.000" pos="0.045 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                       <bone aliases="avatar_mFaceLipLowerCenter" connected="false" end="0.040 0.000 0.002" group="Lips" name="mFaceLipLowerCenter" pivot="0.045 0.000 0.000" pos="0.045 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                       <bone aliases="avatar_mFaceTongueBase" connected="false" end="0.022 0.000 0.007" group="Mouth" name="mFaceTongueBase" pivot="0.039 0.000 0.005" pos="0.039 0.000 0.005" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                          <bone aliases="avatar_mFaceTongueTip" connected="true" end="0.010 0.000 0.000" group="Mouth" name="mFaceTongueTip" pivot="0.022 0.000 0.007" pos="0.022 0.000 0.007" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
                                        </bone>
                                     </bone>
                                  </bone>
-                                 <bone connected="false" end="-0.017 0.000 0.000" group="Face" name="mFaceJawShaper" pivot="0.000 0.000 0.000" pos="0.000 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                 <bone connected="false" end="0.036 0.000 0.000" group="Face" name="mFaceForeheadCenter" pivot="0.069 0.000 0.065" pos="0.069 0.000 0.065" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                 <bone connected="false" end="0.014 0.000 0.000" group="Nose" name="mFaceNoseBase" pivot="0.094 0.000 -0.016" pos="0.094 0.000 -0.016" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                 <bone connected="false" end="0.035 0.000 0.000" group="Mouth" name="mFaceTeethUpper" pivot="0.020 0.000 -0.030" pos="0.020 0.000 -0.030" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                    <bone connected="false" end="0.041 0.015 0.000" group="Lips" name="mFaceLipUpperLeft" pivot="0.045 0.000 -0.003" pos="0.045 0.000 -0.003" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                    <bone connected="false" end="0.041 -0.015 0.000" group="Lips" name="mFaceLipUpperRight" pivot="0.045 0.000 -0.003" pos="0.045 0.000 -0.003" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                    <bone connected="false" end="0.045 0.051 0.000" group="Lips" name="mFaceLipCornerLeft" pivot="0.028 -0.019 -0.010" pos="0.028 -0.019 -0.010" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                    <bone connected="false" end="0.045 -0.051 0.000" group="Lips" name="mFaceLipCornerRight" pivot="0.028 0.019 -0.010" pos="0.028 0.019 -0.010" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                    <bone connected="false" end="0.043 0.000 0.002" group="Lips" name="mFaceLipUpperCenter" pivot="0.045 0.000 -0.003" pos="0.045 0.000 -0.003" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceJawShaper" connected="false" end="-0.017 0.000 0.000" group="Face" name="mFaceJawShaper" pivot="0.000 0.000 0.000" pos="0.000 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceForeheadCenter" connected="false" end="0.036 0.000 0.000" group="Face" name="mFaceForeheadCenter" pivot="0.069 0.000 0.065" pos="0.069 0.000 0.065" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceNoseBase" connected="false" end="0.014 0.000 0.000" group="Nose" name="mFaceNoseBase" pivot="0.094 0.000 -0.016" pos="0.094 0.000 -0.016" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceTeethUpper" connected="false" end="0.035 0.000 0.000" group="Mouth" name="mFaceTeethUpper" pivot="0.020 0.000 -0.030" pos="0.020 0.000 -0.030" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                    <bone aliases="avatar_mFaceLipUpperLeft" connected="false" end="0.041 0.015 0.000" group="Lips" name="mFaceLipUpperLeft" pivot="0.045 0.000 -0.003" pos="0.045 0.000 -0.003" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                    <bone aliases="avatar_mFaceLipUpperRight" connected="false" end="0.041 -0.015 0.000" group="Lips" name="mFaceLipUpperRight" pivot="0.045 0.000 -0.003" pos="0.045 0.000 -0.003" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                    <bone aliases="avatar_mFaceLipCornerLeft" connected="false" end="0.045 0.051 0.000" group="Lips" name="mFaceLipCornerLeft" pivot="0.028 -0.019 -0.010" pos="0.028 -0.019 -0.010" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                    <bone aliases="avatar_mFaceLipCornerRight" connected="false" end="0.045 -0.051 0.000" group="Lips" name="mFaceLipCornerRight" pivot="0.028 0.019 -0.010" pos="0.028 0.019 -0.010" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                    <bone aliases="avatar_mFaceLipUpperCenter" connected="false" end="0.043 0.000 0.002" group="Lips" name="mFaceLipUpperCenter" pivot="0.045 0.000 -0.003" pos="0.045 0.000 -0.003" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
                                  </bone>
-                                 <bone connected="false" end="0.016 0.000 0.000" group="Face" name="mFaceEyecornerInnerLeft" pivot="0.075 0.017 0.032" pos="0.075 0.017 0.032" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                 <bone connected="false" end="0.016 0.000 0.000" group="Face" name="mFaceEyecornerInnerRight" pivot="0.075 -0.017 0.032" pos="0.075 -0.017 0.032" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                 <bone connected="false" end="0.015 0.000 0.008" group="Nose" name="mFaceNoseBridge" pivot="0.091 0.000 0.020" pos="0.091 0.000 0.020" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceEyecornerInnerLeft" connected="false" end="0.016 0.000 0.000" group="Face" name="mFaceEyecornerInnerLeft" pivot="0.075 0.017 0.032" pos="0.075 0.017 0.032" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceEyecornerInnerRight" connected="false" end="0.016 0.000 0.000" group="Face" name="mFaceEyecornerInnerRight" pivot="0.075 -0.017 0.032" pos="0.075 -0.017 0.032" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                 <bone aliases="avatar_mFaceNoseBridge" connected="false" end="0.015 0.000 0.008" group="Nose" name="mFaceNoseBridge" pivot="0.091 0.000 0.020" pos="0.091 0.000 0.020" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
                               </bone>
                            </bone>
                         </bone>
@@ -86,29 +86,29 @@
                                  <collision_volume end="0.000 0.100 -0.001" group="Collision" name="L_LOWER_ARM" pos="0.0 0.1 0.0" rot="-3.000000 0.00000 0.000000" scale="0.04 0.14 0.04" support="base"/>
                                  <bone aliases="lHand avatar_mWristLeft" connected="true" end="0.000 0.060 0.000" group="Arms" name="mWristLeft" pivot="-0.000000 0.204846 0.000000" pos="-0.000 0.205 0.000" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base">
                                     <collision_volume end="0.005 0.049 -0.001" group="Collision" name="L_HAND" pos="0.01 0.05 0.0" rot="-3.000000 0.00000 -10.000000" scale="0.05 0.08 0.03" support="base"/>
-                                    <bone connected="false" end="-0.001 0.040 -0.006" group="Hand" name="mHandMiddle1Left" pivot="0.013 0.101 0.015" pos="0.013 0.101 0.015" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                       <bone connected="true" end="-0.001 0.049 -0.008" group="Hand" name="mHandMiddle2Left" pivot="-0.001 0.040 -0.006" pos="-0.001 0.040 -0.006" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                          <bone connected="true" end="-0.002 0.033 -0.006" group="Hand" name="mHandMiddle3Left" pivot="-0.001 0.049 -0.008" pos="-0.001 0.049 -0.008" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                    <bone aliases="avatar_mHandMiddle1Left" connected="false" end="-0.001 0.040 -0.006" group="Hand" name="mHandMiddle1Left" pivot="0.013 0.101 0.015" pos="0.013 0.101 0.015" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                       <bone aliases="avatar_mHandMiddle2Left" connected="true" end="-0.001 0.049 -0.008" group="Hand" name="mHandMiddle2Left" pivot="-0.001 0.040 -0.006" pos="-0.001 0.040 -0.006" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                          <bone aliases="avatar_mHandMiddle3Left" connected="true" end="-0.002 0.033 -0.006" group="Hand" name="mHandMiddle3Left" pivot="-0.001 0.049 -0.008" pos="-0.001 0.049 -0.008" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
                                        </bone>
                                     </bone>
-                                    <bone connected="false" end="0.017 0.036 -0.006" group="Hand" name="mHandIndex1Left" pivot="0.038 0.097 0.015" pos="0.038 0.097 0.015" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                       <bone connected="true" end="0.014 0.032 -0.006" group="Hand" name="mHandIndex2Left" pivot="0.017 0.036 -0.006" pos="0.017 0.036 -0.006" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                          <bone connected="true" end="0.011 0.025 -0.004" group="Hand" name="mHandIndex3Left" pivot="0.014 0.032 -0.006" pos="0.014 0.032 -0.006" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                    <bone aliases="avatar_mHandIndex1Left" connected="false" end="0.017 0.036 -0.006" group="Hand" name="mHandIndex1Left" pivot="0.038 0.097 0.015" pos="0.038 0.097 0.015" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                       <bone aliases="avatar_mHandIndex2Left" connected="true" end="0.014 0.032 -0.006" group="Hand" name="mHandIndex2Left" pivot="0.017 0.036 -0.006" pos="0.017 0.036 -0.006" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                          <bone aliases="avatar_mHandIndex3Left" connected="true" end="0.011 0.025 -0.004" group="Hand" name="mHandIndex3Left" pivot="0.014 0.032 -0.006" pos="0.014 0.032 -0.006" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
                                        </bone>
                                     </bone>
-                                    <bone connected="false" end="-0.013 0.038 -0.008" group="Hand" name="mHandRing1Left" pivot="-0.010 0.099 0.009" pos="-0.010 0.099 0.009" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                       <bone connected="true" end="-0.013 0.040 -0.009" group="Hand" name="mHandRing2Left" pivot="-0.013 0.038 -0.008" pos="-0.013 0.038 -0.008" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                          <bone connected="true" end="-0.010 0.028 -0.006" group="Hand" name="mHandRing3Left" pivot="-0.013 0.040 -0.009" pos="-0.013 0.040 -0.009" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                    <bone aliases="avatar_mHandRing1Left" connected="false" end="-0.013 0.038 -0.008" group="Hand" name="mHandRing1Left" pivot="-0.010 0.099 0.009" pos="-0.010 0.099 0.009" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                       <bone aliases="avatar_mHandRing2Left" connected="true" end="-0.013 0.040 -0.009" group="Hand" name="mHandRing2Left" pivot="-0.013 0.038 -0.008" pos="-0.013 0.038 -0.008" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                          <bone aliases="avatar_mHandRing3Left" connected="true" end="-0.010 0.028 -0.006" group="Hand" name="mHandRing3Left" pivot="-0.013 0.040 -0.009" pos="-0.013 0.040 -0.009" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
                                        </bone>
                                     </bone>
-                                    <bone connected="false" end="-0.024 0.025 -0.006" group="Hand" name="mHandPinky1Left" pivot="-0.031 0.095 0.003" pos="-0.031 0.095 0.003" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                       <bone connected="true" end="-0.015 0.018 -0.004" group="Hand" name="mHandPinky2Left" pivot="-0.024 0.025 -0.006" pos="-0.024 0.025 -0.006" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                          <bone connected="true" end="-0.013 0.016 -0.004" group="Hand" name="mHandPinky3Left" pivot="-0.015 0.018 -0.004" pos="-0.015 0.018 -0.004" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                    <bone aliases="avatar_mHandPinky1Left" connected="false" end="-0.024 0.025 -0.006" group="Hand" name="mHandPinky1Left" pivot="-0.031 0.095 0.003" pos="-0.031 0.095 0.003" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                       <bone aliases="avatar_mHandPinky2Left" connected="true" end="-0.015 0.018 -0.004" group="Hand" name="mHandPinky2Left" pivot="-0.024 0.025 -0.006" pos="-0.024 0.025 -0.006" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                          <bone aliases="avatar_mHandPinky3Left" connected="true" end="-0.013 0.016 -0.004" group="Hand" name="mHandPinky3Left" pivot="-0.015 0.018 -0.004" pos="-0.015 0.018 -0.004" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
                                        </bone>
                                     </bone>
-                                    <bone connected="false" end="0.028 0.032 0.000" group="Hand" name="mHandThumb1Left" pivot="0.031 0.026 0.004" pos="0.031 0.026 0.004" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                       <bone connected="true" end="0.023 0.031 0.000" group="Hand" name="mHandThumb2Left" pivot="0.028 0.032 -0.001" pos="0.028 0.032 -0.001" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                          <bone connected="true" end="0.015 0.025 0.000" group="Hand" name="mHandThumb3Left" pivot="0.023 0.031 -0.001" pos="0.023 0.031 -0.001" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                    <bone aliases="avatar_mHandThumb1Left" connected="false" end="0.028 0.032 0.000" group="Hand" name="mHandThumb1Left" pivot="0.031 0.026 0.004" pos="0.031 0.026 0.004" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                       <bone aliases="avatar_mHandThumb2Left" connected="true" end="0.023 0.031 0.000" group="Hand" name="mHandThumb2Left" pivot="0.028 0.032 -0.001" pos="0.028 0.032 -0.001" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                          <bone aliases="avatar_mHandThumb3Left" connected="true" end="0.015 0.025 0.000" group="Hand" name="mHandThumb3Left" pivot="0.023 0.031 -0.001" pos="0.023 0.031 -0.001" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
                                        </bone>
                                     </bone>
                                  </bone>
@@ -123,49 +123,49 @@
                                  <collision_volume end="0.000 -0.100 -0.001" group="Collision" name="R_LOWER_ARM" pos="0.0 -0.1 0.0" rot="3.000000 0.00000 0.000000" scale="0.04 0.14 0.04" support="base"/>
                                  <bone aliases="rHand avatar_mWristRight" connected="true" end="0.000 -0.060 0.000" group="Arms" name="mWristRight" pivot="-0.000000 -0.205000 -0.000000" pos="0.000 -0.205 -0.000" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" support="base">
                                     <collision_volume end="0.005 -0.049 -0.001" group="Collision" name="R_HAND" pos="0.01 -0.05 0.0" rot="3.000000 0.00000 10.000000" scale="0.05 0.08 0.03" support="base"/>
-                                    <bone connected="false" end="-0.001 -0.040 -0.006" group="Hand" name="mHandMiddle1Right" pivot="0.013 -0.101 0.015" pos="0.013 -0.101 0.015" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                       <bone connected="true" end="-0.001 -0.049 -0.008" group="Hand" name="mHandMiddle2Right" pivot="-0.001 -0.040 -0.006" pos="-0.001 -0.040 -0.006" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                          <bone connected="true" end="-0.002 -0.033 -0.006" group="Hand" name="mHandMiddle3Right" pivot="-0.001 -0.049 -0.008" pos="-0.001 -0.049 -0.008" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                    <bone aliases="avatar_mHandMiddle1Right" connected="false" end="-0.001 -0.040 -0.006" group="Hand" name="mHandMiddle1Right" pivot="0.013 -0.101 0.015" pos="0.013 -0.101 0.015" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                       <bone aliases="avatar_mHandMiddle2Right" connected="true" end="-0.001 -0.049 -0.008" group="Hand" name="mHandMiddle2Right" pivot="-0.001 -0.040 -0.006" pos="-0.001 -0.040 -0.006" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                          <bone aliases="avatar_mHandMiddle3Right" connected="true" end="-0.002 -0.033 -0.006" group="Hand" name="mHandMiddle3Right" pivot="-0.001 -0.049 -0.008" pos="-0.001 -0.049 -0.008" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
                                        </bone>
                                     </bone>
-                                    <bone connected="false" end="0.017 -0.036 -0.006" group="Hand" name="mHandIndex1Right" pivot="0.038 -0.097 0.015" pos="0.038 -0.097 0.015" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                       <bone connected="true" end="0.014 -0.032 -0.006" group="Hand" name="mHandIndex2Right" pivot="0.017 -0.036 -0.006" pos="0.017 -0.036 -0.006" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                          <bone connected="true" end="0.011 -0.025 -0.004" group="Hand" name="mHandIndex3Right" pivot="0.014 -0.032 -0.006" pos="0.014 -0.032 -0.006" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                    <bone aliases="avatar_mHandIndex1Right" connected="false" end="0.017 -0.036 -0.006" group="Hand" name="mHandIndex1Right" pivot="0.038 -0.097 0.015" pos="0.038 -0.097 0.015" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                       <bone aliases="avatar_mHandIndex2Right" connected="true" end="0.014 -0.032 -0.006" group="Hand" name="mHandIndex2Right" pivot="0.017 -0.036 -0.006" pos="0.017 -0.036 -0.006" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                          <bone aliases="avatar_mHandIndex3Right" connected="true" end="0.011 -0.025 -0.004" group="Hand" name="mHandIndex3Right" pivot="0.014 -0.032 -0.006" pos="0.014 -0.032 -0.006" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
                                        </bone>
                                     </bone>
-                                    <bone connected="false" end="-0.013 -0.038 -0.008" group="Hand" name="mHandRing1Right" pivot="-0.010 -0.099 0.009" pos="-0.010 -0.099 0.009" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                       <bone connected="true" end="-0.013 -0.040 -0.009" group="Hand" name="mHandRing2Right" pivot="-0.013 -0.038 -0.008" pos="-0.013 -0.038 -0.008" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                          <bone connected="true" end="-0.010 -0.028 -0.006" group="Hand" name="mHandRing3Right" pivot="-0.013 -0.040 -0.009" pos="-0.013 -0.040 -0.009" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                    <bone aliases="avatar_mHandRing1Right" connected="false" end="-0.013 -0.038 -0.008" group="Hand" name="mHandRing1Right" pivot="-0.010 -0.099 0.009" pos="-0.010 -0.099 0.009" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                       <bone aliases="avatar_mHandRing2Right" connected="true" end="-0.013 -0.040 -0.009" group="Hand" name="mHandRing2Right" pivot="-0.013 -0.038 -0.008" pos="-0.013 -0.038 -0.008" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                          <bone aliases="avatar_mHandRing3Right" connected="true" end="-0.010 -0.028 -0.006" group="Hand" name="mHandRing3Right" pivot="-0.013 -0.040 -0.009" pos="-0.013 -0.040 -0.009" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
                                        </bone>
                                     </bone>
-                                    <bone connected="false" end="-0.024 -0.025 -0.006" group="Hand" name="mHandPinky1Right" pivot="-0.031 -0.095 0.003" pos="-0.031 -0.095 0.003" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                       <bone connected="true" end="-0.015 -0.018 -0.004" group="Hand" name="mHandPinky2Right" pivot="-0.024 -0.025 -0.006" pos="-0.024 -0.025 -0.006" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                          <bone connected="true" end="-0.013 -0.016 -0.004" group="Hand" name="mHandPinky3Right" pivot="-0.015 -0.018 -0.004" pos="-0.015 -0.018 -0.004" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                    <bone aliases="avatar_mHandPinky1Right" connected="false" end="-0.024 -0.025 -0.006" group="Hand" name="mHandPinky1Right" pivot="-0.031 -0.095 0.003" pos="-0.031 -0.095 0.003" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                       <bone aliases="avatar_mHandPinky2Right" connected="true" end="-0.015 -0.018 -0.004" group="Hand" name="mHandPinky2Right" pivot="-0.024 -0.025 -0.006" pos="-0.024 -0.025 -0.006" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                          <bone aliases="avatar_mHandPinky3Right" connected="true" end="-0.013 -0.016 -0.004" group="Hand" name="mHandPinky3Right" pivot="-0.015 -0.018 -0.004" pos="-0.015 -0.018 -0.004" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
                                        </bone>
                                     </bone>
-                                    <bone connected="false" end="0.028 -0.032 0.000" group="Hand" name="mHandThumb1Right" pivot="0.031 -0.026 0.004" pos="0.031 -0.026 0.004" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                       <bone connected="true" end="0.023 -0.031 0.000" group="Hand" name="mHandThumb2Right" pivot="0.028 -0.032 -0.001" pos="0.028 -0.032 -0.001" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                          <bone connected="true" end="0.015 -0.025 0.000" group="Hand" name="mHandThumb3Right" pivot="0.023 -0.031 -0.001" pos="0.023 -0.031 -0.001" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                    <bone aliases="avatar_mHandThumb1Right" connected="false" end="0.028 -0.032 0.000" group="Hand" name="mHandThumb1Right" pivot="0.031 -0.026 0.004" pos="0.031 -0.026 0.004" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                       <bone aliases="avatar_mHandThumb2Right" connected="true" end="0.023 -0.031 0.000" group="Hand" name="mHandThumb2Right" pivot="0.028 -0.032 -0.001" pos="0.028 -0.032 -0.001" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                          <bone aliases="avatar_mHandThumb3Right" connected="true" end="0.015 -0.025 0.000" group="Hand" name="mHandThumb3Right" pivot="0.023 -0.031 -0.001" pos="0.023 -0.031 -0.001" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
                                        </bone>
                                     </bone>
                                  </bone>
                               </bone>
                            </bone>
                         </bone>
-                        <bone connected="false" end="-0.061 0.000 0.000" group="Wing" name="mWingsRoot" pivot="-0.014 0.000 0.000" pos="-0.014 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                           <bone connected="false" end="-0.168 0.169 0.067" group="Wing" name="mWing1Left" pivot="-0.099 0.105 0.181" pos="-0.099 0.105 0.181" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                              <bone connected="true" end="-0.181 0.183 0.000" group="Wing" name="mWing2Left" pivot="-0.168 0.169 0.067" pos="-0.168 0.169 0.067" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                 <bone connected="true" end="-0.171 0.173 0.000" group="Wing" name="mWing3Left" pivot="-0.181 0.183 0.000" pos="-0.181 0.183 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                    <bone connected="true" end="-0.146 0.132 0.000" group="Wing" name="mWing4Left" pivot="-0.171 0.173 0.000" pos="-0.171 0.173 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                    <bone connected="true" end="-0.068 0.062 -0.159" group="Wing" name="mWing4FanLeft" pivot="-0.171 0.173 0.000" pos="-0.171 0.173 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                        <bone aliases="avatar_mWingsRoot" connected="false" end="-0.061 0.000 0.000" group="Wing" name="mWingsRoot" pivot="-0.014 0.000 0.000" pos="-0.014 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                           <bone aliases="avatar_mWing1Left" connected="false" end="-0.168 0.169 0.067" group="Wing" name="mWing1Left" pivot="-0.099 0.105 0.181" pos="-0.099 0.105 0.181" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                              <bone aliases="avatar_mWing2Left" connected="true" end="-0.181 0.183 0.000" group="Wing" name="mWing2Left" pivot="-0.168 0.169 0.067" pos="-0.168 0.169 0.067" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                 <bone aliases="avatar_mWing3Left" connected="true" end="-0.171 0.173 0.000" group="Wing" name="mWing3Left" pivot="-0.181 0.183 0.000" pos="-0.181 0.183 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                    <bone aliases="avatar_mWing4Left" connected="true" end="-0.146 0.132 0.000" group="Wing" name="mWing4Left" pivot="-0.171 0.173 0.000" pos="-0.171 0.173 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                    <bone aliases="avatar_mWing4FanLeft" connected="true" end="-0.068 0.062 -0.159" group="Wing" name="mWing4FanLeft" pivot="-0.171 0.173 0.000" pos="-0.171 0.173 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
                                  </bone>
                               </bone>
                            </bone>
-                           <bone connected="false" end="-0.168 -0.169 0.067" group="Wing" name="mWing1Right" pivot="-0.099 -0.105 0.181" pos="-0.099 -0.105 0.181" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                              <bone connected="true" end="-0.181 -0.183 0.000" group="Wing" name="mWing2Right" pivot="-0.168 -0.169 0.067" pos="-0.168 -0.169 0.067" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                 <bone connected="true" end="-0.171 -0.173 0.000" group="Wing" name="mWing3Right" pivot="-0.181 -0.183 0.000" pos="-0.181 -0.183 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                                    <bone connected="true" end="-0.146 -0.132 0.000" group="Wing" name="mWing4Right" pivot="-0.171 -0.173 0.000" pos="-0.171 -0.173 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-                                    <bone connected="true" end="-0.068 -0.062 -0.159" group="Wing" name="mWing4FanRight" pivot="-0.171 -0.173 0.000" pos="-0.171 -0.173 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                           <bone aliases="avatar_mWing1Right" connected="false" end="-0.168 -0.169 0.067" group="Wing" name="mWing1Right" pivot="-0.099 -0.105 0.181" pos="-0.099 -0.105 0.181" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                              <bone aliases="avatar_mWing2Right" connected="true" end="-0.181 -0.183 0.000" group="Wing" name="mWing2Right" pivot="-0.168 -0.169 0.067" pos="-0.168 -0.169 0.067" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                 <bone aliases="avatar_mWing3Right" connected="true" end="-0.171 -0.173 0.000" group="Wing" name="mWing3Right" pivot="-0.181 -0.183 0.000" pos="-0.181 -0.183 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                                    <bone aliases="avatar_mWing4Right" connected="true" end="-0.146 -0.132 0.000" group="Wing" name="mWing4Right" pivot="-0.171 -0.173 0.000" pos="-0.171 -0.173 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+                                    <bone aliases="avatar_mWing4FanRight" connected="true" end="-0.068 -0.062 -0.159" group="Wing" name="mWing4FanRight" pivot="-0.171 -0.173 0.000" pos="-0.171 -0.173 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
                                  </bone>
                               </bone>
                            </bone>
@@ -200,30 +200,30 @@
             </bone>
          </bone>
       </bone>
-      <bone connected="false" end="-0.197 0.000 0.000" group="Tail" name="mTail1" pivot="-0.116 0.000 0.047" pos="-0.116 0.000 0.047" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-         <bone connected="true" end="-0.168 0.000 0.000" group="Tail" name="mTail2" pivot="-0.197 0.000 0.000" pos="-0.197 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-            <bone connected="true" end="-0.142 0.000 0.000" group="Tail" name="mTail3" pivot="-0.168 0.000 0.000" pos="-0.168 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-               <bone connected="true" end="-0.112 0.000 0.000" group="Tail" name="mTail4" pivot="-0.142 0.000 0.000" pos="-0.142 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                  <bone connected="true" end="-0.094 0.000 0.000" group="Tail" name="mTail5" pivot="-0.112 0.000 0.000" pos="-0.112 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                     <bone connected="true" end="-0.089 0.000 0.000" group="Tail" name="mTail6" pivot="-0.094 0.000 0.000" pos="-0.094 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+      <bone aliases="avatar_mTail1" connected="false" end="-0.197 0.000 0.000" group="Tail" name="mTail1" pivot="-0.116 0.000 0.047" pos="-0.116 0.000 0.047" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+         <bone aliases="avatar_mTail2" connected="true" end="-0.168 0.000 0.000" group="Tail" name="mTail2" pivot="-0.197 0.000 0.000" pos="-0.197 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+            <bone aliases="avatar_mTail3" connected="true" end="-0.142 0.000 0.000" group="Tail" name="mTail3" pivot="-0.168 0.000 0.000" pos="-0.168 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+               <bone aliases="avatar_mTail4" connected="true" end="-0.112 0.000 0.000" group="Tail" name="mTail4" pivot="-0.142 0.000 0.000" pos="-0.142 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                  <bone aliases="avatar_mTail5" connected="true" end="-0.094 0.000 0.000" group="Tail" name="mTail5" pivot="-0.112 0.000 0.000" pos="-0.112 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                     <bone aliases="avatar_mTail6" connected="true" end="-0.089 0.000 0.000" group="Tail" name="mTail6" pivot="-0.094 0.000 0.000" pos="-0.094 0.000 0.000" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
                   </bone>
                </bone>
             </bone>
          </bone>
       </bone>
-      <bone connected="false" end="0.004 0.000 -0.066" group="Groin" name="mGroin" pivot="0.064 0.000 -0.097" pos="0.064 0.000 -0.097" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
-      <bone connected="false" end="-0.204 0.000 0.000" group="Limb" name="mHindLimbsRoot" pivot="-0.200 0.000 0.084" pos="-0.200 0.000 0.084" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-         <bone connected="false" end="0.002 -0.046 -0.491" group="Limb" name="mHindLimb1Left" pivot="-0.204 0.129 -0.125" pos="-0.204 0.129 -0.125" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-            <bone connected="true" end="-0.030 -0.003 -0.468" group="Limb" name="mHindLimb2Left" pivot="0.002 -0.046 -0.491" pos="0.002 -0.046 -0.491" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-               <bone connected="true" end="0.112 0.000 -0.061" group="Limb" name="mHindLimb3Left" pivot="-0.030 -0.003 -0.468" pos="-0.030 -0.003 -0.468" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                  <bone connected="true" end="0.105 0.008 0.000" group="Limb" name="mHindLimb4Left" pivot="0.112 0.000 -0.061" pos="0.112 0.000 -0.061" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+      <bone aliases="avatar_mGroin" connected="false" end="0.004 0.000 -0.066" group="Groin" name="mGroin" pivot="0.064 0.000 -0.097" pos="0.064 0.000 -0.097" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+      <bone aliases="avatar_mHindLimbsRoot" connected="false" end="-0.204 0.000 0.000" group="Limb" name="mHindLimbsRoot" pivot="-0.200 0.000 0.084" pos="-0.200 0.000 0.084" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+         <bone aliases="avatar_mHindLimb1Left" connected="false" end="0.002 -0.046 -0.491" group="Limb" name="mHindLimb1Left" pivot="-0.204 0.129 -0.125" pos="-0.204 0.129 -0.125" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+            <bone aliases="avatar_mHindLimb2Left" connected="true" end="-0.030 -0.003 -0.468" group="Limb" name="mHindLimb2Left" pivot="0.002 -0.046 -0.491" pos="0.002 -0.046 -0.491" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+               <bone aliases="avatar_mHindLimb3Left" connected="true" end="0.112 0.000 -0.061" group="Limb" name="mHindLimb3Left" pivot="-0.030 -0.003 -0.468" pos="-0.030 -0.003 -0.468" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                  <bone aliases="avatar_mHindLimb4Left" connected="true" end="0.105 0.008 0.000" group="Limb" name="mHindLimb4Left" pivot="0.112 0.000 -0.061" pos="0.112 0.000 -0.061" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
                </bone>
             </bone>
          </bone>
-         <bone connected="false" end="0.002 0.046 -0.491" group="Limb" name="mHindLimb1Right" pivot="-0.204 -0.129 -0.125" pos="-0.204 -0.129 -0.125" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-            <bone connected="true" end="-0.030 0.003 -0.468" group="Limb" name="mHindLimb2Right" pivot="0.002 0.046 -0.491" pos="0.002 0.046 -0.491" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-               <bone connected="true" end="0.112 0.000 -0.061" group="Limb" name="mHindLimb3Right" pivot="-0.030 0.003 -0.468" pos="-0.030 0.003 -0.468" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
-                  <bone connected="true" end="0.105 -0.008 0.000" group="Limb" name="mHindLimb4Right" pivot="0.112 0.000 -0.061" pos="0.112 0.000 -0.061" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
+         <bone aliases="avatar_mHindLimb1Right" connected="false" end="0.002 0.046 -0.491" group="Limb" name="mHindLimb1Right" pivot="-0.204 -0.129 -0.125" pos="-0.204 -0.129 -0.125" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+            <bone aliases="avatar_mHindLimb2Right" connected="true" end="-0.030 0.003 -0.468" group="Limb" name="mHindLimb2Right" pivot="0.002 0.046 -0.491" pos="0.002 0.046 -0.491" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+               <bone aliases="avatar_mHindLimb3Right" connected="true" end="0.112 0.000 -0.061" group="Limb" name="mHindLimb3Right" pivot="-0.030 0.003 -0.468" pos="-0.030 0.003 -0.468" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended">
+                  <bone aliases="avatar_mHindLimb4Right" connected="true" end="0.105 -0.008 0.000" group="Limb" name="mHindLimb4Right" pivot="0.112 0.000 -0.061" pos="0.112 0.000 -0.061" rot="0.000 0.000 0.000" scale="1.00 1.00 1.00" support="extended"/>
                </bone>
             </bone>
          </bone>
diff --git a/indra/newview/cube.dae b/indra/newview/cube.dae
new file mode 100644
index 0000000000000000000000000000000000000000..085b2c73099e6bce7e46de084b04fa5666ff3784
--- /dev/null
+++ b/indra/newview/cube.dae
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.1">
+ <asset>
+  <contributor>
+   modified from https://gist.github.com/wtsnz/bfa11c40e04594b260255b5dc7956f26
+  </contributor>
+  <created>2018-10-25T16:29:03Z</created>
+  <modified>2022-02-18T00:00:00Z</modified>
+  <unit meter="1.000000"/>
+  <up_axis>Y_UP</up_axis>
+ </asset>
+
+ <library_materials>
+  <material id="Blue" name="Blue">
+   <instance_effect url="#effect_Blue"/>
+  </material>
+ </library_materials>
+
+
+ <library_effects>
+  <effect id="effect_Blue">
+   <profile_COMMON>
+    <technique sid="common">
+     <phong>
+      <ambient>
+       <color>0 0 0 1</color>
+      </ambient>
+      <diffuse>
+       <color>0.137255 0.403922 0.870588 1</color>
+      </diffuse>
+      <specular>
+       <color>0.5 0.5 0.5 1</color>
+      </specular>
+      <shininess>
+       <float>16</float>
+      </shininess>
+      <transparent opaque="A_ONE">
+       <color>0 0 0 1</color>
+      </transparent>
+      <transparency>
+       <float>1</float>
+      </transparency>
+      <index_of_refraction>
+       <float>1</float>
+      </index_of_refraction>
+     </phong>
+    </technique>
+   </profile_COMMON>
+  </effect>
+ </library_effects>
+
+
+ <library_geometries>
+  <geometry id="F1" name="default_physics_shape">
+   <mesh>
+
+    <source id="cube-vertex-positions">
+     <float_array id="ID2-array" count="72">-0.5 0.5 0.5 -0.5 -0.5 0.5 0.5 -0.5 0.5 0.5 0.5 0.5 -0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 -0.5 -0.5 0.5 -0.5 -0.5 -0.5 -0.5 0.5 -0.5 -0.5 0.5 -0.5 0.5 -0.5 -0.5 0.5 -0.5 0.5 0.5 -0.5 0.5 -0.5 -0.5 -0.5 -0.5 -0.5 -0.5 0.5 0.5 -0.5 0.5 0.5 -0.5 -0.5 0.5 0.5 -0.5 0.5 0.5 0.5 0.5 0.5 -0.5 0.5 -0.5 -0.5 -0.5 -0.5 -0.5 -0.5 0.5 -0.5 </float_array>
+     <technique_common>
+      <accessor source="#ID2-array" count="24" stride="3">
+       <param name="X" type="float"/>
+       <param name="Y" type="float"/>
+       <param name="Z" type="float"/>
+      </accessor>
+     </technique_common>
+    </source>
+
+    <vertices id="cube-vertices">
+     <input semantic="POSITION" source="#cube-vertex-positions"/>
+    </vertices>
+
+    <triangles count="12" material="geometryElement5">
+     <input semantic="VERTEX" offset="0" source="#cube-vertices"/>
+     <p>0 1 2 0 2 3 4 5 6 4 6 7 8 9 10 8 10 11 12 13 14 12 14 15 16 17 18 16 18 19 20 21 22 20 22 23 </p>
+    </triangles>
+    
+   </mesh>
+  </geometry>
+ </library_geometries>
+ <library_visual_scenes>
+
+
+  <visual_scene id="reportScene">
+  <!-- No Spaces allowed in Name -->
+   <node id="F1" name="Face1">
+    <instance_geometry url="#F1">
+     <bind_material>
+      <technique_common>
+       <instance_material symbol="geometryElement5" target="#Blue"/>
+      </technique_common>
+     </bind_material>
+    </instance_geometry>
+   </node>
+  </visual_scene>
+ </library_visual_scenes>
+
+
+ <scene>
+  <instance_visual_scene url="#reportScene"/>
+ </scene>
+
+
+</COLLADA>
diff --git a/indra/newview/installers/windows/installer_template.nsi b/indra/newview/installers/windows/installer_template.nsi
index 019ac4423d36cab0710c7c5e83a8a9d31fe6e768..c782c901c37d4fb4b38fc7470f06a3467295694e 100644
--- a/indra/newview/installers/windows/installer_template.nsi
+++ b/indra/newview/installers/windows/installer_template.nsi
@@ -343,11 +343,29 @@ Call CloseSecondLife			# Make sure Second Life not currently running
 
 ClearErrors
 
+INSTALL_FILES_START:
+
 Call RemoveProgFilesOnInst		# Remove existing files to prevent certain errors when running the new version of the viewer
 
 # This placeholder is replaced by the complete list of all the files in the installer, by viewer_manifest.py
 %%INSTALL_FILES%%
 
+IfErrors 0 INSTALL_FILES_DONE
+  StrCmp $SKIP_DIALOGS "true" INSTALL_FILES_DONE
+	MessageBox MB_ABORTRETRYIGNORE $(ErrorSecondLifeInstallRetry) IDABORT INSTALL_FILES_CANCEL IDRETRY INSTALL_FILES_START
+    # MB_ABORTRETRYIGNORE does not accept IDIGNORE
+    Goto INSTALL_FILES_DONE
+
+INSTALL_FILES_CANCEL:
+  # We are quiting, cleanup.
+  # Silence warnings from RemoveProgFilesOnInst.
+  StrCpy $SKIP_DIALOGS "true"
+  Call RemoveProgFilesOnInst
+  MessageBox MB_OK $(ErrorSecondLifeInstallSupport)
+  Quit
+
+INSTALL_FILES_DONE:
+
 # Pass the installer's language to the client to use as a default
 StrCpy $SHORTCUT_LANG_PARAM "--set InstallLanguage $(LanguageCode)"
 
@@ -574,7 +592,9 @@ Function RemoveProgFilesOnInst
 Push $0
 StrCpy $0 0
 
-PREINSTALLREMOVE:
+ClearErrors
+
+PREINSTALL_REMOVE:
 
 # Remove old SecondLife.exe to invalidate any old shortcuts to it that may be in non-standard locations. See MAINT-3575
 Delete "$INSTDIR\$INSTEXE"
@@ -594,17 +614,17 @@ RMDir /r "$INSTDIR\llplugin"
 
 IntOp $0 $0 + 1
 
-IfErrors 0 PREINSTALLDONE
-  IntCmp $0 1 PREINSTALLREMOVE #try again once
-    StrCmp $SKIP_DIALOGS "true" PREINSTALLDONE
-      MessageBox MB_ABORTRETRYIGNORE $(CloseSecondLifeInstRM) IDABORT PREINSTALLFAIL IDRETRY PREINSTALLREMOVE
+IfErrors 0 PREINSTALL_DONE
+  IntCmp $0 1 PREINSTALL_REMOVE #try again once
+    StrCmp $SKIP_DIALOGS "true" PREINSTALL_DONE
+      MessageBox MB_ABORTRETRYIGNORE $(CloseSecondLifeInstRM) IDABORT PREINSTALL_FAIL IDRETRY PREINSTALL_REMOVE
       # MB_ABORTRETRYIGNORE does not accept IDIGNORE
-      Goto PREINSTALLDONE
+      Goto PREINSTALL_DONE
 
-PREINSTALLFAIL:
+PREINSTALL_FAIL:
     Quit
 
-PREINSTALLDONE:
+PREINSTALL_DONE:
 
 # We are no longer including release notes with the viewer, so remove them.
 Delete "$SMPROGRAMS\$INSTSHORTCUT\SL Release Notes.lnk"
diff --git a/indra/newview/installers/windows/lang_da.nsi b/indra/newview/installers/windows/lang_da.nsi
index 648ddbfb8502e1bee254d26c8c01ce8e40d1026c..73f23086be22efa016f61757a0246ad56a929a6c 100644
Binary files a/indra/newview/installers/windows/lang_da.nsi and b/indra/newview/installers/windows/lang_da.nsi differ
diff --git a/indra/newview/installers/windows/lang_de.nsi b/indra/newview/installers/windows/lang_de.nsi
index 2556a262e234242b8d34bb36555c3a3243cc9dba..f67a09f84c6f81d44099fcfab04729e8af9e0850 100755
--- a/indra/newview/installers/windows/lang_de.nsi
+++ b/indra/newview/installers/windows/lang_de.nsi
@@ -73,6 +73,10 @@ LangString CloseSecondLifeUnInstMB ${LANG_GERMAN} "Second Life kann nicht entfer
 ; CheckNetworkConnection
 LangString CheckNetworkConnectionDP ${LANG_GERMAN} "Prüfe Netzwerkverbindung..."
 
+; error during installation
+LangString ErrorSecondLifeInstallRetry ${LANG_GERMAN} "Second Life konnte nicht korrekt installiert werden, einige Dateien wurden eventuell nicht korrekt von der Installationroutine kopiert."
+LangString ErrorSecondLifeInstallSupport ${LANG_GERMAN} "Bitte laden Sie den Viewer erneut von https://secondlife.com/support/downloads/ und versuchen Sie die Installation erneut. Sollte das Problem weiterhin bestehen, dann kontaktieren Sie unseren Support unter https://support.secondlife.com."
+
 ; ask to remove user's data files
 LangString RemoveDataFilesMB ${LANG_GERMAN} "Möchten Sie alle anderen zu Second Life gehörigen Dateien ebenfalls ENTFERNEN?$\n$\nWir empfehlen, die Einstellungen und Cache-Dateien zu behalten, wenn Sie andere Versionen von Second Life installiert haben oder eine Deinstallation durchführen, um Second Life auf eine neuere Version zu aktualisieren."
 
diff --git a/indra/newview/installers/windows/lang_en-us.nsi b/indra/newview/installers/windows/lang_en-us.nsi
index b924ae39a4ed75fac65a3c2c11a9602a9b6bce23..a375c55541a5f4111899ff3128da4f903fbfbf86 100644
Binary files a/indra/newview/installers/windows/lang_en-us.nsi and b/indra/newview/installers/windows/lang_en-us.nsi differ
diff --git a/indra/newview/installers/windows/lang_es.nsi b/indra/newview/installers/windows/lang_es.nsi
index ee30651a38983aec3b199fb9f7c27adf122d595d..364cc9f67e2f9aa7b05498d848d38b56ab22e429 100755
Binary files a/indra/newview/installers/windows/lang_es.nsi and b/indra/newview/installers/windows/lang_es.nsi differ
diff --git a/indra/newview/installers/windows/lang_fr.nsi b/indra/newview/installers/windows/lang_fr.nsi
index 7cd90ec3148fdd30e01e9c2aee2408511618eb67..2f34c0c87a1552dd0632baf63dd53cc9bf1edf7b 100755
Binary files a/indra/newview/installers/windows/lang_fr.nsi and b/indra/newview/installers/windows/lang_fr.nsi differ
diff --git a/indra/newview/installers/windows/lang_it.nsi b/indra/newview/installers/windows/lang_it.nsi
index 194062da9a8ea42fb5bb5265b974764fdb9d263e..51214d3a9ce88d5a594a6ab02ea3b8f4b09af8e2 100755
Binary files a/indra/newview/installers/windows/lang_it.nsi and b/indra/newview/installers/windows/lang_it.nsi differ
diff --git a/indra/newview/installers/windows/lang_ja.nsi b/indra/newview/installers/windows/lang_ja.nsi
index a54005ba1466c4e82247fa46d144ad9037b00262..296703d1a3e94080fe18709d1e5ac3031887cde7 100755
Binary files a/indra/newview/installers/windows/lang_ja.nsi and b/indra/newview/installers/windows/lang_ja.nsi differ
diff --git a/indra/newview/installers/windows/lang_pl.nsi b/indra/newview/installers/windows/lang_pl.nsi
index 355d806866959266519b0c3069ac600ff23a0c29..299645bbb78010298d43366cfa5dd3299081a096 100644
Binary files a/indra/newview/installers/windows/lang_pl.nsi and b/indra/newview/installers/windows/lang_pl.nsi differ
diff --git a/indra/newview/installers/windows/lang_pt-br.nsi b/indra/newview/installers/windows/lang_pt-br.nsi
index 97f5d2b44a946ea8d58ae8eec42a1a966d4d30a3..542c8654b5bffbe4d6f57a90c878c329ce7eb997 100755
Binary files a/indra/newview/installers/windows/lang_pt-br.nsi and b/indra/newview/installers/windows/lang_pt-br.nsi differ
diff --git a/indra/newview/installers/windows/lang_ru.nsi b/indra/newview/installers/windows/lang_ru.nsi
index 65a9f4846d42a2331b25181fe99750e6e7e7682c..4e53a4957d49d2cf9b11df0b1ac0849a3101dfaa 100755
Binary files a/indra/newview/installers/windows/lang_ru.nsi and b/indra/newview/installers/windows/lang_ru.nsi differ
diff --git a/indra/newview/installers/windows/lang_tr.nsi b/indra/newview/installers/windows/lang_tr.nsi
index e71886cc6609f89a090c31a2c5d9c6cb714dc907..bae5029ad1ce56a2fd347c95de656848400c052e 100755
Binary files a/indra/newview/installers/windows/lang_tr.nsi and b/indra/newview/installers/windows/lang_tr.nsi differ
diff --git a/indra/newview/installers/windows/lang_zh.nsi b/indra/newview/installers/windows/lang_zh.nsi
index f5f0c6cbdf6539d8683151de663a23a58ede11c1..7922d9df5228d652bf08955f00c0eb62b6794965 100755
Binary files a/indra/newview/installers/windows/lang_zh.nsi and b/indra/newview/installers/windows/lang_zh.nsi differ
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index 0562f8ee389b114fa14e6dd0320fef0e94ae89c2..f4bf92b1bb59c2e78c513463172c1f6b29b79704 100644
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -99,6 +99,7 @@
 #include "llworldmap.h"
 #include "stringize.h"
 #include "llcorehttputil.h"
+#include "lluiusage.h"
 // [RLVa:KB] - Checked: 2011-11-04 (RLVa-1.4.4a)
 #include "rlvactions.h"
 #include "rlvhandler.h"
@@ -602,6 +603,8 @@ void LLAgent::ageChat()
 //-----------------------------------------------------------------------------
 void LLAgent::moveAt(S32 direction, bool reset)
 {
+	LLUIUsage::instance().logCommand("Agent.MoveAt");
+	
 	mMoveTimer.reset();
 	LLFirstUse::notMoving(false);
 
@@ -4294,6 +4297,7 @@ void LLAgent::startTeleportRequest()
     }
 	if (hasPendingTeleportRequest())
 	{
+		LLUIUsage::instance().logCommand("Agent.StartTeleportRequest");
         mTeleportCanceled.reset();
 		if  (!isMaturityPreferenceSyncedWithServer())
 		{
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
index fc63ec1397721a8f57b15e49ffb4990499ef5d66..e2a36c9269a7cb661269f5b45c997a7fb79eaab6 100644
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -58,6 +58,7 @@
 #include "llappviewer.h"
 #include "llcoros.h"
 #include "lleventcoro.h"
+#include "lluiusage.h"
 // [RLVa:KB] - Checked: 2011-05-22 (RLVa-1.3.1)
 #include "rlvactions.h"
 #include "rlvhandler.h"
@@ -1479,6 +1480,9 @@ void LLAppearanceMgr::wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear,
                                         bool replace,
                                         LLPointer<LLInventoryCallback> cb)
 {
+	LL_DEBUGS("UIUsage") << "wearItemsOnAvatar" << LL_ENDL;
+	LLUIUsage::instance().logCommand("Avatar.WearItem");
+
     bool first = true;
 
     LLInventoryObject::const_object_list_t items_to_link;
@@ -3087,6 +3091,7 @@ void LLAppearanceMgr::wearInventoryCategoryOnAvatar( LLInventoryCategory* catego
 
 	LL_INFOS("Avatar") << self_av_string() << "wearInventoryCategoryOnAvatar '" << category->getName()
 			 << "'" << LL_ENDL;
+	LLUIUsage::instance().logCommand("Avatar.WearCategory");
 			 	
 	if (gAgentCamera.cameraCustomizeAvatar())
 	{
@@ -4414,6 +4419,8 @@ void LLAppearanceMgr::makeNewOutfitLinks(const std::string& new_folder_name, boo
 {
 	if (!isAgentAvatarValid()) return;
 
+	LLUIUsage::instance().logCommand("Avatar.CreateNewOutfit");
+
 	LL_DEBUGS("Avatar") << "creating new outfit" << LL_ENDL;
 
 	gAgentWearables.notifyLoadingStarted();
@@ -4455,6 +4462,9 @@ void LLAppearanceMgr::wearBaseOutfit()
 void LLAppearanceMgr::removeItemsFromAvatar(const uuid_vec_t& ids_to_remove, LLPointer<LLInventoryCallback> cb /*= NULL*/, bool immediate_delete /*= false*/)
 // [/SL:KB]
 {
+	LL_DEBUGS("UIUsage") << "removeItemsFromAvatar" << LL_ENDL;
+	LLUIUsage::instance().logCommand("Avatar.RemoveItem");
+
 	if (ids_to_remove.empty())
 	{
 		LL_WARNS() << "called with empty list, nothing to do" << LL_ENDL;
@@ -4972,6 +4982,8 @@ class LLWearFolderHandler : public LLCommandHandler
 																			  "Quick Appearance");
 			if ( gInventory.getCategory( folder_uuid ) != NULL )
 			{
+				// Assume this is coming from the predefined avatars web floater
+				LLUIUsage::instance().logCommand("Avatar.WearPredefinedAppearance");
 				LLAppearanceMgr::getInstance()->wearInventoryCategory(category, true, false);
 				
 				// *TODOw: This may not be necessary if initial outfit is chosen already -- josh
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 2036fd708e433deaec7cccc6880fefd301e8ed8e..9bf3831089bdcfd2f710d350e43c8c712be02b86 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -572,7 +572,7 @@ static void settings_to_globals()
 
 	gDebugWindowProc = gSavedSettings.getBOOL("DebugWindowProc");
 	gShowObjectUpdates = gSavedSettings.getBOOL("ShowObjectUpdates");
-	LLWorldMapView::sMapScale = gSavedSettings.getF32("MapScale");
+    LLWorldMapView::setScaleSetting(gSavedSettings.getF32("MapScale"));
 	
 #if LL_DARWIN
 	gHiDPISupport = gSavedSettings.getBOOL("RenderHiDPI");
@@ -2501,10 +2501,24 @@ bool LLAppViewer::initConfiguration()
 	//Load settings files list
 	std::string settings_file_list = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "settings_files.xml");
 	LLXMLNodePtr root;
-	BOOL success  = LLXMLNode::parseFile(settings_file_list, root, NULL);
+	BOOL success = LLXMLNode::parseFile(settings_file_list, root, NULL);
 	if (!success)
 	{
-        LL_ERRS() << "Cannot load default configuration file " << settings_file_list << LL_ENDL;
+        LL_WARNS() << "Cannot load default configuration file " << settings_file_list << LL_ENDL;
+        if (gDirUtilp->fileExists(settings_file_list))
+        {
+            LL_ERRS() << "Cannot load default configuration file settings_files.xml. "
+                << "Please reinstall viewer from https://secondlife.com/support/downloads/ "
+                << "and contact https://support.secondlife.com if issue persists after reinstall."
+                << LL_ENDL;
+        }
+        else
+        {
+            LL_ERRS() << "Default configuration file settings_files.xml not found. "
+                << "Please reinstall viewer from https://secondlife.com/support/downloads/ "
+                << "and contact https://support.secondlife.com if issue persists after reinstall."
+                << LL_ENDL;
+        }
 	}
 
 	mSettingsLocationList = new SettingsFiles();
@@ -3528,7 +3542,7 @@ void LLAppViewer::cleanupSavedSettings()
 		}
 	}
 
-	gSavedSettings.setF32("MapScale", LLWorldMapView::sMapScale );
+    gSavedSettings.setF32("MapScale", LLWorldMapView::getScaleSetting());
 
 	// Some things are cached in LLAgent.
 	if (gAgent.isInitialized())
@@ -4394,7 +4408,6 @@ bool LLAppViewer::initCache()
 	LLSplashScreen::update(LLTrans::getString("StartupInitializingTextureCache"));
 
 	// Init the texture cache
-	// Allocate 80% of the cache size for textures
 	const S32 MB = 1024 * 1024;
 	const S64 MIN_CACHE_SIZE = 512 * MB;
 	const S64 MAX_CACHE_SIZE = 9984ll * MB;
@@ -4404,7 +4417,7 @@ bool LLAppViewer::initCache()
 
 	LLAppViewer::getTextureCache()->initCache(LL_PATH_CACHE, texture_cache_size, texture_cache_mismatch);
 
-		return true;
+    return true;
 }
 
 void LLAppViewer::addOnIdleCallback(const boost::function<void()>& cb)
@@ -5094,8 +5107,7 @@ void LLAppViewer::idle()
 			audio_update_wind(false);
 
 			// this line actually commits the changes we've made to source positions, etc.
-			const F32 max_audio_decode_time = 0.002f; // 2 ms decode time
-			gAudiop->idle(max_audio_decode_time);
+			gAudiop->idle();
 		}
 	}
 
diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp
index c6efe4edb9ed14fec11f8f47f2cba9da8c35ef15..808eb2bf00773a6b9c19f80b02fa88ef7d26de0b 100644
--- a/indra/newview/llavataractions.cpp
+++ b/indra/newview/llavataractions.cpp
@@ -78,6 +78,7 @@
 #include "llsidepanelinventory.h"
 #include "llavatarname.h"
 #include "llagentui.h"
+#include "lluiusage.h"
 // [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0)
 #include "rlvactions.h"
 #include "rlvcommon.h"
@@ -119,7 +120,7 @@ void LLAvatarActions::requestFriendshipDialog(const LLUUID& id, const std::strin
 	payload["id"] = id;
 	payload["name"] = name;
     
-    	LLNotificationsUtil::add("AddFriendWithMessage", args, payload, &callbackAddFriendWithMessage);
+	LLNotificationsUtil::add("AddFriendWithMessage", args, payload, &callbackAddFriendWithMessage);
 
 	// add friend to recent people list
 	LLRecentPeople::instance().add(id);
@@ -1524,6 +1525,8 @@ bool LLAvatarActions::handleUnfreeze(const LLSD& notification, const LLSD& respo
 void LLAvatarActions::requestFriendship(const LLUUID& target_id, const std::string& target_name, const std::string& message)
 {
 	const LLUUID calling_card_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD);
+	LLUIUsage::instance().logCommand("Agent.SendFriendRequest");
+
 	send_improved_im(target_id,
 					 target_name,
 					 message,
diff --git a/indra/newview/llcallingcard.cpp b/indra/newview/llcallingcard.cpp
index da980d0eb64df5c81836e194d0153e134c2411ab..3d343d24cf554bf307df693ec1278b224cff532f 100644
--- a/indra/newview/llcallingcard.cpp
+++ b/indra/newview/llcallingcard.cpp
@@ -49,6 +49,7 @@
 #include "llviewerobjectlist.h"
 #include "llvoavatar.h"
 #include "llavataractions.h"
+#include "lluiusage.h"
 
 ///----------------------------------------------------------------------------
 /// Local function declarations, constants, enums, and typedefs
@@ -292,6 +293,8 @@ void LLAvatarTracker::copyBuddyList(buddy_map_t& buddies) const
 void LLAvatarTracker::terminateBuddy(const LLUUID& id)
 {
 	LL_DEBUGS() << "LLAvatarTracker::terminateBuddy()" << LL_ENDL;
+	LLUIUsage::instance().logCommand("Agent.TerminateFriendship");
+
 	LLRelationship* buddy = get_ptr_in_map(mBuddyInfo, id);
 	if(!buddy) return;
 	mBuddyInfo.erase(id);
diff --git a/indra/newview/llcolorswatch.cpp b/indra/newview/llcolorswatch.cpp
index 80d810d1591a027b68d483afe7cc6e354a0523b2..036ff170743d90125f4eeca6f8b2cd4c0b842c09 100644
--- a/indra/newview/llcolorswatch.cpp
+++ b/indra/newview/llcolorswatch.cpp
@@ -290,9 +290,14 @@ void LLColorSwatchCtrl::onColorChanged ( void* data, EColorPickOp pick_op )
 									pickerp->getCurG (), 
 									pickerp->getCurB (), 
 									subject->mColor.mV[VALPHA] ); // keep current alpha
-			subject->mColor = updatedColor;
-			subject->setControlValue(updatedColor.getValue());
-			pickerp->setRevertOnCancel(TRUE);
+
+            bool color_changed = subject->mColor != updatedColor;
+            if (color_changed)
+            {
+                subject->mColor = updatedColor;
+                subject->setControlValue(updatedColor.getValue());
+            }
+
 			if (pick_op == COLOR_CANCEL && subject->mOnCancelCallback)
 			{
 				subject->mOnCancelCallback( subject, LLSD());
@@ -306,6 +311,13 @@ void LLColorSwatchCtrl::onColorChanged ( void* data, EColorPickOp pick_op )
 				// just commit change
 				subject->onCommit ();
 			}
+
+            if (pick_op == COLOR_CANCEL || pick_op == COLOR_SELECT)
+            {
+                // both select and cancel close LLFloaterColorPicker
+                // but COLOR_CHANGE does not
+                subject->setFocus(TRUE);
+            }
 		}
 	}
 }
diff --git a/indra/newview/lldrawable.h b/indra/newview/lldrawable.h
index 4fa24211ce048e79c3eab253b4312ddde83e1a54..ed1d29cdef414283d974383a8c51ce8032b4dd1c 100644
--- a/indra/newview/lldrawable.h
+++ b/indra/newview/lldrawable.h
@@ -124,6 +124,7 @@ class LLDrawable
 	inline LLFace*      getFace(const S32 i) const;
 	inline S32			getNumFaces()      	 const;
     face_list_t& getFaces() { return mFaces; }
+    const face_list_t& getFaces() const { return mFaces; }
 
 	//void                removeFace(const S32 i); // SJB: Avoid using this, it's slow
 	LLFace*				addFace(LLFacePool *poolp, LLViewerTexture *texturep);
diff --git a/indra/newview/lldrawpoolalpha.cpp b/indra/newview/lldrawpoolalpha.cpp
index 655b79f26b36f64e847b57f38cb089357b9615e6..cc8a9d9dbc65d9b3a1bb2069bbad15569dff9a4d 100644
--- a/indra/newview/lldrawpoolalpha.cpp
+++ b/indra/newview/lldrawpoolalpha.cpp
@@ -58,6 +58,7 @@ static BOOL deferred_render = FALSE;
 
 // minimum alpha before discarding a fragment
 static const F32 MINIMUM_ALPHA = 0.004f; // ~ 1/255
+
 // minimum alpha before discarding a fragment when rendering impostors
 static const F32 MINIMUM_IMPOSTOR_ALPHA = 0.1f;
 
@@ -147,6 +148,10 @@ void LLDrawPoolAlpha::renderPostDeferred(S32 pass)
         (LLPipeline::sUnderWaterRender) ? &gDeferredAlphaWaterProgram : &gDeferredAlphaProgram;
     prepare_alpha_shader(simple_shader, false, true); //prime simple shader (loads shadow relevant uniforms)
 
+    for (int i = 0; i < LLMaterial::SHADER_COUNT; ++i)
+    {
+        prepare_alpha_shader(LLPipeline::sUnderWaterRender ? &gDeferredMaterialWaterProgram[i] : &gDeferredMaterialProgram[i], false, false); // note: bindDeferredShader will get called during render loop for materials
+    }
 
     // first pass, render rigged objects only and render to depth buffer
     forwardRender(true);
@@ -215,9 +220,15 @@ void LLDrawPoolAlpha::render(S32 pass)
     {
         minimum_alpha = MINIMUM_IMPOSTOR_ALPHA;
     }
+
     prepare_forward_shader(fullbright_shader, minimum_alpha);
     prepare_forward_shader(simple_shader, minimum_alpha);
 
+    for (int i = 0; i < LLMaterial::SHADER_COUNT; ++i)
+    {
+        prepare_forward_shader(LLPipeline::sUnderWaterRender ? &gDeferredMaterialWaterProgram[i] : &gDeferredMaterialProgram[i], minimum_alpha);
+    }
+
     //first pass -- rigged only and drawn to depth buffer
     forwardRender(true);
 
diff --git a/indra/newview/lldynamictexture.cpp b/indra/newview/lldynamictexture.cpp
index 97385d0af3ee5d0169990452daa8a32769e31915..05b60718b6fcdf63d79bda8666b4994ea65ac491 100644
--- a/indra/newview/lldynamictexture.cpp
+++ b/indra/newview/lldynamictexture.cpp
@@ -125,7 +125,7 @@ void LLViewerDynamicTexture::preRender(BOOL clear_depth)
 		llassert(mFullHeight <= static_cast<S32>(gPipeline.mBake.getHeight()));
 	}
 
-	if (gGLManager.mHasFramebufferObject && gPipeline.mBake.isComplete())
+	if (gPipeline.mBake.isComplete())
 	{ //using offscreen render target, just use the bottom left corner
 		mOrigin.set(0, 0);
 	}
@@ -212,7 +212,7 @@ BOOL LLViewerDynamicTexture::updateAllInstances()
 		return TRUE;
 	}
 
-	bool use_fbo = gGLManager.mHasFramebufferObject && gPipeline.mBake.isComplete();
+	bool use_fbo = gPipeline.mBake.isComplete();
 
 	if (use_fbo)
 	{
diff --git a/indra/newview/llfasttimerview.cpp b/indra/newview/llfasttimerview.cpp
index 54d023ec45ad95110c5b26fdfc21812f762052bb..ede8e3e0fbf2e49e9f1c8bc7a53ae241b0d4b3da 100644
--- a/indra/newview/llfasttimerview.cpp
+++ b/indra/newview/llfasttimerview.cpp
@@ -521,20 +521,6 @@ void LLFastTimerView::exportCharts(const std::string& base, const std::string& t
 		is.close();
 	}
 
-	//get time domain
-	LLSD::Real cur_total_time = 0.0;
-
-	for (U32 i = 0; i < cur_data.size(); ++i)
-	{
-		cur_total_time += cur_data[i]["Total"]["Time"].asReal();
-	}
-
-	LLSD::Real base_total_time = 0.0;
-	for (U32 i = 0; i < base_data.size(); ++i)
-	{
-		base_total_time += base_data[i]["Total"]["Time"].asReal();
-	}
-
 	//allocate raw scratch space
 	LLPointer<LLImageRaw> scratch = new LLImageRaw(1024, 512, 3);
 
diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp
index 682dd296cd2f1c14b9eb5fa5bf1e8c030e277a92..815db449cf9fa31ae0ece17611ebdcc543bd89a6 100644
--- a/indra/newview/llfavoritesbar.cpp
+++ b/indra/newview/llfavoritesbar.cpp
@@ -471,7 +471,25 @@ BOOL LLFavoritesBarCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
 			}
 
 			// check if we are dragging an existing item from the favorites bar
-			if (item && mDragItemId == item->getUUID())
+            bool existing_drop = false;
+            if (item && mDragItemId == item->getUUID())
+            {
+                // There is a chance of mDragItemId being obsolete
+                // ex: can happen if something interrupts viewer, which
+                // results in viewer not geting a 'mouse up' signal
+                for (LLInventoryModel::item_array_t::iterator i = mItems.begin(); i != mItems.end(); ++i)
+                {
+                    LLViewerInventoryItem* currItem = *i;
+
+                    if (currItem->getUUID() == mDragItemId)
+                    {
+                        existing_drop = true;
+                        break;
+                    }
+                }
+            }
+
+            if (existing_drop)
 			{
 				*accept = ACCEPT_YES_SINGLE;
 
@@ -500,6 +518,7 @@ BOOL LLFavoritesBarCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
 					if (mItems.empty())
 					{
 						setLandingTab(NULL);
+                        mLastTab = NULL;
 					}
 					handleNewFavoriteDragAndDrop(item, favorites_id, x, y);
 				}
@@ -515,6 +534,12 @@ BOOL LLFavoritesBarCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
 
 void LLFavoritesBarCtrl::handleExistingFavoriteDragAndDrop(S32 x, S32 y)
 {
+    if (mItems.empty())
+    {
+        // Isn't supposed to be empty
+        return;
+    }
+
 	// Identify the button hovered and the side to drop
 	LLFavoriteLandmarkButton* dest = dynamic_cast<LLFavoriteLandmarkButton*>(mLandingTab);
 	bool insert_before = true;	
@@ -787,6 +812,7 @@ void LLFavoritesBarCtrl::updateButtons(bool force_update)
 	if(mItems.empty())
 	{
 		mBarLabel->setVisible(TRUE);
+        mLastTab = NULL;
 	}
 	else
 	{
@@ -833,6 +859,10 @@ void LLFavoritesBarCtrl::updateButtons(bool force_update)
 					dynamic_cast<LLFavoriteLandmarkButton*> (*cur_it);
 			if (button)
 			{
+                if (mLastTab == button)
+                {
+                    mLastTab = NULL;
+                }
 				removeChild(button);
 				delete button;
 			}
@@ -869,6 +899,17 @@ void LLFavoritesBarCtrl::updateButtons(bool force_update)
 
 			mLastTab = last_new_button;
 		}
+        if (!mLastTab && mItems.size() > 0)
+        {
+            // mMoreTextBox was removed, so LLFavoriteLandmarkButtons
+            // should be the only ones in the list
+            LLFavoriteLandmarkButton* button = dynamic_cast<LLFavoriteLandmarkButton*> (childs->back());
+            if (button)
+            {
+                mLastTab = button;
+            }
+        }
+
 		mFirstDropDownItem = j;
 		// Chevron button
 		if (mFirstDropDownItem < mItems.size())
diff --git a/indra/newview/llfloaterbvhpreview.cpp b/indra/newview/llfloaterbvhpreview.cpp
index 11ae15729cdf0fc34526f3f488e884e4e3e5c884..fa344d63f054d0fc4bcf686c4a7e82f3837e68e4 100644
--- a/indra/newview/llfloaterbvhpreview.cpp
+++ b/indra/newview/llfloaterbvhpreview.cpp
@@ -259,7 +259,7 @@ BOOL LLFloaterBvhPreview::postBuild()
 		loaderp->serialize(dp);
 		dp.reset();
 		LL_INFOS("BVH") << "Deserializing motionp" << LL_ENDL;
-		BOOL success = motionp && motionp->deserialize(dp, mMotionID);
+		BOOL success = motionp && motionp->deserialize(dp, mMotionID, false);
 		LL_INFOS("BVH") << "Done" << LL_ENDL;
 
 		delete []buffer;
diff --git a/indra/newview/llfloatercolorpicker.cpp b/indra/newview/llfloatercolorpicker.cpp
index 4f6af31a735cd215f71f88deffcdd9d902e8ffe6..373ef57fa5e89fd6aae8e4a31b43989ea4db6ab9 100644
--- a/indra/newview/llfloatercolorpicker.cpp
+++ b/indra/newview/llfloatercolorpicker.cpp
@@ -167,7 +167,6 @@ void LLFloaterColorPicker::showUI ()
 	openFloater(getKey());
 	setVisible ( TRUE );
 	setFocus ( TRUE );
-	setRevertOnCancel(FALSE);
 
 	// HACK: if system color picker is required - close the SL one we made and use default system dialog
 	if ( gSavedSettings.getBOOL ( "UseDefaultColorPicker" ) )
@@ -179,15 +178,23 @@ void LLFloaterColorPicker::showUI ()
 		// code that will get switched in for default system color picker
 		if ( swatch )
 		{
+            // Todo: this needs to be threaded for viewer not to timeout
 			LLColor4 curCol = swatch->get ();
 			send_agent_pause();
-			getWindow()->dialogColorPicker( &curCol [ 0 ], &curCol [ 1 ], &curCol [ 2 ] );
+			bool commit = getWindow()->dialogColorPicker( &curCol [ 0 ], &curCol [ 1 ], &curCol [ 2 ] );
 			send_agent_resume();
 
-			setOrigRgb ( curCol [ 0 ], curCol [ 1 ], curCol [ 2 ] );
-			setCurRgb( curCol [ 0 ], curCol [ 1 ], curCol [ 2 ] );
-
-			LLColorSwatchCtrl::onColorChanged ( swatch, LLColorSwatchCtrl::COLOR_CHANGE );
+            if (commit)
+            {
+                setOrigRgb(curCol[0], curCol[1], curCol[2]);
+                setCurRgb(curCol[0], curCol[1], curCol[2]);
+
+                LLColorSwatchCtrl::onColorChanged(swatch, LLColorSwatchCtrl::COLOR_SELECT);
+            }
+            else
+            {
+                LLColorSwatchCtrl::onColorChanged(swatch, LLColorSwatchCtrl::COLOR_CANCEL);
+            }
 		}
 
 		closeFloater();
@@ -391,10 +398,7 @@ void LLFloaterColorPicker::onClickCancel ( void* data )
 
 		if ( self )
 		{
-		    if(self->getRevertOnCancel())
-		    {
-		        self->cancelSelection ();
-		    }
+		    self->cancelSelection();
 			self->closeFloater();
 		}
 	}
diff --git a/indra/newview/llfloatercolorpicker.h b/indra/newview/llfloatercolorpicker.h
index 816d501f900749e5e8932faff773b6d6425f8b0e..9d845a96090c199aaf80de5106bd5994ab93310a 100644
--- a/indra/newview/llfloatercolorpicker.h
+++ b/indra/newview/llfloatercolorpicker.h
@@ -104,9 +104,6 @@ class LLFloaterColorPicker final
 		void setMouseDownInSwatch (BOOL mouse_down_in_swatch);
 		BOOL getMouseDownInSwatch () { return mMouseDownInSwatch; }
 
-		void setRevertOnCancel (BOOL revertOnCancel) { mRevertOnCancel = revertOnCancel; };
-		BOOL getRevertOnCancel () { return mRevertOnCancel; }
-
 		BOOL isColorChanged ();
 
 		// called when text entries (RGB/HSL etc.) are changed by user
@@ -149,8 +146,6 @@ class LLFloaterColorPicker final
 		BOOL mMouseDownInHueRegion;
 		BOOL mMouseDownInSwatch;
 
-		BOOL mRevertOnCancel;
-
 		const S32 mRGBViewerImageLeft;
 		const S32 mRGBViewerImageTop;
 		const S32 mRGBViewerImageWidth;
diff --git a/indra/newview/llfloatercreatelandmark.cpp b/indra/newview/llfloatercreatelandmark.cpp
index e2222bb82631567bb8e38e80d453f3f1689b70dc..68f5ab143b2e18adba14ba210bf6a44ba7e14a3e 100644
--- a/indra/newview/llfloatercreatelandmark.cpp
+++ b/indra/newview/llfloatercreatelandmark.cpp
@@ -46,19 +46,60 @@
 
 typedef std::pair<LLUUID, std::string> folder_pair_t;
 
-class LLLandmarksInventoryObserver : public LLInventoryAddedObserver
+class LLLandmarksInventoryObserver : public LLInventoryObserver
 {
 public:
 	LLLandmarksInventoryObserver(LLFloaterCreateLandmark* create_landmark_floater) :
 		mFloater(create_landmark_floater)
 	{}
 
+    void changed(U32 mask) override
+    {
+        if (mFloater->getItem())
+        {
+            checkChanged(mask);
+        }
+        else
+        {
+            checkCreated(mask);
+        }
+    }
+
 protected:
-	/*virtual*/ void done()
+	void checkCreated(U32 mask)
 	{
+        if (gInventory.getAddedIDs().empty())
+        {
+            return;
+        }
+
+        if (!(mask & LLInventoryObserver::ADD) ||
+            !(mask & LLInventoryObserver::CREATE) ||
+            !(mask & LLInventoryObserver::UPDATE_CREATE))
+        {
+            return;
+        }
+
 		mFloater->setItem(gInventory.getAddedIDs());
 	}
 
+    void checkChanged(U32 mask)
+    {
+        if (gInventory.getChangedIDs().empty())
+        {
+            return;
+        }
+
+        if ((mask & LLInventoryObserver::LABEL) ||
+            (mask & LLInventoryObserver::INTERNAL) ||
+            (mask & LLInventoryObserver::REMOVE) ||
+            (mask & LLInventoryObserver::STRUCTURE) ||
+            (mask & LLInventoryObserver::REBUILD))
+        {
+            mFloater->updateItem(gInventory.getChangedIDs(), mask);
+        }
+    }
+
 private:
 	LLFloaterCreateLandmark* mFloater;
 };
@@ -90,6 +131,9 @@ BOOL LLFloaterCreateLandmark::postBuild()
 	getChild<LLButton>("ok_btn")->setClickedCallback(boost::bind(&LLFloaterCreateLandmark::onSaveClicked, this));
 	getChild<LLButton>("cancel_btn")->setClickedCallback(boost::bind(&LLFloaterCreateLandmark::onCancelClicked, this));
 
+    mLandmarkTitleEditor->setCommitCallback([this](LLUICtrl* ctrl, const LLSD& param) { onCommitTextChanges(); });
+    mNotesEditor->setCommitCallback([this](LLUICtrl* ctrl, const LLSD& param) { onCommitTextChanges(); });
+
 	mLandmarksID = gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK);
 
 	return TRUE;
@@ -219,6 +263,33 @@ void LLFloaterCreateLandmark::populateFoldersList(const LLUUID &folder_id)
 	}
 }
 
+void LLFloaterCreateLandmark::onCommitTextChanges()
+{
+    if (mItem.isNull())
+    {
+        return;
+    }
+    std::string current_title_value = mLandmarkTitleEditor->getText();
+    std::string item_title_value = mItem->getName();
+    std::string current_notes_value = mNotesEditor->getText();
+    std::string item_notes_value = mItem->getDescription();
+
+    LLStringUtil::trim(current_title_value);
+    LLStringUtil::trim(current_notes_value);
+
+    if (!current_title_value.empty() &&
+        (item_title_value != current_title_value || item_notes_value != current_notes_value))
+    {
+        LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(mItem);
+        new_item->rename(current_title_value);
+        new_item->setDescription(current_notes_value);
+        LLPointer<LLInventoryCallback> cb;
+        LLInventoryModel::LLCategoryUpdate up(mItem->getParentUUID(), 0);
+        gInventory.accountForUpdate(up);
+        update_inventory_item(new_item, cb);
+    }
+}
+
 void LLFloaterCreateLandmark::onCreateFolderClicked()
 {
 	LLNotificationsUtil::add("CreateLandmarkFolder", LLSD(), LLSD(),
@@ -292,6 +363,8 @@ void LLFloaterCreateLandmark::onSaveClicked()
 		new_item->updateParentOnServer(FALSE);
 	}
 
+    removeObserver();
+
 	gInventory.updateItem(new_item);
 	gInventory.notifyObservers();
 
@@ -302,6 +375,7 @@ void LLFloaterCreateLandmark::onSaveClicked()
 
 void LLFloaterCreateLandmark::onCancelClicked()
 {
+    removeObserver();
 	if (!mItem.isNull())
 	{
 		LLUUID item_id = mItem->getUUID();
@@ -331,10 +405,59 @@ void LLFloaterCreateLandmark::setItem(const uuid_set_t& items)
 		{
 			if(!getItem())
 			{
-				removeObserver();
 				mItem = item;
+                mAssetID = mItem->getAssetUUID();
+                setVisibleAndFrontmost(true);
 				break;
 			}
 		}
 	}
 }
+
+void LLFloaterCreateLandmark::updateItem(const uuid_set_t& items, U32 mask)
+{
+    if (!getItem())
+    {
+        return;
+    }
+
+    LLUUID landmark_id = getItem()->getUUID();
+
+    for (uuid_set_t::const_iterator item_iter = items.begin();
+        item_iter != items.end();
+        ++item_iter)
+    {
+        const LLUUID& item_id = (*item_iter);
+        if (landmark_id == item_id)
+        {
+            if (getItem() != gInventory.getItem(item_id))
+            {
+                // item is obsolete or removed
+                closeFloater();
+            }
+
+            LLUUID folder_id = mFolderCombo->getValue().asUUID();
+            if (folder_id != mItem->getParentUUID())
+            {
+                // user moved landmark in inventory,
+                // assume that we are done all other changes should already be commited
+                closeFloater();
+            }
+
+            if ((mask & LLInventoryObserver::INTERNAL) && mAssetID != mItem->getAssetUUID())
+            {
+                closeFloater();
+            }
+
+            if (mask & LLInventoryObserver::LABEL)
+            {
+                mLandmarkTitleEditor->setText(mItem->getName());
+            }
+
+            if (mask & LLInventoryObserver::INTERNAL)
+            {
+                mNotesEditor->setText(mItem->getDescription());
+            }
+        }
+    }
+}
diff --git a/indra/newview/llfloatercreatelandmark.h b/indra/newview/llfloatercreatelandmark.h
index 64d8fb250e99f0533c69124c21e2a38996990418..7896ad9e65d91057abc663221fb00c1edeb80943 100644
--- a/indra/newview/llfloatercreatelandmark.h
+++ b/indra/newview/llfloatercreatelandmark.h
@@ -50,6 +50,7 @@ class LLFloaterCreateLandmark final :
 	void onClose(bool app_quitting) override;
 
 	void setItem(const uuid_set_t& items);
+    void updateItem(const uuid_set_t& items, U32 mask);
 
 	LLInventoryItem* getItem() { return mItem; }
 
@@ -57,6 +58,7 @@ class LLFloaterCreateLandmark final :
 	void setLandmarkInfo(const LLUUID &folder_id);
 	void removeObserver();
 	void populateFoldersList(const LLUUID &folder_id = LLUUID::null);
+    void onCommitTextChanges();
 	void onCreateFolderClicked();
 	void onSaveClicked();
 	void onCancelClicked();
@@ -67,6 +69,7 @@ class LLFloaterCreateLandmark final :
 	LLLineEditor*	mLandmarkTitleEditor;
 	LLTextEditor*	mNotesEditor;
 	LLUUID			mLandmarksID;
+    LLUUID			mAssetID;
 
 	LLLandmarksInventoryObserver*	mInventoryObserver;
 	LLPointer<LLInventoryItem>		mItem;
diff --git a/indra/newview/llfloatereditextdaycycle.cpp b/indra/newview/llfloatereditextdaycycle.cpp
index 24673d5a7c70796500c968c3a57aeb8c5c4a7982..297ad243595697e0b0902bc6baa59a39079b05ea 100644
--- a/indra/newview/llfloatereditextdaycycle.cpp
+++ b/indra/newview/llfloatereditextdaycycle.cpp
@@ -665,6 +665,7 @@ void LLFloaterEditExtDayCycle::onButtonApply(LLUICtrl *ctrl, const LLSD &data)
     if (ctrl_action == ACTION_SAVE)
     {
         doApplyUpdateInventory(dayclone);
+        clearDirtyFlag();
     }
     else if (ctrl_action == ACTION_SAVEAS)
     {
diff --git a/indra/newview/llfloaterfixedenvironment.cpp b/indra/newview/llfloaterfixedenvironment.cpp
index fec218ca3b4b905cb575daea7998fffdceacf7b5..aa9a2c164a2b897a0d7f720f0685bcd432cf3ccb 100644
--- a/indra/newview/llfloaterfixedenvironment.cpp
+++ b/indra/newview/llfloaterfixedenvironment.cpp
@@ -296,6 +296,7 @@ void LLFloaterFixedEnvironment::onButtonApply(LLUICtrl *ctrl, const LLSD &data)
     if (ctrl_action == ACTION_SAVE)
     {
         doApplyUpdateInventory(setting_clone);
+        clearDirtyFlag();
     }
     else if (ctrl_action == ACTION_SAVEAS)
     {
diff --git a/indra/newview/llfloaterhoverheight.cpp b/indra/newview/llfloaterhoverheight.cpp
index 42c5e40761660311cb129f1072293d1db5b90894..a00fc4aa84889fa36d99c685b634bac57be0eb36 100644
--- a/indra/newview/llfloaterhoverheight.cpp
+++ b/indra/newview/llfloaterhoverheight.cpp
@@ -100,11 +100,14 @@ void LLFloaterHoverHeight::onClose(bool app_quitting)
 // static
 void LLFloaterHoverHeight::onSliderMoved(LLUICtrl* ctrl, void* userData)
 {
-	LLSliderCtrl* sldrCtrl = static_cast<LLSliderCtrl*>(ctrl);
-	F32 value = sldrCtrl->getValueF32();
-	LLVector3 offset(0.0, 0.0, llclamp(value,MIN_HOVER_Z,MAX_HOVER_Z));
-	LL_INFOS("Avatar") << "setting hover from slider moved" << offset[2] << LL_ENDL;
-	gAgentAvatarp->setHoverOffset(offset, false);
+    if (isAgentAvatarValid())
+    {
+        LLSliderCtrl* sldrCtrl = static_cast<LLSliderCtrl*>(ctrl);
+        F32 value = sldrCtrl->getValueF32();
+        LLVector3 offset(0.0, 0.0, llclamp(value, MIN_HOVER_Z, MAX_HOVER_Z));
+        LL_INFOS("Avatar") << "setting hover from slider moved" << offset[2] << LL_ENDL;
+        gAgentAvatarp->setHoverOffset(offset, false);
+    }
 }
 
 // Do send-to-the-server work when slider drag completes, or new
diff --git a/indra/newview/llfloaterimnearbychat.cpp b/indra/newview/llfloaterimnearbychat.cpp
index 060d0b2b66ca4f079a9cd27b05ad1bf33f39b2dc..4c7ecf909ae317e476fd40c7125bc80bcb884503 100644
--- a/indra/newview/llfloaterimnearbychat.cpp
+++ b/indra/newview/llfloaterimnearbychat.cpp
@@ -897,6 +897,11 @@ LLWString LLFloaterIMNearbyChat::stripChannelNumber(const LLWString &mesg, S32*
 void send_chat_from_viewer_impl(std::string utf8_out_text, EChatType type, S32 channel)
 // [/RLVa:KB]
 {
+	LL_DEBUGS("UIUsage") << "Nearby chat, text " << utf8_out_text << " type " << type << " channel " << channel << LL_ENDL;
+	if (type != CHAT_TYPE_START && type != CHAT_TYPE_STOP) // prune back some redundant logging
+	{
+		LLUIUsage::instance().logCommand("Chat.SendNearby"); // pseuo-command
+	}
 // [RLVa:KB] - Checked: 2010-02-27 (RLVa-1.2.0b) | Modified: RLVa-1.2.0a
 	// Only process chat messages (ie not CHAT_TYPE_START, CHAT_TYPE_STOP, etc)
 	if ( (RlvActions::isRlvEnabled()) && ( (CHAT_TYPE_WHISPER == type) || (CHAT_TYPE_NORMAL == type) || (CHAT_TYPE_SHOUT == type) ) )
diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp
index d1e8b95f274350d67e1910a34490aaf86ab9981c..e9f4450708c2bd93d88f1762d4952331aa9c655d 100644
--- a/indra/newview/llfloaterland.cpp
+++ b/indra/newview/llfloaterland.cpp
@@ -3066,7 +3066,8 @@ BOOL LLPanelLandCovenant::postBuild()
 {
 	mLastRegionID = LLUUID::null;
 	mNextUpdateTime = 0;
-
+    mTextEstateOwner = getChild<LLTextBox>("estate_owner_text");
+    mTextEstateOwner->setIsFriendCallback(LLAvatarActions::isFriend);
 	return TRUE;
 }
 
@@ -3174,8 +3175,7 @@ void LLPanelLandCovenant::updateEstateOwnerName(const std::string& name)
 	LLPanelLandCovenant* self = LLFloaterLand::getCurrentPanelLandCovenant();
 	if (self)
 	{
-		LLTextBox* editor = self->getChild<LLTextBox>("estate_owner_text");
-		if (editor) editor->setText(name);
+		self->mTextEstateOwner->setText(name);
 	}
 }
 
diff --git a/indra/newview/llfloaterland.h b/indra/newview/llfloaterland.h
index 65a22aa3fdcd5b4d9a845d281f67af9505296868..54eced4bc614835ce29eabaa04646d9598c41657 100644
--- a/indra/newview/llfloaterland.h
+++ b/indra/newview/llfloaterland.h
@@ -414,6 +414,7 @@ class LLPanelLandCovenant
 private:
 	LLUUID mLastRegionID;
 	F64 mNextUpdateTime; //seconds since client start
+    LLTextBox* mTextEstateOwner;
 };
 
 #endif
diff --git a/indra/newview/llfloatermap.cpp b/indra/newview/llfloatermap.cpp
index afb3f2bde9ea689479efdf48e82d914cd91e1bb6..37d0d430975073a320af5e974c091500f1ab78ae 100644
--- a/indra/newview/llfloatermap.cpp
+++ b/indra/newview/llfloatermap.cpp
@@ -51,7 +51,7 @@
 
 // The minor cardinal direction labels are hidden if their height is more
 // than this proportion of the map.
-const F32 MAP_MINOR_DIR_THRESHOLD = 0.07f;
+const F32 MAP_MINOR_DIR_THRESHOLD = 0.035f;
 const S32 MAP_PADDING_LEFT = 0;
 const S32 MAP_PADDING_TOP = 2;
 const S32 MAP_PADDING_RIGHT = 2;
@@ -81,38 +81,47 @@ LLFloaterMap::~LLFloaterMap()
 
 BOOL LLFloaterMap::postBuild()
 {
-	mMap = getChild<LLNetMap>("Net Map");
-	if (gSavedSettings.getBOOL("DoubleClickTeleport"))
-	{
-		mMap->setToolTipMsg(getString("AltToolTipMsg"));
-	}
-	else if (gSavedSettings.getBOOL("DoubleClickShowWorldMap"))
-	{
-		mMap->setToolTipMsg(getString("ToolTipMsg"));
-	}
-	sendChildToBack(mMap);
-	
-	mTextBoxNorth = getChild<LLTextBox> ("floater_map_north");
-	mTextBoxEast = getChild<LLTextBox> ("floater_map_east");
-	mTextBoxWest = getChild<LLTextBox> ("floater_map_west");
-	mTextBoxSouth = getChild<LLTextBox> ("floater_map_south");
-	mTextBoxSouthEast = getChild<LLTextBox> ("floater_map_southeast");
-	mTextBoxNorthEast = getChild<LLTextBox> ("floater_map_northeast");
-	mTextBoxSouthWest = getChild<LLTextBox> ("floater_map_southwest");
-	mTextBoxNorthWest = getChild<LLTextBox> ("floater_map_northwest");
+    mMap = getChild<LLNetMap>("Net Map");
+    mMap->setToolTipMsg(getString("ToolTipMsg"));
+    mMap->setParcelNameMsg(getString("ParcelNameMsg"));
+    mMap->setParcelSalePriceMsg(getString("ParcelSalePriceMsg"));
+    mMap->setParcelSaleAreaMsg(getString("ParcelSaleAreaMsg"));
+    mMap->setParcelOwnerMsg(getString("ParcelOwnerMsg"));
+    mMap->setRegionNameMsg(getString("RegionNameMsg"));
+    mMap->setToolTipHintMsg(getString("ToolTipHintMsg"));
+    mMap->setAltToolTipHintMsg(getString("AltToolTipHintMsg"));
+    sendChildToBack(mMap);
+
+    mTextBoxNorth     = getChild<LLTextBox>("floater_map_north");
+    mTextBoxEast      = getChild<LLTextBox>("floater_map_east");
+    mTextBoxWest      = getChild<LLTextBox>("floater_map_west");
+    mTextBoxSouth     = getChild<LLTextBox>("floater_map_south");
+    mTextBoxSouthEast = getChild<LLTextBox>("floater_map_southeast");
+    mTextBoxNorthEast = getChild<LLTextBox>("floater_map_northeast");
+    mTextBoxSouthWest = getChild<LLTextBox>("floater_map_southwest");
+    mTextBoxNorthWest = getChild<LLTextBox>("floater_map_northwest");
 
 	stretchMiniMap(getRect().getWidth() - MAP_PADDING_LEFT - MAP_PADDING_RIGHT
 		,getRect().getHeight() - MAP_PADDING_TOP - MAP_PADDING_BOTTOM);
 
-	updateMinorDirections();
+    mTextBoxNorth->reshapeToFitText();
+    mTextBoxEast->reshapeToFitText();
+    mTextBoxWest->reshapeToFitText();
+    mTextBoxSouth->reshapeToFitText();
+    mTextBoxSouthEast->reshapeToFitText();
+    mTextBoxNorthEast->reshapeToFitText();
+    mTextBoxSouthWest->reshapeToFitText();
+    mTextBoxNorthWest->reshapeToFitText();
 
-	// Get the drag handle all the way in back
-	sendChildToBack(getDragHandle());
+    updateMinorDirections();
 
-	// keep onscreen
-	gFloaterView->adjustToFitScreen(this, FALSE);
+    // Get the drag handle all the way in back
+    sendChildToBack(getDragHandle());
 
-	return TRUE;
+    // keep onscreen
+    gFloaterView->adjustToFitScreen(this, false);
+
+    return true;
 }
 
 BOOL LLFloaterMap::handleDoubleClick(S32 x, S32 y, MASK mask)
@@ -145,23 +154,44 @@ BOOL LLFloaterMap::handleDoubleClick(S32 x, S32 y, MASK mask)
 	return TRUE;
 }
 
-void LLFloaterMap::setDirectionPos( LLTextBox* text_box, F32 rotation )
+void LLFloaterMap::setDirectionPos(LLTextBox *text_box, F32 rotation)
 {
-	// Rotation is in radians.
-	// Rotation of 0 means x = 1, y = 0 on the unit circle.
-
-	F32 map_half_height = (F32)(getRect().getHeight() / 2) - getHeaderHeight()/2;
-	F32 map_half_width = (F32)(getRect().getWidth() / 2) ;
-	F32 text_half_height = (F32)(text_box->getRect().getHeight() / 2);
-	F32 text_half_width = (F32)(text_box->getRect().getWidth() / 2);
-	F32 radius = llmin( map_half_height - text_half_height, map_half_width - text_half_width );
-
-	// Inset by a little to account for position display.
-	radius -= 8.f;
-
-	text_box->setOrigin( 
-		ll_round(map_half_width - text_half_width + radius * cos( rotation )),
-		ll_round(map_half_height - text_half_height + radius * sin( rotation )) );
+    // Rotation is in radians.
+    // Rotation of 0 means x = 1, y = 0 on the unit circle.
+
+    F32 map_half_height  = (F32) (getRect().getHeight() / 2) - (getHeaderHeight() / 2);
+    F32 map_half_width   = (F32) (getRect().getWidth() / 2);
+    F32 text_half_height = (F32) (text_box->getRect().getHeight() / 2);
+    F32 text_half_width  = (F32) (text_box->getRect().getWidth() / 2);
+    F32 extra_padding    = (F32) (mTextBoxNorth->getRect().getWidth() / 2);
+    F32 pos_half_height  = map_half_height - text_half_height - extra_padding;
+    F32 pos_half_width   = map_half_width - text_half_width - extra_padding;
+
+    F32 corner_angle               = atan2(pos_half_height, pos_half_width);
+    F32 rotation_mirrored_into_top = abs(fmodf(rotation, F_PI));
+    if (rotation < 0)
+    {
+        rotation_mirrored_into_top = F_PI - rotation_mirrored_into_top;
+    }
+    F32  rotation_mirrored_into_top_right = (F_PI_BY_TWO - abs(rotation_mirrored_into_top - F_PI_BY_TWO));
+    bool at_left_right_edge               = rotation_mirrored_into_top_right < corner_angle;
+
+    F32 part_x = cos(rotation);
+    F32 part_y = sin(rotation);
+    F32 y;
+    F32 x;
+    if (at_left_right_edge)
+    {
+        x = std::copysign(pos_half_width, part_x);
+        y = x * part_y / part_x;
+    }
+    else
+    {
+        y = std::copysign(pos_half_height, part_y);
+        x = y * part_x / part_y;
+    }
+
+    text_box->setOrigin(ll_round(map_half_width + x - text_half_width), ll_round(map_half_height + y - text_half_height));
 }
 
 void LLFloaterMap::updateMinorDirections()
@@ -241,32 +271,6 @@ void LLFloaterMap::reshape(S32 width, S32 height, BOOL called_from_parent)
 	updateMinorDirections();
 }
 
-void LLFloaterMap::handleZoom(const LLSD& userdata)
-{
-	std::string level = userdata.asString();
-	
-	F32 scale = 0.0f;
-	if (level == std::string("default"))
-	{
-		LLControlVariable *pvar = gSavedSettings.getControl("MiniMapScale");
-		if(pvar)
-		{
-			pvar->resetToDefault();
-			scale = gSavedSettings.getF32("MiniMapScale");
-		}
-	}
-	else if (level == std::string("close"))
-		scale = LLNetMap::MAP_SCALE_MAX;
-	else if (level == std::string("medium"))
-		scale = LLNetMap::MAP_SCALE_MID;
-	else if (level == std::string("far"))
-		scale = LLNetMap::MAP_SCALE_MIN;
-	if (scale != 0.0f)
-	{
-		mMap->setScale(scale);
-	}
-}
-
 void LLFloaterMap::setMinimized(BOOL b)
 {
 	LLFloater::setMinimized(b);
diff --git a/indra/newview/llfloatermap.h b/indra/newview/llfloatermap.h
index c9776226d4368a1de173824915b5d4cf7dab6cc4..3e3bebf3b4a04e35f24d4c19a1460b8428c1cb43 100644
--- a/indra/newview/llfloatermap.h
+++ b/indra/newview/llfloatermap.h
@@ -49,7 +49,6 @@ class LLFloaterMap final : public LLFloater
 	/*virtual*/ void	setMinimized(BOOL b) override;
 
 private:
-	void handleZoom(const LLSD& userdata);
 	void setDirectionPos( LLTextBox* text_box, F32 rotation );
 	void updateMinorDirections();
 
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 21203a9a9be668cefb0ef26d15d9aefe021eeef7..eba8c735ce222cf2aa85573f2567f67ac22423f8 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -466,22 +466,25 @@ void LLFloaterModelPreview::loadHighLodModel()
 	loadModel(3);
 }
 
-void LLFloaterModelPreview::loadModel(S32 lod)
+void LLFloaterModelPreview::prepareToLoadModel(S32 lod)
 {
 	mModelPreview->mLoading = true;
 	if (lod == LLModel::LOD_PHYSICS)
 	{
 		// loading physics from file
 		mModelPreview->mPhysicsSearchLOD = lod;
+		mModelPreview->mWarnOfUnmatchedPhyicsMeshes = false;
 	}
-
+}
+void LLFloaterModelPreview::loadModel(S32 lod)
+{
+	prepareToLoadModel(lod);
 	(new LLMeshFilePicker(mModelPreview, lod))->getFile();
 }
 
 void LLFloaterModelPreview::loadModel(S32 lod, const std::string& file_name, bool force_disable_slm)
 {
-	mModelPreview->mLoading = true;
-
+	prepareToLoadModel(lod);
 	mModelPreview->loadModel(file_name, lod, force_disable_slm);
 }
 
@@ -1075,11 +1078,18 @@ void LLFloaterModelPreview::onPhysicsUseLOD(LLUICtrl* ctrl, void* userdata)
 	}
 
 	S32 file_mode = iface->getItemCount() - 1;
-	if (which_mode < file_mode)
+	S32 cube_mode = file_mode - 1;
+	if (which_mode < cube_mode)
 	{
 		S32 which_lod = num_lods - which_mode;
 		sInstance->mModelPreview->setPhysicsFromLOD(which_lod);
 	}
+	else if (which_mode == cube_mode)
+	{
+		std::string path = gDirUtilp->getAppRODataDir();
+		gDirUtilp->append(path, "cube.dae");
+		sInstance->loadModel(LLModel::LOD_PHYSICS, path);
+	}
 
 	LLModelPreview *model_preview = sInstance->mModelPreview;
 	if (model_preview)
@@ -1674,15 +1684,15 @@ LLFloaterModelPreview::DecompRequest::DecompRequest(const std::string& stage, LL
 void LLFloaterModelPreview::setCtrlLoadFromFile(S32 lod)
 {
     if (lod == LLModel::LOD_PHYSICS)
-    {
+	{
         LLComboBox* lod_combo = findChild<LLComboBox>("physics_lod_combo");
         if (lod_combo)
         {
-            lod_combo->setCurrentByIndex(5);
+            lod_combo->setCurrentByIndex(lod_combo->getItemCount() - 1);
         }
     }
     else
-{
+	{
         LLComboBox* lod_combo = findChild<LLComboBox>("lod_source_" + lod_name[lod]);
         if (lod_combo)
 	{
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index 8b3304f94660b8a233ea2831d7d613abfdec6aed..cd09adbf438f0adc6806cb6933d255a8f18c66dc 100644
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -221,6 +221,7 @@ class LLFloaterModelPreview final : public LLFloaterModelUploadBase
 
 	void resetUploadOptions();
 	void clearLogTab();
+	void prepareToLoadModel(S32 lod);
 
 	void createSmoothComboBox(LLComboBox* combo_box, float min, float max);
 
diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp
index 0cc3e8d74d90813d593728afb9f41d21073c5cc6..669d222c825584b1d8ad4de045021bda27cb9986 100644
--- a/indra/newview/llfloaterpreference.cpp
+++ b/indra/newview/llfloaterpreference.cpp
@@ -1583,7 +1583,6 @@ void LLFloaterPreference::refreshEnabledState()
 	BOOL enabled = LLFeatureManager::getInstance()->isFeatureAvailable("RenderDeferred") &&
 						bumpshiny &&
 						shaders && 
-						gGLManager.mHasFramebufferObject &&
 						(ctrl_wind_light->get()) ? TRUE : FALSE;
 
 	ctrl_deferred->setEnabled(enabled);
@@ -1647,7 +1646,6 @@ void LLFloaterPreferenceGraphicsAdvanced::refreshEnabledState()
     
     BOOL enabled = LLFeatureManager::getInstance()->isFeatureAvailable("RenderDeferred") &&
                         ((bumpshiny_ctrl && bumpshiny_ctrl->get()) ? TRUE : FALSE) &&
-                        gGLManager.mHasFramebufferObject &&
                         (ctrl_wind_light->get()) ? TRUE : FALSE;
 
     ctrl_deferred->setEnabled(enabled);
@@ -1802,8 +1800,7 @@ void LLFloaterPreferenceGraphicsAdvanced::disableUnavailableSettings()
 	}
 
 	// disabled deferred
-	if (!LLFeatureManager::getInstance()->isFeatureAvailable("RenderDeferred") ||
-		!gGLManager.mHasFramebufferObject)
+	if (!LLFeatureManager::getInstance()->isFeatureAvailable("RenderDeferred"))
 	{
 		ctrl_shadows->setEnabled(FALSE);
 		ctrl_shadows->setValue(0);
diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp
index 54ebf29e4923c47f559b92ac0e4d072574860cdb..4fca6c085fbba3ba63afc4d256a5a3d24d5cdb49 100644
--- a/indra/newview/llfloaterregioninfo.cpp
+++ b/indra/newview/llfloaterregioninfo.cpp
@@ -47,6 +47,7 @@
 
 #include "llagent.h"
 #include "llappviewer.h"
+#include "llavataractions.h"
 #include "llavatarname.h"
 #include "llfloateravatarpicker.h"
 #include "llbutton.h" 
@@ -1829,7 +1830,7 @@ void LLPanelEstateInfo::updateControls(LLViewerRegion* region)
 	setCtrlsEnabled(god || owner || manager);
 	
 	getChildView("apply_btn")->setEnabled(FALSE);
-
+    getChildView("estate_owner")->setEnabled(TRUE);
 	getChildView("message_estate_btn")->setEnabled(god || owner || manager);
 	getChildView("kick_user_from_estate_btn")->setEnabled(god || owner || manager);
 
@@ -1891,6 +1892,8 @@ BOOL LLPanelEstateInfo::postBuild()
 
 	getChild<LLUICtrl>("externally_visible_radio")->setFocus(TRUE);
 
+    getChild<LLTextBox>("estate_owner")->setIsFriendCallback(LLAvatarActions::isFriend);
+
 	return LLPanelRegionInfo::postBuild();
 }
 
@@ -2137,6 +2140,7 @@ BOOL LLPanelEstateCovenant::postBuild()
 {
 	mEstateNameText = getChild<LLTextBox>("estate_name_text");
 	mEstateOwnerText = getChild<LLTextBox>("estate_owner_text");
+    mEstateOwnerText->setIsFriendCallback(LLAvatarActions::isFriend);
 	mLastModifiedText = getChild<LLTextBox>("covenant_timestamp_text");
 	mEditor = getChild<LLViewerTextEditor>("covenant_editor");
 	LLButton* reset_button = getChild<LLButton>("reset_covenant");
diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp
index fe93f617324514473bc3797e894d90592f73b73b..8cc14a66fa9709a4d8136ef499a52633893186b1 100644
--- a/indra/newview/llfloaterworldmap.cpp
+++ b/indra/newview/llfloaterworldmap.cpp
@@ -87,7 +87,6 @@
 //---------------------------------------------------------------------------
 // Constants
 //---------------------------------------------------------------------------
-static const F32 MAP_ZOOM_TIME = 0.2f;
 
 // Merov: we switched from using the "world size" (which varies depending where the user went) to a fixed
 // width of 512 regions max visible at a time. This makes the zoom slider works in a consistent way across
@@ -292,7 +291,7 @@ void* LLFloaterWorldMap::createWorldMapView(void* data)
 
 BOOL LLFloaterWorldMap::postBuild()
 {
-	mPanel = getChild<LLPanel>("objects_mapview");
+    mMapView = dynamic_cast<LLWorldMapView*>(getChild<LLPanel>("objects_mapview"));
 	mTrackRegionButton = getChild<LLButton>("track_region");
 	
 	LLComboBox *avatar_combo = getChild<LLComboBox>("friend combo");
@@ -314,15 +313,13 @@ BOOL LLFloaterWorldMap::postBuild()
 	landmark_combo->setTextChangedCallback( boost::bind(&LLFloaterWorldMap::onComboTextEntry, this) );
 	mListLandmarkCombo = dynamic_cast<LLCtrlListInterface *>(landmark_combo);
 	
-	mCurZoomVal = log(LLWorldMapView::sMapScale/256.f)/log(2.f);
-	getChild<LLUICtrl>("zoom slider")->setValue(mCurZoomVal);
-
+    F32 slider_zoom = mMapView->getZoom();
+    getChild<LLUICtrl>("zoom slider")->setValue(slider_zoom);
+    
     //getChild<LLPanel>("expand_btn_panel")->setMouseDownCallback(boost::bind(&LLFloaterWorldMap::onExpandCollapseBtn, this));
 	
 	setDefaultBtn(NULL);
 	
-	mZoomTimer.stop();
-	
 	onChangeMaturity();
 	
 	return TRUE;
@@ -332,7 +329,7 @@ BOOL LLFloaterWorldMap::postBuild()
 LLFloaterWorldMap::~LLFloaterWorldMap()
 {
 	// All cleaned up by LLView destructor
-	mPanel = NULL;
+    mMapView = NULL;
 	
 	// Inventory deletes all observers on shutdown
 	mInventory = NULL;
@@ -370,17 +367,15 @@ void LLFloaterWorldMap::onOpen(const LLSD& key)
 	
 	mIsClosing = FALSE;
 	
-	LLWorldMapView* map_panel;
-	map_panel = (LLWorldMapView*)gFloaterWorldMap->mPanel;
-	map_panel->clearLastClick();
+    mMapView->clearLastClick();
 	
 	{
 		// reset pan on show, so it centers on you again
 		if (!center_on_target)
 		{
-			LLWorldMapView::setPan(0, 0, TRUE);
+            mMapView->setPan(0, 0, true);
 		}
-		map_panel->updateVisibleBlocks();
+        mMapView->updateVisibleBlocks();
 		
 		// Reload items as they may have changed
 		LLWorldMap::getInstance()->reloadItems();
@@ -428,18 +423,21 @@ BOOL LLFloaterWorldMap::handleHover(S32 x, S32 y, MASK mask)
 
 BOOL LLFloaterWorldMap::handleScrollWheel(S32 x, S32 y, S32 clicks)
 {
-	if (!isMinimized() && isFrontmost())
-	{
-		if(mPanel->pointInView(x, y))
-		{
-			F32 slider_value = (F32)getChild<LLUICtrl>("zoom slider")->getValue().asReal();
-			slider_value += ((F32)clicks * -0.3333f);
-			getChild<LLUICtrl>("zoom slider")->setValue(LLSD(slider_value));
-			return TRUE;
-		}
-	}
-	
-	return LLFloater::handleScrollWheel(x, y, clicks);
+    if (!isMinimized() && isFrontmost())
+    {
+        S32 map_x = x - mMapView->getRect().mLeft;
+        S32 map_y = y - mMapView->getRect().mBottom;
+        if (mMapView->pointInView(map_x, map_y))
+        {
+            F32 old_slider_zoom = (F32) getChild<LLUICtrl>("zoom slider")->getValue().asReal();
+            F32 slider_zoom     = old_slider_zoom + ((F32) clicks * -0.3333f);
+            getChild<LLUICtrl>("zoom slider")->setValue(LLSD(slider_zoom));
+            mMapView->zoomWithPivot(slider_zoom, map_x, map_y);
+            return true;
+        }
+    }
+
+    return LLFloater::handleScrollWheel(x, y, clicks);
 }
 
 
@@ -523,26 +521,13 @@ void LLFloaterWorldMap::draw()
 	
 	setMouseOpaque(TRUE);
 	getDragHandle()->setMouseOpaque(TRUE);
-	
-	//RN: snaps to zoom value because interpolation caused jitter in the text rendering
-	if (!mZoomTimer.getStarted() && mCurZoomVal != (F32)getChild<LLUICtrl>("zoom slider")->getValue().asReal())
-	{
-		mZoomTimer.start();
-	}
-	F32 interp = mZoomTimer.getElapsedTimeF32() / MAP_ZOOM_TIME;
-	if (interp > 1.f)
-	{
-		interp = 1.f;
-		mZoomTimer.stop();
-	}
-	mCurZoomVal = ll_lerp(mCurZoomVal, (F32)getChild<LLUICtrl>("zoom slider")->getValue().asReal(), interp);
-	F32 map_scale = 256.f*pow(2.f, mCurZoomVal);
-	LLWorldMapView::setScale( map_scale );
+
+    mMapView->zoom((F32)getChild<LLUICtrl>("zoom slider")->getValue().asReal());
 	
 	// Enable/disable checkboxes depending on the zoom level
 	// If above threshold level (i.e. low res) -> Disable all checkboxes
 	// If under threshold level (i.e. high res) -> Enable all checkboxes
-	bool enable = LLWorldMapView::showRegionInfo();
+    bool enable = mMapView->showRegionInfo();
 	getChildView("people_chk")->setEnabled(enable);
 	getChildView("infohub_chk")->setEnabled(enable);
 	getChildView("telehub_chk")->setEnabled(enable);
@@ -1103,9 +1088,7 @@ void LLFloaterWorldMap::adjustZoomSliderBounds()
 	S32 world_height_regions = MAX_VISIBLE_REGIONS;
 	
 	// Find how much space we have to display the world
-	LLWorldMapView* map_panel;
-	map_panel = (LLWorldMapView*)mPanel;
-	LLRect view_rect = map_panel->getRect();
+    LLRect view_rect = mMapView->getRect();
 	
 	// View size in pixels
 	S32 view_width = view_rect.getWidth();
@@ -1373,9 +1356,9 @@ void LLFloaterWorldMap::onShowTargetBtn()
 
 void LLFloaterWorldMap::onShowAgentBtn()
 {
-	LLWorldMapView::setPan( 0, 0, FALSE); // FALSE == animate
-	// Set flag so user's location will be displayed if not tracking anything else
-	mSetToUserPosition = TRUE;	
+    mMapView->setPanWithInterpTime(0, 0, false, 0.1f);  // false == animate
+    // Set flag so user's location will be displayed if not tracking anything else
+    mSetToUserPosition = true;
 }
 
 void LLFloaterWorldMap::onClickTeleportBtn()
@@ -1464,9 +1447,10 @@ void LLFloaterWorldMap::centerOnTarget(BOOL animate)
 		pos_global.clearVec();
 	}
 	
-	LLWorldMapView::setPan( -llfloor((F32)(pos_global.mdV[VX] * (F64)LLWorldMapView::sMapScale / REGION_WIDTH_METERS)), 
-						   -llfloor((F32)(pos_global.mdV[VY] * (F64)LLWorldMapView::sMapScale / REGION_WIDTH_METERS)),
-						   !animate);
+    F64 map_scale = (F64)mMapView->getScale();
+    mMapView->setPanWithInterpTime(-llfloor((F32)(pos_global.mdV[VX] * map_scale / REGION_WIDTH_METERS)),
+                           -llfloor((F32)(pos_global.mdV[VY] * map_scale / REGION_WIDTH_METERS)),
+                           !animate, 0.1f);
 	mWaitingForTracker = FALSE;
 }
 
@@ -1701,7 +1685,7 @@ void LLFloaterWorldMap::onTeleportFinished()
 {
     if(isInVisibleChain())
     {
-        LLWorldMapView::setPan(0, 0, TRUE);
+        mMapView->setPan(0, 0, TRUE);
     }
 }
 
@@ -1775,9 +1759,8 @@ void LLFloaterWorldMap::onChangeMaturity()
 
 void LLFloaterWorldMap::onFocusLost()
 {
-	gViewerWindow->showCursor();
-	LLWorldMapView* map_panel = (LLWorldMapView*)gFloaterWorldMap->mPanel;
-	map_panel->mPanning = FALSE;
+    gViewerWindow->showCursor();
+    mMapView->mPanning = false;
 }
 
 LLPanelHideBeacon::LLPanelHideBeacon() :
diff --git a/indra/newview/llfloaterworldmap.h b/indra/newview/llfloaterworldmap.h
index 721e3025251ad1c1c55c0264009fd909b09d29ae..b640dc5eec764e1f2fbb5fe293740cfc745edc5c 100644
--- a/indra/newview/llfloaterworldmap.h
+++ b/indra/newview/llfloaterworldmap.h
@@ -44,6 +44,7 @@ class LLInventoryObserver;
 class LLItemInfo;
 class LLLineEditor;
 class LLTabContainer;
+class LLWorldMapView;
 
 class LLFloaterWorldMap final : public LLFloater
 {
@@ -157,11 +158,7 @@ class LLFloaterWorldMap final : public LLFloater
     void            onTeleportFinished();
 
 private:
-	LLPanel*			mPanel;		// Panel displaying the map
-
-	// Ties to LLWorldMapView::sMapScale, in pixels per region
-	F32						mCurZoomVal;
-	LLFrameTimer			mZoomTimer;
+    LLWorldMapView* mMapView; // Panel displaying the map
 
 	// update display of teleport destination coordinates - pos is in global coordinates
 	void updateTeleportCoordsDisplay( const LLVector3d& pos );
diff --git a/indra/newview/llgroupmgr.cpp b/indra/newview/llgroupmgr.cpp
index f95d37ddb8e8cbf2f6af80097bba1b226520156d..3b6ff3b8c90d1e7bb1b1b0f85d830b456c7954a6 100644
--- a/indra/newview/llgroupmgr.cpp
+++ b/indra/newview/llgroupmgr.cpp
@@ -53,6 +53,7 @@
 #include "llviewerregion.h"
 #include <boost/regex.hpp>
 #include "llcorehttputil.h"
+#include "lluiusage.h"
 
 #include <boost/lexical_cast.hpp>
 
@@ -1846,6 +1847,9 @@ void LLGroupMgr::sendGroupRoleMemberChanges(const LLUUID& group_id)
 //static
 void LLGroupMgr::sendGroupMemberJoin(const LLUUID& group_id)
 {
+
+	LLUIUsage::instance().logCommand("Group.Join");
+
 	LLMessageSystem *msg = gMessageSystem;
 
 	msg->newMessageFast(_PREHASH_JoinGroupRequest);
diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp
index c03c8658654e20cd204d2f54e557995505a1de41..d0e9fdb412ee04a3929555b63cf422a15512e9d6 100644
--- a/indra/newview/llimview.cpp
+++ b/indra/newview/llimview.cpp
@@ -75,6 +75,7 @@
 #include "message.h"
 #include "llviewerregion.h"
 #include "llcorehttputil.h"
+#include "lluiusage.h"
 // [RLVa:KB] - Checked: 2013-05-10 (RLVa-1.4.9)
 #include "rlvactions.h"
 #include "rlvcommon.h"
@@ -783,6 +784,23 @@ void LLIMModel::LLIMSession::addMessage(const std::string& from, const LLUUID& f
 	message["is_history"] = is_history;
     message["is_announcement"] = bonus.has("announcement");
 
+	LL_DEBUGS("UIUsage") << "addMessage " << " from " << from << " from_id " << from_id << " utf8_text " << utf8_text << " time " << time << " is_history " << is_history << " session mType " << mType << LL_ENDL;
+	if (from_id == gAgent.getID())
+	{
+		if (mType == IM_SESSION_GROUP_START)
+		{
+			LLUIUsage::instance().logCommand("Chat.SendGroup");
+		}
+		else if (mType == IM_NOTHING_SPECIAL)
+		{
+			LLUIUsage::instance().logCommand("Chat.SendIM");
+		}
+		else
+		{
+			LLUIUsage::instance().logCommand("Chat.SendOther");
+		}
+	}
+
 	mMsgs.push_front(message); 
 
 	if (mSpeakers && from_id.notNull())
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index 1cd12a4d303c0516e82e0aecd2eb84cf7fbd4a4c..de9ee65cfd1b877bd01dbbb229bdd296252df4ba 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -4026,7 +4026,20 @@ void LLFolderBridge::perform_pasteFromClipboard()
 				{
 					if (item && can_move_to_landmarks(item))
 					{
-						dropToFavorites(item);
+                        if (LLClipboard::instance().isCutMode())
+                        {
+                            LLViewerInventoryItem* viitem = dynamic_cast<LLViewerInventoryItem*>(item);
+                            llassert(viitem);
+                            if (viitem)
+                            {
+                                //changeItemParent() implicity calls dirtyFilter
+                                changeItemParent(model, viitem, parent_id, FALSE);
+                            }
+                        }
+                        else
+                        {
+                            dropToFavorites(item);
+                        }
 					}
 				}
 				else if (LLClipboard::instance().isCutMode())
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
index 7dd32d2d6bf7ac5f3bd173f8d6a3e61dcb1a72e0..4976cd5b39768ed2c4a2317b972591bb49f70280 100644
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -3299,7 +3299,6 @@ bool LLInventoryModel::messageUpdateCore(LLMessageSystem* msg, bool account, U32
 		gInventory.accountForUpdate(update);
 	}
 
-	U32 changes = 0x0;
 	if (account)
 	{
 		mask |= LLInventoryObserver::CREATE;
@@ -3307,7 +3306,7 @@ bool LLInventoryModel::messageUpdateCore(LLMessageSystem* msg, bool account, U32
 	//as above, this loop never seems to loop more than once per call
 	for (item_array_t::iterator it = items.begin(); it != items.end(); ++it)
 	{
-		changes |= gInventory.updateItem(*it, mask);
+		gInventory.updateItem(*it, mask);
 	}
 	gInventory.notifyObservers();
 	gViewerWindow->getWindow()->decBusyCount();
@@ -4852,12 +4851,10 @@ void LLInventoryModel::FetchItemHttpHandler::processData(LLSD & content, LLCore:
 	}
 
 	// as above, this loop never seems to loop more than once per call
-	U32 changes(0U);
 	for (LLInventoryModel::item_array_t::iterator it = items.begin(); it != items.end(); ++it)
 	{
-		changes |= gInventory.updateItem(*it);
+		gInventory.updateItem(*it);
 	}
-	// *HUH:  Have computed 'changes', nothing uses it.
 	
 	gInventory.notifyObservers();
 	gViewerWindow->getWindow()->decBusyCount();
diff --git a/indra/newview/lllegacyatmospherics.cpp b/indra/newview/lllegacyatmospherics.cpp
index 19f92bfcd8e8f0746bb152230f3de98e3c440cd4..f2bb983bc43668d71af5dc2f809a62f689b14d5f 100644
--- a/indra/newview/lllegacyatmospherics.cpp
+++ b/indra/newview/lllegacyatmospherics.cpp
@@ -386,7 +386,6 @@ void LLAtmospherics::updateFog(const F32 distance, const LLVector3& tosun_in)
 		return;
 	}
 
-	const BOOL hide_clip_plane = TRUE;
 	LLColor4 target_fog(0.f, 0.2f, 0.5f, 0.f);
 
 	const F32 water_height = gAgent.getRegion() ? gAgent.getRegion()->getWaterHeight() : 0.f;
@@ -473,33 +472,17 @@ void LLAtmospherics::updateFog(const F32 distance, const LLVector3& tosun_in)
 
 	render_fog_color = sky_fog_color;
 
-	F32 fog_density = 0.f;
 	fog_distance = mFogRatio * distance;
 	
 	if (camera_height > water_height)
 	{
 		LLColor4 fog(render_fog_color);
 		mGLFogCol = fog;
-
-		if (hide_clip_plane)
-		{
-			// For now, set the density to extend to the cull distance.
-			const F32 f_log = 2.14596602628934723963618357029f; // sqrt(fabs(log(0.01f)))
-			fog_density = f_log/fog_distance;
-		}
-		else
-		{
-			const F32 f_log = 4.6051701859880913680359829093687f; // fabs(log(0.01f))
-			fog_density = (f_log)/fog_distance;
-		}
 	}
 	else
 	{
         const LLSettingsWater::ptr_t& pwater = env.getCurrentWater();
 		F32 depth = water_height - camera_height;
-		
-		// get the water param manager variables
-        float water_fog_density = pwater->getModifiedWaterFogDensity(depth <= 0.0f);
 
 		LLColor4 water_fog_color(pwater->getWaterFogColor());
 
@@ -513,9 +496,6 @@ void LLAtmospherics::updateFog(const F32 distance, const LLVector3& tosun_in)
 
 		// set the gl fog color
 		mGLFogCol = fogCol;
-
-		// set the density based on what the shaders use
-		fog_density = water_fog_density * gSavedSettings.getF32("WaterGLFogDensityScale");
 	}
 
 	mFogColor = sky_fog_color;
diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp
index 1161dea9faa60f5735f5c4f2079af69f998d4e1a..1c015a2c3de3cc025dbdc32389b23c09ace071e7 100644
--- a/indra/newview/llmodelpreview.cpp
+++ b/indra/newview/llmodelpreview.cpp
@@ -87,6 +87,7 @@ static const LLColor4 PREVIEW_DEG_FILL_COL(1.f, 0.f, 0.f, 0.5f);
 static const F32 PREVIEW_DEG_EDGE_WIDTH(3.f);
 static const F32 PREVIEW_DEG_POINT_SIZE(8.f);
 static const F32 PREVIEW_ZOOM_LIMIT(10.f);
+static const std::string DEFAULT_PHYSICS_MESH_NAME = "default_physics_shape";
 
 const F32 SKIN_WEIGHT_CAMERA_DISTANCE = 16.f;
 LLViewerFetchedTexture* bindMaterialDiffuseTexture(const LLImportMaterial& material)
@@ -432,6 +433,20 @@ void LLModelPreview::rebuildUploadData()
                         LLFloaterModelPreview::addStringToLog(out, false);
                     }
                 }
+                if (mWarnOfUnmatchedPhyicsMeshes && !lod_model && (i == LLModel::LOD_PHYSICS))
+                {
+                    // Despite the various strategies above, if we don't now have a physics model, we're going to end up with decomposition.
+                    // That's ok, but might not what they wanted. Use default_physics_shape if found.
+                    std::ostringstream out;
+                    out << "No physics model specified for " << instance.mLabel;
+                    if (mDefaultPhysicsShapeP)
+                    {
+                        out << " - using: " << DEFAULT_PHYSICS_MESH_NAME;
+                        lod_model = mDefaultPhysicsShapeP;
+                    }
+                    LL_WARNS() << out.str() << LL_ENDL;
+                    LLFloaterModelPreview::addStringToLog(out, !mDefaultPhysicsShapeP); // Flash log tab if no default.
+                }
 
                 if (lod_model)
                 {
@@ -1036,6 +1051,13 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)
         }
         else
         {
+            if (loaded_lod == LLModel::LOD_PHYSICS)
+            {   // Explicitly loading physics. See if there is a default mesh.
+                LLMatrix4 ignored_transform; // Each mesh that uses this will supply their own.
+                mDefaultPhysicsShapeP = nullptr;
+                FindModel(mScene[loaded_lod], DEFAULT_PHYSICS_MESH_NAME + getLodSuffix(loaded_lod), mDefaultPhysicsShapeP, ignored_transform);
+                mWarnOfUnmatchedPhyicsMeshes = true;
+            }
             BOOL legacyMatching = gSavedSettings.getBOOL("ImporterLegacyMatching");
             if (!legacyMatching)
             {
@@ -1106,7 +1128,6 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)
                                     LL_WARNS() << out.str() << LL_ENDL;
                                     LLFloaterModelPreview::addStringToLog(out, false);
                                 }
-
                                 mModel[loaded_lod][idx]->mLabel = name;
                             }
                         }
@@ -2682,8 +2703,6 @@ void LLModelPreview::clearBuffers()
 
 void LLModelPreview::genBuffers(S32 lod, bool include_skin_weights)
 {
-    U32 tri_count = 0;
-    U32 vertex_count = 0;
     U32 mesh_count = 0;
 
 
@@ -2818,10 +2837,7 @@ void LLModelPreview::genBuffers(S32 lod, bool include_skin_weights)
 
             mVertexBuffer[lod][mdl].push_back(vb);
 
-            vertex_count += num_vertices;
-            tri_count += num_indices / 3;
             ++mesh_count;
-
         }
     }
 }
diff --git a/indra/newview/llmodelpreview.h b/indra/newview/llmodelpreview.h
index ebe30621e528ee1e849af55b881986128bfb72f4..ed49a48bc2e0c4c1e75db8bfe14b872d82a997ea 100644
--- a/indra/newview/llmodelpreview.h
+++ b/indra/newview/llmodelpreview.h
@@ -226,6 +226,21 @@ class LLModelPreview final : public LLViewerDynamicTexture, public LLMutex
     static U32 countRootModels(LLModelLoader::model_list models);
     LLVector3   mGroundPlane[4];
 	void		renderGroundPlane(float z_offset = 0.0f);
+    /// Indicates whether we should warn of high-lod meshes that do not have a corresponding physics mesh.
+    /// Reset when resetting the modelpreview (i.e., when the uploader dialog is created or reset), and when
+    /// about to process a physics file. Set to true immediately after the file is loaded (before rebuildUploadData()).
+    ///
+    /// (The rules for mapping the correspondence of high-lod meshes to physics meshes are complex. When
+    /// lod rendering meshes are used, there is never an unmatched mesh. Nor is there a mismatch when
+    /// the high-lod file and physics file have ony one mesh each. In these cases, this value is moot.
+    /// When there are multiple meshes in each file, they are matched by name or order, and some meshes
+    /// are broken up by limitations into multiple objects, and thus there can be mismatches.)
+    bool mWarnOfUnmatchedPhyicsMeshes{false};
+    /// A mesh to use as the default physics shape in only those cases where the physics shape is not otherwise specified.
+    /// It is set only when the user chooses a physics shape file that contains a mesh with a name that matches DEFAULT_PHYSICS_MESH_NAME.
+    /// It is reset when such a name is not found, and when resetting the modelpreview.
+    /// Not read unless mWarnOfUnmatchedPhyicsMeshes is true.
+    LLModel* mDefaultPhysicsShapeP{};
 
     typedef enum
     {
diff --git a/indra/newview/llnavigationbar.cpp b/indra/newview/llnavigationbar.cpp
index 426c86d046fe031b22c742019453a7bf8467d85e..97845ddc46ef7ef80c0dbc06c2ff54c1e29e2cac 100644
--- a/indra/newview/llnavigationbar.cpp
+++ b/indra/newview/llnavigationbar.cpp
@@ -454,9 +454,9 @@ void LLNavigationBar::onLocationSelection()
 			
 			if(value.has("AssetUUID"))
 			{
-				
 				gAgent.teleportViaLandmark( LLUUID(value["AssetUUID"].asString()));
-				mSaveToLocationHistory = true;
+                // user teleported by manually inputting inventory landmark's name
+				mSaveToLocationHistory = false;
 				return;
 			}
 			else
diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp
index ce9d9ffda7e9484af554dd6dbbb209d7b9ee9cd8..340dbfa968785eb605440b8f98abd6068f6ba23c 100644
--- a/indra/newview/llnetmap.cpp
+++ b/indra/newview/llnetmap.cpp
@@ -37,6 +37,7 @@
 #include "llfocusmgr.h"
 #include "lllocalcliprect.h"
 #include "llrender.h"
+#include "llresmgr.h"
 #include "llui.h"
 #include "lltooltip.h"
 
@@ -47,12 +48,16 @@
 #include "llagentcamera.h"
 #include "llappviewer.h" // for gDisconnected
 #include "llcallingcard.h" // LLAvatarTracker
+#include "llfloaterland.h"
 #include "llfloaterworldmap.h"
 #include "llparcel.h"
 #include "lltracker.h"
 #include "llsurface.h"
+#include "llurlmatch.h"
+#include "llurlregistry.h"
 #include "llviewercamera.h"
 #include "llviewercontrol.h"
+#include "llviewerparcelmgr.h"
 #include "llviewertexture.h"
 #include "llviewertexturelist.h"
 #include "llviewermenu.h"
@@ -70,9 +75,12 @@
 
 static LLDefaultChildRegistry::Register<LLNetMap> r1("net_map");
 
-const F32 LLNetMap::MAP_SCALE_MIN = 32.f;
-const F32 LLNetMap::MAP_SCALE_MID = 1024.f;
-const F32 LLNetMap::MAP_SCALE_MAX = 4096.f;
+const F32 LLNetMap::MAP_SCALE_MIN = 32;
+const F32 LLNetMap::MAP_SCALE_FAR = 32;
+const F32 LLNetMap::MAP_SCALE_MEDIUM = 128;
+const F32 LLNetMap::MAP_SCALE_CLOSE = 256;
+const F32 LLNetMap::MAP_SCALE_VERY_CLOSE = 1024;
+const F32 LLNetMap::MAP_SCALE_MAX = 4096;
 
 const F32 MAP_SCALE_ZOOM_FACTOR = 1.04f; // Zoom in factor per click of scroll wheel (4%)
 const F32 MIN_DOT_RADIUS = 3.5f;
@@ -87,14 +95,14 @@ LLNetMap::LLNetMap (const Params & p)
 	mUpdateObjectImage(false),
 	mUpdateParcelImage(false),
 	mBackgroundColor (p.bg_color()),
-	mScale( MAP_SCALE_MID ),
-	mPixelsPerMeter( MAP_SCALE_MID / REGION_WIDTH_METERS ),
+    mScale( MAP_SCALE_MEDIUM ),
+    mPixelsPerMeter( MAP_SCALE_MEDIUM / REGION_WIDTH_METERS ),
 	mObjectMapTPM(0.f),
 	mObjectMapPixels(0.f),
 	mPanning(false),
-	mTargetPan(0.f, 0.f),
 	mCurPan(0.f, 0.f),
 	mStartPan(0.f, 0.f),
+    mPopupWorldPos(0.f, 0.f, 0.f),
 	mMouseDown(0, 0),
 	mObjectImageCenterGlobal( gAgentCamera.getCameraPositionGlobal() ),
 	mObjectRawImagep(),
@@ -108,6 +116,13 @@ LLNetMap::LLNetMap (const Params & p)
 	mPopupMenuHandle()
 {
 	setScale(gSavedSettings.getF32("MiniMapScale"));
+    if (gAgent.isFirstLogin())
+    {
+        // *HACK: On first run, set this to false for new users, otherwise the
+        // default is true to maintain consistent experience for existing
+        // users.
+        gSavedSettings.setBOOL("MiniMapRotate", false);
+    }
 }
 
 LLNetMap::~LLNetMap()
@@ -132,18 +147,26 @@ LLNetMap::~LLNetMap()
 
 BOOL LLNetMap::postBuild()
 {
-	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
-	
-	registrar.add("Minimap.Zoom", boost::bind(&LLNetMap::handleZoom, this, _2));
-	registrar.add("Minimap.Tracker", boost::bind(&LLNetMap::handleStopTracking, this, _2));
+    LLUICtrl::CommitCallbackRegistry::ScopedRegistrar commitRegistrar;
+    LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enableRegistrar;
+
+    enableRegistrar.add("Minimap.Zoom.Check", boost::bind(&LLNetMap::isZoomChecked, this, _2));
+    commitRegistrar.add("Minimap.Zoom.Set", boost::bind(&LLNetMap::setZoom, this, _2));
+    commitRegistrar.add("Minimap.Tracker", boost::bind(&LLNetMap::handleStopTracking, this, _2));
+    commitRegistrar.add("Minimap.Center.Activate", boost::bind(&LLNetMap::activateCenterMap, this, _2));
+    enableRegistrar.add("Minimap.MapOrientation.Check", boost::bind(&LLNetMap::isMapOrientationChecked, this, _2));
+    commitRegistrar.add("Minimap.MapOrientation.Set", boost::bind(&LLNetMap::setMapOrientation, this, _2));
+    commitRegistrar.add("Minimap.AboutLand", boost::bind(&LLNetMap::popupShowAboutLand, this, _2));
 
 	LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_mini_map.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+                                                                          LLViewerMenuHolderGL::child_registry_t::instance());
+    menu->setItemEnabled("Re-center map", false);
 	mPopupMenuHandle = menu->getHandle();
 
 	mParcelMgrConn = LLViewerParcelMgr::instance().setCollisionUpdateCallback(boost::bind(&LLNetMap::refreshParcelOverlay, this));
 	mParcelOverlayConn = LLViewerParcelOverlay::setUpdateCallback(boost::bind(&LLNetMap::refreshParcelOverlay, this));
 
-	return TRUE;
+    return true;
 }
 
 void LLNetMap::setScale( F32 scale )
@@ -192,7 +215,7 @@ void LLNetMap::draw()
     static LLUIColor map_shout_ring_color = LLUIColorTable::instance().getColor("MapShoutRingColor", LLColor4::white);
 	//static LLUIColor map_track_disabled_color = LLUIColorTable::instance().getColor("MapTrackDisabledColor", LLColor4::white);
 	static LLUIColor map_frustum_color = LLUIColorTable::instance().getColor("MapFrustumColor", LLColor4::white);
-	static LLUIColor map_frustum_rotating_color = LLUIColorTable::instance().getColor("MapFrustumRotatingColor", LLColor4::white);
+	static LLUIColor map_parcel_outline_color = LLUIColorTable::instance().getColor("MapParcelOutlineColor", LLColor4(LLColor3(LLColor4::yellow), 0.5f));
 	static LLUIColor map_line_color = LLUIColorTable::instance().getColor("MapLineColor", LLColor4::red);
     static LLUIColor map_parcel_line_color = LLUIColorTable::instance().getColor("MapParcelBoundryLine", LLColor4::white);
 	
@@ -211,11 +234,25 @@ void LLNetMap::draw()
 		createParcelImage();
 	}
 
-	static LLUICachedControl<bool> auto_center("MiniMapAutoCenter", true);
-	if (auto_center)
+    static LLUICachedControl<bool> auto_center("MiniMapAutoCenter", true);
+    bool auto_centering = auto_center && !mPanning;
+    mCentering = mCentering && !mPanning;
+
+    if (auto_centering || mCentering)
 	{
-		mCurPan = lerp(mCurPan, mTargetPan, LLSmoothInterpolation::getInterpolant(0.1f));
+        mCurPan = lerp(mCurPan, LLVector2(0.0f, 0.0f) , LLSmoothInterpolation::getInterpolant(0.1f));
 	}
+    bool centered = abs(mCurPan.mV[VX]) < 0.5f && abs(mCurPan.mV[VY]) < 0.5f;
+    if (centered)
+    {
+        mCurPan.mV[0] = 0.0f;
+        mCurPan.mV[1] = 0.0f;
+        mCentering = false;
+    }
+
+    bool can_recenter_map = !(centered || mCentering || auto_centering);
+    mPopupMenu->setItemEnabled("Re-center map", can_recenter_map);
+    updateAboutLandPopupButton();
 
 	// Prepare a scissor region
 	F32 rotation = 0.f;
@@ -254,14 +291,15 @@ void LLNetMap::draw()
 
 		// figure out where agent is
 		S32 region_width = REGION_WIDTH_UNITS;
+        const F32 scale_pixels_per_meter = mScale / region_width;
 
 		for (LLViewerRegion* regionp : worldInst.getRegionList())
 		{
 			// Find x and y position relative to camera's center.
 			LLVector3 origin_agent = regionp->getOriginAgent();
 			LLVector3 rel_region_pos = origin_agent - gAgentCamera.getCameraPositionAgent();
-			F32 relative_x = (rel_region_pos.mV[0] / region_width) * mScale;
-			F32 relative_y = (rel_region_pos.mV[1] / region_width) * mScale;
+			F32 relative_x = rel_region_pos.mV[0] * scale_pixels_per_meter;
+			F32 relative_y = rel_region_pos.mV[1] * scale_pixels_per_meter;
 			const F32 real_width(regionp->getWidth());
 
 			// background region rectangle
@@ -395,8 +433,8 @@ void LLNetMap::draw()
 
 				LLVector3 map_center_agent = gAgent.getPosAgentFromGlobal(mObjectImageCenterGlobal);
 				map_center_agent -= camera_position;
-				map_center_agent.mV[VX] *= mScale / region_width;
-				map_center_agent.mV[VY] *= mScale / region_width;
+				map_center_agent.mV[VX] *= scale_pixels_per_meter;
+				map_center_agent.mV[VY] *= scale_pixels_per_meter;
 
 				gGL.getTexUnit(0)->bind(mObjectImagep);
 
@@ -582,15 +620,17 @@ void LLNetMap::draw()
 		F32 horiz_fov = viewer_camera.getView() * viewer_camera.getAspect();
 		F32 far_clip_meters = viewer_camera.getFar();
 		F32 far_clip_pixels = far_clip_meters * meters_to_pixels;
-
-		F32 half_width_meters = far_clip_meters * tan( horiz_fov / 2 );
-		F32 half_width_pixels = half_width_meters * meters_to_pixels;
 		
-		F32 ctr_x = (F32)center_sw_left;
-		F32 ctr_y = (F32)center_sw_bottom;
+        F32 ctr_x = (F32)center_sw_left;
+        F32 ctr_y = (F32)center_sw_bottom;
 
+        const F32 steps_per_circle = 40.0f;
+        const F32 steps_per_radian = steps_per_circle / F_TWO_PI;
+        const F32 arc_start = -(horiz_fov / 2.0f) + F_PI_BY_TWO;
+        const F32 arc_end = (horiz_fov / 2.0f) + F_PI_BY_TWO;
+        const S32 steps = llmax(1, (S32)((horiz_fov * steps_per_radian) + 0.5f));
 
-		gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+        gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
 		
 		LLColor4 frust_col = rotate_map ? map_frustum_color() : map_frustum_rotating_color();
 		
@@ -617,14 +657,8 @@ void LLNetMap::draw()
 			gGL.rotateUI(rot);
 		}
 
-		gGL.begin( LLRender::TRIANGLES  );
-			gGL.color4fv(frust_col.mV);
-			gGL.vertex2f( 0.f, 0.f );
-			frust_col.mV[VW] *= .1f;
-			gGL.color4fv(frust_col.mV);
-			gGL.vertex2f( half_width_pixels, far_clip_pixels );
-			gGL.vertex2f( -half_width_pixels, far_clip_pixels );
-		gGL.end();
+		gl_washer_segment_2d(far_clip_pixels, 0, arc_start, arc_end, steps, map_frustum_color(), map_frustum_color());
+
 		if (render_guide_line)
 		{
 			gGL.begin(LLRender::LINES);
@@ -702,6 +736,65 @@ void LLNetMap::drawTracking(const LLVector3d& pos_global, const LLColor4& color,
 	}
 }
 
+bool LLNetMap::isMouseOnPopupMenu()
+{
+    if (!mPopupMenu->isOpen())
+    {
+        return false;
+    }
+
+    S32 popup_x;
+    S32 popup_y;
+    LLUI::getInstance()->getMousePositionLocal(mPopupMenu, &popup_x, &popup_y);
+    // *NOTE: Tolerance is larger than it needs to be because the context menu is offset from the mouse when the menu is opened from certain
+    // directions. This may be a quirk of LLMenuGL::showPopup. -Cosmic,2022-03-22
+    constexpr S32 tolerance = 10;
+    // Test tolerance from all four corners, as the popup menu can appear from a different direction if there's not enough space.
+    // Assume the size of the popup menu is much larger than the provided tolerance.
+    // In practice, this is a [tolerance]px margin around the popup menu.
+    for (S32 sign_x = -1; sign_x <= 1; sign_x += 2)
+    {
+        for (S32 sign_y = -1; sign_y <= 1; sign_y += 2)
+        {
+            if (mPopupMenu->pointInView(popup_x + (sign_x * tolerance), popup_y + (sign_y * tolerance)))
+            {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+void LLNetMap::updateAboutLandPopupButton()
+{
+    if (!mPopupMenu->isOpen())
+    {
+        return;
+    }
+
+    LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosGlobal(mPopupWorldPos);
+    if (!region)
+    {
+        mPopupMenu->setItemEnabled("About Land", false);
+    }
+    else
+    {
+        // Check if the mouse is in the bounds of the popup. If so, it's safe to assume no other hover function will be called, so the hover
+        // parcel can be used to check if location-sensitive tooltip options are available.
+        if (isMouseOnPopupMenu())
+        {
+            LLViewerParcelMgr::getInstance()->setHoverParcel(mPopupWorldPos);
+            LLParcel *hover_parcel = LLViewerParcelMgr::getInstance()->getHoverParcel();
+            bool      valid_parcel = false;
+            if (hover_parcel)
+            {
+                valid_parcel = hover_parcel->getOwnerID().notNull();
+            }
+            mPopupMenu->setItemEnabled("About Land", valid_parcel);
+        }
+    }
+}
+
 LLVector3d LLNetMap::viewPosToGlobal( S32 x, S32 y )
 {
 	x -= ll_round(getRect().getWidth() / 2 + mCurPan.mV[VX]);
@@ -729,78 +822,164 @@ LLVector3d LLNetMap::viewPosToGlobal( S32 x, S32 y )
 
 BOOL LLNetMap::handleScrollWheel(S32 x, S32 y, S32 clicks)
 {
-	// note that clicks are reversed from what you'd think: i.e. > 0  means zoom out, < 0 means zoom in
-	F32 new_scale = mScale * pow(MAP_SCALE_ZOOM_FACTOR, -clicks);
+    // note that clicks are reversed from what you'd think: i.e. > 0  means zoom out, < 0 means zoom in
+    F32 new_scale = mScale * pow(MAP_SCALE_ZOOM_FACTOR, -clicks);
 	F32 old_scale = mScale;
 
-	setScale(new_scale);
+    setScale(new_scale);
 
-	static LLUICachedControl<bool> auto_center("MiniMapAutoCenter", true);
-	if (!auto_center)
-	{
-		// Adjust pan to center the zoom on the mouse pointer
-		LLVector2 zoom_offset;
-		zoom_offset.mV[VX] = x - getRect().getWidth() / 2;
-		zoom_offset.mV[VY] = y - getRect().getHeight() / 2;
-		mCurPan -= zoom_offset * mScale / old_scale - zoom_offset;
-	}
+    static LLUICachedControl<bool> auto_center("MiniMapAutoCenter", true);
+    if (!auto_center)
+    {
+        // Adjust pan to center the zoom on the mouse pointer
+        LLVector2 zoom_offset;
+        zoom_offset.mV[VX] = x - getRect().getWidth() / 2;
+        zoom_offset.mV[VY] = y - getRect().getHeight() / 2;
+        mCurPan -= zoom_offset * mScale / old_scale - zoom_offset;
+    }
 
-	return TRUE;
+    return true;
 }
 
-BOOL LLNetMap::handleToolTip( S32 x, S32 y, MASK mask )
+BOOL LLNetMap::handleToolTip(S32 x, S32 y, MASK mask)
 {
-	if (gDisconnected)
-	{
-		return FALSE;
-	}
+    if (gDisconnected)
+    {
+        return false;
+    }
 
-	// If the cursor is near an avatar on the minimap, a mini-inspector will be
-	// shown for the avatar, instead of the normal map tooltip.
+    // If the cursor is near an avatar on the minimap, a mini-inspector will be
+    // shown for the avatar, instead of the normal map tooltip.
 //	if (handleToolTipAgent(mClosestAgentToCursor))
 // [RLVa:KB] - Checked: RLVa-1.2.2
 	bool fRlvCanShowName = (mClosestAgentToCursor.notNull()) && (RlvActions::canShowName(RlvActions::SNC_DEFAULT, mClosestAgentToCursor));
 	if ( (fRlvCanShowName) && (handleToolTipAgent(mClosestAgentToCursor)) )
 // [/RLVa:KB]
-	{
-		return TRUE;
-	}
+    {
+        return true;
+    }
 
 // [RLVa:KB] - Checked: RLVa-1.2.2
 	LLStringUtil::format_map_t args; LLAvatarName avName;
 	args["[AGENT]"] = ( (!fRlvCanShowName) && (mClosestAgentToCursor.notNull()) && (LLAvatarNameCache::get(mClosestAgentToCursor, &avName)) ) ? RlvStrings::getAnonym(avName) + "\n" : "";
 // [/RLVa:KB]
 
-	LLRect sticky_rect;
-	std::string region_name;
-	LLViewerRegion*	region = LLWorld::getInstance()->getRegionFromPosGlobal( viewPosToGlobal( x, y ) );
-	if(region)
-	{
-		// set sticky_rect
-		S32 SLOP = 4;
-		localPointToScreen(x - SLOP, y - SLOP, &(sticky_rect.mLeft), &(sticky_rect.mBottom));
-		sticky_rect.mRight = sticky_rect.mLeft + 2 * SLOP;
-		sticky_rect.mTop = sticky_rect.mBottom + 2 * SLOP;
+    // The popup menu uses the hover parcel when it is open and the mouse is on
+    // top of it, with some additional tolerance. Returning early here prevents
+    // fighting over that hover parcel when getting tooltip info in the
+    // tolerance region.
+    if (isMouseOnPopupMenu())
+    {
+        return false;
+    }
 
-//		region_name = region->getName();
+    LLRect sticky_rect;
+    S32 SLOP = 4;
+    localPointToScreen(x - SLOP, y - SLOP, &(sticky_rect.mLeft), &(sticky_rect.mBottom));
+    sticky_rect.mRight = sticky_rect.mLeft + 2 * SLOP;
+    sticky_rect.mTop   = sticky_rect.mBottom + 2 * SLOP;
+
+    std::string parcel_name_msg;
+    std::string parcel_sale_price_msg;
+    std::string parcel_sale_area_msg;
+    std::string parcel_owner_msg;
+    std::string region_name_msg;
+
+    LLVector3d      posGlobal = viewPosToGlobal(x, y);
+    LLViewerRegion *region    = LLWorld::getInstance()->getRegionFromPosGlobal(posGlobal);
+    if (region)
+    {
+//        std::string region_name = region->getName();
 // [RLVa:KB] - Checked: RLVa-1.2.2
-		region_name = (RlvActions::canShowLocation()) ? region->getName() : RlvStrings::getString(RlvStringKeys::Hidden::Region);
+		std::string region_name = (RlvActions::canShowLocation()) ? region->getName() : RlvStrings::getString(RlvStringKeys::Hidden::Region);
 // [/RLVa:KB]
-		if (!region_name.empty())
-		{
-			region_name += "\n";
-		}
-	}
+        if (!region_name.empty())
+        {
+            region_name_msg = mRegionNameMsg;
+            LLStringUtil::format(region_name_msg, {{"[REGION_NAME]", region_name}});
+        }
 
-//	LLStringUtil::format_map_t args;
-	args["[REGION]"] = region_name;
-	std::string msg = mToolTipMsg;
-	LLStringUtil::format(msg, args);
-	LLToolTipMgr::instance().show(LLToolTip::Params()
-		.message(msg)
-		.sticky_rect(sticky_rect));
-		
-	return TRUE;
+        // Only show parcel information in the tooltip if property lines are visible. Otherwise, the parcel the tooltip is referring to is
+        // ambiguous.
+        if (gSavedSettings.getBOOL("MiniMapShowPropertyLines"))
+        {
+            LLViewerParcelMgr::getInstance()->setHoverParcel(posGlobal);
+            LLParcel *hover_parcel = LLViewerParcelMgr::getInstance()->getHoverParcel();
+            if (hover_parcel)
+            {
+                std::string parcel_name = hover_parcel->getName();
+                if (!parcel_name.empty())
+                {
+                    parcel_name_msg = mParcelNameMsg;
+                    LLStringUtil::format(parcel_name_msg, {{"[PARCEL_NAME]", parcel_name}});
+                }
+
+                const LLUUID      parcel_owner          = hover_parcel->getOwnerID();
+                std::string       parcel_owner_name_url = LLSLURL("agent", parcel_owner, "inspect").getSLURLString();
+                static LLUrlMatch parcel_owner_name_url_match;
+                LLUrlRegistry::getInstance()->findUrl(parcel_owner_name_url, parcel_owner_name_url_match);
+                if (!parcel_owner_name_url_match.empty())
+                {
+                    parcel_owner_msg              = mParcelOwnerMsg;
+                    std::string parcel_owner_name = parcel_owner_name_url_match.getLabel();
+                    LLStringUtil::format(parcel_owner_msg, {{"[PARCEL_OWNER]", parcel_owner_name}});
+                }
+
+                if (hover_parcel->getForSale())
+                {
+                    const LLUUID auth_buyer_id = hover_parcel->getAuthorizedBuyerID();
+                    const LLUUID agent_id      = gAgent.getID();
+                    bool         show_for_sale = auth_buyer_id.isNull() || auth_buyer_id == agent_id || parcel_owner == agent_id;
+                    if (show_for_sale)
+                    {
+                        S32 price        = hover_parcel->getSalePrice();
+                        S32 area         = hover_parcel->getArea();
+                        F32 cost_per_sqm = 0.0f;
+                        if (area > 0)
+                        {
+                            cost_per_sqm = F32(price) / area;
+                        }
+                        std::string formatted_price          = LLResMgr::getInstance()->getMonetaryString(price);
+                        std::string formatted_cost_per_meter = llformat("%.1f", cost_per_sqm);
+                        parcel_sale_price_msg                = mParcelSalePriceMsg;
+                        LLStringUtil::format(parcel_sale_price_msg,
+                                             {{"[PRICE]", formatted_price}, {"[PRICE_PER_SQM]", formatted_cost_per_meter}});
+                        std::string formatted_area = llformat("%d", area);
+                        parcel_sale_area_msg       = mParcelSaleAreaMsg;
+                        LLStringUtil::format(parcel_sale_area_msg, {{"[AREA]", formatted_area}});
+                    }
+                }
+            }
+        }
+    }
+
+    std::string tool_tip_hint_msg;
+    if (gSavedSettings.getBOOL("DoubleClickTeleport"))
+    {
+        tool_tip_hint_msg = mAltToolTipHintMsg;
+    }
+    else if (gSavedSettings.getBOOL("DoubleClickShowWorldMap"))
+    {
+        tool_tip_hint_msg = mToolTipHintMsg;
+    }
+
+    LLStringUtil::format_map_t args;
+    args["[PARCEL_NAME_MSG]"]       = parcel_name_msg.empty() ? "" : parcel_name_msg + '\n';
+    args["[PARCEL_SALE_PRICE_MSG]"] = parcel_sale_price_msg.empty() ? "" : parcel_sale_price_msg + '\n';
+    args["[PARCEL_SALE_AREA_MSG]"]  = parcel_sale_area_msg.empty() ? "" : parcel_sale_area_msg + '\n';
+    args["[PARCEL_OWNER_MSG]"]      = parcel_owner_msg.empty() ? "" : parcel_owner_msg + '\n';
+    args["[REGION_NAME_MSG]"]       = region_name_msg.empty() ? "" : region_name_msg + '\n';
+    args["[TOOL_TIP_HINT_MSG]"]     = tool_tip_hint_msg.empty() ? "" : tool_tip_hint_msg + '\n';
+
+    std::string msg                 = mToolTipMsg;
+    LLStringUtil::format(msg, args);
+    if (msg.back() == '\n')
+    {
+        msg.resize(msg.size() - 1);
+    }
+    LLToolTipMgr::instance().show(LLToolTip::Params().message(msg).sticky_rect(sticky_rect));
+
+    return true;
 }
 
 BOOL LLNetMap::handleToolTipAgent(const LLUUID& avatar_id)
@@ -1092,50 +1271,48 @@ void LLNetMap::createParcelImage()
 	mUpdateParcelImage = true;
 }
 
-BOOL LLNetMap::handleMouseDown( S32 x, S32 y, MASK mask )
+BOOL LLNetMap::handleMouseDown(S32 x, S32 y, MASK mask)
 {
-	if (!(mask & MASK_SHIFT)) return FALSE;
-
-	// Start panning
-	gFocusMgr.setMouseCapture(this);
+    // Start panning
+    gFocusMgr.setMouseCapture(this);
 
-	mStartPan = mCurPan;
-	mMouseDown.mX = x;
-	mMouseDown.mY = y;
-	return TRUE;
+    mStartPan     = mCurPan;
+    mMouseDown.mX = x;
+    mMouseDown.mY = y;
+    return true;
 }
 
-BOOL LLNetMap::handleMouseUp( S32 x, S32 y, MASK mask )
+BOOL LLNetMap::handleMouseUp(S32 x, S32 y, MASK mask)
 {
-	if(abs(mMouseDown.mX-x)<3 && abs(mMouseDown.mY-y)<3)
-		handleClick(x,y,mask);
+    if (abs(mMouseDown.mX - x) < 3 && abs(mMouseDown.mY - y) < 3)
+    {
+        handleClick(x, y, mask);
+    }
 
-	if (hasMouseCapture())
-	{
-		if (mPanning)
-		{
-			// restore mouse cursor
-			S32 local_x, local_y;
-			local_x = mMouseDown.mX + llfloor(mCurPan.mV[VX] - mStartPan.mV[VX]);
-			local_y = mMouseDown.mY + llfloor(mCurPan.mV[VY] - mStartPan.mV[VY]);
-			LLRect clip_rect = getRect();
-			clip_rect.stretch(-8);
-			clip_rect.clipPointToRect(mMouseDown.mX, mMouseDown.mY, local_x, local_y);
+    if (hasMouseCapture())
+    {
+        if (mPanning)
+        {
+            // restore mouse cursor
+            S32 local_x, local_y;
+            local_x          = mMouseDown.mX + llfloor(mCurPan.mV[VX] - mStartPan.mV[VX]);
+            local_y          = mMouseDown.mY + llfloor(mCurPan.mV[VY] - mStartPan.mV[VY]);
+            LLRect clip_rect = getRect();
+            clip_rect.stretch(-8);
+            clip_rect.clipPointToRect(mMouseDown.mX, mMouseDown.mY, local_x, local_y);
 			LLUI::setMousePositionLocal(this, local_x, local_y);
 
-			// finish the pan
-			mPanning = false;
+            // finish the pan
+            mPanning = false;
 
-			mMouseDown.set(0, 0);
+            mMouseDown.set(0, 0);
+        }
+        gViewerWindow->showCursor();
+        gFocusMgr.setMouseCapture(NULL);
+        return true;
+    }
 
-			// auto centre
-			mTargetPan.setZero();
-		}
-		gViewerWindow->showCursor();
-		gFocusMgr.setMouseCapture(NULL);
-		return TRUE;
-	}
-	return FALSE;
+    return false;
 }
 
 BOOL LLNetMap::handleRightMouseDown(S32 x, S32 y, MASK mask)
@@ -1143,9 +1320,10 @@ BOOL LLNetMap::handleRightMouseDown(S32 x, S32 y, MASK mask)
 	auto menu = static_cast<LLMenuGL*>(mPopupMenuHandle.get());
 	if (menu)
 	{
+        mPopupWorldPos = viewPosToGlobal(x, y);
 		menu->buildDrawLabels();
 		menu->updateParent(LLMenuGL::sMenuContainer);
-		menu->setItemEnabled("Stop Tracking", LLTracker::isTracking(0));
+		menu->setItemEnabled("Stop tracking", LLTracker::isTracking(0));
 		LLMenuGL::showPopup(this, menu, x, y);
 	}
 	return TRUE;
@@ -1193,6 +1371,27 @@ BOOL LLNetMap::handleDoubleClick(S32 x, S32 y, MASK mask)
 	return TRUE;
 }
 
+F32 LLNetMap::getScaleForName(std::string scale_name)
+{
+    if (scale_name == "very close")
+    {
+        return LLNetMap::MAP_SCALE_VERY_CLOSE;
+    }
+    else if (scale_name == "close")
+    {
+        return LLNetMap::MAP_SCALE_CLOSE;
+    }
+    else if (scale_name == "medium")
+    {
+        return LLNetMap::MAP_SCALE_MEDIUM;
+    }
+    else if (scale_name == "far")
+    {
+        return LLNetMap::MAP_SCALE_FAR;
+    }
+    return 0.0f;
+}
+
 // static
 bool LLNetMap::outsideSlop( S32 x, S32 y, S32 start_x, S32 start_y, S32 slop )
 {
@@ -1210,7 +1409,7 @@ BOOL LLNetMap::handleHover( S32 x, S32 y, MASK mask )
 		{
 			if (!mPanning)
 			{
-				// just started panning, so hide cursor
+                // Just started panning. Hide cursor.
 				mPanning = true;
 				gViewerWindow->hideCursor();
 			}
@@ -1220,54 +1419,40 @@ BOOL LLNetMap::handleHover( S32 x, S32 y, MASK mask )
 
 			// Set pan to value at start of drag + offset
 			mCurPan += delta;
-			mTargetPan = mCurPan;
 
 			gViewerWindow->moveCursorToCenter();
 		}
-
-		// Doesn't really matter, cursor should be hidden
-		gViewerWindow->setCursor( UI_CURSOR_TOOLPAN );
-	}
-	else
-	{
-		if (mask & MASK_SHIFT)
-		{
-			// If shift is held, change the cursor to hint that the map can be dragged
-			gViewerWindow->setCursor( UI_CURSOR_TOOLPAN );
-		}
-		else
-		{
-			gViewerWindow->setCursor( UI_CURSOR_CROSS );
-		}
 	}
 
+    if (mask & MASK_SHIFT)
+    {
+        // If shift is held, change the cursor to hint that the map can be
+        // dragged. However, holding shift is not required to drag the map.
+        gViewerWindow->setCursor( UI_CURSOR_TOOLPAN );
+    }
+    else
+    {
+        gViewerWindow->setCursor( UI_CURSOR_CROSS );
+    }
+
 	return TRUE;
 }
 
-void LLNetMap::handleZoom(const LLSD& userdata)
+bool LLNetMap::isZoomChecked(const LLSD &userdata)
 {
-	std::string level = userdata.asString();
-	
-	F32 scale = 0.0f;
-	if (level == std::string("default"))
-	{
-		LLControlVariable *pvar = gSavedSettings.getControl("MiniMapScale");
-		if(pvar)
-		{
-			pvar->resetToDefault();
-			scale = gSavedSettings.getF32("MiniMapScale");
-		}
-	}
-	else if (level == std::string("close"))
-		scale = LLNetMap::MAP_SCALE_MAX;
-	else if (level == std::string("medium"))
-		scale = LLNetMap::MAP_SCALE_MID;
-	else if (level == std::string("far"))
-		scale = LLNetMap::MAP_SCALE_MIN;
-	if (scale != 0.0f)
-	{
-		setScale(scale);
-	}
+    std::string level = userdata.asString();
+    F32         scale = getScaleForName(level);
+    return scale == mScale;
+}
+
+void LLNetMap::setZoom(const LLSD &userdata)
+{
+    std::string level = userdata.asString();
+    F32         scale = getScaleForName(level);
+    if (scale != 0.0f)
+    {
+        setScale(scale);
+    }
 }
 
 void LLNetMap::handleStopTracking (const LLSD& userdata)
@@ -1275,7 +1460,49 @@ void LLNetMap::handleStopTracking (const LLSD& userdata)
 	auto menu = static_cast<LLMenuGL*>(mPopupMenuHandle.get());
 	if (menu)
 	{
-		menu->setItemEnabled ("Stop Tracking", false);
+		menu->setItemEnabled ("Stop tracking", false);
 		LLTracker::stopTracking (LLTracker::isTracking(NULL));
 	}
 }
+
+void LLNetMap::activateCenterMap(const LLSD &userdata) { mCentering = true; }
+
+bool LLNetMap::isMapOrientationChecked(const LLSD &userdata)
+{
+    const std::string command_name = userdata.asString();
+    const bool        rotate_map   = gSavedSettings.getBOOL("MiniMapRotate");
+    if (command_name == "north_at_top")
+    {
+        return !rotate_map;
+    }
+
+    if (command_name == "camera_at_top")
+    {
+        return rotate_map;
+    }
+
+    return false;
+}
+
+void LLNetMap::setMapOrientation(const LLSD &userdata)
+{
+    const std::string command_name = userdata.asString();
+    if (command_name == "north_at_top")
+    {
+        gSavedSettings.setBOOL("MiniMapRotate", false);
+    }
+    else if (command_name == "camera_at_top")
+    {
+        gSavedSettings.setBOOL("MiniMapRotate", true);
+    }
+}
+
+void LLNetMap::popupShowAboutLand(const LLSD &userdata)
+{
+    // Update parcel selection. It's important to deselect land first so the "About Land" floater doesn't refresh with the old selection.
+    LLViewerParcelMgr::getInstance()->deselectLand();
+    LLParcelSelectionHandle selection = LLViewerParcelMgr::getInstance()->selectParcelAt(mPopupWorldPos);
+    gMenuHolder->setParcelSelection(selection);
+
+    LLFloaterReg::showInstance("about_land", LLSD(), false);
+}
diff --git a/indra/newview/llnetmap.h b/indra/newview/llnetmap.h
index bef77b2ab0e083223f0807033aa32f580012444b..76e9e1ca0bc516f175738cd279859af70407aa09 100644
--- a/indra/newview/llnetmap.h
+++ b/indra/newview/llnetmap.h
@@ -63,9 +63,12 @@ class LLNetMap final : public LLUICtrl
 public:
 	virtual ~LLNetMap();
 
-	static const F32 MAP_SCALE_MIN;
-	static const F32 MAP_SCALE_MID;
-	static const F32 MAP_SCALE_MAX;
+    static const F32 MAP_SCALE_MIN;
+    static const F32 MAP_SCALE_FAR;
+    static const F32 MAP_SCALE_MEDIUM;
+    static const F32 MAP_SCALE_CLOSE;
+    static const F32 MAP_SCALE_VERY_CLOSE;
+    static const F32 MAP_SCALE_MAX;
 
 	/*virtual*/ void	draw() override;
 	/*virtual*/ BOOL	handleScrollWheel(S32 x, S32 y, S32 clicks) override;
@@ -82,8 +85,17 @@ class LLNetMap final : public LLUICtrl
 
 	void			refreshParcelOverlay() { mUpdateParcelImage = true; }
 
-	void			setScale( F32 scale );
-	void			setToolTipMsg(const std::string& msg) { mToolTipMsg = msg; }
+    void            setScale(F32 scale);
+
+    void            setToolTipMsg(const std::string& msg) { mToolTipMsg = msg; }
+    void            setParcelNameMsg(const std::string& msg) { mParcelNameMsg = msg; }
+    void            setParcelSalePriceMsg(const std::string& msg) { mParcelSalePriceMsg = msg; }
+    void            setParcelSaleAreaMsg(const std::string& msg) { mParcelSaleAreaMsg = msg; }
+    void            setParcelOwnerMsg(const std::string& msg) { mParcelOwnerMsg = msg; }
+    void            setRegionNameMsg(const std::string& msg) { mRegionNameMsg = msg; }
+    void            setToolTipHintMsg(const std::string& msg) { mToolTipHintMsg = msg; }
+    void            setAltToolTipHintMsg(const std::string& msg) { mAltToolTipHintMsg = msg; }
+
 	void			renderScaledPointGlobal( const LLVector3d& pos, const LLColor4U &color, F32 radius );
 
 private:
@@ -97,6 +109,8 @@ class LLNetMap final : public LLUICtrl
 	void			drawTracking( const LLVector3d& pos_global, 
 								  const LLColor4& color,
 								  BOOL draw_arrow = TRUE);
+    bool            isMouseOnPopupMenu();
+    void            updateAboutLandPopupButton();
 	BOOL			handleToolTipAgent(const LLUUID& avatar_id);
 	static void		showAvatarInspector(const LLUUID& avatar_id);
 
@@ -105,6 +119,7 @@ class LLNetMap final : public LLUICtrl
 	void			createParcelImage();
 	void			renderPropertyLinesForRegion(const LLViewerRegion* pRegion, const LLColor4U& clrOverlay);
 
+    F32             getScaleForName(std::string scale_name);
 	static bool		outsideSlop(S32 x, S32 y, S32 start_x, S32 start_y, S32 slop);
 
 private:
@@ -119,11 +134,12 @@ class LLNetMap final : public LLUICtrl
 	F32				mObjectMapPixels;		// Width of object map in pixels
 	F32				mDotRadius;				// Size of avatar markers
 
-	bool			mPanning;			// map is being dragged
-	LLVector2		mTargetPan;
-	LLVector2		mCurPan;
-	LLVector2		mStartPan;		// pan offset at start of drag
-	LLCoordGL		mMouseDown;			// pointer position at start of drag
+    bool            mPanning; // map is being dragged
+    bool            mCentering; // map is being re-centered around the agent
+    LLVector2       mCurPan;
+    LLVector2       mStartPan; // pan offset at start of drag
+    LLVector3d      mPopupWorldPos; // world position picked under mouse when context menu is opened
+    LLCoordGL       mMouseDown; // pointer position at start of drag
 
 	LLVector3d		mObjectImageCenterGlobal;
 	LLPointer<LLImageRaw> mObjectRawImagep;
@@ -139,14 +155,26 @@ class LLNetMap final : public LLUICtrl
 	LLUUID			mClosestAgentToCursor;
 	LLUUID			mClosestAgentAtLastRightClick;
 
-	std::string		mToolTipMsg;
+    std::string     mToolTipMsg;
+    std::string     mParcelNameMsg;
+    std::string     mParcelSalePriceMsg;
+    std::string     mParcelSaleAreaMsg;
+    std::string     mParcelOwnerMsg;
+    std::string     mRegionNameMsg;
+    std::string     mToolTipHintMsg;
+    std::string     mAltToolTipHintMsg;
 
 public:
 	void			setSelected(uuid_vec_t uuids) { gmSelected=uuids; };
 
 private:
-	void handleZoom(const LLSD& userdata);
-	void handleStopTracking (const LLSD& userdata);
+    bool isZoomChecked(const LLSD& userdata);
+    void setZoom(const LLSD& userdata);
+    void handleStopTracking(const LLSD& userdata);
+    void activateCenterMap(const LLSD& userdata);
+    bool isMapOrientationChecked(const LLSD& userdata);
+    void setMapOrientation(const LLSD& userdata);
+    void popupShowAboutLand(const LLSD& userdata);
 
 	LLHandle<LLView>		mPopupMenuHandle;
 	uuid_vec_t		gmSelected;
diff --git a/indra/newview/llpatchvertexarray.cpp b/indra/newview/llpatchvertexarray.cpp
index 4b79458c07dfe5bfe9f0de0109fc9df34ac64559..86870787463fdac91d10071c64241f38082ed000 100644
--- a/indra/newview/llpatchvertexarray.cpp
+++ b/indra/newview/llpatchvertexarray.cpp
@@ -69,11 +69,9 @@ void LLPatchVertexArray::create(U32 surface_width, U32 patch_width, F32 meters_p
 	// (The -1 is there because an LLSurface has a buffer of 1 on 
 	// its East and North edges).
 	U32 power_of_two = 1;
-	U32 surface_order = 0;
 	while (power_of_two < (surface_width-1))
 	{
 		power_of_two *= 2;
-		surface_order += 1;
 	}
 
 	if (power_of_two != (surface_width-1))
diff --git a/indra/newview/llphysicsshapebuilderutil.cpp b/indra/newview/llphysicsshapebuilderutil.cpp
index 5bfe5c9941c470443abd6bd3e96170040c240a4a..9603ee63295b0cc58dfff1fb5b3d16d8934b00df 100644
--- a/indra/newview/llphysicsshapebuilderutil.cpp
+++ b/indra/newview/llphysicsshapebuilderutil.cpp
@@ -29,7 +29,7 @@
 #include "llphysicsshapebuilderutil.h"
 
 /* static */
-void LLPhysicsShapeBuilderUtil::determinePhysicsShape( const LLPhysicsVolumeParams& volume_params, const LLVector3& scale, PhysicsShapeSpecification& specOut )
+void LLPhysicsShapeBuilderUtil::determinePhysicsShape( const LLPhysicsVolumeParams& volume_params, const LLVector3& scale, PhysicsShapeSpecification& specOut)
 {
 	const LLProfileParams& profile_params = volume_params.getProfileParams();
 	const LLPathParams& path_params = volume_params.getPathParams();
@@ -191,6 +191,7 @@ void LLPhysicsShapeBuilderUtil::determinePhysicsShape( const LLPhysicsVolumePara
 
 	if ( volume_params.shouldForceConvex() )
 	{
+        // Server distinguishes between convex of a prim vs isSculpt, but we don't care.
 		specOut.mType = PhysicsShapeSpecification::USER_CONVEX;
 	}	
 	// Make a simpler convex shape if we can.
@@ -199,6 +200,16 @@ void LLPhysicsShapeBuilderUtil::determinePhysicsShape( const LLPhysicsVolumePara
 	{
 		specOut.mType = PhysicsShapeSpecification::PRIM_CONVEX;
 	}
+    else if (volume_params.isMeshSculpt() &&
+             // Check overall dimensions, not individual triangles.
+             (scale.mV[0] < SHAPE_BUILDER_USER_MESH_CONVEXIFICATION_SIZE ||
+              scale.mV[1] < SHAPE_BUILDER_USER_MESH_CONVEXIFICATION_SIZE ||
+              scale.mV[2] < SHAPE_BUILDER_USER_MESH_CONVEXIFICATION_SIZE
+              ) )
+    {
+        // Server distinguishes between user-specified or default convex mesh, vs server's thin-triangle override, but we don't.
+        specOut.mType = PhysicsShapeSpecification::PRIM_CONVEX;
+    }
 	else if ( volume_params.isSculpt() ) // Is a sculpt of any kind (mesh or legacy)
 	{
 		specOut.mType = volume_params.isMeshSculpt() ? PhysicsShapeSpecification::USER_MESH : PhysicsShapeSpecification::SCULPT;
diff --git a/indra/newview/llphysicsshapebuilderutil.h b/indra/newview/llphysicsshapebuilderutil.h
index bd5b7d799cc7994855649f12bb469bd1536782d9..b3b100296f48e7cfbd759a28c9ed75d2b16a5a78 100644
--- a/indra/newview/llphysicsshapebuilderutil.h
+++ b/indra/newview/llphysicsshapebuilderutil.h
@@ -47,6 +47,7 @@ const F32 SHAPE_BUILDER_ENTRY_SNAP_SCALE_BIN_SIZE = 0.15f;
 const F32 SHAPE_BUILDER_ENTRY_SNAP_PARAMETER_BIN_SIZE = 0.010f;
 const F32 SHAPE_BUILDER_CONVEXIFICATION_SIZE = 2.f * COLLISION_TOLERANCE;
 const F32 SHAPE_BUILDER_MIN_GEOMETRY_SIZE = 0.5f * COLLISION_TOLERANCE;
+const F32 SHAPE_BUILDER_USER_MESH_CONVEXIFICATION_SIZE = 0.5f;
 
 class LLPhysicsVolumeParams : public LLVolumeParams
 {
diff --git a/indra/newview/llsceneview.cpp b/indra/newview/llsceneview.cpp
index f7aa63e34d8a504696272cb98d0e37503d13f006..e250f9bc7a998730c056b98cc448ad670f229f06 100644
--- a/indra/newview/llsceneview.cpp
+++ b/indra/newview/llsceneview.cpp
@@ -266,14 +266,11 @@ void LLSceneView::draw()
 
 			U32 count = triangles[idx].size();
 
-			U32 total = 0;
-
 			gGL.begin(LLRender::LINE_STRIP);
 			//plot triangles
 			for (U32 i = 0; i < count; ++i)
 			{
 				U32 tri_count = triangles[idx][i];
-				total += tri_count;	
 				F32 y = (F32) (tri_count-tri_domain[0])/triangle_range*tri_rect.getHeight()+tri_rect.mBottom;
 				F32 x = (F32) i / count * tri_rect.getWidth() + tri_rect.mLeft;
 
@@ -290,15 +287,8 @@ void LLSceneView::draw()
 			gGL.end();
 			gGL.flush();
 
-			U32 total_visible = 0;
 			count = visible_triangles[idx].size();
 
-			for (U32 i = 0; i < count; ++i)
-			{
-				U32 tri_count = visible_triangles[idx][i];
-				total_visible += tri_count;	
-			}
-
 			std::string label = llformat("%s Object Triangle Counts (Ktris) -- Visible: %.2f/%.2f (%.2f KB Visible)",
 				category[idx], total_visible_triangles[idx]/1024.f, total_triangles[idx]/1024.f, total_visible_bytes[idx]/1024.f);
 
diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp
index 95302715eb7a8081a38f1403b8b02a744f0d6b3a..e0d0e65be2c2001a08f81990aa9dfb74718d325b 100644
--- a/indra/newview/llspatialpartition.cpp
+++ b/indra/newview/llspatialpartition.cpp
@@ -1573,6 +1573,62 @@ void pushVertsColorCoded(LLSpatialGroup* group, U32 mask)
 	}
 }
 
+// return false if drawable is rigged and:
+//  - a linked rigged drawable has a different spatial group
+//  - a linked rigged drawable face has the wrong draw order index
+bool check_rigged_group(LLDrawable* drawable)
+{
+    if (drawable->isState(LLDrawable::RIGGED))
+    {
+        LLSpatialGroup* group = drawable->getSpatialGroup();
+        LLDrawable* root = drawable->getRoot();
+
+        if (root->isState(LLDrawable::RIGGED) && root->getSpatialGroup() != group)
+        {
+            llassert(false);
+            return false;
+        }
+
+        S32 last_draw_index = -1;
+        if (root->isState(LLDrawable::RIGGED))
+        {
+            for (auto& face : root->getFaces())
+            {
+                if ((S32) face->getDrawOrderIndex() <= last_draw_index)
+                {
+                    llassert(false);
+                    return false;
+                }
+                last_draw_index = face->getDrawOrderIndex();
+            }
+        }
+
+        for (auto& child : root->getVObj()->getChildren())
+        {
+            if (child->mDrawable->isState(LLDrawable::RIGGED))
+            {
+                for (auto& face : child->mDrawable->getFaces())
+                {
+                    if ((S32) face->getDrawOrderIndex() <= last_draw_index)
+                    {
+                        llassert(false);
+                        return false;
+                    }
+                    last_draw_index = face->getDrawOrderIndex();
+                }
+            }
+            
+            if (child->mDrawable->getSpatialGroup() != group)
+            {
+                llassert(false);
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
 void renderOctree(LLSpatialGroup* group)
 {
 	//render solid object bounding box, color
@@ -1617,6 +1673,9 @@ void renderOctree(LLSpatialGroup* group)
 				{
 					continue;
 				}
+
+                llassert(check_rigged_group(drawable));
+
 				if (!group->getSpatialPartition()->isBridge())
 				{
 					gGL.pushMatrix();
@@ -3025,7 +3084,7 @@ class LLRenderOctreeRaycast : public LLOctreeTriangleRayIntersect
 
 	}
 
-	void visit(const LLOctreeNode<LLVolumeTriangle>* branch)
+    void visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* branch)
 	{
 		LLVolumeOctreeListener* vl = (LLVolumeOctreeListener*) branch->getListener(0);
 
@@ -3067,7 +3126,7 @@ class LLRenderOctreeRaycast : public LLOctreeTriangleRayIntersect
 			}
 
 			gGL.begin(LLRender::TRIANGLES);
-			for (LLOctreeNode<LLVolumeTriangle>::const_element_iter iter = branch->getDataBegin(), end = branch->getDataEnd();
+            for (LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>::const_element_iter iter = branch->getDataBegin(), end = branch->getDataEnd();
 					iter != end;
 					++iter)
 			{
@@ -3157,14 +3216,14 @@ void renderRaycast(LLDrawable* drawablep)
 					{
 						F32 t = 1.f;
 
-						if (!face.mOctree)
+                        if (!face.getOctree())
 						{
 							((LLVolumeFace*) &face)->createOctree(); 
 						}
 
 						LLRenderOctreeRaycast render(start, dir, &t);
 					
-						render.traverse(face.mOctree);
+                        render.traverse(face.getOctree());
 					}
 
 					gGL.popMatrix();		
@@ -3783,7 +3842,7 @@ BOOL LLSpatialPartition::isVisible(const LLVector3& v)
 }
 
 LL_ALIGN_PREFIX(16)
-class LLOctreeIntersect : public LLOctreeTraveler<LLViewerOctreeEntry>
+class LLOctreeIntersect : public LLOctreeTraveler<LLViewerOctreeEntry, LLPointer<LLViewerOctreeEntry>>
 {
 public:
 	LL_ALIGN_16(LLVector4a mStart);
diff --git a/indra/newview/llspatialpartition.h b/indra/newview/llspatialpartition.h
index 5544a99fac45e0eee3ed7aefaad3df8fad5834e0..1b82b37682a3f4cfa6b05a58685e899d26e2f18f 100644
--- a/indra/newview/llspatialpartition.h
+++ b/indra/newview/llspatialpartition.h
@@ -266,7 +266,7 @@ class LLSpatialGroup final : public LLOcclusionCullingGroup
                 return lhs->mAvatarp < rhs->mAvatarp;
             }
 
-            return lhs->mRenderOrder > rhs->mRenderOrder;
+            return lhs->mRenderOrder < rhs->mRenderOrder;
         }
     };
 
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 160cc207266d65bc736c8f8a4b1b0f08f2e3740e..6e4106eb036c09cbc444f13e78aa4d2b90373d0b 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -693,7 +693,7 @@ bool idle_startup()
 #else
 				void* window_handle = NULL;
 #endif
-				bool init = gAudiop->init(kAUDIO_NUM_SOURCES, window_handle, LLAppViewer::instance()->getSecondLifeTitle());
+				bool init = gAudiop->init(window_handle, LLAppViewer::instance()->getSecondLifeTitle());
 				if(init)
 				{
 					gAudiop->setMuted(TRUE);
diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp
index 1c90d01b5dc785bd2d721f2e6714be339e89de57..567d1f0bc85ecd05ca8096d88f4b5190470b9191 100644
--- a/indra/newview/lltexturecache.cpp
+++ b/indra/newview/lltexturecache.cpp
@@ -1048,7 +1048,8 @@ void LLTextureCache::setReadOnly(BOOL read_only)
 	mReadOnly = read_only ;
 }
 
-//called in the main thread.
+// Called in the main thread.
+// Returns the unused amount of max_size if any
 S64 LLTextureCache::initCache(ELLPath location, S64 max_size, BOOL texture_cache_mismatch)
 {
 	llassert_always(getPending() == 0) ; //should not start accessing the texture cache before initialized.
diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp
index 8735517cfa6b623fcc98fcf069800011f0987190..e8fcb7bf952f9cb61ba0c4280db78542d101654b 100644
--- a/indra/newview/lltooldraganddrop.cpp
+++ b/indra/newview/lltooldraganddrop.cpp
@@ -60,6 +60,7 @@
 #include "llvoavatarself.h"
 #include "llworld.h"
 #include "llpanelface.h"
+#include "lluiusage.h"
 // [RLVa:KB] - Checked: 2011-05-22 (RLVa-1.3.1)
 #include "rlvactions.h"
 #include "rlvhandler.h"
@@ -1324,10 +1325,12 @@ void LLToolDragAndDrop::dropObject(LLViewerObject* raycast_target,
 	LLMessageSystem* msg = gMessageSystem;
 	if (mSource == SOURCE_NOTECARD)
 	{
+		LLUIUsage::instance().logCommand("Object.RezObjectFromNotecard");
 		msg->newMessageFast(_PREHASH_RezObjectFromNotecard);
 	}
 	else
 	{
+		LLUIUsage::instance().logCommand("Object.RezObject");
 		msg->newMessageFast(_PREHASH_RezObject);
 	}
 	msg->nextBlockFast(_PREHASH_AgentData);
diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp
index a54c96161b3f34b63650cc73f39f906982e500b8..99e400504303454fb3290e74a232284d398a6fff 100644
--- a/indra/newview/lltoolpie.cpp
+++ b/indra/newview/lltoolpie.cpp
@@ -71,6 +71,7 @@
 #include "llui.h"
 #include "llweb.h"
 #include "pipeline.h"	// setHighlightObject
+#include "lluiusage.h"
 // [RLVa:KB] - Checked: 2010-03-06 (RLVa-1.2.0c)
 #include "rlvactions.h"
 #include "rlvhandler.h"
@@ -628,6 +629,8 @@ bool LLToolPie::walkToClickedLocation()
         return false;
     }
 
+	LLUIUsage::instance().logCommand("Agent.WalkToClickedLocation");
+	
     LLPickInfo saved_pick = mPick;
     if (gAgentCamera.getCameraMode() != CAMERA_MODE_MOUSELOOK)
     {
diff --git a/indra/newview/lltoolplacer.cpp b/indra/newview/lltoolplacer.cpp
index 8ebd470bb4e87fe61c531722af017dcc2ffcfddc..6a9dc84ded187daf76305c0d0e120a0c2f4dcd58 100644
--- a/indra/newview/lltoolplacer.cpp
+++ b/indra/newview/lltoolplacer.cpp
@@ -66,6 +66,7 @@
 #include "llprimitive.h"
 #include "llwindow.h"			// incBusyCount()
 #include "material_codes.h"
+#include "lluiusage.h"
 
 #include "llparcel.h"
 #include "llviewerparcelmgr.h"
@@ -265,6 +266,7 @@ BOOL LLToolPlacer::addObject( LLPCode pcode, S32 x, S32 y, U8 use_physics )
 							   gAgent.getID(), 1.0f, LLAudioEngine::AUDIO_TYPE_UI);
 	}
 
+	LLUIUsage::instance().logCommand("Build.ObjectAdd");
 	gMessageSystem->newMessageFast(_PREHASH_ObjectAdd);
 	gMessageSystem->nextBlockFast(_PREHASH_AgentData);
 	gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
diff --git a/indra/newview/llviewerassetupload.cpp b/indra/newview/llviewerassetupload.cpp
index e5f795360c1e975b24b60a1e8962c34c49a20699..3f498bfe1b717fc8ff290c5a762266b746d4bda4 100644
--- a/indra/newview/llviewerassetupload.cpp
+++ b/indra/newview/llviewerassetupload.cpp
@@ -51,6 +51,10 @@
 #include "llpreviewnotecard.h"
 #include "llpreviewgesture.h"
 #include "llcoproceduremanager.h"
+#include "llthread.h"
+#include "llkeyframemotion.h"
+#include "lldatapacker.h"
+#include "llvoavatarself.h"
 #include "lldatapacker.h"
 #include "llbvhloader.h"
 #include "llbvhconsts.h"
@@ -491,9 +495,62 @@ LLSD LLNewFileResourceUploadInfo::exportTempFile()
         }
         infile.close();
     }
-    else if (assetType == LLAssetType::AT_ANIMATION)
+    else if (exten == "anim")
+    {
+		// Default unless everything succeeds
+		errorLabel = "ProblemWithFile";
+		error = true;
+
+        // read from getFileName()
+		LLAPRFile infile;
+		infile.open(getFileName(),LL_APR_RB);
+		if (!infile.getFileHandle())
+		{
+			LL_WARNS() << "Couldn't open file for reading: " << getFileName() << LL_ENDL;
+			errorMessage = llformat("Failed to open animation file %s\n", getFileName().c_str());
+		}
+		else
+		{
+			S32 size = LLAPRFile::size(getFileName());
+			U8* buffer = new U8[size];
+			S32 size_read = infile.read(buffer,size);
+			if (size_read != size)
+			{
+				errorMessage = llformat("Failed to read animation file %s: wanted %d bytes, got %d\n", getFileName().c_str(), size, size_read);
+			}
+			else
+			{
+				LLDataPackerBinaryBuffer dp(buffer, size);
+				LLKeyframeMotion *motionp = new LLKeyframeMotion(getAssetId());
+				motionp->setCharacter(gAgentAvatarp);
+				if (motionp->deserialize(dp, getAssetId(), false))
+				{
+					// write to temp file
+					bool succ = motionp->dumpToFile(filename);
+					if (succ)
+					{
+						assetType = LLAssetType::AT_ANIMATION;
+						errorLabel = "";
+						error = false;
+					}
+					else
+					{
+						errorMessage = "Failed saving temporary animation file";
+					}
+				}
+				else
+				{
+					errorMessage = "Failed reading animation file";
+				}
+			}
+		}
+    }
+    else
     {
-        filename = getFileName();
+        // Unknown extension
+        errorMessage = llformat(LLTrans::getString("UnknownFileExtension").c_str(), exten.c_str());
+        errorLabel = "ErrorMessage";
+        error = TRUE;;
     }
 
     if (error)
@@ -911,6 +968,7 @@ void LLViewerAssetUpload::HandleUploadError(LLCore::HttpStatus status, LLSD &res
     {
         args["FILE"] = uploadInfo->getDisplayName();
         args["REASON"] = reason;
+        args["ERROR"] = reason;
     }
 
     LLNotificationsUtil::add(label, args);
diff --git a/indra/newview/llvieweraudio.h b/indra/newview/llvieweraudio.h
index ef5560c5070e04e9515f7b736bf2f48e663d1ac0..652e403381a360f355443f0916d022d9eb540066 100644
--- a/indra/newview/llvieweraudio.h
+++ b/indra/newview/llvieweraudio.h
@@ -33,8 +33,6 @@
 // comment out to turn off wind
 #define kAUDIO_ENABLE_WIND 
 //#define kAUDIO_ENABLE_WATER 1	// comment out to turn off water
-#define kAUDIO_NUM_BUFFERS 30
-#define kAUDIO_NUM_SOURCES 30 
 
 void init_audio();
 void audio_update_volume(bool force_update = true);
diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
index 66090dfd6c6f7cdf67964bb18467724ac376d97f..19125c85c40fb6885eacd687c2d96ebe36efd8cb 100644
--- a/indra/newview/llviewercontrol.cpp
+++ b/indra/newview/llviewercontrol.cpp
@@ -678,164 +678,193 @@ bool toggle_show_object_render_cost(const LLSD& newvalue)
 void handleRenderAutoMuteByteLimitChanged(const LLSD& new_value);
 ////////////////////////////////////////////////////////////////////////////
 
+LLPointer<LLControlVariable> setting_get_control(LLControlGroup& group, const std::string& setting)
+{
+    LLPointer<LLControlVariable> cntrl_ptr = group.getControl(setting);
+    if (cntrl_ptr.isNull())
+    {
+        LL_ERRS() << "Unable to set up setting listener for " << setting
+            << ". Please reinstall viewer from  https ://secondlife.com/support/downloads/ and contact https://support.secondlife.com if issue persists after reinstall."
+            << LL_ENDL;
+    }
+    return cntrl_ptr;
+}
+
+void setting_setup_signal_listener(LLControlGroup& group, const std::string& setting, std::function<void(const LLSD& newvalue)> callback)
+{
+    setting_get_control(group, setting)->getSignal()->connect([callback](LLControlVariable* control, const LLSD& new_val, const LLSD& old_val)
+    {
+        callback(new_val);
+    });
+}
+
+void setting_setup_signal_listener(LLControlGroup& group, const std::string& setting, std::function<void()> callback)
+{
+    setting_get_control(group, setting)->getSignal()->connect([callback](LLControlVariable* control, const LLSD& new_val, const LLSD& old_val)
+    {
+        callback();
+    });
+}
+
 void settings_setup_listeners()
 {
-	gSavedSettings.getControl("FirstPersonAvatarVisible")->getSignal()->connect(boost::bind(&handleRenderAvatarMouselookChanged, _2));
-	gSavedSettings.getControl("RenderFarClip")->getSignal()->connect(boost::bind(&handleRenderFarClipChanged, _2));
-	gSavedSettings.getControl("RenderTerrainDetail")->getSignal()->connect(boost::bind(&handleTerrainDetailChanged, _2));
-	gSavedSettings.getControl("OctreeStaticObjectSizeFactor")->getSignal()->connect(boost::bind(&handleRepartition, _2));
-	gSavedSettings.getControl("OctreeDistanceFactor")->getSignal()->connect(boost::bind(&handleRepartition, _2));
-	gSavedSettings.getControl("OctreeMaxNodeCapacity")->getSignal()->connect(boost::bind(&handleRepartition, _2));
-	gSavedSettings.getControl("OctreeAlphaDistanceFactor")->getSignal()->connect(boost::bind(&handleRepartition, _2));
-	gSavedSettings.getControl("OctreeAttachmentSizeFactor")->getSignal()->connect(boost::bind(&handleRepartition, _2));
-	gSavedSettings.getControl("RenderMaxTextureIndex")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2));
-	gSavedSettings.getControl("RenderUseTriStrips")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2));
-	gSavedSettings.getControl("RenderUIBuffer")->getSignal()->connect(boost::bind(&handleWindowResized, _2));
-	gSavedSettings.getControl("RenderDepthOfField")->getSignal()->connect(boost::bind(&handleReleaseGLBufferChanged, _2));
-	gSavedSettings.getControl("RenderFSAASamples")->getSignal()->connect(boost::bind(&handleReleaseGLBufferChanged, _2));
-	gSavedSettings.getControl("RenderSpecularResX")->getSignal()->connect(boost::bind(&handleLUTBufferChanged, _2));
-	gSavedSettings.getControl("RenderSpecularResY")->getSignal()->connect(boost::bind(&handleLUTBufferChanged, _2));
-	gSavedSettings.getControl("RenderSpecularExponent")->getSignal()->connect(boost::bind(&handleLUTBufferChanged, _2));
-	gSavedSettings.getControl("RenderShadowResolutionScale")->getSignal()->connect(boost::bind(&handleShadowsResized, _2));
-	gSavedSettings.getControl("RenderGlow")->getSignal()->connect(boost::bind(&handleReleaseGLBufferChanged, _2));
-	gSavedSettings.getControl("RenderGlow")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2));
-	gSavedSettings.getControl("RenderGlowResolutionPow")->getSignal()->connect(boost::bind(&handleReleaseGLBufferChanged, _2));
-	gSavedSettings.getControl("RenderAvatarCloth")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2));
-	gSavedSettings.getControl("WindLightUseAtmosShaders")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2));
-	gSavedSettings.getControl("RenderGammaFull")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2));
-	gSavedSettings.getControl("RenderVolumeLODFactor")->getSignal()->connect(boost::bind(&handleVolumeLODChanged, _2));
-	gSavedSettings.getControl("RenderAvatarLODFactor")->getSignal()->connect(boost::bind(&handleAvatarLODChanged, _2));
-	gSavedSettings.getControl("RenderAvatarPhysicsLODFactor")->getSignal()->connect(boost::bind(&handleAvatarPhysicsLODChanged, _2));
-	gSavedSettings.getControl("RenderTerrainLODFactor")->getSignal()->connect(boost::bind(&handleTerrainLODChanged, _2));
-	gSavedSettings.getControl("RenderTreeLODFactor")->getSignal()->connect(boost::bind(&handleTreeLODChanged, _2));
-	gSavedSettings.getControl("RenderFlexTimeFactor")->getSignal()->connect(boost::bind(&handleFlexLODChanged, _2));
-	gSavedSettings.getControl("RenderGamma")->getSignal()->connect(boost::bind(&handleGammaChanged, _2));
-	gSavedSettings.getControl("RenderFogRatio")->getSignal()->connect(boost::bind(&handleFogRatioChanged, _2));
-	gSavedSettings.getControl("RenderMaxPartCount")->getSignal()->connect(boost::bind(&handleMaxPartCountChanged, _2));
-	gSavedSettings.getControl("RenderDynamicLOD")->getSignal()->connect(boost::bind(&handleRenderDynamicLODChanged, _2));
-	gSavedSettings.getControl("RenderLocalLights")->getSignal()->connect(boost::bind(&handleRenderLocalLightsChanged, _2));
-	gSavedSettings.getControl("RenderDebugTextureBind")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2));
-	gSavedSettings.getControl("RenderAutoMaskAlphaDeferred")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2));
-	gSavedSettings.getControl("RenderAutoMaskAlphaNonDeferred")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2));
+    setting_setup_signal_listener(gSavedSettings, "FirstPersonAvatarVisible", handleRenderAvatarMouselookChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderFarClip", handleRenderFarClipChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderTerrainDetail", handleTerrainDetailChanged);
+	setting_setup_signal_listener(gSavedSettings, "OctreeStaticObjectSizeFactor", handleRepartition);
+	setting_setup_signal_listener(gSavedSettings, "OctreeDistanceFactor", handleRepartition);
+	setting_setup_signal_listener(gSavedSettings, "OctreeMaxNodeCapacity", handleRepartition);
+	setting_setup_signal_listener(gSavedSettings, "OctreeAlphaDistanceFactor", handleRepartition);
+	setting_setup_signal_listener(gSavedSettings, "OctreeAttachmentSizeFactor", handleRepartition);
+	setting_setup_signal_listener(gSavedSettings, "RenderMaxTextureIndex", handleSetShaderChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderUseTriStrips", handleResetVertexBuffersChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderUIBuffer", handleWindowResized);
+	setting_setup_signal_listener(gSavedSettings, "RenderDepthOfField", handleReleaseGLBufferChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderFSAASamples", handleReleaseGLBufferChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderSpecularResX", handleLUTBufferChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderSpecularResY", handleLUTBufferChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderSpecularExponent", handleLUTBufferChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderShadowResolutionScale", handleShadowsResized);
+	setting_setup_signal_listener(gSavedSettings, "RenderGlow", handleReleaseGLBufferChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderGlow", handleSetShaderChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderGlowResolutionPow", handleReleaseGLBufferChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderAvatarCloth", handleSetShaderChanged);
+	setting_setup_signal_listener(gSavedSettings, "WindLightUseAtmosShaders", handleSetShaderChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderGammaFull", handleSetShaderChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderVolumeLODFactor", handleVolumeLODChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderAvatarLODFactor", handleAvatarLODChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderAvatarPhysicsLODFactor", handleAvatarPhysicsLODChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderTerrainLODFactor", handleTerrainLODChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderTreeLODFactor", handleTreeLODChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderFlexTimeFactor", handleFlexLODChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderGamma", handleGammaChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderFogRatio", handleFogRatioChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderMaxPartCount", handleMaxPartCountChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderDynamicLOD", handleRenderDynamicLODChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderLocalLights", handleRenderLocalLightsChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderDebugTextureBind", handleResetVertexBuffersChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderAutoMaskAlphaDeferred", handleResetVertexBuffersChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderAutoMaskAlphaNonDeferred", handleResetVertexBuffersChanged);
 	gSavedSettings.getControl("RenderAutoMaskAlphaUseRMSE")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2));
 	gSavedSettings.getControl("RenderAutoMaskAlphaMaxRMSE")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2));
 	gSavedSettings.getControl("RenderAutoMaskAlphaMaxMid")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2));
-	gSavedSettings.getControl("RenderObjectBump")->getSignal()->connect(boost::bind(&handleRenderBumpChanged, _2));
-	gSavedSettings.getControl("RenderMaxVBOSize")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2));
-    gSavedSettings.getControl("RenderVSyncEnable")->getSignal()->connect(boost::bind(&handleVSyncChanged, _2));
-	gSavedSettings.getControl("RenderDeferredNoise")->getSignal()->connect(boost::bind(&handleReleaseGLBufferChanged, _2));
-	gSavedSettings.getControl("RenderDebugPipeline")->getSignal()->connect(boost::bind(&handleRenderDebugPipelineChanged, _2));
+	setting_setup_signal_listener(gSavedSettings, "RenderObjectBump", handleRenderBumpChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderMaxVBOSize", handleResetVertexBuffersChanged);
+    setting_setup_signal_listener(gSavedSettings, "RenderVSyncEnable", handleVSyncChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderDeferredNoise", handleReleaseGLBufferChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderDebugPipeline", handleRenderDebugPipelineChanged);
 	gSavedSettings.getControl("RenderResolutionDivisor")->getValidateSignal()->connect(boost::bind(&validateRenderResolutionDivisor, _2));
-	gSavedSettings.getControl("RenderResolutionDivisor")->getSignal()->connect(boost::bind(&handleRenderResolutionDivisorChanged, _2));
+	setting_setup_signal_listener(gSavedSettings, "RenderResolutionDivisor", handleRenderResolutionDivisorChanged);
 // [SL:KB] - Patch: Settings-RenderResolutionMultiplier | Checked: Catznip-5.4
 	gSavedSettings.getControl("RenderResolutionMultiplier")->getSignal()->connect(boost::bind(&handleRenderResolutionDivisorChanged, _2));
 // [/SL:KB]
 	gSavedSettings.getControl("RenderWaterRefResolution")->getSignal()->connect(boost::bind(&handleReleaseGLBufferChanged, _2));
-	gSavedSettings.getControl("RenderDeferred")->getSignal()->connect(boost::bind(&handleRenderDeferredChanged, _2));
-	gSavedSettings.getControl("RenderShadowDetail")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2));
-	gSavedSettings.getControl("RenderDeferredSSAO")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2));
-	gSavedSettings.getControl("RenderPerformanceTest")->getSignal()->connect(boost::bind(&handleRenderPerfTestChanged, _2));
-	gSavedSettings.getControl("TextureMemory")->getSignal()->connect(boost::bind(&handleVideoMemoryChanged, _2));
-	gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&handleChatFontSizeChanged, _2));
-	gSavedSettings.getControl("ChatPersistTime")->getSignal()->connect(boost::bind(&handleChatPersistTimeChanged, _2));
-	gSavedSettings.getControl("ConsoleMaxLines")->getSignal()->connect(boost::bind(&handleConsoleMaxLinesChanged, _2));
-	gSavedSettings.getControl("UploadBakedTexOld")->getSignal()->connect(boost::bind(&handleUploadBakedTexOldChanged, _2));
-	gSavedSettings.getControl("UseOcclusion")->getSignal()->connect(boost::bind(&handleUseOcclusionChanged, _2));
-	gSavedSettings.getControl("AudioLevelMaster")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _2));
-	gSavedSettings.getControl("AudioLevelSFX")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _2));
-	gSavedSettings.getControl("AudioLevelUI")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _2));
-	gSavedSettings.getControl("AudioLevelAmbient")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _2));
-	gSavedSettings.getControl("AudioLevelMusic")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _2));
-	gSavedSettings.getControl("AudioLevelMedia")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _2));
-	gSavedSettings.getControl("AudioLevelVoice")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _2));
-	gSavedSettings.getControl("AudioLevelDoppler")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _2));
-	gSavedSettings.getControl("AudioLevelRolloff")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _2));
-	gSavedSettings.getControl("AudioLevelUnderwaterRolloff")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _2));
-	gSavedSettings.getControl("MuteAudio")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _2));
-	gSavedSettings.getControl("MuteMusic")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _2));
-	gSavedSettings.getControl("MuteMedia")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _2));
-	gSavedSettings.getControl("MuteVoice")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _2));
-	gSavedSettings.getControl("MuteAmbient")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _2));
-	gSavedSettings.getControl("MuteUI")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _2));
-	gSavedSettings.getControl("RenderVBOEnable")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2));
-	gSavedSettings.getControl("RenderUseVAO")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2));
-	gSavedSettings.getControl("RenderVBOMappingDisable")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2));
-	gSavedSettings.getControl("RenderUseStreamVBO")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2));
-	gSavedSettings.getControl("RenderPreferStreamDraw")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2));
-	gSavedSettings.getControl("WLSkyDetail")->getSignal()->connect(boost::bind(&handleWLSkyDetailChanged, _2));
-	gSavedSettings.getControl("JoystickAxis0")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("JoystickAxis1")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("JoystickAxis2")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("JoystickAxis3")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("JoystickAxis4")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("JoystickAxis5")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("JoystickAxis6")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("FlycamAxisScale0")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("FlycamAxisScale1")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("FlycamAxisScale2")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("FlycamAxisScale3")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("FlycamAxisScale4")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("FlycamAxisScale5")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("FlycamAxisScale6")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("FlycamAxisDeadZone0")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("FlycamAxisDeadZone1")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("FlycamAxisDeadZone2")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("FlycamAxisDeadZone3")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("FlycamAxisDeadZone4")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("FlycamAxisDeadZone5")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("FlycamAxisDeadZone6")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("AvatarAxisScale0")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("AvatarAxisScale1")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("AvatarAxisScale2")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("AvatarAxisScale3")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("AvatarAxisScale4")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("AvatarAxisScale5")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("AvatarAxisDeadZone0")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("AvatarAxisDeadZone1")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("AvatarAxisDeadZone2")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("AvatarAxisDeadZone3")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("AvatarAxisDeadZone4")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("AvatarAxisDeadZone5")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("BuildAxisScale0")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("BuildAxisScale1")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("BuildAxisScale2")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("BuildAxisScale3")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("BuildAxisScale4")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("BuildAxisScale5")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("BuildAxisDeadZone0")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("BuildAxisDeadZone1")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("BuildAxisDeadZone2")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("BuildAxisDeadZone3")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("BuildAxisDeadZone4")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("BuildAxisDeadZone5")->getSignal()->connect(boost::bind(&handleJoystickChanged, _2));
-	gSavedSettings.getControl("DebugViews")->getSignal()->connect(boost::bind(&handleDebugViewsChanged, _2));
-	gSavedSettings.getControl("UserLogFile")->getSignal()->connect(boost::bind(&handleLogFileChanged, _2));
-	gSavedSettings.getControl("RenderHideGroupTitle")->getSignal()->connect(boost::bind(handleHideGroupTitleChanged, _2));
-	gSavedSettings.getControl("HighResSnapshot")->getSignal()->connect(boost::bind(handleHighResSnapshotChanged, _2));
-	gSavedSettings.getControl("EnableVoiceChat")->getSignal()->connect(boost::bind(&handleVoiceClientPrefsChanged, _2));
-	gSavedSettings.getControl("PTTCurrentlyEnabled")->getSignal()->connect(boost::bind(&handleVoiceClientPrefsChanged, _2));
-	gSavedSettings.getControl("PushToTalkButton")->getSignal()->connect(boost::bind(&handleVoiceClientPrefsChanged, _2));
-	gSavedSettings.getControl("PushToTalkToggle")->getSignal()->connect(boost::bind(&handleVoiceClientPrefsChanged, _2));
-	gSavedSettings.getControl("VoiceEarLocation")->getSignal()->connect(boost::bind(&handleVoiceClientPrefsChanged, _2));
-	gSavedSettings.getControl("VoiceInputAudioDevice")->getSignal()->connect(boost::bind(&handleVoiceClientPrefsChanged, _2));
-	gSavedSettings.getControl("VoiceOutputAudioDevice")->getSignal()->connect(boost::bind(&handleVoiceClientPrefsChanged, _2));
-	gSavedSettings.getControl("AudioLevelMic")->getSignal()->connect(boost::bind(&handleVoiceClientPrefsChanged, _2));
-	gSavedSettings.getControl("LipSyncEnabled")->getSignal()->connect(boost::bind(&handleVoiceClientPrefsChanged, _2));	
-	gSavedSettings.getControl("VelocityInterpolate")->getSignal()->connect(boost::bind(&handleVelocityInterpolate, _2));
-	gSavedSettings.getControl("QAMode")->getSignal()->connect(boost::bind(&show_debug_menus));
-	gSavedSettings.getControl("UseDebugMenus")->getSignal()->connect(boost::bind(&show_debug_menus));
-	gSavedSettings.getControl("AgentPause")->getSignal()->connect(boost::bind(&toggle_agent_pause, _2));
-	gSavedSettings.getControl("ShowNavbarNavigationPanel")->getSignal()->connect(boost::bind(&toggle_show_navigation_panel, _2));
-	gSavedSettings.getControl("ShowMiniLocationPanel")->getSignal()->connect(boost::bind(&toggle_show_mini_location_panel, _2));
-	gSavedSettings.getControl("ShowObjectRenderingCost")->getSignal()->connect(boost::bind(&toggle_show_object_render_cost, _2));
-	gSavedSettings.getControl("ForceShowGrid")->getSignal()->connect(boost::bind(&handleForceShowGrid, _2));
-	gSavedSettings.getControl("RenderTransparentWater")->getSignal()->connect(boost::bind(&handleRenderTransparentWaterChanged, _2));
-	gSavedSettings.getControl("SpellCheck")->getSignal()->connect(boost::bind(&handleSpellCheckChanged));
-	gSavedSettings.getControl("SpellCheckDictionary")->getSignal()->connect(boost::bind(&handleSpellCheckChanged));
-	gSavedSettings.getControl("LoginLocation")->getSignal()->connect(boost::bind(&handleLoginLocationChanged));
-	gSavedSettings.getControl("DebugAvatarJoints")->getCommitSignal()->connect(boost::bind(&handleDebugAvatarJointsChanged, _2));
-	gSavedSettings.getControl("RenderAutoMuteByteLimit")->getSignal()->connect(boost::bind(&handleRenderAutoMuteByteLimitChanged, _2));
-	gSavedPerAccountSettings.getControl("AvatarHoverOffsetZ")->getCommitSignal()->connect(boost::bind(&handleAvatarHoverOffsetChanged, _2));
+	setting_setup_signal_listener(gSavedSettings, "RenderDeferred", handleRenderDeferredChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderShadowDetail", handleSetShaderChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderDeferredSSAO", handleSetShaderChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderPerformanceTest", handleRenderPerfTestChanged);
+	setting_setup_signal_listener(gSavedSettings, "TextureMemory", handleVideoMemoryChanged);
+	setting_setup_signal_listener(gSavedSettings, "ChatFontSize", handleChatFontSizeChanged);
+	setting_setup_signal_listener(gSavedSettings, "ChatPersistTime", handleChatPersistTimeChanged);
+	setting_setup_signal_listener(gSavedSettings, "ConsoleMaxLines", handleConsoleMaxLinesChanged);
+	setting_setup_signal_listener(gSavedSettings, "UploadBakedTexOld", handleUploadBakedTexOldChanged);
+	setting_setup_signal_listener(gSavedSettings, "UseOcclusion", handleUseOcclusionChanged);
+	setting_setup_signal_listener(gSavedSettings, "AudioLevelMaster", handleAudioVolumeChanged);
+	setting_setup_signal_listener(gSavedSettings, "AudioLevelSFX", handleAudioVolumeChanged);
+	setting_setup_signal_listener(gSavedSettings, "AudioLevelUI", handleAudioVolumeChanged);
+	setting_setup_signal_listener(gSavedSettings, "AudioLevelAmbient", handleAudioVolumeChanged);
+	setting_setup_signal_listener(gSavedSettings, "AudioLevelMusic", handleAudioVolumeChanged);
+	setting_setup_signal_listener(gSavedSettings, "AudioLevelMedia", handleAudioVolumeChanged);
+	setting_setup_signal_listener(gSavedSettings, "AudioLevelVoice", handleAudioVolumeChanged);
+	setting_setup_signal_listener(gSavedSettings, "AudioLevelDoppler", handleAudioVolumeChanged);
+	setting_setup_signal_listener(gSavedSettings, "AudioLevelRolloff", handleAudioVolumeChanged);
+	setting_setup_signal_listener(gSavedSettings, "AudioLevelUnderwaterRolloff", handleAudioVolumeChanged);
+	setting_setup_signal_listener(gSavedSettings, "MuteAudio", handleAudioVolumeChanged);
+	setting_setup_signal_listener(gSavedSettings, "MuteMusic", handleAudioVolumeChanged);
+	setting_setup_signal_listener(gSavedSettings, "MuteMedia", handleAudioVolumeChanged);
+	setting_setup_signal_listener(gSavedSettings, "MuteVoice", handleAudioVolumeChanged);
+	setting_setup_signal_listener(gSavedSettings, "MuteAmbient", handleAudioVolumeChanged);
+	setting_setup_signal_listener(gSavedSettings, "MuteUI", handleAudioVolumeChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderVBOEnable", handleResetVertexBuffersChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderUseVAO", handleResetVertexBuffersChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderVBOMappingDisable", handleResetVertexBuffersChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderUseStreamVBO", handleResetVertexBuffersChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderPreferStreamDraw", handleResetVertexBuffersChanged);
+	setting_setup_signal_listener(gSavedSettings, "WLSkyDetail", handleWLSkyDetailChanged);
+	setting_setup_signal_listener(gSavedSettings, "JoystickAxis0", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "JoystickAxis1", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "JoystickAxis2", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "JoystickAxis3", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "JoystickAxis4", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "JoystickAxis5", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "JoystickAxis6", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "FlycamAxisScale0", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "FlycamAxisScale1", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "FlycamAxisScale2", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "FlycamAxisScale3", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "FlycamAxisScale4", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "FlycamAxisScale5", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "FlycamAxisScale6", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "FlycamAxisDeadZone0", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "FlycamAxisDeadZone1", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "FlycamAxisDeadZone2", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "FlycamAxisDeadZone3", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "FlycamAxisDeadZone4", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "FlycamAxisDeadZone5", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "FlycamAxisDeadZone6", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "AvatarAxisScale0", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "AvatarAxisScale1", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "AvatarAxisScale2", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "AvatarAxisScale3", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "AvatarAxisScale4", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "AvatarAxisScale5", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "AvatarAxisDeadZone0", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "AvatarAxisDeadZone1", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "AvatarAxisDeadZone2", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "AvatarAxisDeadZone3", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "AvatarAxisDeadZone4", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "AvatarAxisDeadZone5", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "BuildAxisScale0", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "BuildAxisScale1", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "BuildAxisScale2", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "BuildAxisScale3", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "BuildAxisScale4", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "BuildAxisScale5", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "BuildAxisDeadZone0", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "BuildAxisDeadZone1", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "BuildAxisDeadZone2", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "BuildAxisDeadZone3", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "BuildAxisDeadZone4", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "BuildAxisDeadZone5", handleJoystickChanged);
+	setting_setup_signal_listener(gSavedSettings, "DebugViews", handleDebugViewsChanged);
+	setting_setup_signal_listener(gSavedSettings, "UserLogFile", handleLogFileChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderHideGroupTitle", handleHideGroupTitleChanged);
+	setting_setup_signal_listener(gSavedSettings, "HighResSnapshot", handleHighResSnapshotChanged);
+	setting_setup_signal_listener(gSavedSettings, "EnableVoiceChat", handleVoiceClientPrefsChanged);
+	setting_setup_signal_listener(gSavedSettings, "PTTCurrentlyEnabled", handleVoiceClientPrefsChanged);
+	setting_setup_signal_listener(gSavedSettings, "PushToTalkButton", handleVoiceClientPrefsChanged);
+	setting_setup_signal_listener(gSavedSettings, "PushToTalkToggle", handleVoiceClientPrefsChanged);
+	setting_setup_signal_listener(gSavedSettings, "VoiceEarLocation", handleVoiceClientPrefsChanged);
+	setting_setup_signal_listener(gSavedSettings, "VoiceInputAudioDevice", handleVoiceClientPrefsChanged);
+	setting_setup_signal_listener(gSavedSettings, "VoiceOutputAudioDevice", handleVoiceClientPrefsChanged);
+	setting_setup_signal_listener(gSavedSettings, "AudioLevelMic", handleVoiceClientPrefsChanged);
+	setting_setup_signal_listener(gSavedSettings, "LipSyncEnabled", handleVoiceClientPrefsChanged);	
+	setting_setup_signal_listener(gSavedSettings, "VelocityInterpolate", handleVelocityInterpolate);
+	setting_setup_signal_listener(gSavedSettings, "QAMode", show_debug_menus);
+	setting_setup_signal_listener(gSavedSettings, "UseDebugMenus", show_debug_menus);
+	setting_setup_signal_listener(gSavedSettings, "AgentPause", toggle_agent_pause);
+	setting_setup_signal_listener(gSavedSettings, "ShowNavbarNavigationPanel", toggle_show_navigation_panel);
+	setting_setup_signal_listener(gSavedSettings, "ShowMiniLocationPanel", toggle_show_mini_location_panel);
+	setting_setup_signal_listener(gSavedSettings, "ShowObjectRenderingCost", toggle_show_object_render_cost);
+	setting_setup_signal_listener(gSavedSettings, "ForceShowGrid", handleForceShowGrid);
+	setting_setup_signal_listener(gSavedSettings, "RenderTransparentWater", handleRenderTransparentWaterChanged);
+	setting_setup_signal_listener(gSavedSettings, "SpellCheck", handleSpellCheckChanged);
+	setting_setup_signal_listener(gSavedSettings, "SpellCheckDictionary", handleSpellCheckChanged);
+	setting_setup_signal_listener(gSavedSettings, "LoginLocation", handleLoginLocationChanged);
+	setting_setup_signal_listener(gSavedSettings, "DebugAvatarJoints", handleDebugAvatarJointsChanged);
+	setting_setup_signal_listener(gSavedSettings, "RenderAutoMuteByteLimit", handleRenderAutoMuteByteLimitChanged);
+
+    setting_setup_signal_listener(gSavedPerAccountSettings, "AvatarHoverOffsetZ", handleAvatarHoverOffsetChanged);
 // [RLVa:KB] - Checked: 2015-12-27 (RLVa-1.5.0)
 	gSavedSettings.getControl(RlvSettingNames::Main)->getSignal()->connect(boost::bind(&RlvSettings::onChangedSettingMain, _2));
 // [/RLVa:KB]
diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp
index 6d053f6bf9d8a3012b4acff91b07fcb7a6397d92..f1bcfb1a35c63c4ec20375b267a44aadf19b37b8 100644
--- a/indra/newview/llviewermedia.cpp
+++ b/indra/newview/llviewermedia.cpp
@@ -4007,7 +4007,7 @@ bool LLViewerMediaImpl::shouldShowBasedOnClass() const
 //
 bool LLViewerMediaImpl::isObscured() const
 {
-    if (getUsedInUI() || isParcelMedia()) return false;
+    if (getUsedInUI() || isParcelMedia() || isAttachedToHUD()) return false;
 
     LLParcel* agent_parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
     if (!agent_parcel)
@@ -4023,6 +4023,20 @@ bool LLViewerMediaImpl::isObscured() const
     return false;
 }
 
+bool LLViewerMediaImpl::isAttachedToHUD() const
+{
+    std::list< LLVOVolume* >::const_iterator iter = mObjectList.begin();
+    std::list< LLVOVolume* >::const_iterator end = mObjectList.end();
+    for ( ; iter != end; iter++)
+    {
+        if ((*iter)->isHUDAttachment())
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
 //////////////////////////////////////////////////////////////////////////////////////////
 //
 bool LLViewerMediaImpl::isAttachedToAnotherAvatar() const
diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h
index 1cd9bd892554686cd3ec0fc0da33634293cccfcf..e3e4ddeb8c6cbc0fa8a2f7aa5b2160303608b6a8 100644
--- a/indra/newview/llviewermedia.h
+++ b/indra/newview/llviewermedia.h
@@ -423,6 +423,8 @@ class LLViewerMediaImpl
 	
 	void cancelMimeTypeProbe();
 	
+    bool isAttachedToHUD() const;
+
 	// Is this media attached to an avatar *not* self
 	bool isAttachedToAnotherAvatar() const;
 	
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 159ee1a3b9098a8493a4788852966c21cfe4be30..60d01aaced253a189e5a289cd125f5da0adc4f0a 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -59,6 +59,7 @@
 #include "llcompilequeue.h"
 #include "llconsole.h"
 #include "lldebugview.h"
+#include "lldiskcache.h"
 #include "llenvironment.h"
 #include "llfilepicker.h"
 #include "llfirstuse.h"
@@ -2147,6 +2148,32 @@ class LLAdvancedDropPacket : public view_listener_t
 	}
 };
 
+//////////////////////
+// PURGE DISK CACHE //
+//////////////////////
+
+
+class LLAdvancedPurgeDiskCache : public view_listener_t
+{
+	bool handleEvent(const LLSD& userdata)
+	{
+        LL::WorkQueue::ptr_t main_queue = LL::WorkQueue::getInstance("mainloop");
+        LL::WorkQueue::ptr_t general_queue = LL::WorkQueue::getInstance("General");
+        llassert_always(main_queue);
+        llassert_always(general_queue);
+        main_queue->postTo(
+            general_queue,
+            []() // Work done on general queue
+            {
+                LLDiskCache::getInstance()->purge();
+                // Nothing needed to return
+            },
+            [](){}); // Callback to main thread is empty as there is nothing left to do
+
+		return true;
+	}
+};
+
 
 ////////////////////
 // EVENT Recorder //
@@ -2372,18 +2399,6 @@ class LLAdvancedEnableObjectObjectOcclusion: public view_listener_t
 }
 };
 
-/////////////////////////////////////
-// Enable Framebuffer Objects	  ///
-/////////////////////////////////////
-class LLAdvancedEnableRenderFBO: public view_listener_t
-{
-	bool handleEvent(const LLSD& userdata)
-	{
-		bool new_value = gGLManager.mHasFramebufferObject;
-		return new_value;
-	}
-};
-
 /////////////////////////////////////
 // Enable Deferred Rendering	  ///
 /////////////////////////////////////
@@ -2391,7 +2406,7 @@ class LLAdvancedEnableRenderDeferred: public view_listener_t
 {
 	bool handleEvent(const LLSD& userdata)
 	{
-		bool new_value = gGLManager.mHasFramebufferObject && LLViewerShaderMgr::instance()->getShaderLevel(LLViewerShaderMgr::SHADER_WINDLIGHT) > 1 &&
+		bool new_value = LLViewerShaderMgr::instance()->getShaderLevel(LLViewerShaderMgr::SHADER_WINDLIGHT) > 1 &&
 			LLViewerShaderMgr::instance()->getShaderLevel(LLViewerShaderMgr::SHADER_AVATAR) > 0;
 		return new_value;
 	}
@@ -2404,7 +2419,7 @@ class LLAdvancedEnableRenderDeferredOptions: public view_listener_t
 {
 	bool handleEvent(const LLSD& userdata)
 	{
-		bool new_value = gGLManager.mHasFramebufferObject && LLViewerShaderMgr::instance()->getShaderLevel(LLViewerShaderMgr::SHADER_WINDLIGHT) > 1 &&
+		bool new_value = LLViewerShaderMgr::instance()->getShaderLevel(LLViewerShaderMgr::SHADER_WINDLIGHT) > 1 &&
 			LLViewerShaderMgr::instance()->getShaderLevel(LLViewerShaderMgr::SHADER_AVATAR) > 0 && gSavedSettings.getBOOL("RenderDeferred");
 		return new_value;
 	}
@@ -9749,7 +9764,6 @@ void initialize_menus()
 	view_listener_t::addMenu(new LLAdvancedCheckWireframe(), "Advanced.CheckWireframe");
 	// Develop > Render
 	view_listener_t::addMenu(new LLAdvancedEnableObjectObjectOcclusion(), "Advanced.EnableObjectObjectOcclusion");
-	view_listener_t::addMenu(new LLAdvancedEnableRenderFBO(), "Advanced.EnableRenderFBO");
 	view_listener_t::addMenu(new LLAdvancedEnableRenderDeferred(), "Advanced.EnableRenderDeferred");
 	view_listener_t::addMenu(new LLAdvancedEnableRenderDeferredOptions(), "Advanced.EnableRenderDeferredOptions");
 	view_listener_t::addMenu(new LLAdvancedToggleRandomizeFramerate(), "Advanced.ToggleRandomizeFramerate");
@@ -9851,6 +9865,9 @@ void initialize_menus()
 	view_listener_t::addMenu(new LLAdvancedDisableMessageLog(), "Advanced.DisableMessageLog");
 	view_listener_t::addMenu(new LLAdvancedDropPacket(), "Advanced.DropPacket");
 
+    // Advanced > Cache
+    view_listener_t::addMenu(new LLAdvancedPurgeDiskCache(), "Advanced.PurgeDiskCache");
+
 	// Advanced > Recorder
 	view_listener_t::addMenu(new LLAdvancedAgentPilot(), "Advanced.AgentPilot");
 	view_listener_t::addMenu(new LLAdvancedToggleAgentPilotLoop(), "Advanced.ToggleAgentPilotLoop");
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 4bdc70a2fd29cafa17ca10557e8d529da9ae63bc..625a54ce001a34f77f02cc32287f7c7a999df8f2 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -35,6 +35,7 @@
 #include "llcurrencywrapper.h"
 #include "lleconomy.h"
 #include "lleventtimer.h"
+#include "llfloatercreatelandmark.h"
 #include "llfloaterreg.h"
 #include "llfolderview.h"
 #include "llfollowcamparams.h"
@@ -130,6 +131,7 @@
 #include "llexperiencecache.h"
 
 #include "llexperiencecache.h"
+#include "lluiusage.h"
 
 extern void on_new_message(const LLSD& msg);
 
@@ -269,6 +271,7 @@ bool friendship_offer_callback(const LLSD& notification, const LLSD& response)
 	    {
 	    case 0:
 	    {
+			LLUIUsage::instance().logCommand("Agent.AcceptFriendship");
 		    // accept
 		    LLAvatarTracker::formFriendship(payload["from_id"]);
 
@@ -311,6 +314,7 @@ bool friendship_offer_callback(const LLSD& notification, const LLSD& response)
 	    // fall-through
 	    case 2: // Send IM - decline and start IM session
 		    {
+				LLUIUsage::instance().logCommand("Agent.DeclineFriendship");
 			    // decline
 			    // We no longer notify other viewers, but we DO still send
                 // the rejection to the simulator to delete the pending userop.
@@ -852,6 +856,11 @@ void send_join_group_response(LLUUID group_id, LLUUID transaction_id, bool accep
 
         EInstantMessage type = accept_invite ? IM_GROUP_INVITATION_ACCEPT : IM_GROUP_INVITATION_DECLINE;
 
+		if (accept_invite)
+		{
+			LLUIUsage::instance().logCommand("Group.Join");
+		}
+
         send_improved_im(group_id,
             std::string("name"),
             std::string("message"),
@@ -1579,6 +1588,17 @@ bool highlight_offered_object(const LLUUID& obj_id)
 		}
 	}
 
+    if (obj->getType() == LLAssetType::AT_LANDMARK)
+    {
+        LLFloaterCreateLandmark *floater = LLFloaterReg::findTypedInstance<LLFloaterCreateLandmark>("add_landmark");
+        if (floater && floater->getItem() && floater->getItem()->getUUID() == obj_id)
+        {
+            // LLFloaterCreateLandmark is supposed to handle this,
+            // keep landmark creation floater at the front
+            return false;
+        }
+    }
+
 	return true;
 }
 
diff --git a/indra/newview/llvieweroctree.h b/indra/newview/llvieweroctree.h
index e6974b0f84ef715c1fc26b4f819a5ac6c051908b..7666062f990944701ad64be09627cd7f2a28e7ed 100644
--- a/indra/newview/llvieweroctree.h
+++ b/indra/newview/llvieweroctree.h
@@ -45,11 +45,11 @@ class LLViewerOctreeGroup;
 class LLViewerOctreeEntry;
 class LLViewerOctreePartition;
 
-typedef LLOctreeListener<LLViewerOctreeEntry>	OctreeListener;
-typedef LLTreeNode<LLViewerOctreeEntry>			TreeNode;
-typedef LLOctreeNode<LLViewerOctreeEntry>		OctreeNode;
-typedef LLOctreeRoot<LLViewerOctreeEntry>		OctreeRoot;
-typedef LLOctreeTraveler<LLViewerOctreeEntry>	OctreeTraveler;
+typedef LLOctreeListener<LLViewerOctreeEntry, LLPointer<LLViewerOctreeEntry>> OctreeListener;
+typedef LLTreeNode<LLViewerOctreeEntry> TreeNode;
+typedef LLOctreeNode<LLViewerOctreeEntry, LLPointer<LLViewerOctreeEntry>> OctreeNode;
+typedef LLOctreeRoot<LLViewerOctreeEntry, LLPointer<LLViewerOctreeEntry>> OctreeRoot;
+typedef LLOctreeTraveler<LLViewerOctreeEntry, LLPointer<LLViewerOctreeEntry>> OctreeTraveler;
 
 #if LL_OCTREE_PARANOIA_CHECK
 #define assert_octree_valid(x) x->validate()
@@ -179,7 +179,7 @@ class LLViewerOctreeEntryData : public LLRefCount
 //defines an octree group for an octree node, which contains multiple entries.
 //LL_ALIGN_PREFIX(16)
 class LLViewerOctreeGroup
-:	public LLOctreeListener<LLViewerOctreeEntry>
+:	public OctreeListener
 {
     LL_ALIGN_NEW
 	friend class LLViewerOctreeCull;
@@ -198,8 +198,8 @@ class LLViewerOctreeGroup
 	};
 
 public:
-	typedef LLOctreeNode<LLViewerOctreeEntry>::element_iter element_iter;
-	typedef LLOctreeNode<LLViewerOctreeEntry>::element_list element_list;
+	typedef OctreeNode::element_iter element_iter;
+	typedef OctreeNode::element_list element_list;
 
 	LLViewerOctreeGroup(OctreeNode* node);
 	LLViewerOctreeGroup(const LLViewerOctreeGroup& rhs)
@@ -245,7 +245,6 @@ class LLViewerOctreeGroup
 	const LLVector4a* getObjectExtents() const {return mObjectExtents;}
 
 	//octree wrappers to make code more readable
-	element_list& getData() { return mOctreeNode->getData(); }
 	element_iter getDataBegin() { return mOctreeNode->getDataBegin(); }
 	element_iter getDataEnd() { return mOctreeNode->getDataEnd(); }
 	U32 getElementCount() const { return mOctreeNode->getElementCount(); }
diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp
index 5f7b58e030d1b10996a48facfb96db956da2e6cf..a545463d98c6bb51e1b21a2de9fd69970c5f1202 100644
--- a/indra/newview/llviewerparcelmgr.cpp
+++ b/indra/newview/llviewerparcelmgr.cpp
@@ -1623,6 +1623,8 @@ void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **use
     else if (sequence_id == 0 || sequence_id > parcel_mgr.mAgentParcelSequenceID)
     {
         // new agent parcel
+        // *TODO: Does it really make sense to set the agent parcel to this
+        // parcel if the client doesn't know what kind of parcel data this is?
         parcel_mgr.mAgentParcelSequenceID = sequence_id;
         parcel = parcel_mgr.mAgentParcel;
     }
@@ -1914,8 +1916,13 @@ void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **use
 	}
 	else
 	{
-		// Check for video
-		LLViewerParcelMedia::getInstance()->update(parcel);
+        if (gNonInteractive)
+        {
+            return;
+        }
+    
+        // Check for video
+        LLViewerParcelMedia::getInstance()->update(parcel);
 
 		// Then check for music
 		if (gAudiop)
diff --git a/indra/newview/llviewerparceloverlay.cpp b/indra/newview/llviewerparceloverlay.cpp
index 7f98a3e52b021a96f90fb114445b0702655003b7..0ce295031e7925309c06aa0854d7f5f15237cfbb 100644
--- a/indra/newview/llviewerparceloverlay.cpp
+++ b/indra/newview/llviewerparceloverlay.cpp
@@ -274,7 +274,7 @@ BOOL LLViewerParcelOverlay::isSoundLocal(const LLVector3& pos) const
 {
 	S32 row =    S32(pos.mV[VY] / PARCEL_GRID_STEP_METERS);
 	S32 column = S32(pos.mV[VX] / PARCEL_GRID_STEP_METERS);
-	return PARCEL_SOUND_LOCAL & mOwnership[row * mParcelGridsPerEdge + column];
+    return parcelFlags(row, column, PARCEL_SOUND_LOCAL);
 }
 
 U8 LLViewerParcelOverlay::ownership( const LLVector3& pos) const
@@ -288,12 +288,19 @@ U8 LLViewerParcelOverlay::parcelLineFlags(const LLVector3& pos) const
 {
     S32 row = S32(pos.mV[VY] / PARCEL_GRID_STEP_METERS);
     S32 column = S32(pos.mV[VX] / PARCEL_GRID_STEP_METERS);
-    return parcelLineFlags(row, column);
+    return parcelFlags(row, column, PARCEL_WEST_LINE | PARCEL_SOUTH_LINE);
 }
 U8 LLViewerParcelOverlay::parcelLineFlags(S32 row, S32 col) const
 {
-    U8 flags = PARCEL_WEST_LINE | PARCEL_SOUTH_LINE;
-    if (row > mParcelGridsPerEdge || col > mParcelGridsPerEdge)
+    return parcelFlags(row, col, PARCEL_WEST_LINE | PARCEL_SOUTH_LINE);
+}
+
+U8 LLViewerParcelOverlay::parcelFlags(S32 row, S32 col, U8 flags) const
+{
+    if (row >= mParcelGridsPerEdge
+        || col >= mParcelGridsPerEdge
+        || row < 0
+        || col < 0)
     {
         LL_WARNS() << "Attempted to get ownership out of region's overlay, row: " << row << " col: " << col << LL_ENDL;
         return flags;
@@ -931,8 +938,8 @@ S32 LLViewerParcelOverlay::renderPropertyLines	()
 	// Always fudge a little vertically.
 	pull_toward_camera.mV[VZ] += 0.01f;
 
-	gGL.matrixMode(LLRender::MM_MODELVIEW);
-	gGL.pushMatrix();
+    gGL.matrixMode(LLRender::MM_MODELVIEW);
+    gGL.pushMatrix();
 
 	// Move to appropriate region coords
 	LLVector3 origin = mRegion->getOriginAgent();
@@ -1037,11 +1044,70 @@ S32 LLViewerParcelOverlay::renderPropertyLines	()
 		
 	}
 
-	gGL.popMatrix();
+    gGL.popMatrix();
 
 	return drawn;
 }
 
+// Draw half of a single cell (no fill) in a grid drawn from left to right and from bottom to top
+void grid_2d_part_lines(const F32 left, const F32 top, const F32 right, const F32 bottom, bool has_left, bool has_bottom)
+{
+    gGL.begin(LLRender::LINES);
+
+    if (has_left)
+    {
+        gGL.vertex2f(left, bottom);
+        gGL.vertex2f(left, top);
+    }
+    if (has_bottom)
+    {
+        gGL.vertex2f(left, bottom);
+        gGL.vertex2f(right, bottom);
+    }
+
+    gGL.end();
+}
+
+void LLViewerParcelOverlay::renderPropertyLinesOnMinimap(F32 scale_pixels_per_meter, const F32 *parcel_outline_color)
+{
+    if (!mOwnership)
+    {
+        return;
+    }
+    if (!gSavedSettings.getBOOL("MiniMapShowPropertyLines"))
+    {
+        return;
+    }
+
+    LLVector3 origin_agent     = mRegion->getOriginAgent();
+    LLVector3 rel_region_pos   = origin_agent - gAgentCamera.getCameraPositionAgent();
+    F32       region_left      = rel_region_pos.mV[0] * scale_pixels_per_meter;
+    F32       region_bottom    = rel_region_pos.mV[1] * scale_pixels_per_meter;
+    F32       map_parcel_width = PARCEL_GRID_STEP_METERS * scale_pixels_per_meter;
+    const S32 GRIDS_PER_EDGE   = mParcelGridsPerEdge;
+
+    gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+    glLineWidth(1.0f);
+    gGL.color4fv(parcel_outline_color);
+    for (S32 i = 0; i < GRIDS_PER_EDGE + 1; i++)
+    {
+        const F32 bottom = region_bottom + (i * map_parcel_width);
+        const F32 top    = bottom + map_parcel_width;
+        for (S32 j = 0; j < GRIDS_PER_EDGE + 1; j++)
+        {
+            const F32  left               = region_left + (j * map_parcel_width);
+            const F32  right              = left + map_parcel_width;
+            const bool is_region_boundary = i == GRIDS_PER_EDGE || j == GRIDS_PER_EDGE;
+            const U8   overlay            = is_region_boundary ? 0 : mOwnership[(i * GRIDS_PER_EDGE) + j];
+            // The property line vertices are three-dimensional, but here we only care about the x and y coordinates, as we are drawing on a
+            // 2D map
+            const bool has_left   = i != GRIDS_PER_EDGE && (j == GRIDS_PER_EDGE || (overlay & PARCEL_WEST_LINE));
+            const bool has_bottom = j != GRIDS_PER_EDGE && (i == GRIDS_PER_EDGE || (overlay & PARCEL_SOUTH_LINE));
+            grid_2d_part_lines(left, top, right, bottom, has_left, has_bottom);
+        }
+    }
+}
+
 boost::signals2::connection LLViewerParcelOverlay::setUpdateCallback(const update_signal_t::slot_type& cb)
 {
 	if (!mUpdateSignal)
diff --git a/indra/newview/llviewerparceloverlay.h b/indra/newview/llviewerparceloverlay.h
index 922548744bf0df2b6be88ab43b0bf5b480a16665..f1797db3b4c376b48b4a40eea35f39ab79189b90 100644
--- a/indra/newview/llviewerparceloverlay.h
+++ b/indra/newview/llviewerparceloverlay.h
@@ -70,6 +70,7 @@ class LLViewerParcelOverlay final : public LLGLUpdate
 
 	// Returns the number of vertices drawn
 	S32				renderPropertyLines();
+    void			renderPropertyLinesOnMinimap(F32 scale_pixels_per_meter, const F32* parcel_outline_color);
 
 	U8				ownership( const LLVector3& pos) const;
 	U8				parcelLineFlags( const LLVector3& pos) const;
@@ -86,12 +87,14 @@ class LLViewerParcelOverlay final : public LLGLUpdate
 
 	typedef boost::signals2::signal<void (const LLViewerRegion*)> update_signal_t;
 	static boost::signals2::connection setUpdateCallback(const update_signal_t::slot_type & cb);
-
+    
 private:
 	// This is in parcel rows and columns, not grid rows and columns
 	// Stored in bottom three bits.
 	U8		ownership(S32 row, S32 col) const	
-				{ return 0x7 & mOwnership[row * mParcelGridsPerEdge + col]; }
+				{ return parcelFlags(row, col, (U8)0x7); }
+
+    U8		parcelFlags(S32 row, S32 col, U8 flags) const;
 
 	void	addPropertyLine(std::vector<LLVector3>& vertex_array,
 				std::vector<LLColor4U>& color_array,
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
old mode 100644
new mode 100755
index edf83b803094bebdc7b9c20295e5a3ecaf2cec30..32dfff141af9bda7123ef1fdf4854a167406223d
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -330,6 +330,23 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle)
             return;
         }
 
+        if (!LLWorld::instanceExists())
+        {
+            LL_WARNS("AppInit", "Capabilities") << "Received capabilities, but world no longer exists!" << LL_ENDL;
+            return;
+        }
+
+        regionp = LLWorld::getInstance()->getRegionFromHandle(regionHandle);
+        if (!regionp) //region was removed
+        {
+            LL_WARNS("AppInit", "Capabilities") << "Received capabilities for region that no longer exists!" << LL_ENDL;
+            return; // this error condition is not recoverable.
+        }
+
+        impl = regionp->getRegionImplNC();
+
+        ++(impl->mSeedCapAttempts);
+
         if (!result.isMap() || result.has("error"))
         {
             LL_WARNS("AppInit", "Capabilities") << "Malformed response" << LL_ENDL;
@@ -351,23 +368,6 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle)
         // remove the http_result from the llsd
         result.erase("http_result");
 
-        if (!LLWorld::instanceExists())
-        {
-            LL_WARNS("AppInit", "Capabilities") << "Received capabilities, but world no longer exists!" << LL_ENDL;
-            return;
-        }
-
-        regionp = LLWorld::getInstance()->getRegionFromHandle(regionHandle);
-        if (!regionp) //region was removed
-        {
-            LL_WARNS("AppInit", "Capabilities") << "Received capabilities for region that no longer exists!" << LL_ENDL;
-            return; // this error condition is not recoverable.
-        }
-
-        impl = regionp->getRegionImplNC();
-
-        ++(impl->mSeedCapAttempts);
-
         if (id != impl->mHttpResponderID) // region is no longer referring to this request
         {
             LL_WARNS("AppInit", "Capabilities") << "Received results for a stale capabilities request!" << LL_ENDL;
@@ -1111,6 +1111,15 @@ S32 LLViewerRegion::renderPropertyLines()
 	}
 }
 
+void LLViewerRegion::renderPropertyLinesOnMinimap(F32 scale_pixels_per_meter, const F32 *parcel_outline_color)
+{
+    if (mParcelOverlay)
+    {
+        mParcelOverlay->renderPropertyLinesOnMinimap(scale_pixels_per_meter, parcel_outline_color);
+    }
+}
+
+
 // This gets called when the height field changes.
 void LLViewerRegion::dirtyHeights()
 {
diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h
index 2ed155d1bfb91406d0798ca5fe98c9dd9f376396..7d6ee3e7c324a4af0559ba7b1c6f34260d033fab 100644
--- a/indra/newview/llviewerregion.h
+++ b/indra/newview/llviewerregion.h
@@ -158,6 +158,8 @@ class LLViewerRegion final : public LLCapabilityProvider // implements this inte
 	// Draw lines in the dirt showing ownership. Return number of 
 	// vertices drawn.
 	S32 renderPropertyLines();
+    void renderPropertyLinesOnMinimap(F32 scale_pixels_per_meter, const F32* parcel_outline_color);
+
 
 	// Call this whenever you change the height data in the region.
 	// (Automatically called by LLSurfacePatch's update routine)
diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp
index 3697cca787f88e8fff89324f26160622952a42e6..2dcd6d05529a80ee5d8f54bbdab42b9922074152 100644
--- a/indra/newview/llviewershadermgr.cpp
+++ b/indra/newview/llviewershadermgr.cpp
@@ -550,15 +550,14 @@ void LLViewerShaderMgr::setShaders()
     mShaderLevel[SHADER_WINDLIGHT] = wl_class;
     mShaderLevel[SHADER_DEFERRED] = deferred_class;
 
-    BOOL loaded = loadBasicShaders();
-    if (loaded)
+    std::string shader_name = loadBasicShaders();
+    if (shader_name.empty())
     {
         LL_INFOS() << "Loaded basic shaders." << LL_ENDL;
     }
     else
     {
-        LL_ERRS() << "Unable to load basic shaders, verify graphics driver installed and current." << LL_ENDL;
-        llassert(loaded);
+        LL_ERRS() << "Unable to load basic shader " << shader_name << ", verify graphics driver installed and current." << LL_ENDL;
         reentrance = false; // For hygiene only, re-try probably helps nothing 
         return;
     }
@@ -566,7 +565,7 @@ void LLViewerShaderMgr::setShaders()
     gPipeline.mShadersLoaded = true;
 
     // Load all shaders to set max levels
-    loaded = loadShadersEnvironment();
+    BOOL loaded = loadShadersEnvironment();
 
     if (loaded)
     {
@@ -841,7 +840,7 @@ void LLViewerShaderMgr::unloadShaders()
 	gPipeline.mShadersLoaded = false;
 }
 
-BOOL LLViewerShaderMgr::loadBasicShaders()
+std::string LLViewerShaderMgr::loadBasicShaders()
 {
 	// Load basic dependency shaders first
 	// All of these have to load for any shaders to function
@@ -927,8 +926,8 @@ BOOL LLViewerShaderMgr::loadBasicShaders()
 		// Note usage of GL_VERTEX_SHADER
 		if (loadShaderFile(shaders[i].first, shaders[i].second, GL_VERTEX_SHADER, &attribs) == 0)
 		{
-			LL_SHADER_LOADING_WARNS() << "Failed to load vertex shader " << shaders[i].first << LL_ENDL;
-			return FALSE;
+			LL_WARNS("Shader") << "Failed to load vertex shader " << shaders[i].first << LL_ENDL;
+			return shaders[i].first;
 		}
 	}
 
@@ -987,12 +986,12 @@ BOOL LLViewerShaderMgr::loadBasicShaders()
 		// Note usage of GL_FRAGMENT_SHADER
 		if (loadShaderFile(shaders[i].first, shaders[i].second, GL_FRAGMENT_SHADER, &attribs, index_channels[i]) == 0)
 		{
-			LL_SHADER_LOADING_WARNS() << "Failed to load fragment shader " << shaders[i].first << LL_ENDL;
-			return FALSE;
+			LL_WARNS("Shader") << "Failed to load fragment shader " << shaders[i].first << LL_ENDL;
+			return shaders[i].first;
 		}
 	}
 
-	return TRUE;
+	return std::string();
 }
 
 BOOL LLViewerShaderMgr::loadShadersEnvironment()
@@ -1480,6 +1479,12 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 
             gDeferredMaterialProgram[i].addPermutation("DIFFUSE_ALPHA_MODE", llformat("%d", alpha_mode));
 
+            if (alpha_mode != 0)
+            {
+                gDeferredMaterialProgram[i].mFeatures.hasAlphaMask = true;
+                gDeferredMaterialProgram[i].addPermutation("HAS_ALPHA_MASK", "1");
+            }
+
             if (use_sun_shadow)
             {
                 gDeferredMaterialProgram[i].addPermutation("HAS_SUN_SHADOW", "1");
@@ -1538,6 +1543,12 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
             }
 
             gDeferredMaterialWaterProgram[i].addPermutation("DIFFUSE_ALPHA_MODE", llformat("%d", alpha_mode));
+            if (alpha_mode != 0)
+            {
+                gDeferredMaterialWaterProgram[i].mFeatures.hasAlphaMask = true;
+                gDeferredMaterialWaterProgram[i].addPermutation("HAS_ALPHA_MASK", "1");
+            }
+
             if (use_sun_shadow)
             {
                 gDeferredMaterialWaterProgram[i].addPermutation("HAS_SUN_SHADOW", "1");
diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h
index 681957dbf36873c7dbfadf418ffb444c740df4ba..1a474f6a20bb42fce2ee735cf3ab18d894fe9058 100644
--- a/indra/newview/llviewershadermgr.h
+++ b/indra/newview/llviewershadermgr.h
@@ -51,7 +51,11 @@ class LLViewerShaderMgr: public LLShaderMgr
 	void setShaders();
 	void unloadShaders();
     S32  getShaderLevel(S32 type);
-	BOOL loadBasicShaders();
+
+    // loadBasicShaders in case of a failure returns
+    // name of a file error happened at, otherwise
+    // returns an empty string
+    std::string loadBasicShaders();
 	BOOL loadShadersEffects();
 	BOOL loadShadersDeferred();
 	BOOL loadShadersObject();
diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp
index 671c67557691cdbfabb21e980df74b7c927613ac..b39816b781702700a640768889a0690cc091980c 100644
--- a/indra/newview/llviewertexturelist.cpp
+++ b/indra/newview/llviewertexturelist.cpp
@@ -1159,15 +1159,14 @@ F32 LLViewerTextureList::updateImagesFetchTextures(F32 max_time)
 			total_update_count--;
 		}
 	}
-	
-	S32 fetch_count = 0;
+
 	size_t min_update_count = llmin(MIN_UPDATE_COUNT,(S32)(entries.size()-max_priority_count));
 	S32 min_count = max_priority_count + min_update_count;
 	for (entries_list_t::iterator iter3 = entries.begin();
 		 iter3 != entries.end(); )
 	{
 		LLViewerFetchedTexture* imagep = *iter3++;
-		fetch_count += (imagep->updateFetch() ? 1 : 0);
+        imagep->updateFetch();
 		if (min_count <= min_update_count)
 		{
 			mLastFetchKey = LLTextureKey(imagep->getID(), (ETexListType)imagep->getTextureListType());
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index dce302760bae90efea0ce929cb8878a656382505..fe8824c101f2f8694a15bb0e58bed3e664c0f38c 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -2043,7 +2043,7 @@ LLViewerWindow::LLViewerWindow(const Params& p)
 	}
 	LLVertexBuffer::initClass(gSavedSettings.getBOOL("RenderVBOEnable"), gSavedSettings.getBOOL("RenderVBOMappingDisable"));
 	LL_INFOS("RenderInit") << "LLVertexBuffer initialization done." << LL_ENDL ;
-	gGL.init() ;
+	gGL.init(true);
 
 	if (LLFeatureManager::getInstance()->isSafe()
 		|| (gSavedSettings.getS32("LastFeatureVersion") != LLFeatureManager::getInstance()->getVersion())
@@ -2556,10 +2556,11 @@ void LLViewerWindow::reshape(S32 width, S32 height)
 
 		//glViewport(0, 0, width, height );
 
-		if (height > 0)
+        LLViewerCamera * camera = LLViewerCamera::getInstance(); // simpleton, might not exist
+		if (height > 0 && camera)
 		{ 
-			LLViewerCamera::getInstance()->setViewHeightInPixels( mWorldViewRectRaw.getHeight() );
-			LLViewerCamera::getInstance()->setAspect( getWorldViewAspectRatio() );
+            camera->setViewHeightInPixels( mWorldViewRectRaw.getHeight() );
+            camera->setAspect( getWorldViewAspectRatio() );
 		}
 
 		calcDisplayScale();
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index c57d8cd8169d1cce0c0853598583f26ed46d01c6..086d8b3808fc109dd6b3924c37d5ff31612657f5 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -2840,6 +2840,7 @@ void LLVOAvatar::idleUpdateMisc(bool detailed_update)
 	{
         U32 draw_order = 0;
 		auto& selectMgr = LLSelectMgr::instance();
+        S32 attachment_selected = selectMgr.getSelection()->getObjectCount() && selectMgr.getSelection()->isAttachment();
 #if SLOW_ATTACHMENT_LIST
 		for (const auto& attach_point_pair : mAttachmentPoints)
 		{
@@ -2881,7 +2882,7 @@ void LLVOAvatar::idleUpdateMisc(bool detailed_update)
                     }
 
                     // if selecting any attachments, update all of them as non-damped
-					if (selectMgr.getSelection()->getObjectCount() && selectMgr.getSelection()->isAttachment())
+                    if (attachment_selected)
                     {
                         gPipeline.updateMoveNormalAsync(attached_object->mDrawable);
                     }
@@ -6419,7 +6420,21 @@ LLJoint *LLVOAvatar::getJoint( const std::string &name )
 
 	if (iter == mJointMap.end() || iter->second == NULL)
 	{   //search for joint and cache found joint in lookup table
-		jointp = mRoot->findJoint(name);
+		if (mJointAliasMap.empty())
+		{
+			getJointAliases();
+		}
+		joint_alias_map_t::const_iterator alias_iter = mJointAliasMap.find(name);
+		std::string canonical_name;
+		if (alias_iter != mJointAliasMap.end())
+		{
+			canonical_name = alias_iter->second;
+		}
+		else
+		{
+			canonical_name = name;
+		}
+		jointp = mRoot->findJoint(canonical_name);
 		mJointMap[name] = jointp;
 	}
 	else
diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp
index 0e148eebde938c1b1f944177c240655bd77f4190..772a133fc2164614e1bd7758e0572aba091ba720 100644
--- a/indra/newview/llvoavatarself.cpp
+++ b/indra/newview/llvoavatarself.cpp
@@ -62,6 +62,7 @@
 #include "llsdserialize.h"
 #include "llcallstack.h"
 #include "llcorehttputil.h"
+#include "lluiusage.h"
 // [RLVa:KB] - Checked: RLVa-2.0.2
 #include "rlvhandler.h"
 #include "rlvhelper.h"
@@ -3012,6 +3013,7 @@ void LLVOAvatarSelf::onCustomizeStart(bool disable_camera_switch)
 {
 	if (isAgentAvatarValid())
 	{
+		LLUIUsage::instance().logCommand("Avatar.CustomizeStart");
 		if (!gAgentAvatarp->mEndCustomizeCallback.get())
 		{
 			gAgentAvatarp->mEndCustomizeCallback = new LLUpdateAppearanceOnDestroy;
diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp
index 39e004e96eb78019a1b6080ee5c87b0d1cd62422..2ef06b048a632d7018e7c4bbaba43ed6d9965374 100644
--- a/indra/newview/llvocache.cpp
+++ b/indra/newview/llvocache.cpp
@@ -367,15 +367,6 @@ void LLVOCacheEntry::updateDebugSettings()
 	}
 	timer.reset();
 
-	//the number of frames invisible objects stay in memory
-	static LLCachedControl<U32> inv_obj_time(gSavedSettings,"NonvisibleObjectsInMemoryTime");
-	sMinFrameRange = inv_obj_time - 1; //make 0 to be the maximum 
-
-	//min radius: all objects within this radius remain loaded in memory
-	static LLCachedControl<F32> min_radius(gSavedSettings,"SceneLoadMinRadius");
-	sNearRadius = llmin((F32)min_radius, gAgentCamera.mDrawDistance); //can not exceed the draw distance
-	sNearRadius = llmax(sNearRadius, 1.f); //minimum value is 1.0m
-
 	//objects within the view frustum whose visible area is greater than this threshold will be loaded
 	static LLCachedControl<F32> front_pixel_threshold(gSavedSettings,"SceneLoadFrontPixelThreshold");
 	sFrontPixelThreshold = front_pixel_threshold;
@@ -385,29 +376,38 @@ void LLVOCacheEntry::updateDebugSettings()
 	sRearPixelThreshold = rear_pixel_threshold;
 	sRearPixelThreshold = llmax(sRearPixelThreshold, sFrontPixelThreshold); //can not be smaller than sFrontPixelThreshold.
 
-	// a percentage of draw distance beyond which all objects outside of view frustum will be unloaded, regardless of pixel threshold
-	static LLCachedControl<F32> rear_max_radius_frac(gSavedSettings,"SceneLoadRearMaxRadiusFraction");
-	sRearFarRadius = llmax(rear_max_radius_frac * gAgentCamera.mDrawDistance / 100.f, 1.0f); //minimum value is 1.0m
-	sRearFarRadius = llmax(sRearFarRadius, (F32)min_radius); //can not be less than "SceneLoadMinRadius".
-	sRearFarRadius = llmin(sRearFarRadius, gAgentCamera.mDrawDistance); //can not be more than the draw distance.
-
-	//make the above parameters adaptive to memory usage
+	//make parameters adaptive to memory usage
 	//starts to put restrictions from low_mem_bound_MB, apply tightest restrictions when hits high_mem_bound_MB
 	static LLCachedControl<U32> low_mem_bound_MB(gSavedSettings,"SceneLoadLowMemoryBound");
 	static LLCachedControl<U32> high_mem_bound_MB(gSavedSettings,"SceneLoadHighMemoryBound");
 	
 	LLMemory::updateMemoryInfo() ;
 	U32 allocated_mem = LLMemory::getAllocatedMemKB().value();
-	allocated_mem /= 1024; //convert to MB.
-	if(allocated_mem < low_mem_bound_MB)
-	{
-		return; 
-	}
-	F32 adjust_factor = llmax(0.f, (F32)(high_mem_bound_MB - allocated_mem) / (high_mem_bound_MB - low_mem_bound_MB));
-
-	sRearFarRadius = llmin(adjust_factor * sRearFarRadius, 96.f);  //[0.f, 96.f]
-	sMinFrameRange = (U32)llclamp(adjust_factor * sMinFrameRange, 10.f, 64.f);  //[10, 64]
-	sNearRadius    = llmax(adjust_factor * sNearRadius, 1.0f);
+    static const F32 KB_to_MB = 1.f / 1024.f;
+	U32 clamped_memory = llclamp(allocated_mem * KB_to_MB, (F32) low_mem_bound_MB, (F32) high_mem_bound_MB);
+    const F32 adjust_range = high_mem_bound_MB - low_mem_bound_MB;
+    const F32 adjust_factor = (high_mem_bound_MB - clamped_memory) / adjust_range; // [0, 1]
+
+    //min radius: all objects within this radius remain loaded in memory
+    static LLCachedControl<F32> min_radius(gSavedSettings,"SceneLoadMinRadius");
+    static const F32 MIN_RADIUS = 1.0f;
+    const F32 draw_radius = gAgentCamera.mDrawDistance;
+    const F32 clamped_min_radius = llclamp((F32) min_radius, MIN_RADIUS, draw_radius); // [1, mDrawDistance]
+    sNearRadius = MIN_RADIUS + ((clamped_min_radius - MIN_RADIUS) * adjust_factor);
+
+    // a percentage of draw distance beyond which all objects outside of view frustum will be unloaded, regardless of pixel threshold
+    static LLCachedControl<F32> rear_max_radius_frac(gSavedSettings,"SceneLoadRearMaxRadiusFraction");
+    const F32 min_radius_plus_one = sNearRadius + 1.f;
+    const F32 max_radius = rear_max_radius_frac * gAgentCamera.mDrawDistance;
+    const F32 clamped_max_radius = llclamp(max_radius, min_radius_plus_one, draw_radius); // [sNearRadius, mDrawDistance]
+    sRearFarRadius = min_radius_plus_one + ((clamped_max_radius - min_radius_plus_one) * adjust_factor);
+
+    //the number of frames invisible objects stay in memory
+    static LLCachedControl<U32> inv_obj_time(gSavedSettings,"NonvisibleObjectsInMemoryTime");
+    static const U32 MIN_FRAMES = 10;
+    static const U32 MAX_FRAMES = 64;
+    const U32 clamped_frames = inv_obj_time ? llclamp((U32) inv_obj_time, MIN_FRAMES, MAX_FRAMES) : MAX_FRAMES; // [10, 64], with zero => 64
+    sMinFrameRange = MIN_FRAMES + ((clamped_frames - MIN_FRAMES) * adjust_factor);
 }
 
 //static 
diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp
index 487e294d54c88397dc1d446c034d1382a4af9be3..c3164dd71431f15c42cb50baf7e5c6e26addf8d9 100644
--- a/indra/newview/llvoiceclient.cpp
+++ b/indra/newview/llvoiceclient.cpp
@@ -37,6 +37,7 @@
 #include "llui.h"
 #include "llkeyboard.h"
 #include "llagent.h"
+#include "lluiusage.h"
 
 const F32 LLVoiceClient::OVERDRIVEN_POWER_LEVEL = 0.7f;
 
@@ -603,6 +604,10 @@ void LLVoiceClient::setMuteMic(bool muted)
 
 void LLVoiceClient::setUserPTTState(bool ptt)
 {
+	if (ptt)
+	{
+		LLUIUsage::instance().logCommand("Agent.EnableMicrophone");
+	}
 	mUserPTTState = ptt;
 	updateMicMuteLogic();
 	mMicroChangedSignal();
diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp
index 8662b1ff672c8550bf23d60a0cb3c7795a4fc746..f42bca727a66b2255285cd3d413cbffe36574e69 100644
--- a/indra/newview/llvoicevivox.cpp
+++ b/indra/newview/llvoicevivox.cpp
@@ -1635,13 +1635,33 @@ bool LLVivoxVoiceClient::requestParcelVoiceInfo()
         }
         else
         {
-            LL_WARNS("Voice") << "No voice channel credentials" << LL_ENDL;
-
+            LLVoiceChannel* channel = LLVoiceChannel::getCurrentVoiceChannel();
+            if (channel != NULL)
+            {
+                if (channel->getSessionName().empty() && channel->getSessionID().isNull())
+                {
+                    if (LLViewerParcelMgr::getInstance()->allowAgentVoice())
+                    {
+                        LL_WARNS("Voice") << "No channel credentials for default channel" << LL_ENDL;
+                    }
+                }
+                else
+                {
+                    LL_WARNS("Voice") << "No voice channel credentials" << LL_ENDL;
+                }
+            }
         }
     }
     else
     {
-        LL_WARNS("Voice") << "No voice credentials" << LL_ENDL;
+        if (LLViewerParcelMgr::getInstance()->allowAgentVoice())
+        {
+            LL_WARNS("Voice") << "No voice credentials" << LL_ENDL;
+        }
+        else
+        {
+            LL_DEBUGS("Voice") << "No voice credentials" << LL_ENDL;
+        }
     }
 
     // set the spatial channel.  If no voice credentials or uri are 
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index 0d2e307d68a28c961b7025f4a5ed780c7f9f3b49..260adcb42dc251c1b4ae33f71636820a7c3b4ba8 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -1761,7 +1761,7 @@ BOOL LLVOVolume::genBBoxes(BOOL force_global, BOOL should_update_octree_bounds)
         // updates needed, set REBUILD_RIGGED accordingly.
 
         // Without the flag, this will remove unused rigged volumes, which we are not currently very aggressive about.
-        updateRiggedVolume();
+        updateRiggedVolume(false);
     }
 
     LLVolume* volume = mRiggedVolume;
@@ -2020,7 +2020,7 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable)
 	
 	if (mDrawable->isState(LLDrawable::REBUILD_RIGGED))
 	{
-		updateRiggedVolume();
+        updateRiggedVolume(false);
 		genBBoxes(FALSE);
 		mDrawable->clearState(LLDrawable::REBUILD_RIGGED);
 	}
@@ -4660,7 +4660,7 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a&
 	{
 		if (pick_rigged || LLToolMgr::getInstance()->inBuildMode())
 		{
-			updateRiggedVolume(true);
+            updateRiggedVolume(true, LLRiggedVolume::DO_NOT_UPDATE_FACES);
 			volume = mRiggedVolume;
 			transform = false;
 		}
@@ -4735,6 +4735,9 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a&
 				continue;
 			}
 
+            // This calculates the bounding box of the skinned mesh from scratch. It's actually quite expensive, but not nearly as expensive as building a full octree.
+            // rebuild_face_octrees = false because an octree for this face will be built later only if needed for narrow phase picking.
+            updateRiggedVolume(true, i, false);
 			face_hit = volume->lineSegmentIntersect(local_start, local_end, i,
 													&p, &tc, &n, &tn);
 			
@@ -4858,13 +4861,13 @@ void LLVOVolume::clearRiggedVolume()
 	}
 }
 
-void LLVOVolume::updateRiggedVolume(bool force_update)
+void LLVOVolume::updateRiggedVolume(bool force_treat_as_rigged, LLRiggedVolume::FaceIndex face_index, bool rebuild_face_octrees)
 {
     LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME;
 	//Update mRiggedVolume to match current animation frame of avatar. 
 	//Also update position/size in octree.  
 
-	if ((!force_update) && (!treatAsRigged()))
+    if ((!force_treat_as_rigged) && (!treatAsRigged()))
 	{
 		clearRiggedVolume();
 		
@@ -4898,10 +4901,10 @@ void LLVOVolume::updateRiggedVolume(bool force_update)
 		updateRelativeXform();
 	}
 
-	mRiggedVolume->update(mSkinInfo, avatar, volume, this);
+    mRiggedVolume->update(mSkinInfo, avatar, volume, face_index, rebuild_face_octrees);
 }
 
-void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, const LLVolume* volume, LLVOVolume* src_object)
+void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, const LLVolume* volume, FaceIndex face_index, bool rebuild_face_octrees)
 {
     LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME;
 	bool copy = false;
@@ -4933,7 +4936,7 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons
 		if (is_paused)
 		{
             S32 frames_paused = LLFrameTimer::getFrameCount() - avatar->getMotionController().getPausedFrame();
-            if (frames_paused > 2)
+            if (frames_paused > 1)
             {
                 return;
             }
@@ -4952,7 +4955,24 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons
     S32 rigged_vert_count = 0;
     S32 rigged_face_count = 0;
     LLVector4a box_min, box_max;
-	for (S32 i = 0; i < vol_num_faces; ++i)
+    S32 face_begin;
+    S32 face_end;
+    if (face_index == DO_NOT_UPDATE_FACES)
+    {
+        face_begin = 0;
+        face_end = 0;
+    }
+    else if (face_index == UPDATE_ALL_FACES)
+    {
+        face_begin = 0;
+        face_end = vol_num_faces;
+    }
+    else
+    {
+        face_begin = face_index;
+        face_end = face_begin + 1;
+    }
+    for (S32 i = face_begin; i < face_end; ++i)
 	{
 		const LLVolumeFace& vol_face = volume->getVolumeFace(i);
 		
@@ -5013,15 +5033,10 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons
 
 			}
 
+            if (rebuild_face_octrees)
 			{
-    			delete dst_face.mOctree;
-				dst_face.mOctree = NULL;
-
-				LLVector4a size;
-				size.setSub(dst_face.mExtents[1], dst_face.mExtents[0]);
-				size.splat(size.getLength3().getF32()*0.5f);
-			
-				dst_face.createOctree(1.f);
+                dst_face.destroyOctree();
+                dst_face.createOctree();
 			}
 		}
 	}
@@ -5535,7 +5550,7 @@ static inline void add_face(T*** list, U32* count, T* face)
     {
         if (count[1] < MAX_FACE_COUNT)
         {
-            face->setDrawOrderIndex(count[1]);
+            //face->setDrawOrderIndex(count[1]);
             list[1][count[1]++] = face;
         }
     }
@@ -5543,15 +5558,40 @@ static inline void add_face(T*** list, U32* count, T* face)
     {
         if (count[0] < MAX_FACE_COUNT)
         {
-            face->setDrawOrderIndex(count[0]);
+            //face->setDrawOrderIndex(count[0]);
             list[0][count[0]++] = face;
         }
     }
 }
 
+// return index into linkset for given object (0 for root prim)
+U32 get_linkset_index(LLVOVolume* vobj)
+{
+    LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWABLE;
+    if (vobj->isRootEdit())
+    {
+        return 0;
+    }
+
+    LLViewerObject* root = vobj->getRootEdit();
+    U32 idx = 1;
+    for (const auto& child : root->getChildren())
+    {
+        if (child == vobj)
+        {
+            return idx;
+        }
+        ++idx;
+    }
+
+    llassert(false);
+    return idx; //should never get here
+}
+
 void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 {
     LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME;
+
 	if (group->changeLOD())
 	{
 		group->mLastUpdateDistance = group->mDistance;
@@ -5696,6 +5736,8 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
                 avatar->addAttachmentOverridesForObject(vobj, NULL, false);
             }
             
+            U32 linkset_index = get_linkset_index(vobj);
+
             // Standard rigged mesh attachments: 
 			bool rigged = !vobj->isAnimatedObject() && skinInfo && vobj->isAttachment();
             // Animated objects. Have to check for isRiggedMesh() to
@@ -5715,6 +5757,9 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 					continue;
 				}
 
+                // order by linkset index first and face index second
+                facep->setDrawOrderIndex(linkset_index * 100 + i);
+
 				//ALWAYS null out vertex buffer on rebuild -- if the face lands in a render
 				// batch, it will recover its vertex buffer reference from the spatial group
 				facep->setVertexBuffer(NULL);
@@ -5739,11 +5784,6 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
                     if (facep->isState(LLFace::RIGGED))
                     { 
                         //face is not rigged but used to be, remove from rigged face pool
-                        LLDrawPoolAvatar* pool = (LLDrawPoolAvatar*) facep->getPool();
-                        if (pool)
-                        {
-                            pool->removeFace(facep);
-                        }
                         facep->clearState(LLFace::RIGGED);
                         facep->mAvatar = NULL;
                         facep->mSkinInfo = NULL;
@@ -5925,7 +5965,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 			else
 			{
 				drawablep->clearState(LLDrawable::RIGGED);
-                vobj->updateRiggedVolume();
+                vobj->updateRiggedVolume(false);
 			}
 		}
 	}
@@ -6035,7 +6075,7 @@ void LLVolumeGeometryManager::rebuildMesh(LLSpatialGroup* group)
 			{
 				LLDrawable* drawablep = (LLDrawable*)(*drawable_iter)->getDrawable();
 
-				if (drawablep && !drawablep->isDead() && drawablep->isState(LLDrawable::REBUILD_ALL) && !drawablep->isState(LLDrawable::RIGGED) )
+				if (drawablep && !drawablep->isDead() && drawablep->isState(LLDrawable::REBUILD_ALL))
 				{
 					LLVOVolume* vobj = drawablep->getVOVolume();
 					
@@ -6072,8 +6112,6 @@ void LLVolumeGeometryManager::rebuildMesh(LLSpatialGroup* group)
 							LLVertexBuffer* buff = face->getVertexBuffer();
 							if (buff)
 							{
-								llassert(!face->isState(LLFace::RIGGED));
-
 								if (!face->getGeometryVolume(*volume, face->getTEOffset(), 
 									vobj->getRelativeXform(), vobj->getRelativeXformInvTrans(), face->getGeomIndex()))
 								{ //something's gone wrong with the vertex buffer accounting, rebuild this group 
@@ -6202,6 +6240,13 @@ struct CompareBatchBreakerRigged
     }
 };
 
+struct CompareDrawOrder
+{
+    bool operator()(const LLFace* const& lhs, const LLFace* const& rhs)
+    {
+        return lhs->getDrawOrderIndex() < rhs->getDrawOrderIndex();
+    }
+};
 
 U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace** faces, U32 face_count, BOOL distance_sort, BOOL batch_textures, BOOL rigged)
 {
@@ -6236,6 +6281,11 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace
                 //sort faces by things that break batches, including avatar and mesh id
                 std::sort(faces, faces + face_count, CompareBatchBreakerRigged());
             }
+            else
+            {
+                // preserve legacy draw order for rigged faces
+                std::sort(faces, faces + face_count, CompareDrawOrder());
+            }
         }
         else if (!distance_sort)
         {
diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h
index 270029e45f8dfb228959aeff3bd2df66702bbd43..fff8538e644cb30df21f3e4dfb6b9ac38858f395 100644
--- a/indra/newview/llvovolume.h
+++ b/indra/newview/llvovolume.h
@@ -67,7 +67,10 @@ class LLRiggedVolume final : public LLVolume
 	{
 	}
 
-	void update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, const LLVolume* src_volume, LLVOVolume* src_object);
+    using FaceIndex = S32;
+    static const FaceIndex UPDATE_ALL_FACES = -1;
+    static const FaceIndex DO_NOT_UPDATE_FACES = -2;
+    void update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, const LLVolume* src_volume, FaceIndex face_index = UPDATE_ALL_FACES, bool rebuild_face_octrees = true);
 
     std::string mExtraDebugText;
 };
@@ -370,8 +373,9 @@ class LLVOVolume final : public LLViewerObject
 	S32 getMDCImplCount() { return mMDCImplCount; }
 	
 
-	//rigged volume update (for raycasting)
-	void updateRiggedVolume(bool force_update = false);
+    // Rigged volume update (for raycasting)
+    // By default, this updates the bounding boxes of all the faces and builds an octree for precise per-triangle raycasting
+    void updateRiggedVolume(bool force_treat_as_rigged, LLRiggedVolume::FaceIndex face_index = LLRiggedVolume::UPDATE_ALL_FACES, bool rebuild_face_octrees = true);
 	LLRiggedVolume* getRiggedVolume();
 
 	//returns true if volume should be treated as a rigged volume
diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp
index fc6f7083bf788a6b0be0bc05a3b9f28b810929f5..af24e410fd922750acd5f30bf4881b3a439d5d47 100644
--- a/indra/newview/llworld.cpp
+++ b/indra/newview/llworld.cpp
@@ -825,14 +825,13 @@ void LLWorld::updateParticles()
 void LLWorld::renderPropertyLines()
 {
 	S32 region_count = 0;
-	S32 vertex_count = 0;
 
 	for (region_list_t::iterator iter = mVisibleRegionList.begin();
 		 iter != mVisibleRegionList.end(); ++iter)
 	{
 		LLViewerRegion* regionp = *iter;
 		region_count++;
-		vertex_count += regionp->renderPropertyLines();
+		regionp->renderPropertyLines();
 	}
 }
 
@@ -840,13 +839,11 @@ void LLWorld::renderPropertyLines()
 void LLWorld::updateNetStats()
 {
 	F64Bits bits;
-	U32 packets = 0;
 
 	for (LLViewerRegion* regionp : mActiveRegionList)
 	{
 		regionp->updateNetStats();
 		bits += regionp->mBitsReceived;
-		packets += llfloor( regionp->mPacketsReceived );
 		regionp->mBitsReceived = (F32Bits)0.f;
 		regionp->mPacketsReceived = 0.f;
 	}
diff --git a/indra/newview/llworldmapview.cpp b/indra/newview/llworldmapview.cpp
index d4d7fe6933eb75af6d2bcd672dcf5a8a3f5c3a56..9d5e9831a95e5eca24cc65dee2be469e21100c7a 100644
--- a/indra/newview/llworldmapview.cpp
+++ b/indra/newview/llworldmapview.cpp
@@ -63,9 +63,15 @@
 
 #include "llglheaders.h"
 
+// # Constants
+static const F32 MAP_DEFAULT_SCALE = 128.f;
+static const F32 MAP_ITERP_TIME_CONSTANT = 0.75f;
+static const F32 MAP_ZOOM_ACCELERATION_TIME = 0.3f;
+static const F32 MAP_ZOOM_MAX_INTERP = 0.5f;
+static const F32 MAP_SCALE_SNAP_THRESHOLD = 0.005f;
+
 // Basically a C++ implementation of the OCEAN_COLOR defined in mapstitcher.py 
 // Please ensure consistency between those 2 files (TODO: would be better to get that color from an asset source...)
-// # Constants
 // OCEAN_COLOR = "#1D475F"
 const F32 OCEAN_RED   = (F32)(0x1D)/255.f;
 const F32 OCEAN_GREEN = (F32)(0x47)/255.f;
@@ -98,14 +104,12 @@ LLUIImagePtr LLWorldMapView::sClassifiedsImage = NULL;
 LLUIImagePtr LLWorldMapView::sForSaleImage = NULL;
 LLUIImagePtr LLWorldMapView::sForSaleAdultImage = NULL;
 
-F32 LLWorldMapView::sPanX = 0.f;
-F32 LLWorldMapView::sPanY = 0.f;
-F32 LLWorldMapView::sTargetPanX = 0.f;
-F32 LLWorldMapView::sTargetPanY = 0.f;
 S32 LLWorldMapView::sTrackingArrowX = 0;
 S32 LLWorldMapView::sTrackingArrowY = 0;
 bool LLWorldMapView::sVisibleTilesLoaded = false;
-F32 LLWorldMapView::sMapScale = 128.f;
+F32 LLWorldMapView::sMapScaleSetting = MAP_DEFAULT_SCALE;
+LLVector2 LLWorldMapView::sZoomPivot = LLVector2(0.0f, 0.0f);
+LLFrameTimer LLWorldMapView::sZoomTimer = LLFrameTimer();
 
 std::map<std::string,std::string> LLWorldMapView::sStringsMap;
 
@@ -174,20 +178,27 @@ void LLWorldMapView::cleanupClass()
 	sForSaleAdultImage = NULL;
 }
 
-LLWorldMapView::LLWorldMapView()
-:	LLPanel(),
-	mBackgroundColor( LLColor4( OCEAN_RED, OCEAN_GREEN, OCEAN_BLUE, 1.f ) ),
-	mItemPicked(FALSE),
-	mPanning( FALSE ),
-	mMouseDownPanX( 0 ),
-	mMouseDownPanY( 0 ),
-	mMouseDownX( 0 ),
-	mMouseDownY( 0 ),
-	mSelectIDStart(0)
+LLWorldMapView::LLWorldMapView() :
+    LLPanel(),
+    mBackgroundColor(LLColor4(OCEAN_RED, OCEAN_GREEN, OCEAN_BLUE, 1.f)),
+    mItemPicked(FALSE),
+    mPanX(0.f),
+    mPanY(0.f),
+    mTargetPanX(0.f),
+    mTargetPanY(0.f),
+    mPanning(FALSE),
+    mMouseDownPanX(0),
+    mMouseDownPanY(0),
+    mMouseDownX(0),
+    mMouseDownY(0),
+    mSelectIDStart(0),
+    mMapScale(0.f),
+    mTargetMapScale(0.f),
+    mMapIterpTime(MAP_ITERP_TIME_CONSTANT)
 {
-	//LL_INFOS("WorldMap") << "Creating the Map -> LLWorldMapView::LLWorldMapView()" << LL_ENDL;
+    // LL_INFOS("WorldMap") << "Creating the Map -> LLWorldMapView::LLWorldMapView()" << LL_ENDL;
 
-	clearLastClick();
+    clearLastClick();
 }
 
 BOOL LLWorldMapView::postBuild()
@@ -218,6 +229,9 @@ BOOL LLWorldMapView::postBuild()
 	mTextBoxNorthEast ->reshapeToFitText();
 	mTextBoxSouthWest->reshapeToFitText();
 	mTextBoxNorthWest ->reshapeToFitText();
+    
+    sZoomTimer.stop();
+    setScale(sMapScaleSetting, true);
 
 	return true;
 }
@@ -235,59 +249,111 @@ void LLWorldMapView::cleanupTextures()
 {
 }
 
+void LLWorldMapView::zoom(F32 zoom)
+{
+    mTargetMapScale = scaleFromZoom(zoom);
+    if (!sZoomTimer.getStarted() && mMapScale != mTargetMapScale)
+    {
+        sZoomPivot = LLVector2(0, 0);
+        sZoomTimer.start();
+    }
+}
 
-// static
-void LLWorldMapView::setScale( F32 scale )
+void LLWorldMapView::zoomWithPivot(F32 zoom, S32 x, S32 y)
 {
-	if (scale != sMapScale)
-	{
-		F32 old_scale = sMapScale;
+    mTargetMapScale = scaleFromZoom(zoom);
+    sZoomPivot      = LLVector2(x, y);
+    if (!sZoomTimer.getStarted() && mMapScale != mTargetMapScale)
+    {
+        sZoomTimer.start();
+    }
+}
 
-		sMapScale = scale;
-		if (sMapScale <= 0.f)
-		{
-			sMapScale = 0.1f;
-		}
+F32 LLWorldMapView::getZoom() { return LLWorldMapView::zoomFromScale(mMapScale); }
 
-		F32 ratio = (scale / old_scale);
-		sPanX *= ratio;
-		sPanY *= ratio;
-		sTargetPanX = sPanX;
-		sTargetPanY = sPanY;
-		sVisibleTilesLoaded = false;
-	}
-}
+F32 LLWorldMapView::getScale() { return mMapScale; }
 
+// static
+void LLWorldMapView::setScaleSetting(F32 scaleSetting) { sMapScaleSetting = scaleSetting; }
+
+// static
+F32 LLWorldMapView::getScaleSetting() { return sMapScaleSetting; }
+
+void LLWorldMapView::setScale(F32 scale, bool snap)
+{
+    if (scale != mMapScale)
+    {
+        F32 old_scale = mMapScale;
+
+        mMapScale = scale;
+        // Set the scale used when saving the setting
+        sMapScaleSetting = scale;
+        if (mMapScale <= 0.f)
+        {
+            mMapScale = 0.1f;
+        }
+        mMapIterpTime = MAP_ITERP_TIME_CONSTANT;
+        F32 ratio = (scale / old_scale);
+        mPanX *= ratio;
+        mPanY *= ratio;
+        mTargetPanX         = mPanX;
+        mTargetPanY         = mPanY;
+        sVisibleTilesLoaded = false;
+
+        // If we are zooming relative to somewhere else rather than the center of the map, compensate for the difference in panning here
+        if (!sZoomPivot.isExactlyZero())
+        {
+            LLVector2 relative_pivot;
+            relative_pivot.mV[VX]     = sZoomPivot.mV[VX] - (getRect().getWidth() / 2.0);
+            relative_pivot.mV[VY]     = sZoomPivot.mV[VY] - (getRect().getHeight() / 2.0);
+            LLVector2 zoom_pan_offset = relative_pivot - (relative_pivot * scale / old_scale);
+            mPanX += zoom_pan_offset.mV[VX];
+            mPanY += zoom_pan_offset.mV[VY];
+            mTargetPanX += zoom_pan_offset.mV[VX];
+            mTargetPanY += zoom_pan_offset.mV[VY];
+        }
+    }
+
+    if (snap)
+    {
+        mTargetMapScale = scale;
+    }
+}
 
 // static
-void LLWorldMapView::translatePan( S32 delta_x, S32 delta_y )
+void LLWorldMapView::translatePan(S32 delta_x, S32 delta_y)
 {
-	sPanX += delta_x;
-	sPanY += delta_y;
-	sTargetPanX = sPanX;
-	sTargetPanY = sPanY;
-	sVisibleTilesLoaded = false;
+    mPanX += delta_x;
+    mPanY += delta_y;
+    mTargetPanX         = mPanX;
+    mTargetPanY         = mPanY;
+    sVisibleTilesLoaded = false;
 }
 
 
 // static
-void LLWorldMapView::setPan( S32 x, S32 y, BOOL snap )
+void LLWorldMapView::setPan(S32 x, S32 y, BOOL snap)
 {
-	sTargetPanX = (F32)x;
-	sTargetPanY = (F32)y;
-	if (snap)
-	{
-		sPanX = sTargetPanX;
-		sPanY = sTargetPanY;
-	}
-	sVisibleTilesLoaded = false;
+    mMapIterpTime = MAP_ITERP_TIME_CONSTANT;
+    mTargetPanX = (F32) x;
+    mTargetPanY = (F32) y;
+    if (snap)
+    {
+        mPanX = mTargetPanX;
+        mPanY = mTargetPanY;
+    }
+    sVisibleTilesLoaded = false;
 }
 
-bool LLWorldMapView::showRegionInfo()
+// static
+void LLWorldMapView::setPanWithInterpTime(S32 x, S32 y, BOOL snap, F32 interp_time)
 {
-	return (LLWorldMipmap::scaleToLevel(sMapScale) <= DRAW_SIMINFO_THRESHOLD ? true : false);
+    setPan(x, y, snap);
+    mMapIterpTime = interp_time;
 }
 
+bool LLWorldMapView::showRegionInfo() { return (LLWorldMipmap::scaleToLevel(mMapScale) <= DRAW_SIMINFO_THRESHOLD ? true : false); }
+
 ///////////////////////////////////////////////////////////////////////////////////
 // HELPERS
 
@@ -308,9 +374,28 @@ void LLWorldMapView::draw()
 
 	mVisibleRegions.clear();
 
-	// animate pan if necessary
-	sPanX = ll_lerp(sPanX, sTargetPanX, LLSmoothInterpolation::getInterpolant(0.1f));
-	sPanY = ll_lerp(sPanY, sTargetPanY, LLSmoothInterpolation::getInterpolant(0.1f));
+    // animate pan if necessary
+    mPanX = lerp(mPanX, mTargetPanX, LLSmoothInterpolation::getInterpolant(mMapIterpTime));
+    mPanY = lerp(mPanY, mTargetPanY, LLSmoothInterpolation::getInterpolant(mMapIterpTime));
+    
+    //RN: snaps to zoom value because interpolation caused jitter in the text rendering
+    if (!sZoomTimer.getStarted() && mMapScale != mTargetMapScale)
+    {
+        sZoomTimer.start();
+    }
+    bool snap_scale = false;
+    F32 interp = llmin(MAP_ZOOM_MAX_INTERP, sZoomTimer.getElapsedTimeF32() / MAP_ZOOM_ACCELERATION_TIME);
+    F32 current_zoom_val = zoomFromScale(mMapScale);
+    F32 target_zoom_val = zoomFromScale(mTargetMapScale);
+    F32 new_zoom_val = lerp(current_zoom_val, target_zoom_val, interp);
+    if (abs(new_zoom_val - current_zoom_val) < MAP_SCALE_SNAP_THRESHOLD)
+    {
+        sZoomTimer.stop();
+        snap_scale = true;
+        new_zoom_val = target_zoom_val;
+    }
+    F32 map_scale = scaleFromZoom(new_zoom_val);
+    setScale(map_scale, snap_scale);
 
 	const S32 width = getRect().getWidth();
 	const S32 height = getRect().getHeight();
@@ -318,7 +403,7 @@ void LLWorldMapView::draw()
 	const F32 half_height = F32(height) / 2.0f;
 	LLVector3d camera_global = gAgentCamera.getCameraPositionGlobal();
 
-	S32 level = LLWorldMipmap::scaleToLevel(sMapScale);
+	S32 level = LLWorldMipmap::scaleToLevel(mMapScale);
 
 	LLLocalClipRect clip(getLocalRect());
 	{
@@ -354,15 +439,15 @@ void LLWorldMapView::draw()
 
 		// Find x and y position relative to camera's center.
 		LLVector3d rel_region_pos = origin_global - camera_global;
-		F32 relative_x = (rel_region_pos.mdV[0] / REGION_WIDTH_METERS) * sMapScale;
-		F32 relative_y = (rel_region_pos.mdV[1] / REGION_WIDTH_METERS) * sMapScale;
+		F32 relative_x = (rel_region_pos.mdV[0] / REGION_WIDTH_METERS) * mMapScale;
+		F32 relative_y = (rel_region_pos.mdV[1] / REGION_WIDTH_METERS) * mMapScale;
 
 		// Coordinates of the sim in pixels in the UI panel
 		// When the view isn't panned, 0,0 = center of rectangle
 		F32 bottom =    sPanY + half_height + relative_y;
 		F32 left =      sPanX + half_width + relative_x;
-		F32 top =		bottom+ (sMapScale * (info->getSizeY() / REGION_WIDTH_METERS));
-		F32 right =		left  + (sMapScale * (info->getSizeX() / REGION_WIDTH_METERS));
+		F32 top =		bottom+ (mMapScale * (info->getSizeY() / REGION_WIDTH_METERS));
+		F32 right =		left  + (mMapScale * (info->getSizeX() / REGION_WIDTH_METERS));
 
 		// Discard if region is outside the screen rectangle (not visible on screen)
 		if ((top < 0.f)   || (bottom > height) ||
@@ -423,8 +508,8 @@ void LLWorldMapView::draw()
 			if (overlayimage)
 			{
 				// Inform the fetch mechanism of the size we need
-				S32 x_draw_size = ll_round(sMapScale);
-				S32 y_draw_size = ll_round(sMapScale);
+				S32 x_draw_size = ll_round(mMapScale);
+				S32 y_draw_size = ll_round(mMapScale);
 				x_draw_size *= (info->getSizeX() / REGION_WIDTH_METERS);
 				y_draw_size *= (info->getSizeY() / REGION_WIDTH_METERS);
 
@@ -455,7 +540,7 @@ void LLWorldMapView::draw()
 		}
 
 		// Draw the region name in the lower left corner
-		if (sMapScale >= DRAW_TEXT_THRESHOLD)
+		if (mMapScale >= DRAW_TEXT_THRESHOLD)
 		{
 			static LLCachedControl<bool> mapShowAgentCount(gSavedSettings, "AlchemyMapShowAgentCount", true);
 			LLFontGL* font = LLFontGL::getFont(LLFontDescriptor("SansSerif", "Small", LLFontGL::BOLD));
@@ -493,7 +578,7 @@ void LLWorldMapView::draw()
 					LLColor4::white,
 					LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::DROP_SHADOW,
 					S32_MAX, //max_chars
-					sMapScale, //max_pixels
+					mMapScale, //max_pixels
 					NULL,
 					TRUE); //use ellipses
 			}
@@ -620,7 +705,7 @@ void LLWorldMapView::setVisible(BOOL visible)
 void LLWorldMapView::drawMipmap(S32 width, S32 height)
 {
 	// Compute the level of the mipmap to use for the current scale level
-	S32 level = LLWorldMipmap::scaleToLevel(sMapScale);
+	S32 level = LLWorldMipmap::scaleToLevel(mMapScale);
 	// Set the tile boost level so that unused tiles get to 0
 	LLWorldMap::getInstance()->equalizeBoostLevels();
 
@@ -905,7 +990,7 @@ void LLWorldMapView::drawFrustum()
 	auto& viewerCamera = LLViewerCamera::instance();
 
 	// Draw frustum
-	F32 meters_to_pixels = sMapScale/ REGION_WIDTH_METERS;
+	F32 meters_to_pixels = mMapScale/ REGION_WIDTH_METERS;
 
 	F32 horiz_fov = viewerCamera.getView() * viewerCamera.getAspect();
 	F32 far_clip_meters = viewerCamera.getFar();
@@ -915,8 +1000,8 @@ void LLWorldMapView::drawFrustum()
 	F32 half_width_pixels = half_width_meters * meters_to_pixels;
 	
 	// Compute the frustum coordinates. Take the UI scale into account.
-    F32 ctr_x = ((getLocalRect().getWidth() * 0.5f + sPanX)  * LLUI::getScaleFactor().mV[VX]);
-    F32 ctr_y = ((getLocalRect().getHeight() * 0.5f + sPanY) * LLUI::getScaleFactor().mV[VY]);
+    F32 ctr_x = ((getLocalRect().getWidth() * 0.5f + mPanX)  * LLUI::getScaleFactor().mV[VX]);
+    F32 ctr_y = ((getLocalRect().getHeight() * 0.5f + mPanY) * LLUI::getScaleFactor().mV[VY]);
 
 	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
 
@@ -973,13 +1058,13 @@ LLVector3 LLWorldMapView::globalPosToView( const LLVector3d& global_pos )
 	LLVector3 pos_local;
 	pos_local.setVec(relative_pos_global);  // convert to floats from doubles
 
-	pos_local.mV[VX] *= sMapScale / REGION_WIDTH_METERS;
-	pos_local.mV[VY] *= sMapScale / REGION_WIDTH_METERS;
+	pos_local.mV[VX] *= mMapScale / REGION_WIDTH_METERS;
+	pos_local.mV[VY] *= mMapScale / REGION_WIDTH_METERS;
 	// leave Z component in meters
 
 
-	pos_local.mV[VX] += getRect().getWidth() / 2 + sPanX;
-	pos_local.mV[VY] += getRect().getHeight() / 2 + sPanY;
+	pos_local.mV[VX] += getRect().getWidth() / 2 + mPanX;
+	pos_local.mV[VY] += getRect().getHeight() / 2 + mPanY;
 
 	return pos_local;
 }
@@ -1053,12 +1138,12 @@ void LLWorldMapView::drawTracking(const LLVector3d& pos_global, const LLColor4&
 // If you change this, then you need to change LLTracker::getTrackedPositionGlobal() as well
 LLVector3d LLWorldMapView::viewPosToGlobal( S32 x, S32 y )
 {
-	x -= llfloor((getRect().getWidth() / 2 + sPanX));
-	y -= llfloor((getRect().getHeight() / 2 + sPanY));
+	x -= llfloor((getRect().getWidth() / 2 + mPanX));
+	y -= llfloor((getRect().getHeight() / 2 + mPanY));
 
 	LLVector3 pos_local( (F32)x, (F32)y, 0.f );
 
-	pos_local *= ( REGION_WIDTH_METERS / sMapScale );
+	pos_local *= ( REGION_WIDTH_METERS / mMapScale );
 	
 	LLVector3d pos_global;
 	pos_global.setVec( pos_local );
@@ -1520,7 +1605,7 @@ void LLWorldMapView::handleClick(S32 x, S32 y, MASK mask,
 
 	LLWorldMap::getInstance()->cancelTracking();
 
-	S32 level = LLWorldMipmap::scaleToLevel(sMapScale);
+	S32 level = LLWorldMipmap::scaleToLevel(mMapScale);
 	// If the zoom level is not too far out already, test hits
 	if (level <= DRAW_SIMINFO_THRESHOLD)
 	{
@@ -1637,8 +1722,8 @@ BOOL LLWorldMapView::handleMouseDown( S32 x, S32 y, MASK mask )
 {
 	gFocusMgr.setMouseCapture( this );
 
-	mMouseDownPanX = ll_round(sPanX);
-	mMouseDownPanY = ll_round(sPanY);
+	mMouseDownPanX = ll_round(mPanX);
+	mMouseDownPanY = ll_round(mPanY);
 	mMouseDownX = x;
 	mMouseDownY = y;
 	sHandledLastClick = TRUE;
@@ -1653,8 +1738,8 @@ BOOL LLWorldMapView::handleMouseUp( S32 x, S32 y, MASK mask )
 		{
 			// restore mouse cursor
 			S32 local_x, local_y;
-			local_x = mMouseDownX + llfloor(sPanX - mMouseDownPanX);
-			local_y = mMouseDownY + llfloor(sPanY - mMouseDownPanY);
+			local_x = mMouseDownX + llfloor(mPanX - mMouseDownPanX);
+			local_y = mMouseDownY + llfloor(mPanY - mMouseDownPanY);
 			LLRect clip_rect = getRect();
 			clip_rect.stretch(-8);
 			clip_rect.clipPointToRect(mMouseDownX, mMouseDownY, local_x, local_y);
@@ -1682,7 +1767,7 @@ BOOL LLWorldMapView::handleMouseUp( S32 x, S32 y, MASK mask )
 
 void LLWorldMapView::updateVisibleBlocks()
 {
-	if (LLWorldMipmap::scaleToLevel(sMapScale) > DRAW_SIMINFO_THRESHOLD)
+	if (LLWorldMipmap::scaleToLevel(mMapScale) > DRAW_SIMINFO_THRESHOLD)
 	{
 		// If we're zoomed out too much, we just don't load all those sim info: too much!
 		return;
@@ -1698,16 +1783,16 @@ void LLWorldMapView::updateVisibleBlocks()
 	const F32 half_height = F32(height) / 2.0f;
 
 	// Compute center into sim grid coordinates
-	S32 world_center_x = S32((-sPanX / sMapScale) + (camera_global.mdV[0] / REGION_WIDTH_METERS));
-	S32 world_center_y = S32((-sPanY / sMapScale) + (camera_global.mdV[1] / REGION_WIDTH_METERS));
+	S32 world_center_x = S32((-mPanX / mMapScale) + (camera_global.mdV[0] / REGION_WIDTH_METERS));
+	S32 world_center_y = S32((-mPanY / mMapScale) + (camera_global.mdV[1] / REGION_WIDTH_METERS));
 
 	// Compute the boundaries into sim grid coordinates
-	S32 world_left   = world_center_x - S32(half_width  / sMapScale) - 1;
-	S32 world_right  = world_center_x + S32(half_width  / sMapScale) + 1;
-	S32 world_bottom = world_center_y - S32(half_height / sMapScale) - 1;
-	S32 world_top    = world_center_y + S32(half_height / sMapScale) + 1;
+	S32 world_left   = world_center_x - S32(half_width  / mMapScale) - 1;
+	S32 world_right  = world_center_x + S32(half_width  / mMapScale) + 1;
+	S32 world_bottom = world_center_y - S32(half_height / mMapScale) - 1;
+	S32 world_top    = world_center_y + S32(half_height / mMapScale) + 1;
 
-	//LL_INFOS("WorldMap") << "LLWorldMapView::updateVisibleBlocks() : sMapScale = " << sMapScale << ", left = " << world_left << ", right = " << world_right << ", bottom  = " << world_bottom << ", top = " << world_top << LL_ENDL;
+	//LL_INFOS("WorldMap") << "LLWorldMapView::updateVisibleBlocks() : mMapScale = " << mMapScale << ", left = " << world_left << ", right = " << world_right << ", bottom  = " << world_bottom << ", top = " << world_top << LL_ENDL;
 	LLWorldMap::getInstance()->updateRegions(world_left, world_bottom, world_right, world_top);
 }
 
@@ -1728,10 +1813,10 @@ BOOL LLWorldMapView::handleHover( S32 x, S32 y, MASK mask )
 			F32 delta_y = (F32)(gViewerWindow->getCurrentMouseDY());
 
 			// Set pan to value at start of drag + offset
-			sPanX += delta_x;
-			sPanY += delta_y;
-			sTargetPanX = sPanX;
-			sTargetPanY = sPanY;
+			mPanX += delta_x;
+			mPanY += delta_y;
+			mTargetPanX = mPanX;
+			mTargetPanY = mPanY;
 
 			gViewerWindow->moveCursorToCenter();
 		}
@@ -1830,4 +1915,8 @@ BOOL LLWorldMapView::handleDoubleClick( S32 x, S32 y, MASK mask )
 	return FALSE;
 }
 
+// static
+F32 LLWorldMapView::scaleFromZoom(F32 zoom) { return exp2(zoom) * 256.0f; }
 
+// static
+F32 LLWorldMapView::zoomFromScale(F32 scale) { return log2(scale / 256.f); }
diff --git a/indra/newview/llworldmapview.h b/indra/newview/llworldmapview.h
index c607816192304365fe38366b67d3c9fd6fc8846d..5f9a560ad6fcc7087001615907ff33bda539dbb1 100644
--- a/indra/newview/llworldmapview.h
+++ b/indra/newview/llworldmapview.h
@@ -67,12 +67,22 @@ class LLWorldMapView final : public LLPanel
 	bool			checkItemHit(S32 x, S32 y, LLItemInfo& item, LLUUID* id, bool track);
 	void			handleClick(S32 x, S32 y, MASK mask, S32* hit_type, LLUUID* id);
 
-	// Scale and pan are shared across all instances! (i.e. Terrain and Objects maps are always registered)
-	static void		setScale( F32 scale );
-	static void		translatePan( S32 delta_x, S32 delta_y );
-	static void		setPan( S32 x, S32 y, BOOL snap = TRUE );
+    // Scale, aka zoom, is shared across all instances! (i.e. Terrain and Objects maps are always registered)
+    // Zoom is used for UI and will interpolate the map scale over multiple frames.
+    void zoom(F32 zoom);
+    void zoomWithPivot(F32 zoom, S32 x, S32 y);
+    F32 getZoom();
+    // Scale is a linear scaling factor of in-world coordinates
+    F32 getScale();
+    // setScaleSetting/getScaleSetting are for the default map setting on login
+    static void setScaleSetting(F32 scaleSetting);
+    static F32 getScaleSetting();
+    // Pan is in pixels relative to the center of the map.
+	void translatePan( S32 delta_x, S32 delta_y );
+    void setPan( S32 x, S32 y, BOOL snap = TRUE );
+    void setPanWithInterpTime(S32 x, S32 y, BOOL snap, F32 interp_time);
 	// Return true if the current scale level is above the threshold for accessing region info
-	static bool		showRegionInfo();
+    bool showRegionInfo();
 
 	LLVector3		globalPosToView(const LLVector3d& global_pos);
 	LLVector3d		viewPosToGlobal(S32 x,S32 y);
@@ -154,14 +164,12 @@ class LLWorldMapView final : public LLPanel
 	static LLUIImagePtr	sForSaleAdultImage;
 	static LLUIImagePtr	sIFFArrowImage;
 
-	static F32		sMapScale;				// scale = size of a region in pixels
-
 	BOOL			mItemPicked;
 
-	static F32		sPanX;		// in pixels
-	static F32		sPanY;		// in pixels
-	static F32		sTargetPanX;		// in pixels
-	static F32		sTargetPanY;		// in pixels
+    F32 mPanX; // in pixels
+    F32 mPanY; // in pixels
+    F32 mTargetPanX; // in pixels
+    F32 mTargetPanY; // in pixels
 	static S32		sTrackingArrowX;
 	static S32		sTrackingArrowY;
 	static bool		sVisibleTilesLoaded;
@@ -195,6 +203,19 @@ class LLWorldMapView final : public LLPanel
 
 private:
 	void drawTileOutline(S32 level, F32 top, F32 left, F32 bottom, F32 right);
+
+    void setScale(F32 scale, bool snap = true);
+
+    static F32 scaleFromZoom(F32 zoom);
+    static F32 zoomFromScale(F32 scale);
+
+    F32 mMapScale;
+    F32 mTargetMapScale;
+    static F32 sMapScaleSetting;
+    static LLVector2 sZoomPivot;
+    static LLFrameTimer sZoomTimer;
+
+    F32 mMapIterpTime;
 };
 
 #endif
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index a70ab8ecd26cbba6fc41e5784cf119f1d3c7c73f..4acb2984279d60301870e61565ade4f541d5cede 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -7379,6 +7379,7 @@ void LLPipeline::doResetVertexBuffers(bool forced)
 	mDeferredVB = NULL;
 
 	mCubeVB = NULL;
+    mDeferredVB = NULL;
 
 	mALRenderUtil->resetVertexBuffers();
 
@@ -7412,11 +7413,12 @@ void LLPipeline::doResetVertexBuffers(bool forced)
 		LLPathingLib::getInstance()->cleanupVBOManager();
 	}
 	LLVOPartGroup::destroyGL();
+    gGL.resetVertexBuffer();
 
 	gGL.resetVertexBuffers();
 	SUBSYSTEM_CLEANUP(LLVertexBuffer);
 	
-	if (LLVertexBuffer::sGLCount > 0)
+	if (LLVertexBuffer::sGLCount != 0)
 	{
 		LL_WARNS() << "VBO wipe failed -- " << LLVertexBuffer::sGLCount << " buffers remaining." << LL_ENDL;
 	}
@@ -7437,6 +7439,10 @@ void LLPipeline::doResetVertexBuffers(bool forced)
 	LLPipeline::sTextureBindTest = gSavedSettings.getBOOL("RenderDebugTextureBind");
 
 	LLVertexBuffer::initClass(LLVertexBuffer::sEnableVBOs, LLVertexBuffer::sDisableVBOMapping);
+    gGL.initVertexBuffer();
+
+    mDeferredVB = new LLVertexBuffer(DEFERRED_VB_MASK, 0);
+    mDeferredVB->allocateBuffer(8, 0, true);
 
 	gGL.restoreVertexBuffers();
 
diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml
index e519ea5e851e986280a99b9e9d17b541b95e7754..15e45944590c25c7a2ac4109e508ed1e26d25e39 100644
--- a/indra/newview/skins/default/colors.xml
+++ b/indra/newview/skins/default/colors.xml
@@ -528,6 +528,9 @@
     <color
      name="MapParcelBoundryLine"
      reference="White" />
+    <color
+     name="MapParcelOutlineColor"
+     value="1 1 0 0.5" />
     <color
      name="MapTrackColor"
      reference="Red" />
@@ -619,10 +622,10 @@
      reference="Black_10" />
     <color
      name="NetMapGroupOwnAboveWater"
-     reference="Purple" />
+     value="0.85 0 0.85 1" />
     <color
      name="NetMapGroupOwnBelowWater"
-     value="0.78 0 0.78 1" />
+     value="0.63 0 0.63 1" />
     <color
      name="NetMapOtherOwnAboveWater"
      value="0.24 0.24 0.24 1" />
@@ -631,10 +634,10 @@
      value="0.12 0.12 0.12 1" />
     <color
      name="NetMapYouOwnAboveWater"
-     value="0 1 1 1" />
+     value="0 0.85 0.85 1" />
     <color
      name="NetMapYouOwnBelowWater"
-     value="0 0.78 0.78 1" />
+     value="0 0.63 0.63 1" />
     <color
      name="NotifyBoxColor"
      value="LtGray" />
diff --git a/indra/newview/skins/default/xui/en/floater_create_landmark.xml b/indra/newview/skins/default/xui/en/floater_create_landmark.xml
index 5edc3d767488bdd9f148da3adc9fa064e749e508..6735e43f79ec8c32136ac8abb9ebf69bd9d756a9 100644
--- a/indra/newview/skins/default/xui/en/floater_create_landmark.xml
+++ b/indra/newview/skins/default/xui/en/floater_create_landmark.xml
@@ -88,6 +88,7 @@
      spellcheck="true"
      text_readonly_color="white"
      text_type="ascii_with_newline"
+     commit_on_focus_lost="true"
      top_pad="5"
      width="290"
      wrap="true" />
diff --git a/indra/newview/skins/default/xui/en/floater_map.xml b/indra/newview/skins/default/xui/en/floater_map.xml
index 42aef9dd0cf94c40e2ec7f8bcb452f13aaac1075..201563290eb204ad0f221dbe16e68950136465f6 100644
--- a/indra/newview/skins/default/xui/en/floater_map.xml
+++ b/indra/newview/skins/default/xui/en/floater_map.xml
@@ -19,11 +19,35 @@
     <!-- RLVa shows the anonymized name on the regular tooltip when @shownames=n restricted (rather than show the avatar inspector) --> 
     <floater.string
      name="ToolTipMsg">
-        [AGENT][REGION](Double-click to open Map, shift-drag to pan)
+        [PARCEL_NAME_MSG][PARCEL_SALE_PRICE_MSG][PARCEL_SALE_AREA_MSG][PARCEL_OWNER_MSG][REGION_NAME_MSG][TOOL_TIP_HINT_MSG]
+    </floater.string>
+    <floater.string
+     name="ParcelNameMsg">
+        [PARCEL_NAME]
+    </floater.string>
+    <floater.string
+     name="ParcelSalePriceMsg">
+        Price: L$[PRICE] (L$[PRICE_PER_SQM]/m²)
+    </floater.string>
+    <floater.string
+     name="ParcelSaleAreaMsg">
+        Area: [AREA]m²
+    </floater.string>
+    <floater.string
+     name="ParcelOwnerMsg">
+        Owner: [PARCEL_OWNER]
+    </floater.string>
+    <floater.string
+     name="RegionNameMsg">
+        Region: [REGION_NAME]
     </floater.string>
 	<floater.string
-     name="AltToolTipMsg">
-		[REGION](Double-click to teleport, shift-drag to pan)
+     name="ToolTipHintMsg">
+        Double-click to open map
+	</floater.string>
+	<floater.string
+     name="AltToolTipHintMsg">
+        Double-click to teleport
 	</floater.string>
 	<floater.string name="mini_map_caption">
 	Mini-map
@@ -41,113 +65,73 @@
     <text
      type="string"
      length="1"
-     bottom="218"
      label="N"
      layout="topleft"
-     left="0"
      name="floater_map_north"
-     right="10"
-     text_color="1 1 1 0.7"
-     font_shadow="hard"
-     top="189">
+     text_color="1 1 1 0.7">
         N
     </text>
     <text
      type="string"
      length="1"
-     bottom="218"
      label="E"
      layout="topleft"
-     left="0"
      name="floater_map_east"
-     right="10"
-     text_color="1 1 1 0.7"
-     font_shadow="hard"
-     top="189">
+     text_color="1 1 1 0.7">
         E
     </text>
     <text
      type="string"
      length="1"
-     bottom="205"
      label="W"
      layout="topleft"
-     left="0"
      name="floater_map_west"
-     right="11"
-     text_color="1 1 1 0.7"
-     font_shadow="hard"
-     top="175">
+     text_color="1 1 1 0.7">
         W
     </text>
     <text
      type="string"
      length="1"
-     bottom="218"
      label="S"
      layout="topleft"
-     left="0"
      name="floater_map_south"
-     right="10"
-     text_color="1 1 1 0.7"
-     font_shadow="hard"
-     top="189">
+     text_color="1 1 1 0.7">
         S
     </text>
     <text
      type="string"
      length="1"
-     bottom="218"
      label="SE"
      layout="topleft"
-     left="0"
      name="floater_map_southeast"
-     right="20"
-     text_color="1 1 1 0.7"
-     font_shadow="hard"
-     top="189">
+     text_color="1 1 1 0.7">
         SE
     </text>
     <text
      type="string"
      length="1"
-     bottom="218"
      label="NE"
      layout="topleft"
-     left="0"
      name="floater_map_northeast"
-     right="20"
-     text_color="1 1 1 0.7"
-     font_shadow="hard"
-     top="189">
+     text_color="1 1 1 0.7">
         NE
     </text>
     <text
      type="string"
      length="1"
-     bottom="218"
      label="SW"
      layout="topleft"
-     left="0"
      name="floater_map_southwest"
-     right="20"
-     text_color="1 1 1 0.7"
-     font_shadow="hard"
-     top="189">
+     text_color="1 1 1 0.7">
         SW
     </text>
     <text
      type="string"
      length="1"
-     bottom="218"
      label="NW"
      layout="topleft"
-     left="0"
      name="floater_map_northwest"
-     right="20"
-     text_color="1 1 1 0.7"
-     font_shadow="hard"
-     top="189">
+     text_color="1 1 1 0.7">
         NW
     </text>
 </floater>
diff --git a/indra/newview/skins/default/xui/en/floater_model_preview.xml b/indra/newview/skins/default/xui/en/floater_model_preview.xml
index e499ddbc2f396f3ea6789c060c29fc775cad031d..21c894d3afeef19e9cd40fcdb670579cf13993a1 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -830,6 +830,7 @@
                         <combo_item name="physics_medium"> Medium </combo_item>
                         <combo_item name="physics_low">    Low    </combo_item>
                         <combo_item name="physics_lowest"> Lowest </combo_item>
+                        <combo_item name="physics_bounding_box"> Bounding Box </combo_item>
                         <combo_item name="load_from_file"> From file   </combo_item>
                     </combo_box>
                     <line_editor
diff --git a/indra/newview/skins/default/xui/en/floater_world_map.xml b/indra/newview/skins/default/xui/en/floater_world_map.xml
index a78f0efeee8de889e34044275be07d9bcf6677ba..ee2159a7e15318b83e1a98474f2bb12f657f7885 100644
--- a/indra/newview/skins/default/xui/en/floater_world_map.xml
+++ b/indra/newview/skins/default/xui/en/floater_world_map.xml
@@ -735,6 +735,7 @@
      name="zoom_icon"
      top_pad="7"
      width="16" ></icon>
+    <!-- NOTE: min_val for zoom slider is hardcoded for performance reasons -->
     <slider
      follows="right|left|bottom"
      height="16"
@@ -742,7 +743,7 @@
      initial_value="-2"
      left_pad="1"
      layout="topleft"
-     max_val="0"
+     max_val="4"
      min_val="-8"
      name="zoom slider"
      show_text="false"
diff --git a/indra/newview/skins/default/xui/en/menu_mini_map.xml b/indra/newview/skins/default/xui/en/menu_mini_map.xml
index ac3c4db40a0b6dc4117e4953e87ef7108b98a1a7..cc57fcd755394e61cdc7093c318f8959b6a71d08 100644
--- a/indra/newview/skins/default/xui/en/menu_mini_map.xml
+++ b/indra/newview/skins/default/xui/en/menu_mini_map.xml
@@ -8,52 +8,86 @@
  top="724"
  visible="false"
  width="128">
-	<menu_item_call
-     label="Zoom Close"
-     name="Zoom Close">
-        <menu_item_call.on_click
-         function="Minimap.Zoom"
+    <menu_item_check
+     label="Zoom very close"
+     name="Zoom very close">
+        <menu_item_check.on_check
+         function="Minimap.Zoom.Check"
+         parameter="very close" />
+        <menu_item_check.on_click
+         function="Minimap.Zoom.Set"
+         parameter="very close" />
+    </menu_item_check>
+    <menu_item_check
+     label="Zoom close"
+     name="Zoom close">
+        <menu_item_check.on_check
+         function="Minimap.Zoom.Check"
          parameter="close" />
-    </menu_item_call>
-    <menu_item_call
-     label="Zoom Medium"
-     name="Zoom Medium">
-        <menu_item_call.on_click
-         function="Minimap.Zoom"
+        <menu_item_check.on_click
+         function="Minimap.Zoom.Set"
+         parameter="close" />
+    </menu_item_check>
+    <menu_item_check
+     label="Zoom medium"
+     name="Zoom medium">
+        <menu_item_check.on_check
+         function="Minimap.Zoom.Check"
          parameter="medium" />
-    </menu_item_call>
-    <menu_item_call
-     label="Zoom Far"
-     name="Zoom Far">
-        <menu_item_call.on_click
-         function="Minimap.Zoom"
+        <menu_item_check.on_click
+         function="Minimap.Zoom.Set"
+         parameter="medium" />
+    </menu_item_check>
+    <menu_item_check
+     label="Zoom far"
+     name="Zoom far">
+        <menu_item_check.on_check
+         function="Minimap.Zoom.Check"
          parameter="far" />
-    </menu_item_call>
-	<menu_item_call
-     label="Zoom Default"
-     name="Zoom Default">
-		<menu_item_call.on_click
-         function="Minimap.Zoom"
-         parameter="default" />
-	</menu_item_call>
+        <menu_item_check.on_click
+         function="Minimap.Zoom.Set"
+         parameter="far" />
+    </menu_item_check>
 	<menu_item_separator />
     <menu_item_check
-       label="Rotate Map"
-       name="Rotate Map">
+       label="North at top"
+       name="North at top">
           <menu_item_check.on_check
-             control="MiniMapRotate" />
+             function="Minimap.MapOrientation.Check"
+             parameter="north_at_top" />
           <menu_item_check.on_click
-             function="ToggleControl"
-             parameter="MiniMapRotate" />
+             function="Minimap.MapOrientation.Set"
+             parameter="north_at_top" />
     </menu_item_check>
     <menu_item_check
-       label="Auto Center"
-       name="Auto Center">
+       label="Camera at top"
+       name="Camera at top">
           <menu_item_check.on_check
-             control="MiniMapAutoCenter" />
+             function="Minimap.MapOrientation.Check"
+             parameter="camera_at_top" />
           <menu_item_check.on_click
-             function="ToggleControl"
-             parameter="MiniMapAutoCenter" />
+             function="Minimap.MapOrientation.Set"
+             parameter="camera_at_top" />
+    </menu_item_check>
+	<menu_item_separator />
+    <menu_item_check
+     label="Show parcel boundaries"
+     name="Show parcel boundaries">
+        <menu_item_check.on_check
+         control="MiniMapShowPropertyLines" />
+        <menu_item_check.on_click
+         function="ToggleControl"
+         parameter="MiniMapShowPropertyLines" />
+    </menu_item_check>
+    <menu_item_separator />
+    <menu_item_check
+     label="Auto-center map"
+     name="Auto-center map">
+        <menu_item_check.on_check
+         control="MiniMapAutoCenter" />
+        <menu_item_check.on_click
+         function="ToggleControl"
+         parameter="MiniMapAutoCenter" />
     </menu_item_check>
     <menu_item_separator />
   <menu_item_check
@@ -125,13 +159,25 @@
   
     <menu_item_separator />
     <menu_item_call
-     label="Stop Tracking"
-     name="Stop Tracking">
+     label="Re-center map"
+     name="Re-center map">
+        <menu_item_call.on_click
+         function="Minimap.Center.Activate" />
+    </menu_item_call>
+    <menu_item_call
+     label="Stop tracking"
+     name="Stop tracking">
         <menu_item_call.on_click
          function="Minimap.Tracker"
          parameter="task_properties" />
     </menu_item_call>
     <menu_item_separator />
+    <menu_item_call
+     label="About Land"
+     name="About Land">
+        <menu_item_call.on_click
+         function="Minimap.AboutLand" />
+    </menu_item_call>
     <menu_item_call
      label="World Map"
      name="World Map">
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 34067048c5881c565a0cd9539a108fb390413e11..d8ea6f6c40ad83e7040422655cac01483b55ec4c 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -3760,6 +3760,18 @@ function="World.EnvPreset"
                  function="Advanced.DropPacket" />
             </menu_item_call>
         </menu>
+        <menu
+         create_jump_keys="true"
+         label="Cache"
+         name="Cache"
+         tear_off="true">
+            <menu_item_call
+             label="Purge Disk Cache"
+             name="Purge Disk Cache">
+                <menu_item_call.on_click
+                 function="Advanced.PurgeDiskCache" />
+            </menu_item_call>
+        </menu>
         <menu_item_call
          label="Dump Scripted Camera"
          name="Dump Scripted Camera">
diff --git a/indra/newview/skins/default/xui/en/panel_avatar_list_item.xml b/indra/newview/skins/default/xui/en/panel_avatar_list_item.xml
index 7428b8d5412a733e0b8da1cbdbd0a70a38bba776..2f9c6439524158f92466d2ef6bdd6c8609d7a9a9 100644
--- a/indra/newview/skins/default/xui/en/panel_avatar_list_item.xml
+++ b/indra/newview/skins/default/xui/en/panel_avatar_list_item.xml
@@ -183,6 +183,7 @@
      right="-3"
      mouse_opaque="true"
      name="speaking_indicator"
+     tool_tip="Voice volume"
      visible="true"
      width="20" />
 </panel>
diff --git a/indra/newview/skins/default/xui/ru/panel_navigation_bar.xml b/indra/newview/skins/default/xui/ru/panel_navigation_bar.xml
index 5e3de180f92c589fa951e1902f13455482e8e295..331ba889d87a66657ea3dbb13e7fdace03ccaa1b 100644
--- a/indra/newview/skins/default/xui/ru/panel_navigation_bar.xml
+++ b/indra/newview/skins/default/xui/ru/panel_navigation_bar.xml
@@ -14,7 +14,7 @@
 				<label name="favorites_bar_label" tool_tip="Перетаскивайте сюда закладки, чтобы было удобнее переходить в любимые места в Second Life!">
 					Избранное
 				</label>
-				<more_button name="&gt;&gt;" tool_tip="Показать больше избранного">
+				<more_button name="&gt;&gt;" tool_tip="Показать больше избранного" width="60">
 					Больше ▼
 				</more_button>
 			</favorites_bar>
diff --git a/indra/newview/skins/default/xui/tr/panel_navigation_bar.xml b/indra/newview/skins/default/xui/tr/panel_navigation_bar.xml
index 8d43e3fb5a26afd0f5e1d78accce0fbb08f06ec5..ae9bc33bfa7e3f17ea8f826effc1b4076cf931fc 100644
--- a/indra/newview/skins/default/xui/tr/panel_navigation_bar.xml
+++ b/indra/newview/skins/default/xui/tr/panel_navigation_bar.xml
@@ -14,7 +14,7 @@
 				<label name="favorites_bar_label" tool_tip="Second Life içerisinde sık kullandığınız yerlere hızla erişmek için Yer İmlerini buraya sürükleyin!">
 					Favoriler Çubuğu
 				</label>
-				<more_button name="&gt;&gt;" tool_tip="Favorilerimden daha çok göster">
+				<more_button name="&gt;&gt;" tool_tip="Favorilerimden daha çok göster" width="65">
 					Daha Fazla â–¼
 				</more_button>
 			</favorites_bar>
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index 07b924a169895745f693f8bc8d6c1d5ce39a3007..dec983f33204658ceb71b5f13d919a62143d7c5c 100755
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -542,6 +542,7 @@ def construct(self):
 
         self.path(src="licenses-win32.txt", dst="licenses.txt")
         self.path("featuretable.txt")
+        self.path("cube.dae")
 
         with self.prefix(src=pkgdir):
             self.path("ca-bundle.crt")
@@ -902,6 +903,7 @@ def construct(self):
 
                 self.path("licenses-mac.txt", dst="licenses.txt")
                 self.path("featuretable_mac.txt")
+                self.path("cube.dae")
 
                 with self.prefix(src=pkgdir,dst=""):
                     self.path("ca-bundle.crt")
@@ -1159,6 +1161,7 @@ def construct(self):
             self.path("*.pak")
 
         self.path("featuretable_linux.txt")
+        self.path("cube.dae")
 
         with self.prefix(src=pkgdir, dst="app_settings"):
             self.path("ca-bundle.crt")
diff --git a/scripts/content_tools/anim_tool.py b/scripts/content_tools/anim_tool.py
index e7b86a88fa07f76f17a532c0e80b11dea0f50b52..4a0773951ecb9dfdf635badfe11a3fbcf7fe48ab 100644
--- a/scripts/content_tools/anim_tool.py
+++ b/scripts/content_tools/anim_tool.py
@@ -611,6 +611,7 @@ def main(*argv):
     parser = argparse.ArgumentParser(description="process SL animations")
     parser.add_argument("--verbose", help="verbose flag", action="store_true")
     parser.add_argument("--dump", help="dump to stdout", action="store_true")
+    parser.add_argument("--use_aliases", help="use alias names for bones", action="store_true")
     parser.add_argument("--rot", help="specify sequence of rotations", type=float_triple, nargs="+")
     parser.add_argument("--rand_pos", help="request NUM random positions (default %(default)s)",
                         metavar="NUM", type=int, default=2)
@@ -637,6 +638,7 @@ def main(*argv):
     parser.add_argument("--no_hud", help="omit hud joints from list of attachments", action="store_true")
     parser.add_argument("--base_priority", help="set base priority", type=int)
     parser.add_argument("--joint_priority", help="set joint priority for all joints", type=int)
+    parser.add_argument("--force_joints", help="don't check validity of joint names", action="store_true")
     parser.add_argument("infilename", help="name of a .anim file to input")
     parser.add_argument("outfilename", nargs="?", help="name of a .anim file to output")
     args = parser.parse_args(argv)
@@ -661,7 +663,12 @@ def main(*argv):
         if lad_tree is None:
             raise Error("failed to parse " + args.lad)
     if args.joints:
-        joints = resolve_joints(args.joints, skel_tree, lad_tree, args.no_hud)
+        if args.force_joints:
+            joints = args.joints
+        else:
+            joints = resolve_joints(args.joints, skel_tree, lad_tree, args.no_hud)
+        if args.use_aliases:
+            joints = map(lambda name: "avatar_" + name, joints)
         if args.verbose:
             print("joints resolved to",joints)
         for name in joints:
diff --git a/scripts/content_tools/skel_tool.py b/scripts/content_tools/skel_tool.py
index 449ecd6a6ce01ede5688b5b1d10e6d303150efb7..696e4e29238f030c143581df7ea02d1eaecaad71 100644
--- a/scripts/content_tools/skel_tool.py
+++ b/scripts/content_tools/skel_tool.py
@@ -72,6 +72,22 @@ def check_symmetry(name, field, vec1, vec2):
     if vec1[2] != vec2[2]:
         print(name,field,"z match fail")
 
+def enforce_alias_rules(tree, element, fix=False):
+    if element.tag != "bone":
+        return
+    alias_lis = []
+    aliases = element.get("aliases")
+    if aliases:
+        alias_lis = aliases.split(" ")
+    name = element.get("name")
+    if name:
+        std_alias = "avatar_" + name
+        if not std_alias in alias_lis:
+            print "missing expected alias",name,std_alias
+        for alias in alias_lis:
+            if alias.startswith("avatar_") and alias != std_alias:
+                print "invalid avatar_ alias",name,alias
+
 def enforce_symmetry(tree, element, field, fix=False):
     name = element.get("name")
     if not name:
@@ -209,6 +225,7 @@ def validate_skel_tree(tree, ogtree, reftree, fix=False):
                             unfixable += 1
 
         fix_name(element)
+        enforce_alias_rules(tree, element, fix)
         enforce_precision_rules(element)
         for field in ["pos","pivot"]:
             enforce_symmetry(tree, element, field, fix)