diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp
index b1175836b78775ffe7b9d57adfa17e9aacfe1f4f..df7ea214cc55fb0f8ba3eb0f2ac199d8676812f8 100644
--- a/indra/llcommon/llthread.cpp
+++ b/indra/llcommon/llthread.cpp
@@ -62,6 +62,9 @@
 // 
 //----------------------------------------------------------------------------
 
+U32 ll_thread_local sThreadID = 0;
+U32 LLThread::sIDIter = 0;
+
 //
 // Handed to the APR thread creation function
 //
@@ -72,6 +75,8 @@ void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap
 	// Set thread state to running
 	threadp->mStatus = RUNNING;
 
+	sThreadID = threadp->mID;
+
 	// Run the user supplied function
 	threadp->run();
 
@@ -90,6 +95,8 @@ LLThread::LLThread(const std::string& name, apr_pool_t *poolp) :
 	mAPRThreadp(NULL),
 	mStatus(STOPPED)
 {
+	mID = ++sIDIter;
+
 	// Thread creation probably CAN be paranoid about APR being initialized, if necessary
 	if (poolp)
 	{
@@ -273,7 +280,7 @@ void LLThread::wakeLocked()
 //============================================================================
 
 LLMutex::LLMutex(apr_pool_t *poolp) :
-	mAPRMutexp(NULL)
+	mAPRMutexp(NULL), mCount(0), mLockingThread(NO_THREAD)
 {
 	//if (poolp)
 	//{
@@ -305,7 +312,14 @@ LLMutex::~LLMutex()
 
 void LLMutex::lock()
 {
+	if (mLockingThread == sThreadID)
+	{ //redundant lock
+		mCount++;
+		return;
+	}
+
 	apr_thread_mutex_lock(mAPRMutexp);
+	
 #if MUTEX_DEBUG
 	// Have to have the lock before we can access the debug info
 	U32 id = LLThread::currentID();
@@ -313,10 +327,18 @@ void LLMutex::lock()
 		llerrs << "Already locked in Thread: " << id << llendl;
 	mIsLocked[id] = TRUE;
 #endif
+
+	mLockingThread = sThreadID;
 }
 
 void LLMutex::unlock()
 {
+	if (mCount > 0)
+	{ //not the root unlock
+		mCount--;
+		return;
+	}
+	
 #if MUTEX_DEBUG
 	// Access the debug info while we have the lock
 	U32 id = LLThread::currentID();
@@ -324,6 +346,8 @@ void LLMutex::unlock()
 		llerrs << "Not locked in Thread: " << id << llendl;	
 	mIsLocked[id] = FALSE;
 #endif
+
+	mLockingThread = NO_THREAD;
 	apr_thread_mutex_unlock(mAPRMutexp);
 }
 
@@ -341,6 +365,11 @@ bool LLMutex::isLocked()
 	}
 }
 
+U32 LLMutex::lockingThread() const
+{
+	return mLockingThread;
+}
+
 //============================================================================
 
 LLCondition::LLCondition(apr_pool_t *poolp) :
diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h
index d8aa90de2e8a1e876de3719893431968e3ee003c..72c39f2e93c1123c26fc8795f40a754d1bdc3c96 100644
--- a/indra/llcommon/llthread.h
+++ b/indra/llcommon/llthread.h
@@ -1,262 +1,284 @@
-/** 
- * @file llthread.h
- * @brief Base classes for thread, mutex and condition handling.
- *
- * $LicenseInfo:firstyear=2004&license=viewergpl$
- * 
- * Copyright (c) 2004-2009, Linden Research, Inc.
- * 
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab.  Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- * 
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- * 
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- * 
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLTHREAD_H
-#define LL_LLTHREAD_H
-
-#include "llapp.h"
-#include "apr_thread_cond.h"
-
-class LLThread;
-class LLMutex;
-class LLCondition;
-
-class LL_COMMON_API LLThread
-{
-public:
-	typedef enum e_thread_status
-	{
-		STOPPED = 0,	// The thread is not running.  Not started, or has exited its run function
-		RUNNING = 1,	// The thread is currently running
-		QUITTING= 2 	// Someone wants this thread to quit
-	} EThreadStatus;
-
-	LLThread(const std::string& name, apr_pool_t *poolp = NULL);
-	virtual ~LLThread(); // Warning!  You almost NEVER want to destroy a thread unless it's in the STOPPED state.
-	virtual void shutdown(); // stops the thread
-	
-	bool isQuitting() const { return (QUITTING == mStatus); }
-	bool isStopped() const { return (STOPPED == mStatus); }
-	
-	static U32 currentID(); // Return ID of current thread
-	static void yield(); // Static because it can be called by the main thread, which doesn't have an LLThread data structure.
-	
-public:
-	// PAUSE / RESUME functionality. See source code for important usage notes.
-	// Called from MAIN THREAD.
-	void pause();
-	void unpause();
-	bool isPaused() { return isStopped() || mPaused == TRUE; }
-	
-	// Cause the thread to wake up and check its condition
-	void wake();
-
-	// Same as above, but to be used when the condition is already locked.
-	void wakeLocked();
-
-	// Called from run() (CHILD THREAD). Pause the thread if requested until unpaused.
-	void checkPause();
-
-	// this kicks off the apr thread
-	void start(void);
-
-	apr_pool_t *getAPRPool() { return mAPRPoolp; }
-	LLVolatileAPRPool* getLocalAPRFilePool() { return mLocalAPRFilePoolp ; }
-
-private:
-	BOOL				mPaused;
-	
-	// static function passed to APR thread creation routine
-	static void *APR_THREAD_FUNC staticRun(apr_thread_t *apr_threadp, void *datap);
-
-protected:
-	std::string			mName;
-	LLCondition*		mRunCondition;
-
-	apr_thread_t		*mAPRThreadp;
-	apr_pool_t			*mAPRPoolp;
-	BOOL				mIsLocalPool;
-	EThreadStatus		mStatus;
-
-	//a local apr_pool for APRFile operations in this thread. If it exists, LLAPRFile::sAPRFilePoolp should not be used.
-	//Note: this pool is used by APRFile ONLY, do NOT use it for any other purposes.
-	//      otherwise it will cause severe memory leaking!!! --bao
-	LLVolatileAPRPool  *mLocalAPRFilePoolp ; 
-
-	void setQuitting();
-	
-	// virtual function overridden by subclass -- this will be called when the thread runs
-	virtual void run(void) = 0; 
-	
-	// virtual predicate function -- returns true if the thread should wake up, false if it should sleep.
-	virtual bool runCondition(void);
-
-	// Lock/Unlock Run Condition -- use around modification of any variable used in runCondition()
-	inline void lockData();
-	inline void unlockData();
-	
-	// This is the predicate that decides whether the thread should sleep.  
-	// It should only be called with mRunCondition locked, since the virtual runCondition() function may need to access
-	// data structures that are thread-unsafe.
-	bool shouldSleep(void) { return (mStatus == RUNNING) && (isPaused() || (!runCondition())); }
-
-	// To avoid spurious signals (and the associated context switches) when the condition may or may not have changed, you can do the following:
-	// mRunCondition->lock();
-	// if(!shouldSleep())
-	//     mRunCondition->signal();
-	// mRunCondition->unlock();
-};
-
-//============================================================================
-
-#define MUTEX_DEBUG (LL_DEBUG || LL_RELEASE_WITH_DEBUG_INFO)
-
-class LL_COMMON_API LLMutex
-{
-public:
-	LLMutex(apr_pool_t *apr_poolp); // NULL pool constructs a new pool for the mutex
-	~LLMutex();
-	
-	void lock();		// blocks
-	void unlock();
-	bool isLocked(); 	// non-blocking, but does do a lock/unlock so not free
-	
-protected:
-	apr_thread_mutex_t *mAPRMutexp;
-	apr_pool_t			*mAPRPoolp;
-	BOOL				mIsLocalPool;
-#if MUTEX_DEBUG
-	std::map<U32, BOOL> mIsLocked;
-#endif
-};
-
-// Actually a condition/mutex pair (since each condition needs to be associated with a mutex).
-class LL_COMMON_API LLCondition : public LLMutex
-{
-public:
-	LLCondition(apr_pool_t *apr_poolp); // Defaults to global pool, could use the thread pool as well.
-	~LLCondition();
-	
-	void wait();		// blocks
-	void signal();
-	void broadcast();
-	
-protected:
-	apr_thread_cond_t *mAPRCondp;
-};
-
-class LLMutexLock
-{
-public:
-	LLMutexLock(LLMutex* mutex)
-	{
-		mMutex = mutex;
-		mMutex->lock();
-	}
-	~LLMutexLock()
-	{
-		mMutex->unlock();
-	}
-private:
-	LLMutex* mMutex;
-};
-
-//============================================================================
-
-void LLThread::lockData()
-{
-	mRunCondition->lock();
-}
-
-void LLThread::unlockData()
-{
-	mRunCondition->unlock();
-}
-
-
-//============================================================================
-
-// see llmemory.h for LLPointer<> definition
-
-class LL_COMMON_API LLThreadSafeRefCount
-{
-public:
-	static void initThreadSafeRefCount(); // creates sMutex
-	static void cleanupThreadSafeRefCount(); // destroys sMutex
-	
-private:
-	static LLMutex* sMutex;
-
-private:
-	LLThreadSafeRefCount(const LLThreadSafeRefCount&); // not implemented
-	LLThreadSafeRefCount&operator=(const LLThreadSafeRefCount&); // not implemented
-
-protected:
-	virtual ~LLThreadSafeRefCount(); // use unref()
-	
-public:
-	LLThreadSafeRefCount();
-	
-	void ref()
-	{
-		if (sMutex) sMutex->lock();
-		mRef++; 
-		if (sMutex) sMutex->unlock();
-	} 
-
-	S32 unref()
-	{
-		llassert(mRef >= 1);
-		if (sMutex) sMutex->lock();
-		S32 res = --mRef;
-		if (sMutex) sMutex->unlock();
-		if (0 == res) 
-		{
-			delete this; 
-			return 0;
-		}
-		return res;
-	}	
-	S32 getNumRefs() const
-	{
-		return mRef;
-	}
-
-private: 
-	S32	mRef; 
-};
-
-//============================================================================
-
-// Simple responder for self destructing callbacks
-// Pure virtual class
-class LL_COMMON_API LLResponder : public LLThreadSafeRefCount
-{
-protected:
-	virtual ~LLResponder();
-public:
-	virtual void completed(bool success) = 0;
-};
-
-//============================================================================
-
-#endif // LL_LLTHREAD_H
+/** 
+ * @file llthread.h
+ * @brief Base classes for thread, mutex and condition handling.
+ *
+ * $LicenseInfo:firstyear=2004&license=viewergpl$
+ * 
+ * Copyright (c) 2004-2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLTHREAD_H
+#define LL_LLTHREAD_H
+
+#include "llapp.h"
+#include "apr_thread_cond.h"
+
+class LLThread;
+class LLMutex;
+class LLCondition;
+
+#if LL_WINDOWS
+#define ll_thread_local __declspec(thread)
+#else
+#define ll_thread_local __thread
+#endif
+
+class LL_COMMON_API LLThread
+{
+private:
+	static U32 sIDIter;
+
+public:
+	typedef enum e_thread_status
+	{
+		STOPPED = 0,	// The thread is not running.  Not started, or has exited its run function
+		RUNNING = 1,	// The thread is currently running
+		QUITTING= 2 	// Someone wants this thread to quit
+	} EThreadStatus;
+
+	LLThread(const std::string& name, apr_pool_t *poolp = NULL);
+	virtual ~LLThread(); // Warning!  You almost NEVER want to destroy a thread unless it's in the STOPPED state.
+	virtual void shutdown(); // stops the thread
+	
+	bool isQuitting() const { return (QUITTING == mStatus); }
+	bool isStopped() const { return (STOPPED == mStatus); }
+	
+	static U32 currentID(); // Return ID of current thread
+	static void yield(); // Static because it can be called by the main thread, which doesn't have an LLThread data structure.
+	
+public:
+	// PAUSE / RESUME functionality. See source code for important usage notes.
+	// Called from MAIN THREAD.
+	void pause();
+	void unpause();
+	bool isPaused() { return isStopped() || mPaused == TRUE; }
+	
+	// Cause the thread to wake up and check its condition
+	void wake();
+
+	// Same as above, but to be used when the condition is already locked.
+	void wakeLocked();
+
+	// Called from run() (CHILD THREAD). Pause the thread if requested until unpaused.
+	void checkPause();
+
+	// this kicks off the apr thread
+	void start(void);
+
+	apr_pool_t *getAPRPool() { return mAPRPoolp; }
+	LLVolatileAPRPool* getLocalAPRFilePool() { return mLocalAPRFilePoolp ; }
+
+	U32 getID() const { return mID; }
+
+private:
+	BOOL				mPaused;
+	
+	// static function passed to APR thread creation routine
+	static void *APR_THREAD_FUNC staticRun(apr_thread_t *apr_threadp, void *datap);
+
+protected:
+	std::string			mName;
+	LLCondition*		mRunCondition;
+
+	apr_thread_t		*mAPRThreadp;
+	apr_pool_t			*mAPRPoolp;
+	BOOL				mIsLocalPool;
+	EThreadStatus		mStatus;
+	U32					mID;
+
+	//a local apr_pool for APRFile operations in this thread. If it exists, LLAPRFile::sAPRFilePoolp should not be used.
+	//Note: this pool is used by APRFile ONLY, do NOT use it for any other purposes.
+	//      otherwise it will cause severe memory leaking!!! --bao
+	LLVolatileAPRPool  *mLocalAPRFilePoolp ; 
+
+	void setQuitting();
+	
+	// virtual function overridden by subclass -- this will be called when the thread runs
+	virtual void run(void) = 0; 
+	
+	// virtual predicate function -- returns true if the thread should wake up, false if it should sleep.
+	virtual bool runCondition(void);
+
+	// Lock/Unlock Run Condition -- use around modification of any variable used in runCondition()
+	inline void lockData();
+	inline void unlockData();
+	
+	// This is the predicate that decides whether the thread should sleep.  
+	// It should only be called with mRunCondition locked, since the virtual runCondition() function may need to access
+	// data structures that are thread-unsafe.
+	bool shouldSleep(void) { return (mStatus == RUNNING) && (isPaused() || (!runCondition())); }
+
+	// To avoid spurious signals (and the associated context switches) when the condition may or may not have changed, you can do the following:
+	// mRunCondition->lock();
+	// if(!shouldSleep())
+	//     mRunCondition->signal();
+	// mRunCondition->unlock();
+};
+
+//============================================================================
+
+#define MUTEX_DEBUG (LL_DEBUG || LL_RELEASE_WITH_DEBUG_INFO)
+
+class LL_COMMON_API LLMutex
+{
+public:
+	typedef enum
+	{
+		NO_THREAD = 0xFFFFFFFF
+	} e_locking_thread;
+
+	LLMutex(apr_pool_t *apr_poolp); // NULL pool constructs a new pool for the mutex
+	~LLMutex();
+	
+	void lock();		// blocks
+	void unlock();
+	bool isLocked(); 	// non-blocking, but does do a lock/unlock so not free
+	U32 lockingThread() const; //get ID of locking thread
+	
+protected:
+	apr_thread_mutex_t *mAPRMutexp;
+	mutable U32			mCount;
+	mutable U32			mLockingThread;
+	
+	apr_pool_t			*mAPRPoolp;
+	BOOL				mIsLocalPool;
+	S32					mLockCount;
+#if MUTEX_DEBUG
+	std::map<U32, BOOL> mIsLocked;
+#endif
+};
+
+// Actually a condition/mutex pair (since each condition needs to be associated with a mutex).
+class LL_COMMON_API LLCondition : public LLMutex
+{
+public:
+	LLCondition(apr_pool_t *apr_poolp); // Defaults to global pool, could use the thread pool as well.
+	~LLCondition();
+	
+	void wait();		// blocks
+	void signal();
+	void broadcast();
+	
+protected:
+	apr_thread_cond_t *mAPRCondp;
+};
+
+class LLMutexLock
+{
+public:
+	LLMutexLock(LLMutex* mutex)
+	{
+		mMutex = mutex;
+		mMutex->lock();
+	}
+	~LLMutexLock()
+	{
+		mMutex->unlock();
+	}
+private:
+	LLMutex* mMutex;
+};
+
+//============================================================================
+
+void LLThread::lockData()
+{
+	mRunCondition->lock();
+}
+
+void LLThread::unlockData()
+{
+	mRunCondition->unlock();
+}
+
+
+//============================================================================
+
+// see llmemory.h for LLPointer<> definition
+
+class LL_COMMON_API LLThreadSafeRefCount
+{
+public:
+	static void initThreadSafeRefCount(); // creates sMutex
+	static void cleanupThreadSafeRefCount(); // destroys sMutex
+	
+private:
+	static LLMutex* sMutex;
+
+private:
+	LLThreadSafeRefCount(const LLThreadSafeRefCount&); // not implemented
+	LLThreadSafeRefCount&operator=(const LLThreadSafeRefCount&); // not implemented
+
+protected:
+	virtual ~LLThreadSafeRefCount(); // use unref()
+	
+public:
+	LLThreadSafeRefCount();
+	
+	void ref()
+	{
+		if (sMutex) sMutex->lock();
+		mRef++; 
+		if (sMutex) sMutex->unlock();
+	} 
+
+	S32 unref()
+	{
+		llassert(mRef >= 1);
+		if (sMutex) sMutex->lock();
+		S32 res = --mRef;
+		if (sMutex) sMutex->unlock();
+		if (0 == res) 
+		{
+			delete this; 
+			return 0;
+		}
+		return res;
+	}	
+	S32 getNumRefs() const
+	{
+		return mRef;
+	}
+
+private: 
+	S32	mRef; 
+};
+
+//============================================================================
+
+// Simple responder for self destructing callbacks
+// Pure virtual class
+class LL_COMMON_API LLResponder : public LLThreadSafeRefCount
+{
+protected:
+	virtual ~LLResponder();
+public:
+	virtual void completed(bool success) = 0;
+};
+
+//============================================================================
+
+#endif // LL_LLTHREAD_H
diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
index fb2de92e351d41c646f14a8106d99dfcffcc18f9..844918432d23525806ab872824fa95ce0eb5b0a5 100644
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -1952,8 +1952,12 @@ BOOL LLVolume::createVolumeFacesFromStream(std::istream& is)
 	}
 
 	is.seekg(header[nm[lod]]["offset"].asInteger(), std::ios_base::cur);
-	
 
+	return unpackVolumeFaces(is, header[nm[lod]]["size"].asInteger());
+}
+
+BOOL LLVolume::unpackVolumeFaces(std::istream& is, S32 size)
+{
 	U8* result = NULL;
 	U32 cur_size = 0;
 
@@ -1964,7 +1968,6 @@ BOOL LLVolume::createVolumeFacesFromStream(std::istream& is)
 		
 		const U32 CHUNK = 65536;
 
-		S32 size = header[nm[lod]]["size"].asInteger();
 		U8 *in = new U8[size];
 		is.read((char*) in, size); 
 
@@ -2168,7 +2171,7 @@ void LLVolume::makeTetrahedron()
 
 	LLVolumeFace face;
 
-	F32 x = 0.5f;
+	F32 x = 0.25f;
 	LLVector3 p[] = 
 	{ //unit tetrahedron corners
 		LLVector3(x,x,x),
diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h
index 59c60ccd92bc9aff2ca5145d86d8440b3363eb24..9970b24a9434520bb65661a3bc88a08f9c863656 100644
--- a/indra/llmath/llvolume.h
+++ b/indra/llmath/llvolume.h
@@ -971,6 +971,8 @@ class LLVolume : public LLRefCount
 public:
 	virtual BOOL createVolumeFacesFromFile(const std::string& file_name);
 	virtual BOOL createVolumeFacesFromStream(std::istream& is);
+	virtual BOOL unpackVolumeFaces(std::istream& is, S32 size);
+
 	virtual void makeTetrahedron();
 	virtual BOOL isTetrahedron();
 
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 3b3b62460053596dd35288ede403eaf225eecac5..fac1dedb02e506e17b3cdbedebd74e35d811391f 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -285,6 +285,7 @@ set(viewer_SOURCE_FILES
     llmediaremotectrl.cpp
     llmemoryview.cpp
     llmenucommands.cpp
+    llmeshrepository.cpp
     llmetricperformancetester.cpp
     llmimetypes.cpp
     llmorphview.cpp
@@ -797,6 +798,7 @@ set(viewer_HEADER_FILES
     llmediaremotectrl.h
     llmemoryview.h
     llmenucommands.h
+    llmeshrepository.h
     llmetricperformancetester.h
     llmimetypes.h
     llmorphview.h
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index cc1957838692cf3ef8e171579b3fa0d004e47725..d1ec04842ec96a0650e89214bd6d1edf70e02c0d 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -7495,6 +7495,17 @@
     <key>Value</key>
     <integer>8</integer>
   </map>
+  <key>MeshMaxConcurrentRequests</key>
+  <map>
+    <key>Comment</key>
+    <string>Number of threads to use for loading meshes.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>U32</string>
+    <key>Value</key>
+    <integer>32</integer>
+  </map>
 
   <key>SafeMode</key>
     <map>
@@ -10063,7 +10074,7 @@
       <key>Type</key>
       <string>Boolean</string>
       <key>Value</key>
-      <integer>1</integer>
+      <integer>0</integer>
     </map>
     <key>UseStartScreen</key>
     <map>
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 3250343b253abbee297ed097d83744e58e491bcf..8dfc1657cf36a302dc4099342012ea9a375dca29 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -48,6 +48,7 @@
 #include "llwindow.h"
 #include "llviewerstats.h"
 #include "llmd5.h"
+#include "llmeshrepository.h"
 #include "llpumpio.h"
 #include "llmimetypes.h"
 #include "llslurl.h"
@@ -1273,6 +1274,9 @@ bool LLAppViewer::cleanup()
 
 	llinfos << "Cleaning Up" << llendflush;
 
+	// shut down mesh streamer
+	gMeshRepo.shutdown();
+
 	// Must clean up texture references before viewer window is destroyed.
 	LLHUDManager::getInstance()->updateEffects();
 	LLHUDObject::updateAll();
@@ -1685,6 +1689,9 @@ bool LLAppViewer::initThreads()
 		mFastTimerLogThread->start();
 	}
 
+	// Mesh streaming and caching
+	gMeshRepo.init();
+
 	// *FIX: no error handling here!
 	return true;
 }
@@ -2401,6 +2408,7 @@ bool LLAppViewer::initWindow()
 		gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), TRUE );
 	
 		gPipeline.init();
+		
 		stop_glerror();
 		gViewerWindow->initGLDefaults();
 
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index 831783752090b7e0be5d6e09a275bf07f26589a7..f173149bf4f9d2fdb4409b10540cfbbf26a1dcd4 100644
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -1432,6 +1432,7 @@ void LLViewerRegion::setSeedCapability(const std::string& url)
 	capabilityNames.append("FetchLib");
 	capabilityNames.append("FetchLibDescendents");
 	capabilityNames.append("GetTexture");
+	capabilityNames.append("GetMesh");
 	capabilityNames.append("GroupProposalBallot");
 	capabilityNames.append("HomeLocation");
 	capabilityNames.append("MapLayer");
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index 00b02035b5d3691a973ccd7497699006fd9c957a..60294ccb2697ed4899d53b9d827380a3cae31382 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -70,6 +70,7 @@
 #include "llsdutil.h"
 #include "llmediaentry.h"
 #include "llmediadataclient.h"
+#include "llmeshrepository.h"
 #include "llagent.h"
 
 const S32 MIN_QUIET_FRAMES_COALESCE = 30;
@@ -880,6 +881,24 @@ BOOL LLVOVolume::setVolume(const LLVolumeParams &params, const S32 detail, bool
 {
 	LLVolumeParams volume_params = params;
 
+	S32 lod = mLOD;
+
+	if (isSculpted())
+	{
+		// if it's a mesh
+		if ((volume_params.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH)
+		{ //meshes might not have all LODs, get the force detail to best existing LOD
+			LLUUID mesh_id = params.getSculptID();
+
+			//profile and path params don't matter for meshes
+			volume_params = LLVolumeParams();
+			volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
+			volume_params.setSculptID(mesh_id, LL_SCULPT_TYPE_MESH);
+
+			lod = gMeshRepo.getActualMeshLOD(mesh_id, lod);
+		}
+	}
+
 	// Check if we need to change implementations
 	bool is_flexible = (volume_params.getPathParams().getCurveType() == LL_PCODE_PATH_FLEXIBLE);
 	if (is_flexible)
@@ -907,13 +926,15 @@ BOOL LLVOVolume::setVolume(const LLVolumeParams &params, const S32 detail, bool
 		}
 	}
 	
-	if ((LLPrimitive::setVolume(volume_params, mLOD, (mVolumeImpl && mVolumeImpl->isVolumeUnique()))) || mSculptChanged)
+	
+
+	if ((LLPrimitive::setVolume(volume_params, lod, (mVolumeImpl && mVolumeImpl->isVolumeUnique()))) || mSculptChanged)
 	{
 		mFaceMappingChanged = TRUE;
 		
 		if (mVolumeImpl)
 		{
-			mVolumeImpl->onSetVolume(volume_params, detail);
+			mVolumeImpl->onSetVolume(volume_params, mLOD);
 		}
 		
 		if (isSculpted())
@@ -925,7 +946,11 @@ BOOL LLVOVolume::setVolume(const LLVolumeParams &params, const S32 detail, bool
 				{ 
 					//load request not yet issued, request pipeline load this mesh
 					LLUUID asset_id = volume_params.getSculptID();
-					gPipeline.loadMesh(this, asset_id, LLVolumeLODGroup::getVolumeDetailFromScale(getVolume()->getDetail()));
+					S32 available_lod = gMeshRepo.loadMesh(this, asset_id, lod);
+					if (available_lod != lod)
+					{
+						LLPrimitive::setVolume(volume_params, available_lod);
+					}
 				}
 			}
 			else // otherwise is sculptie
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index 610804c5ebc0f9590a11b132000a3efbd9432ed6..b37645d2de4296c431c6ad8cec7db5175a10f3c7 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -70,6 +70,7 @@
 #include "llgldbg.h"
 #include "llhudmanager.h"
 #include "lllightconstants.h"
+#include "llmeshrepository.h"
 #include "llresmgr.h"
 #include "llselectmgr.h"
 #include "llsky.h"
@@ -103,6 +104,7 @@
 #include "llspatialpartition.h"
 #include "llmutelist.h"
 #include "lltoolpie.h"
+#include "llcurl.h"
 
 
 #ifdef _DEBUG
@@ -342,8 +344,6 @@ LLPipeline::LLPipeline() :
 	mGlowPool(NULL),
 	mBumpPool(NULL),
 	mWLSkyPool(NULL),
-	mMeshMutex(NULL),
-	mMeshThreadCount(0),
 	mLightMask(0),
 	mLightMovingMask(0),
 	mLightingDetail(0),
@@ -403,7 +403,6 @@ void LLPipeline::init()
 
 	stop_glerror();
 
-	mMeshMutex = new LLMutex(NULL);
 	for (U32 i = 0; i < 2; ++i)
 	{
 		mSpotLightFade[i] = 1.f;
@@ -478,9 +477,6 @@ void LLPipeline::cleanup()
 	//delete mWLSkyPool;
 	mWLSkyPool = NULL;
 
-	delete mMeshMutex;
-	mMeshMutex = NULL;
-
 	releaseGLBuffers();
 
 	mBloomImagep = NULL;
@@ -1802,6 +1798,8 @@ void LLPipeline::rebuildPriorityGroups()
 	
 	assertInitialized();
 
+	gMeshRepo.notifyLoadedMeshes();
+
 	// Iterate through all drawables on the priority build queue,
 	for (LLSpatialGroup::sg_list_t::iterator iter = mGroupQ1.begin();
 		 iter != mGroupQ1.end(); ++iter)
@@ -1881,8 +1879,6 @@ void LLPipeline::updateGeom(F32 max_dtime)
 	// for now, only LLVOVolume does this to throttle LOD changes
 	LLVOVolume::preUpdateGeom();
 
-	notifyLoadedMeshes();
-
 	// Iterate through all drawables on the priority build queue,
 	for (LLDrawable::drawable_list_t::iterator iter = mBuildQ1.begin();
 		 iter != mBuildQ1.end();)
@@ -8912,246 +8908,3 @@ LLCullResult::sg_list_t::iterator LLPipeline::endAlphaGroups()
 }
 
 
-void LLPipeline::loadMesh(LLVOVolume* vobj, LLUUID mesh_id, S32 detail)
-{
-	if (detail < 0 || detail > 4)
-	{
-		return;
-	}
-
-	{
-		LLMutexLock lock(mMeshMutex);
-		//add volume to list of loading meshes
-		mesh_load_map::iterator iter = mLoadingMeshes[detail].find(mesh_id);
-		if (iter != mLoadingMeshes[detail].end())
-		{ //request pending for this mesh, append volume id to list
-			iter->second.insert(vobj->getID());
-			return;
-		}
-
-		//first request for this mesh
-		mLoadingMeshes[detail][mesh_id].insert(vobj->getID());
-	}
-
-	if (gAssetStorage->hasLocalAsset(mesh_id, LLAssetType::AT_MESH))
-	{ //already have asset, load desired LOD in background
-		mPendingMeshes.push_back(new LLMeshThread(mesh_id, vobj->getVolume(), detail));
-	}
-	else
-	{ //fetch asset and load when done
-		gAssetStorage->getAssetData(mesh_id, LLAssetType::AT_MESH,
-									getMeshAssetCallback, vobj->getVolume(), TRUE);
-	}
-
-	//do a quick search to see if we can't display something while we wait for this mesh to load
-	LLVolume* volume = vobj->getVolume();
-
-	if (volume)
-	{
-		LLVolumeParams params = volume->getParams();
-
-		LLVolumeLODGroup* group = LLPrimitive::getVolumeManager()->getGroup(params);
-
-		if (group)
-		{
-			//first see what the next lowest LOD available might be
-			for (S32 i = detail-1; i >= 0; --i)
-			{
-				LLVolume* lod = group->refLOD(i);
-				if (lod && !lod->isTetrahedron() && lod->getNumVolumeFaces() > 0)
-				{
-					volume->copyVolumeFaces(lod);
-					group->derefLOD(lod);
-					return;
-				}
-
-				group->derefLOD(lod);
-			}
-
-			//no lower LOD is a available, is a higher lod available?
-			for (S32 i = detail+1; i < 4; ++i)
-			{
-				LLVolume* lod = group->refLOD(i);
-				if (lod && !lod->isTetrahedron() && lod->getNumVolumeFaces() > 0)
-				{
-					volume->copyVolumeFaces(lod);
-					group->derefLOD(lod);
-					return;
-				}
-
-				group->derefLOD(lod);
-			}
-		}
-		else
-		{
-			llerrs << "WTF?" << llendl;
-		}
-
-		//nothing found, so make a tetrahedron
-		volume->makeTetrahedron();
-	}
-}
-
-//static
-void LLPipeline::getMeshAssetCallback(LLVFS *vfs,
-										  const LLUUID& asset_uuid,
-										  LLAssetType::EType type,
-										  void* user_data, S32 status, LLExtStat ext_status)
-{
-	gPipeline.mPendingMeshes.push_back(new LLMeshThread(asset_uuid, (LLVolume*) user_data));
-}
-
-
-LLPipeline::LLMeshThread::LLMeshThread(LLUUID mesh_id, LLVolume* target, S32 detail)
-: LLThread("mesh_loading_thread")
-{
-	mMeshID = mesh_id;
-	mVolume = NULL;
-	mDetail = target->getDetail();
-
-	if (detail == -1)
-	{
-		mDetailIndex = LLVolumeLODGroup::getVolumeDetailFromScale(target->getDetail());
-	}
-	else
-	{
-		mDetailIndex = detail;
-	}
-
-	mTargetVolume = target;
-}
-
-LLPipeline::LLMeshThread::~LLMeshThread()
-{
-
-}
-
-void LLPipeline::LLMeshThread::run()
-{
-	if (!gAssetStorage || LLApp::instance()->isQuitting())
-	{
-		return;
-	}
-
-	char* buffer = NULL;
-	S32 size = 0;
-	
-	LLVFS* vfs = gAssetStorage->mVFS;
-
-	{
-		LLVFile file(vfs, mMeshID, LLAssetType::AT_MESH, LLVFile::READ);
-		file.waitForLock(VFSLOCK_READ);
-		size = file.getSize();
-		
-		if (size == 0)
-		{
-			gPipeline.meshLoaded(this);
-			return;
-		}
-		
-		buffer = new char[size];
-		file.read((U8*)&buffer[0], size);
-	}
-
-	{
-		std::string buffer_string(buffer, size);
-		std::istringstream buffer_stream(buffer_string);
-
-		{
-			LLVolumeParams volume_params;
-			volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
-			volume_params.setSculptID(mMeshID, LL_SCULPT_TYPE_MESH);
-			mVolume = new LLVolume(volume_params, mDetail);
-			mVolume->createVolumeFacesFromStream(buffer_stream);
-		}
-	}
-	delete[] buffer;
-	
-	gPipeline.meshLoaded(this);
-}
-
-void LLPipeline::meshLoaded(LLPipeline::LLMeshThread* mesh_thread)
-{
-	LLMutexLock lock(mMeshMutex);
-	mLoadedMeshes.push_back(mesh_thread);
-}
-
-void LLPipeline::notifyLoadedMeshes()
-{ //called from main thread
-
-	U32 max_thread_count = llmax(gSavedSettings.getU32("MeshThreadCount"), (U32) 1);
-	while (mMeshThreadCount < max_thread_count && !mPendingMeshes.empty())
-	{
-		LLMeshThread* mesh_thread = mPendingMeshes.front();
-		mesh_thread->start();
-		++mMeshThreadCount;
-		mPendingMeshes.pop_front();
-	}
-
-	LLMutexLock lock(mMeshMutex);
-	std::list<LLMeshThread*> stopping_threads;
-
-	for (std::list<LLMeshThread*>::iterator iter = mLoadedMeshes.begin(); iter != mLoadedMeshes.end(); ++iter)
-	{ //for each mesh done loading
-		LLMeshThread* mesh = *iter;
-		
-		if (!mesh->isStopped())
-		{ //don't process a LLMeshThread until it's stopped
-			stopping_threads.push_back(mesh);
-			continue;
-		}
-
-		S32 detail = mesh->mDetailIndex;
-
-		//get list of objects waiting to be notified this mesh is loaded
-		mesh_load_map::iterator obj_iter = mLoadingMeshes[detail].find(mesh->mMeshID);
-
-		if (mesh->mVolume && obj_iter != mLoadingMeshes[detail].end())
-		{
-			//make sure target volume is still valid
-			BOOL valid = FALSE;
-
-			for (std::set<LLUUID>::iterator vobj_iter = obj_iter->second.begin(); vobj_iter != obj_iter->second.end(); ++vobj_iter)
-			{
-				LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*vobj_iter);
-
-				if (vobj)
-				{
-					if (vobj->getVolume() == mesh->mTargetVolume)
-					{
-						valid = TRUE;
-					}
-				}
-			}
-
-
-			if (valid)
-			{
-				if (mesh->mVolume->getNumVolumeFaces() <= 0)
-				{
-					llwarns << "Mesh loading returned empty volume." << llendl;
-					mesh->mVolume->makeTetrahedron();
-				}
-				
-				mesh->mTargetVolume->copyVolumeFaces(mesh->mVolume);
-
-				for (std::set<LLUUID>::iterator vobj_iter = obj_iter->second.begin(); vobj_iter != obj_iter->second.end(); ++vobj_iter)
-				{
-					LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*vobj_iter);
-					if (vobj)
-					{
-						vobj->notifyMeshLoaded();
-					}
-				}
-			}
-
-			mLoadingMeshes[detail].erase(mesh->mMeshID);
-		}
-
-		delete mesh;
-		--mMeshThreadCount;
-	}
-
-	mLoadedMeshes = stopping_threads;
-}
-
diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h
index f41f6173a90e696f1b3501cee5cb48bbc636f890..0b040acf51a3847d4f4cafdd317124e01532514e 100644
--- a/indra/newview/pipeline.h
+++ b/indra/newview/pipeline.h
@@ -58,6 +58,8 @@ class LLCubeMap;
 class LLCullResult;
 class LLVOAvatar;
 class LLGLSLShader;
+class LLCurlRequest;
+class LLMeshResponder;
 
 typedef enum e_avatar_skinning_method
 {
@@ -278,10 +280,6 @@ class LLPipeline
 	LLCullResult::sg_list_t::iterator beginAlphaGroups();
 	LLCullResult::sg_list_t::iterator endAlphaGroups();
 	
-
-	//mesh management functions
-	void loadMesh(LLVOVolume* volume, LLUUID mesh_id, S32 detail = 0);
-	
 	void addTrianglesDrawn(S32 count);
 	BOOL hasRenderType(const U32 type) const				{ return (type && (mRenderTypeMask & (1<<type))) ? TRUE : FALSE; }
 	BOOL hasRenderDebugFeatureMask(const U32 mask) const	{ return (mRenderDebugFeatureMask & mask) ? TRUE : FALSE; }
@@ -683,32 +681,8 @@ class LLPipeline
 	typedef std::map<LLUUID, std::set<LLUUID> > mesh_load_map;
 	mesh_load_map mLoadingMeshes[4];
 	
-	LLMutex*					mMeshMutex;
-
-	class LLMeshThread : public LLThread
-	{
-	public:
-		LLPointer<LLVolume> mVolume;
-		LLVolume* mTargetVolume;
-		LLUUID mMeshID;
-		F32 mDetail;
-		S32 mDetailIndex;
-		LLMeshThread(LLUUID mesh_id, LLVolume* target, S32 detail = -1);
-		~LLMeshThread();
-		void run();
-	};
-	
-	static void getMeshAssetCallback(LLVFS *vfs,
-										  const LLUUID& asset_uuid,
-										  LLAssetType::EType type,
-										  void* user_data, S32 status, LLExtStat ext_status);
-
-	std::list<LLMeshThread*> mLoadedMeshes;
-	std::list<LLMeshThread*> mPendingMeshes;
-	U32 mMeshThreadCount;
-
-	void meshLoaded(LLMeshThread* mesh_thread);
-	void notifyLoadedMeshes();
+	typedef std::list<LLMeshResponder*> mesh_response_list;
+	mesh_response_list			mMeshResponseList;
 
 	LLPointer<LLViewerFetchedTexture>	mFaceSelectImagep;
 	LLPointer<LLViewerTexture>	mBloomImagep;