diff --git a/.hgtags b/.hgtags
index f05b8bd17add39be304a63331a813a260edbdbf5..0cc10b010006185fa307e139c121700d4265cff7 100755
--- a/.hgtags
+++ b/.hgtags
@@ -507,3 +507,4 @@ d07f76c5b9860fb87924d00ca729f7d4532534d6 3.7.29-release
 3f61ed662347dc7c6941b8266e72746a66d90e2a 3.8.1-release
 3a62616f3dd8bd512fcdfd29ef033b2505b11213 3.8.2-release
 60572f718879f786f6bc8b5c9373ebebf4693078 3.8.3-release
+27e3cf444c4cc645884960a61325a9ee0e9a2d0f 3.8.4-release
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 1459b9ada2785be76b7a8183123832be4895a2b1..5863310162c82713d9702f97c0b2439e11617b56 100755
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -37,6 +37,7 @@ set(llcommon_SOURCE_FILES
     llbase32.cpp
     llbase64.cpp
     llbitpack.cpp
+    llcallbacklist.cpp
     llcommon.cpp
     llcommonutils.cpp
     llcoros.cpp
@@ -129,6 +130,7 @@ set(llcommon_HEADER_FILES
     llbase64.h
     llbitpack.h
     llboost.h
+    llcallbacklist.h
     llcommon.h
     llcommonutils.h
     llcoros.h
diff --git a/indra/llcommon/llcallbacklist.cpp b/indra/llcommon/llcallbacklist.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..541ff75ee4d834382d61dba0a7f2522084921708
--- /dev/null
+++ b/indra/llcommon/llcallbacklist.cpp
@@ -0,0 +1,230 @@
+/** 
+ * @file llcallbacklist.cpp
+ * @brief A simple list of callback functions to call.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "llcallbacklist.h"
+#include "lleventtimer.h"
+#include "llerrorlegacy.h"
+
+// Globals
+//
+LLCallbackList gIdleCallbacks;
+
+//
+// Member functions
+//
+
+LLCallbackList::LLCallbackList()
+{
+	// nothing
+}
+
+LLCallbackList::~LLCallbackList()
+{
+}
+
+
+void LLCallbackList::addFunction( callback_t func, void *data)
+{
+	if (!func)
+	{
+		return;
+	}
+
+	// only add one callback per func/data pair
+	//
+	if (containsFunction(func))
+	{
+		return;
+	}
+	
+	callback_pair_t t(func, data);
+	mCallbackList.push_back(t);
+}
+
+bool LLCallbackList::containsFunction( callback_t func, void *data)
+{
+	callback_pair_t t(func, data);
+	callback_list_t::iterator iter = find(func,data);
+	if (iter != mCallbackList.end())
+	{
+		return TRUE;
+	}
+	else
+	{
+		return FALSE;
+	}
+}
+
+
+bool LLCallbackList::deleteFunction( callback_t func, void *data)
+{
+	callback_list_t::iterator iter = find(func,data);
+	if (iter != mCallbackList.end())
+	{
+		mCallbackList.erase(iter);
+		return TRUE;
+	}
+	else
+	{
+		return FALSE;
+	}
+}
+
+inline 
+LLCallbackList::callback_list_t::iterator
+LLCallbackList::find(callback_t func, void *data)
+{
+	callback_pair_t t(func, data);
+	return std::find(mCallbackList.begin(), mCallbackList.end(), t);
+}
+
+void LLCallbackList::deleteAllFunctions()
+{
+	mCallbackList.clear();
+}
+
+
+void LLCallbackList::callFunctions()
+{
+	for (callback_list_t::iterator iter = mCallbackList.begin(); iter != mCallbackList.end();  )
+	{
+		callback_list_t::iterator curiter = iter++;
+		curiter->first(curiter->second);
+	}
+}
+
+// Shim class to allow arbitrary boost::bind
+// expressions to be run as one-time idle callbacks.
+class OnIdleCallbackOneTime
+{
+public:
+	OnIdleCallbackOneTime(nullary_func_t callable):
+		mCallable(callable)
+	{
+	}
+	static void onIdle(void *data)
+	{
+		gIdleCallbacks.deleteFunction(onIdle, data);
+		OnIdleCallbackOneTime* self = reinterpret_cast<OnIdleCallbackOneTime*>(data);
+		self->call();
+		delete self;
+	}
+	void call()
+	{
+		mCallable();
+	}
+private:
+	nullary_func_t mCallable;
+};
+
+void doOnIdleOneTime(nullary_func_t callable)
+{
+	OnIdleCallbackOneTime* cb_functor = new OnIdleCallbackOneTime(callable);
+	gIdleCallbacks.addFunction(&OnIdleCallbackOneTime::onIdle,cb_functor);
+}
+
+// Shim class to allow generic boost functions to be run as
+// recurring idle callbacks.  Callable should return true when done,
+// false to continue getting called.
+class OnIdleCallbackRepeating
+{
+public:
+	OnIdleCallbackRepeating(bool_func_t callable):
+		mCallable(callable)
+	{
+	}
+	// Will keep getting called until the callable returns true.
+	static void onIdle(void *data)
+	{
+		OnIdleCallbackRepeating* self = reinterpret_cast<OnIdleCallbackRepeating*>(data);
+		bool done = self->call();
+		if (done)
+		{
+			gIdleCallbacks.deleteFunction(onIdle, data);
+			delete self;
+		}
+	}
+	bool call()
+	{
+		return mCallable();
+	}
+private:
+	bool_func_t mCallable;
+};
+
+void doOnIdleRepeating(bool_func_t callable)
+{
+	OnIdleCallbackRepeating* cb_functor = new OnIdleCallbackRepeating(callable);
+	gIdleCallbacks.addFunction(&OnIdleCallbackRepeating::onIdle,cb_functor);
+}
+
+class NullaryFuncEventTimer: public LLEventTimer
+{
+public:
+	NullaryFuncEventTimer(nullary_func_t callable, F32 seconds):
+		LLEventTimer(seconds),
+		mCallable(callable)
+	{
+	}
+
+private:
+	BOOL tick()
+	{
+		mCallable();
+		return TRUE;
+	}
+
+	nullary_func_t mCallable;
+};
+
+// Call a given callable once after specified interval.
+void doAfterInterval(nullary_func_t callable, F32 seconds)
+{
+	new NullaryFuncEventTimer(callable, seconds);
+}
+
+class BoolFuncEventTimer: public LLEventTimer
+{
+public:
+	BoolFuncEventTimer(bool_func_t callable, F32 seconds):
+		LLEventTimer(seconds),
+		mCallable(callable)
+	{
+	}
+private:
+	BOOL tick()
+	{
+		return mCallable();
+	}
+
+	bool_func_t mCallable;
+};
+
+// Call a given callable every specified number of seconds, until it returns true.
+void doPeriodically(bool_func_t callable, F32 seconds)
+{
+	new BoolFuncEventTimer(callable, seconds);
+}
diff --git a/indra/newview/llcallbacklist.h b/indra/llcommon/llcallbacklist.h
old mode 100755
new mode 100644
similarity index 76%
rename from indra/newview/llcallbacklist.h
rename to indra/llcommon/llcallbacklist.h
index 0516c9cdb44e663d47d123b555a7550ce0c774c3..89716cd74ca985696fbc785d5b99b22a704ae91d
--- a/indra/newview/llcallbacklist.h
+++ b/indra/llcommon/llcallbacklist.h
@@ -28,27 +28,34 @@
 #define LL_LLCALLBACKLIST_H
 
 #include "llstl.h"
+#include <boost/function.hpp>
+#include <list>
 
 class LLCallbackList
 {
 public:
 	typedef void (*callback_t)(void*);
+
+	typedef std::pair< callback_t,void* >	callback_pair_t;
+	// NOTE: It is confirmed that we DEPEND on the order provided by using a list :(
+	//
+	typedef std::list< callback_pair_t >	callback_list_t; 
 	
 	LLCallbackList();
 	~LLCallbackList();
 
-	void addFunction( callback_t func, void *data = NULL );		// register a callback, which will be called as func(data)
-	BOOL containsFunction( callback_t func, void *data = NULL );	// true if list already contains the function/data pair
-	BOOL deleteFunction( callback_t func, void *data = NULL );		// removes the first instance of this function/data pair from the list, false if not found
-	void callFunctions();												// calls all functions
+	void addFunction( callback_t func, void *data = NULL );			// register a callback, which will be called as func(data)
+	bool containsFunction( callback_t func, void *data = NULL );	// true if list already contains the function/data pair
+	bool deleteFunction( callback_t func, void *data = NULL );		// removes the first instance of this function/data pair from the list, false if not found
+	void callFunctions();														// calls all functions
 	void deleteAllFunctions();
 
 	static void test();
 
 protected:
-	// Use a list so that the callbacks are ordered in case that matters
-	typedef std::pair<callback_t,void*> callback_pair_t;
-	typedef std::list<callback_pair_t > callback_list_t;
+
+	inline callback_list_t::iterator find(callback_t func, void *data);
+
 	callback_list_t	mCallbackList;
 };
 
diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h
index fd09eb9debdcffcc298d87cd5c6feac7e25a05f4..0fb257aab1dbe6d89340c5831045928f6779e782 100755
--- a/indra/llcommon/llmemory.h
+++ b/indra/llcommon/llmemory.h
@@ -28,6 +28,7 @@
 
 #include "linden_common.h"
 #include "llunits.h"
+#include "stdtypes.h"
 #if !LL_WINDOWS
 #include <stdint.h>
 #endif
@@ -59,7 +60,7 @@ class LLMutex ;
 LL_COMMON_API void ll_assert_aligned_func(uintptr_t ptr,U32 alignment);
 
 #ifdef SHOW_ASSERT
-#define ll_assert_aligned(ptr,alignment) ll_assert_aligned_func(reinterpret_cast<uintptr_t>(ptr),((U32)alignment))
+#define ll_assert_aligned(ptr,alignment) ll_assert_aligned_func(uintptr_t(ptr),((U32)alignment))
 #else
 #define ll_assert_aligned(ptr,alignment)
 #endif
@@ -69,13 +70,13 @@ LL_COMMON_API void ll_assert_aligned_func(uintptr_t ptr,U32 alignment);
 template <typename T> T* LL_NEXT_ALIGNED_ADDRESS(T* address) 
 { 
 	return reinterpret_cast<T*>(
-		(reinterpret_cast<uintptr_t>(address) + 0xF) & ~0xF);
+		(uintptr_t(address) + 0xF) & ~0xF);
 }
 
 template <typename T> T* LL_NEXT_ALIGNED_ADDRESS_64(T* address) 
 { 
 	return reinterpret_cast<T*>(
-		(reinterpret_cast<uintptr_t>(address) + 0x3F) & ~0x3F);
+		(uintptr_t(address) + 0x3F) & ~0x3F);
 }
 
 #if LL_LINUX || LL_DARWIN
diff --git a/indra/llcommon/llmutex.h b/indra/llcommon/llmutex.h
index 3659a319a2efd3e90d267a800ac05e24950ce901..ea535cee86d1783be719df606b9dea747694e7cd 100644
--- a/indra/llcommon/llmutex.h
+++ b/indra/llcommon/llmutex.h
@@ -33,6 +33,10 @@
 
 #define MUTEX_DEBUG (LL_DEBUG || LL_RELEASE_WITH_DEBUG_INFO)
 
+#if MUTEX_DEBUG
+#include <map>
+#endif
+
 struct apr_thread_mutex_t;
 struct apr_pool_t;
 struct apr_thread_cond_t;
diff --git a/indra/llcommon/llstl.h b/indra/llcommon/llstl.h
index 02f10fa2ba470d19ed5b743fa327438e970954bf..0435cb8a08d9515afda8ea2316719b86822c2234 100755
--- a/indra/llcommon/llstl.h
+++ b/indra/llcommon/llstl.h
@@ -27,6 +27,7 @@
 #ifndef LL_LLSTL_H
 #define LL_LLSTL_H
 
+#include "stdtypes.h"
 #include <functional>
 #include <algorithm>
 #include <map>
@@ -272,6 +273,7 @@ inline T get_if_there(const std::map<K,T>& inmap, const K& key, T default_value)
 	}
 };
 
+// Useful for replacing the removeObj() functionality of LLDynamicArray
 // Example:
 //  for (std::vector<T>::iterator iter = mList.begin(); iter != mList.end(); )
 //  {
@@ -530,7 +532,7 @@ bool before(const std::type_info* lhs, const std::type_info* rhs)
     return strcmp(lhs->name(), rhs->name()) < 0;
 #else  // not Linux, or gcc 4.4+
     // Just use before(), as we normally would
-    return lhs->before(*rhs);
+    return lhs->before(*rhs) ? true : false;
 #endif
 }
 
diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
index 79c134da3046cb757f2c401c107e56a56ffa8e31..8608e45a91dcf810dcbe8ae4fee2e130ba6b5c44 100755
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -6084,7 +6084,7 @@ void LLVolumeFace::pushVertex(const LLVector4a& pos, const LLVector4a& norm, con
 
 	if (new_verts > mNumAllocatedVertices)
 	{ 
-		//double buffer size on expansion
+		// double buffer size on expansion
 		new_verts *= 2;
 
 		S32 new_tc_size = ((new_verts*8)+0xF) & ~0xF;
@@ -6100,18 +6100,21 @@ void LLVolumeFace::pushVertex(const LLVector4a& pos, const LLVector4a& norm, con
 		mNormals = mPositions+new_verts;
 		mTexCoords = (LLVector2*) (mNormals+new_verts);
 
-	//positions
-		LLVector4a::memcpyNonAliased16((F32*) mPositions, (F32*) old_buf, old_vsize);
-	
-	//normals
-		LLVector4a::memcpyNonAliased16((F32*) mNormals, (F32*) (old_buf+mNumVertices), old_vsize);
+		if (old_buf != NULL)
+		{
+			// copy old positions into new buffer
+			LLVector4a::memcpyNonAliased16((F32*)mPositions, (F32*)old_buf, old_vsize);
 
-	//tex coords
-		LLVector4a::memcpyNonAliased16((F32*) mTexCoords, (F32*) (old_buf+mNumVertices*2), old_tc_size);
+			// normals
+			LLVector4a::memcpyNonAliased16((F32*)mNormals, (F32*)(old_buf + mNumVertices), old_vsize);
 
-	//just clear tangents
-	ll_aligned_free_16(mTangents);
-	mTangents = NULL;
+			// tex coords
+			LLVector4a::memcpyNonAliased16((F32*)mTexCoords, (F32*)(old_buf + mNumVertices * 2), old_tc_size);
+		}
+
+		// just clear tangents
+		ll_aligned_free_16(mTangents);
+		mTangents = NULL;
 		ll_aligned_free<64>(old_buf);
 
 		mNumAllocatedVertices = new_verts;
diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h
index e1161682b51c77ee4eb497f4a73346a68623e775..1da2d0c6b15f9ea47bed4f9c8b64b693625ddb3d 100755
--- a/indra/llmath/llvolume.h
+++ b/indra/llmath/llvolume.h
@@ -972,6 +972,7 @@ protected:
 	~LLVolume(); // use unref
 
 public:
+	typedef std::vector<LLVolumeFace> face_list_t;
 		
 	struct FaceParams
 	{
@@ -1045,6 +1046,10 @@ public:
 																				// conversion if *(LLVolume*) to LLVolume&
 	const LLVolumeFace &getVolumeFace(const S32 f) const {return mVolumeFaces[f];} // DO NOT DELETE VOLUME WHILE USING THIS REFERENCE, OR HOLD A POINTER TO THIS VOLUMEFACE
 	
+	LLVolumeFace &getVolumeFace(const S32 f) {return mVolumeFaces[f];} // DO NOT DELETE VOLUME WHILE USING THIS REFERENCE, OR HOLD A POINTER TO THIS VOLUMEFACE
+
+	face_list_t& getVolumeFaces() { return mVolumeFaces; }
+
 	U32					mFaceMask;			// bit array of which faces exist in this volume
 	LLVector3			mLODScaleBias;		// vector for biasing LOD based on scale
 	
@@ -1084,7 +1089,6 @@ public:
 	
 	
 	BOOL mGenerateSingleFace;
-	typedef std::vector<LLVolumeFace> face_list_t;
 	face_list_t mVolumeFaces;
 
 public:
diff --git a/indra/llmath/m4math.cpp b/indra/llmath/m4math.cpp
index 6a1b4143cfd64e9474f51e94645d54e2c7cda146..d89c482804d760ec49504fb713b959d590bddca4 100755
--- a/indra/llmath/m4math.cpp
+++ b/indra/llmath/m4math.cpp
@@ -274,6 +274,19 @@ const LLMatrix4&	LLMatrix4::invert(void)
 	return *this;
 }
 
+// Convenience func for simplifying comparison-heavy code by
+// intentionally stomping values in [-FLT_EPS,FLT_EPS] to 0.0f
+//
+void LLMatrix4::condition(void)
+{
+	U32 i;
+	U32 j;
+	for (i = 0; i < 3;i++)
+		for (j = 0; j < 3;j++)
+			mMatrix[i][j] = ((mMatrix[i][j] > -FLT_EPSILON)
+							  && (mMatrix[i][j] < FLT_EPSILON)) ? 0.0f : mMatrix[i][j];
+}
+
 LLVector4 LLMatrix4::getFwdRow4() const
 {
 	return LLVector4(mMatrix[VX][VX], mMatrix[VX][VY], mMatrix[VX][VZ], mMatrix[VX][VW]);
diff --git a/indra/llmath/m4math.h b/indra/llmath/m4math.h
index a7dce10397839e4ea21c88d59f4689d29f9193ed..a77c5bc76d11111149559b0f3552823ac9164f9f 100755
--- a/indra/llmath/m4math.h
+++ b/indra/llmath/m4math.h
@@ -180,6 +180,11 @@ public:
 	const LLMatrix4& setTranslation(const LLVector4 &translation);
 	const LLMatrix4& setTranslation(const LLVector3 &translation);
 
+	// Convenience func for simplifying comparison-heavy code by
+	// intentionally stomping values [-FLT_EPS,FLT_EPS] to 0.0
+	//
+	void condition(void);
+
 	///////////////////////////
 	//
 	// Get properties of a matrix
diff --git a/indra/llprimitive/CMakeLists.txt b/indra/llprimitive/CMakeLists.txt
index d1475cf7342a2fb86751c6254c4c7952f4acad2b..75ce624a58d4d5576055cf7d78864fb58e6fff81 100755
--- a/indra/llprimitive/CMakeLists.txt
+++ b/indra/llprimitive/CMakeLists.txt
@@ -8,6 +8,7 @@ include(LLMath)
 include(LLMessage)
 include(LLXML)
 include(LLPhysicsExtensions)
+include(LLCharacter)
 
 include_directories(
     ${LLCOMMON_INCLUDE_DIRS}
@@ -16,6 +17,7 @@ include_directories(
     ${LLXML_INCLUDE_DIRS}
     ${LIBS_PREBUILT_DIR}/include/collada
     ${LIBS_PREBUILT_DIR}/include/collada/1.4
+    ${LLCHARACTER_INCLUDE_DIRS}
     )
 include_directories(SYSTEM
     ${LLCOMMON_SYSTEM_INCLUDE_DIRS}
@@ -24,11 +26,13 @@ include_directories(SYSTEM
     )
 
 set(llprimitive_SOURCE_FILES
+    lldaeloader.cpp
     llmaterialid.cpp
     llmaterial.cpp
     llmaterialtable.cpp
     llmediaentry.cpp
     llmodel.cpp
+    llmodelloader.cpp
     llprimitive.cpp
     llprimtexturelist.cpp
     lltextureanim.cpp
@@ -40,16 +44,17 @@ set(llprimitive_SOURCE_FILES
 
 set(llprimitive_HEADER_FILES
     CMakeLists.txt
-
+    lldaeloader.h
     legacy_object_types.h
-    lllslconstants.h
     llmaterial.h
     llmaterialid.h
     llmaterialtable.h
     llmediaentry.h
     llmodel.h
+    llmodelloader.h
     llprimitive.h
     llprimtexturelist.h
+    lllslconstants.h
     lltextureanim.h
     lltextureentry.h
     lltreeparams.h
@@ -72,6 +77,7 @@ target_link_libraries(llprimitive
     ${LLMESSAGE_LIBRARIES}
     ${LLXML_LIBRARIES}
     ${LLPHYSICSEXTENSIONS_LIBRARIES}
+    ${LLCHARACTER_LIBRARIES}
     )
 
 
diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c1b74b1fd7ee5cd96a5b2de9f07218b4dd2dcb62
--- /dev/null
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -0,0 +1,2436 @@
+/**
+ * @file lldaeloader.cpp
+ * @brief LLDAELoader class implementation
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2013, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#if LL_MSVC
+#pragma warning (disable : 4263)
+#pragma warning (disable : 4264)
+#endif
+#include "dae.h"
+#include "dom/domAsset.h"
+#include "dom/domBind_material.h"
+#include "dom/domCOLLADA.h"
+#include "dom/domConstants.h"
+#include "dom/domController.h"
+#include "dom/domEffect.h"
+#include "dom/domGeometry.h"
+#include "dom/domInstance_geometry.h"
+#include "dom/domInstance_material.h"
+#include "dom/domInstance_node.h"
+#include "dom/domInstance_effect.h"
+#include "dom/domMaterial.h"
+#include "dom/domMatrix.h"
+#include "dom/domNode.h"
+#include "dom/domProfile_COMMON.h"
+#include "dom/domRotate.h"
+#include "dom/domScale.h"
+#include "dom/domTranslate.h"
+#include "dom/domVisual_scene.h"
+#if LL_MSVC
+#pragma warning (default : 4263)
+#pragma warning (default : 4264)
+#endif
+
+#include <boost/lexical_cast.hpp>
+
+#include "lldaeloader.h"
+#include "llsdserialize.h"
+#include "lljoint.h"
+
+#include "glh/glh_linear.h"
+#include "llmatrix4a.h"
+
+std::string colladaVersion[VERSIONTYPE_COUNT+1] = 
+{
+	"1.4.0",
+	"1.4.1",
+	"Unsupported"
+};
+
+static const std::string lod_suffix[LLModel::NUM_LODS] =
+{
+	"_LOD0",
+	"_LOD1",
+	"_LOD2",
+	"",
+	"_PHYS",
+};
+
+const U32 LIMIT_MATERIALS_OUTPUT = 12;
+
+bool get_dom_sources(const domInputLocalOffset_Array& inputs, S32& pos_offset, S32& tc_offset, S32& norm_offset, S32 &idx_stride,
+	domSource* &pos_source, domSource* &tc_source, domSource* &norm_source)
+{
+	idx_stride = 0;
+
+	for (U32 j = 0; j < inputs.getCount(); ++j)
+	{
+		idx_stride = llmax((S32) inputs[j]->getOffset(), idx_stride);
+
+		if (strcmp(COMMON_PROFILE_INPUT_VERTEX, inputs[j]->getSemantic()) == 0)
+		{ //found vertex array
+			const domURIFragmentType& uri = inputs[j]->getSource();
+			daeElementRef elem = uri.getElement();
+			domVertices* vertices = (domVertices*) elem.cast();
+			if ( !vertices )
+			{
+				return false;
+			}
+
+			domInputLocal_Array& v_inp = vertices->getInput_array();
+
+
+			for (U32 k = 0; k < v_inp.getCount(); ++k)
+			{
+				if (strcmp(COMMON_PROFILE_INPUT_POSITION, v_inp[k]->getSemantic()) == 0)
+				{
+					pos_offset = inputs[j]->getOffset();
+
+					const domURIFragmentType& uri = v_inp[k]->getSource();
+					daeElementRef elem = uri.getElement();
+					pos_source = (domSource*) elem.cast();
+				}
+
+				if (strcmp(COMMON_PROFILE_INPUT_NORMAL, v_inp[k]->getSemantic()) == 0)
+				{
+					norm_offset = inputs[j]->getOffset();
+
+					const domURIFragmentType& uri = v_inp[k]->getSource();
+					daeElementRef elem = uri.getElement();
+					norm_source = (domSource*) elem.cast();
+				}
+			}
+		}
+
+		if (strcmp(COMMON_PROFILE_INPUT_NORMAL, inputs[j]->getSemantic()) == 0)
+		{
+			//found normal array for this triangle list
+			norm_offset = inputs[j]->getOffset();
+			const domURIFragmentType& uri = inputs[j]->getSource();
+			daeElementRef elem = uri.getElement();
+			norm_source = (domSource*) elem.cast();
+		}
+		else if (strcmp(COMMON_PROFILE_INPUT_TEXCOORD, inputs[j]->getSemantic()) == 0)
+		{ //found texCoords
+			tc_offset = inputs[j]->getOffset();
+			const domURIFragmentType& uri = inputs[j]->getSource();
+			daeElementRef elem = uri.getElement();
+			tc_source = (domSource*) elem.cast();
+		}
+	}
+
+	idx_stride += 1;
+
+	return true;
+}
+
+LLModel::EModelStatus load_face_from_dom_triangles(std::vector<LLVolumeFace>& face_list, std::vector<std::string>& materials, domTrianglesRef& tri)
+{
+	LLVolumeFace face;
+	std::vector<LLVolumeFace::VertexData> verts;
+	std::vector<U16> indices;
+	
+	const domInputLocalOffset_Array& inputs = tri->getInput_array();
+
+	S32 pos_offset = -1;
+	S32 tc_offset = -1;
+	S32 norm_offset = -1;
+
+	domSource* pos_source = NULL;
+	domSource* tc_source = NULL;
+	domSource* norm_source = NULL;
+
+	S32 idx_stride = 0;
+
+	if ( !get_dom_sources(inputs, pos_offset, tc_offset, norm_offset, idx_stride, pos_source, tc_source, norm_source))
+	{
+		return LLModel::BAD_ELEMENT;
+	}
+
+	if (!pos_source || !pos_source->getFloat_array())
+	{
+		LL_WARNS() << "Unable to process mesh without position data; invalid model;  invalid model." << LL_ENDL;
+		return LLModel::BAD_ELEMENT;
+	}
+
+	domPRef p = tri->getP();
+	domListOfUInts& idx = p->getValue();
+	
+	domListOfFloats  dummy ;
+	domListOfFloats& v = pos_source ? pos_source->getFloat_array()->getValue() : dummy ;
+	domListOfFloats& tc = tc_source ? tc_source->getFloat_array()->getValue() : dummy ;
+	domListOfFloats& n = norm_source ? norm_source->getFloat_array()->getValue() : dummy ;
+
+	if (pos_source)
+	{
+		if(v.getCount() == 0)
+		{
+			return LLModel::BAD_ELEMENT;
+		}
+
+		face.mExtents[0].set(v[0], v[1], v[2]);
+		face.mExtents[1].set(v[0], v[1], v[2]);
+	}
+	
+	LLVolumeFace::VertexMapData::PointMap point_map;
+	
+	for (U32 i = 0; i < idx.getCount(); i += idx_stride)
+	{
+		LLVolumeFace::VertexData cv;
+		if (pos_source)
+		{
+			cv.setPosition(LLVector4a(v[idx[i+pos_offset]*3+0],
+								v[idx[i+pos_offset]*3+1],
+								v[idx[i+pos_offset]*3+2]));
+		}
+
+		if (tc_source)
+		{
+			cv.mTexCoord.setVec(tc[idx[i+tc_offset]*2+0],
+								tc[idx[i+tc_offset]*2+1]);
+		}
+		
+		if (norm_source)
+		{
+			cv.setNormal(LLVector4a(n[idx[i+norm_offset]*3+0],
+								n[idx[i+norm_offset]*3+1],
+								n[idx[i+norm_offset]*3+2]));
+		}
+		
+		BOOL found = FALSE;
+			
+		LLVolumeFace::VertexMapData::PointMap::iterator point_iter;
+		point_iter = point_map.find(LLVector3(cv.getPosition().getF32ptr()));
+		
+		if (point_iter != point_map.end())
+		{
+			for (U32 j = 0; j < point_iter->second.size(); ++j)
+			{
+				// We have a matching loc
+				//
+				if ((point_iter->second)[j] == cv)
+				{
+					U16 shared_index	= (point_iter->second)[j].mIndex;
+
+					// Don't share verts within the same tri, degenerate
+					//
+					if ((indices.size() % 3) && (indices[indices.size()-1] != shared_index))
+					{
+						found = true;
+						indices.push_back(shared_index);
+					}
+					break;
+				}
+			}
+		}
+
+		if (!found)
+		{
+			update_min_max(face.mExtents[0], face.mExtents[1], cv.getPosition());
+			verts.push_back(cv);
+			if (verts.size() >= 65535)
+			{
+				//llerrs << "Attempted to write model exceeding 16-bit index buffer limitation." << LL_ENDL;
+				return LLModel::VERTEX_NUMBER_OVERFLOW ;
+			}
+			U16 index = (U16) (verts.size()-1);
+			indices.push_back(index);
+
+			LLVolumeFace::VertexMapData d;
+			d.setPosition(cv.getPosition());
+			d.mTexCoord = cv.mTexCoord;
+			d.setNormal(cv.getNormal());
+			d.mIndex = index;
+			if (point_iter != point_map.end())
+			{
+				point_iter->second.push_back(d);
+			}
+			else
+			{
+				point_map[LLVector3(d.getPosition().getF32ptr())].push_back(d);
+			}
+		}
+
+		if (indices.size()%3 == 0 && verts.size() >= 65532)
+		{
+			std::string material;
+
+			if (tri->getMaterial())
+			{
+				material = std::string(tri->getMaterial());
+			}
+
+			materials.push_back(material);
+			face_list.push_back(face);
+			face_list.rbegin()->fillFromLegacyData(verts, indices);
+			LLVolumeFace& new_face = *face_list.rbegin();
+			if (!norm_source)
+			{
+				//ll_aligned_free_16(new_face.mNormals);
+				new_face.mNormals = NULL;
+			}
+
+			if (!tc_source)
+			{
+				//ll_aligned_free_16(new_face.mTexCoords);
+				new_face.mTexCoords = NULL;
+			}
+
+			face = LLVolumeFace();
+			point_map.clear();
+		}
+	}
+
+	if (!verts.empty())
+	{
+		std::string material;
+
+		if (tri->getMaterial())
+		{
+			material = std::string(tri->getMaterial());
+		}
+		
+		materials.push_back(material);
+		face_list.push_back(face);
+
+		face_list.rbegin()->fillFromLegacyData(verts, indices);
+		LLVolumeFace& new_face = *face_list.rbegin();
+		if (!norm_source)
+		{
+			//ll_aligned_free_16(new_face.mNormals);
+			new_face.mNormals = NULL;
+		}
+
+		if (!tc_source)
+		{
+			//ll_aligned_free_16(new_face.mTexCoords);
+			new_face.mTexCoords = NULL;
+		}
+	}
+
+	return LLModel::NO_ERRORS ;
+}
+
+LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& face_list, std::vector<std::string>& materials, domPolylistRef& poly)
+{
+	domPRef p = poly->getP();
+	domListOfUInts& idx = p->getValue();
+
+	if (idx.getCount() == 0)
+	{
+		return LLModel::NO_ERRORS ;
+	}
+
+	const domInputLocalOffset_Array& inputs = poly->getInput_array();
+
+
+	domListOfUInts& vcount = poly->getVcount()->getValue();
+	
+	S32 pos_offset = -1;
+	S32 tc_offset = -1;
+	S32 norm_offset = -1;
+
+	domSource* pos_source = NULL;
+	domSource* tc_source = NULL;
+	domSource* norm_source = NULL;
+
+	S32 idx_stride = 0;
+
+	if (!get_dom_sources(inputs, pos_offset, tc_offset, norm_offset, idx_stride, pos_source, tc_source, norm_source))
+	{
+		return LLModel::BAD_ELEMENT;
+	}
+
+	LLVolumeFace face;
+
+	std::vector<U16> indices;
+	std::vector<LLVolumeFace::VertexData> verts;
+
+	domListOfFloats v;
+	domListOfFloats tc;
+	domListOfFloats n;
+
+	if (pos_source)
+	{
+		v = pos_source->getFloat_array()->getValue();
+		face.mExtents[0].set(v[0], v[1], v[2]);
+		face.mExtents[1].set(v[0], v[1], v[2]);
+	}
+
+	if (tc_source)
+	{
+		tc = tc_source->getFloat_array()->getValue();
+	}
+
+	if (norm_source)
+	{
+		n = norm_source->getFloat_array()->getValue();
+	}
+	
+	LLVolumeFace::VertexMapData::PointMap point_map;
+
+	U32 cur_idx = 0;
+	for (U32 i = 0; i < vcount.getCount(); ++i)
+	{ //for each polygon
+		U32 first_index = 0;
+		U32 last_index = 0;
+		for (U32 j = 0; j < vcount[i]; ++j)
+		{ //for each vertex
+
+			LLVolumeFace::VertexData cv;
+
+			if (pos_source)
+			{
+				cv.getPosition().set(v[idx[cur_idx+pos_offset]*3+0],
+									v[idx[cur_idx+pos_offset]*3+1],
+									v[idx[cur_idx+pos_offset]*3+2]);
+				if (!cv.getPosition().isFinite3())
+				{
+					LL_WARNS() << "Found NaN while loading position data from DAE-Model, invalid model." << LL_ENDL;
+					return LLModel::BAD_ELEMENT;
+				}
+			}
+
+			if (tc_source)
+			{
+				cv.mTexCoord.setVec(tc[idx[cur_idx+tc_offset]*2+0],
+									tc[idx[cur_idx+tc_offset]*2+1]);
+			}
+			
+			if (norm_source)
+			{
+				cv.getNormal().set(n[idx[cur_idx+norm_offset]*3+0],
+									n[idx[cur_idx+norm_offset]*3+1],
+									n[idx[cur_idx+norm_offset]*3+2]);
+
+				if (!cv.getNormal().isFinite3())
+				{
+					LL_WARNS() << "Found NaN while loading normals from DAE-Model, invalid model." << LL_ENDL;
+					return LLModel::BAD_ELEMENT;
+				}
+			}
+			
+			cur_idx += idx_stride;
+			
+			BOOL found = FALSE;
+				
+			LLVolumeFace::VertexMapData::PointMap::iterator point_iter;
+			LLVector3 pos3(cv.getPosition().getF32ptr());
+			point_iter = point_map.find(pos3);
+			
+			if (point_iter != point_map.end())
+			{
+				for (U32 k = 0; k < point_iter->second.size(); ++k)
+				{
+					if ((point_iter->second)[k] == cv)
+					{
+						found = TRUE;
+						U32 index = (point_iter->second)[k].mIndex;
+						if (j == 0)
+						{
+							first_index = index;
+						}
+						else if (j == 1)
+						{
+							last_index = index;
+						}
+						else
+						{
+							// if these are the same, we have a very, very skinny triangle (coincident verts on one or more edges)
+							//
+							llassert((first_index != last_index) && (last_index != index) && (first_index != index));
+							indices.push_back(first_index);
+							indices.push_back(last_index);
+							indices.push_back(index);
+							last_index = index;
+						}
+
+						break;
+					}
+				}
+			}
+
+			if (!found)
+			{
+				update_min_max(face.mExtents[0], face.mExtents[1], cv.getPosition());
+				verts.push_back(cv);
+				if (verts.size() >= 65535)
+				{
+					//llerrs << "Attempted to write model exceeding 16-bit index buffer limitation." << LL_ENDL;
+					return LLModel::VERTEX_NUMBER_OVERFLOW ;
+				}
+				U16 index = (U16) (verts.size()-1);
+			
+				if (j == 0)
+				{
+					first_index = index;
+				}
+				else if (j == 1)
+				{
+					last_index = index;
+				}
+				else
+				{
+					// detect very skinny degenerate triangles with collapsed edges
+					//
+					llassert((first_index != last_index) && (last_index != index) && (first_index != index));
+					indices.push_back(first_index);
+					indices.push_back(last_index);
+					indices.push_back(index);
+					last_index = index;
+				}	
+
+				LLVolumeFace::VertexMapData d;
+				d.setPosition(cv.getPosition());
+				d.mTexCoord = cv.mTexCoord;
+				d.setNormal(cv.getNormal());
+				d.mIndex = index;
+				if (point_iter != point_map.end())
+				{
+					point_iter->second.push_back(d);
+				}
+				else
+				{
+					point_map[pos3].push_back(d);
+				}
+			}
+
+			if (indices.size()%3 == 0 && indices.size() >= 65532)
+			{
+				std::string material;
+
+				if (poly->getMaterial())
+				{
+					material = std::string(poly->getMaterial());
+				}
+
+				materials.push_back(material);
+				face_list.push_back(face);
+				face_list.rbegin()->fillFromLegacyData(verts, indices);
+				LLVolumeFace& new_face = *face_list.rbegin();
+				if (!norm_source)
+				{
+					//ll_aligned_free_16(new_face.mNormals);
+					new_face.mNormals = NULL;
+				}
+
+				if (!tc_source)
+				{
+					//ll_aligned_free_16(new_face.mTexCoords);
+					new_face.mTexCoords = NULL;
+				}
+
+				face = LLVolumeFace();
+				verts.clear();
+				indices.clear();
+				point_map.clear();
+			}
+		}
+	}
+
+	if (!verts.empty())
+	{
+		std::string material;
+
+		if (poly->getMaterial())
+		{
+			material = std::string(poly->getMaterial());
+		}
+	
+		materials.push_back(material);
+		face_list.push_back(face);
+		face_list.rbegin()->fillFromLegacyData(verts, indices);
+
+		LLVolumeFace& new_face = *face_list.rbegin();
+		if (!norm_source)
+		{
+			//ll_aligned_free_16(new_face.mNormals);
+			new_face.mNormals = NULL;
+		}
+
+		if (!tc_source)
+		{
+			//ll_aligned_free_16(new_face.mTexCoords);
+			new_face.mTexCoords = NULL;
+		}
+	}
+
+	return LLModel::NO_ERRORS ;
+}
+
+LLModel::EModelStatus load_face_from_dom_polygons(std::vector<LLVolumeFace>& face_list, std::vector<std::string>& materials, domPolygonsRef& poly)
+{
+	LLVolumeFace face;
+	std::vector<U16> indices;
+	std::vector<LLVolumeFace::VertexData> verts;
+
+	const domInputLocalOffset_Array& inputs = poly->getInput_array();
+
+	S32 v_offset = -1;
+	S32 n_offset = -1;
+	S32 t_offset = -1;
+
+	domListOfFloats* v = NULL;
+	domListOfFloats* n = NULL;
+	domListOfFloats* t = NULL;
+	
+	U32 stride = 0;
+	for (U32 i = 0; i < inputs.getCount(); ++i)
+	{
+		stride = llmax((U32) inputs[i]->getOffset()+1, stride);
+
+		if (strcmp(COMMON_PROFILE_INPUT_VERTEX, inputs[i]->getSemantic()) == 0)
+		{ //found vertex array
+			v_offset = inputs[i]->getOffset();
+
+			const domURIFragmentType& uri = inputs[i]->getSource();
+			daeElementRef elem = uri.getElement();
+			domVertices* vertices = (domVertices*) elem.cast();
+			if (!vertices)
+			{
+				return LLModel::BAD_ELEMENT;
+			}
+			domInputLocal_Array& v_inp = vertices->getInput_array();
+
+			for (U32 k = 0; k < v_inp.getCount(); ++k)
+			{
+				if (strcmp(COMMON_PROFILE_INPUT_POSITION, v_inp[k]->getSemantic()) == 0)
+				{
+					const domURIFragmentType& uri = v_inp[k]->getSource();
+					daeElementRef elem = uri.getElement();
+					domSource* src = (domSource*) elem.cast();
+					if (!src)
+					{
+						return LLModel::BAD_ELEMENT;
+					}
+					v = &(src->getFloat_array()->getValue());
+				}
+			}
+		}
+		else if (strcmp(COMMON_PROFILE_INPUT_NORMAL, inputs[i]->getSemantic()) == 0)
+		{
+			n_offset = inputs[i]->getOffset();
+			//found normal array for this triangle list
+			const domURIFragmentType& uri = inputs[i]->getSource();
+			daeElementRef elem = uri.getElement();
+			domSource* src = (domSource*) elem.cast();
+			if (!src)
+			{
+				return LLModel::BAD_ELEMENT;
+			}
+			n = &(src->getFloat_array()->getValue());
+		}
+		else if (strcmp(COMMON_PROFILE_INPUT_TEXCOORD, inputs[i]->getSemantic()) == 0 && inputs[i]->getSet() == 0)
+		{ //found texCoords
+			t_offset = inputs[i]->getOffset();
+			const domURIFragmentType& uri = inputs[i]->getSource();
+			daeElementRef elem = uri.getElement();
+			domSource* src = (domSource*) elem.cast();
+			if (!src)
+			{
+				return LLModel::BAD_ELEMENT;
+			}
+			t = &(src->getFloat_array()->getValue());
+		}
+	}
+
+	domP_Array& ps = poly->getP_array();
+
+	//make a triangle list in <verts>
+	for (U32 i = 0; i < ps.getCount(); ++i)
+	{ //for each polygon
+		domListOfUInts& idx = ps[i]->getValue();
+		for (U32 j = 0; j < idx.getCount()/stride; ++j)
+		{ //for each vertex
+			if (j > 2)
+			{
+				U32 size = verts.size();
+				LLVolumeFace::VertexData v0 = verts[size-3];
+				LLVolumeFace::VertexData v1 = verts[size-1];
+
+				verts.push_back(v0);
+				verts.push_back(v1);
+			}
+
+			LLVolumeFace::VertexData vert;
+
+
+			if (v)
+			{
+				U32 v_idx = idx[j*stride+v_offset]*3;
+				v_idx = llclamp(v_idx, (U32) 0, (U32) v->getCount());
+				vert.getPosition().set(v->get(v_idx),
+								v->get(v_idx+1),
+								v->get(v_idx+2));
+			}
+			
+			//bounds check n and t lookups because some FBX to DAE converters
+			//use negative indices and empty arrays to indicate data does not exist
+			//for a particular channel
+			if (n && n->getCount() > 0)
+			{
+				U32 n_idx = idx[j*stride+n_offset]*3;
+				n_idx = llclamp(n_idx, (U32) 0, (U32) n->getCount());
+				vert.getNormal().set(n->get(n_idx),
+								n->get(n_idx+1),
+								n->get(n_idx+2));
+			}
+			else
+			{
+				vert.getNormal().clear();
+			}
+
+			
+			if (t && t->getCount() > 0)
+			{
+				U32 t_idx = idx[j*stride+t_offset]*2;
+				t_idx = llclamp(t_idx, (U32) 0, (U32) t->getCount());
+				vert.mTexCoord.setVec(t->get(t_idx),
+								t->get(t_idx+1));								
+			}
+			else
+			{
+				vert.mTexCoord.clear();
+			}
+
+						
+			verts.push_back(vert);
+		}
+	}
+
+	if (verts.empty())
+	{
+		return LLModel::NO_ERRORS;
+	}
+
+	face.mExtents[0] = verts[0].getPosition();
+	face.mExtents[1] = verts[0].getPosition();
+	
+	//create a map of unique vertices to indices
+	std::map<LLVolumeFace::VertexData, U32> vert_idx;
+
+	U32 cur_idx = 0;
+	for (U32 i = 0; i < verts.size(); ++i)
+	{
+		std::map<LLVolumeFace::VertexData, U32>::iterator iter = vert_idx.find(verts[i]);
+		if (iter == vert_idx.end())
+		{
+			vert_idx[verts[i]] = cur_idx++;
+		}
+	}
+
+	//build vertex array from map
+	std::vector<LLVolumeFace::VertexData> new_verts;
+	new_verts.resize(vert_idx.size());
+
+	for (std::map<LLVolumeFace::VertexData, U32>::iterator iter = vert_idx.begin(); iter != vert_idx.end(); ++iter)
+	{
+		new_verts[iter->second] = iter->first;
+		update_min_max(face.mExtents[0], face.mExtents[1], iter->first.getPosition());
+	}
+
+	//build index array from map
+	indices.resize(verts.size());
+
+	for (U32 i = 0; i < verts.size(); ++i)
+	{
+		indices[i] = vert_idx[verts[i]];
+		llassert(!i || (indices[i-1] != indices[i]));
+	}
+
+	// DEBUG just build an expanded triangle list
+	/*for (U32 i = 0; i < verts.size(); ++i)
+	{
+		indices.push_back((U16) i);
+		update_min_max(face.mExtents[0], face.mExtents[1], verts[i].getPosition());
+	}*/
+
+    if (!new_verts.empty())
+	{
+		std::string material;
+
+		if (poly->getMaterial())
+		{
+			material = std::string(poly->getMaterial());
+		}
+
+		materials.push_back(material);
+		face_list.push_back(face);
+		face_list.rbegin()->fillFromLegacyData(new_verts, indices);
+
+		LLVolumeFace& new_face = *face_list.rbegin();
+		if (!n)
+		{
+			//ll_aligned_free_16(new_face.mNormals);
+			new_face.mNormals = NULL;
+		}
+
+		if (!t)
+		{
+			//ll_aligned_free_16(new_face.mTexCoords);
+			new_face.mTexCoords = NULL;
+		}
+	}
+
+	return LLModel::NO_ERRORS ;
+}
+
+//-----------------------------------------------------------------------------
+// LLDAELoader
+//-----------------------------------------------------------------------------
+LLDAELoader::LLDAELoader(
+	std::string				filename,
+	S32						lod,
+	load_callback_t		load_cb,
+	joint_lookup_func_t	joint_lookup_func,
+	texture_load_func_t	texture_load_func,
+	state_callback_t		state_cb,
+	void*						opaque_userdata,
+	JointTransformMap&	jointMap,
+	JointSet&				jointsFromNodes,
+	U32					modelLimit)
+: LLModelLoader(
+		filename,
+		lod,
+		load_cb,
+		joint_lookup_func,
+		texture_load_func,
+		state_cb,
+		opaque_userdata,
+		jointMap,
+		jointsFromNodes),
+mGeneratedModelLimit(modelLimit)
+{
+}
+
+LLDAELoader::~LLDAELoader()
+{
+}
+
+struct ModelSort
+{
+	bool operator()(const LLPointer< LLModel >& lhs, const LLPointer< LLModel >& rhs)
+	{
+        if (lhs->mSubmodelID < rhs->mSubmodelID)
+        {
+            return true;
+        }
+		return LLStringUtil::compareInsensitive(lhs->mLabel, rhs->mLabel) < 0;
+	}
+};
+
+bool LLDAELoader::OpenFile(const std::string& filename)
+{
+	//no suitable slm exists, load from the .dae file
+	DAE dae;
+	domCOLLADA* dom = dae.open(filename);
+	
+	if (!dom)
+	{
+		LL_INFOS() <<" Error with dae - traditionally indicates a corrupt file."<<LL_ENDL;
+		setLoadState( ERROR_PARSING );
+		return false;
+	}
+	//Dom version
+	daeString domVersion = dae.getDomVersion();
+	std::string sldom(domVersion);
+	LL_INFOS()<<"Collada Importer Version: "<<sldom<<LL_ENDL;
+	//Dae version
+	domVersionType docVersion = dom->getVersion();
+	//0=1.4
+	//1=1.4.1
+	//2=Currently unsupported, however may work
+	if (docVersion > 1 ) 
+	{ 
+		docVersion = VERSIONTYPE_COUNT;
+	}
+	LL_INFOS()<<"Dae version "<<colladaVersion[docVersion]<<LL_ENDL;
+	
+	
+	daeDatabase* db = dae.getDatabase();
+	
+	daeInt count = db->getElementCount(NULL, COLLADA_TYPE_MESH);
+	
+	daeDocument* doc = dae.getDoc(mFilename);
+	if (!doc)
+	{
+		LL_WARNS() << "can't find internal doc" << LL_ENDL;
+		return false;
+	}
+	
+	daeElement* root = doc->getDomRoot();
+	if (!root)
+	{
+		LL_WARNS() << "document has no root" << LL_ENDL;
+		return false;
+	}
+	
+	//Verify some basic properties of the dae
+	//1. Basic validity check on controller 
+	U32 controllerCount = (int) db->getElementCount( NULL, "controller" );
+	bool result = false;
+	for ( int i=0; i<controllerCount; ++i )
+	{
+		domController* pController = NULL;
+		db->getElement( (daeElement**) &pController, i , NULL, "controller" );
+		result = verifyController( pController );
+		if (!result)
+		{
+			LL_INFOS() << "Could not verify controller" << LL_ENDL;
+			setLoadState( ERROR_PARSING );
+			return true;
+		}
+	}
+
+
+	//get unit scale
+	mTransform.setIdentity();
+	
+	domAsset::domUnit* unit = daeSafeCast<domAsset::domUnit>(root->getDescendant(daeElement::matchType(domAsset::domUnit::ID())));
+
+	if (unit)
+	{
+		F32 meter = unit->getMeter();
+		mTransform.mMatrix[0][0] = meter;
+		mTransform.mMatrix[1][1] = meter;
+		mTransform.mMatrix[2][2] = meter;
+	}
+	
+	//get up axis rotation
+	LLMatrix4 rotation;
+	
+	domUpAxisType up = UPAXISTYPE_Y_UP;  // default is Y_UP
+	domAsset::domUp_axis* up_axis =
+	daeSafeCast<domAsset::domUp_axis>(root->getDescendant(daeElement::matchType(domAsset::domUp_axis::ID())));
+	
+	if (up_axis)
+	{
+		up = up_axis->getValue();
+	}
+	
+	if (up == UPAXISTYPE_X_UP)
+	{
+		rotation.initRotation(0.0f, 90.0f * DEG_TO_RAD, 0.0f);
+	}
+	else if (up == UPAXISTYPE_Y_UP)
+	{
+		rotation.initRotation(90.0f * DEG_TO_RAD, 0.0f, 0.0f);
+	}
+	
+	rotation *= mTransform;
+	mTransform = rotation;
+
+	mTransform.condition();	
+
+	U32 submodel_limit = count > 0 ? mGeneratedModelLimit/count : 0;
+	for (daeInt idx = 0; idx < count; ++idx)
+	{ //build map of domEntities to LLModel
+		domMesh* mesh = NULL;
+		db->getElement((daeElement**) &mesh, idx, NULL, COLLADA_TYPE_MESH);
+		
+		if (mesh)
+		{
+
+			std::vector<LLModel*> models;
+
+			loadModelsFromDomMesh(mesh, models, submodel_limit);
+
+			std::vector<LLModel*>::iterator i;
+			i = models.begin();
+			while (i != models.end())
+			{
+				LLModel* mdl = *i;
+				if(mdl->getStatus() != LLModel::NO_ERRORS)
+				{
+					setLoadState(ERROR_MODEL + mdl->getStatus()) ;
+					return false; //abort
+				}
+
+				if (mdl && validate_model(mdl))
+				{
+					mModelList.push_back(mdl);
+					mModelsMap[mesh].push_back(mdl);
+				}
+				i++;
+			}
+		}
+	}
+
+	std::sort(mModelList.begin(), mModelList.end(), ModelSort());
+
+	model_list::iterator model_iter = mModelList.begin();
+	while (model_iter != mModelList.end())
+	{
+		LLModel* mdl = *model_iter;
+		U32 material_count = mdl->mMaterialList.size();
+		LL_INFOS() << "Importing " << mdl->mLabel << " model with " << material_count << " material references" << LL_ENDL;
+		std::vector<std::string>::iterator mat_iter = mdl->mMaterialList.begin();
+		std::vector<std::string>::iterator end_iter = material_count > LIMIT_MATERIALS_OUTPUT
+														? mat_iter + LIMIT_MATERIALS_OUTPUT
+														: mdl->mMaterialList.end();
+		while (mat_iter != end_iter)
+		{
+			LL_INFOS() << mdl->mLabel << " references " << (*mat_iter) << LL_ENDL;
+			mat_iter++;
+		}
+		model_iter++;
+	}
+
+	count = db->getElementCount(NULL, COLLADA_TYPE_SKIN);
+	for (daeInt idx = 0; idx < count; ++idx)
+	{ //add skinned meshes as instances
+		domSkin* skin = NULL;
+		db->getElement((daeElement**) &skin, idx, NULL, COLLADA_TYPE_SKIN);
+		
+		if (skin)
+		{
+			domGeometry* geom = daeSafeCast<domGeometry>(skin->getSource().getElement());
+			
+			if (geom)
+			{
+				domMesh* mesh = geom->getMesh();
+				if (mesh)
+				{
+					std::vector< LLPointer< LLModel > >::iterator i = mModelsMap[mesh].begin();
+					while (i != mModelsMap[mesh].end())
+					{
+						LLPointer<LLModel> mdl = *i;
+						LLDAELoader::processDomModel(mdl, &dae, root, mesh, skin);
+						i++;
+					}
+				}
+			}
+		}
+	}
+
+	LL_INFOS()<< "Collada skins processed: " << count <<LL_ENDL;
+
+	daeElement* scene = root->getDescendant("visual_scene");
+	
+	if (!scene)
+	{
+		LL_WARNS() << "document has no visual_scene" << LL_ENDL;
+		setLoadState( ERROR_PARSING );
+		return true;
+	}
+	
+	setLoadState( DONE );
+
+	bool badElement = false;
+	
+	processElement( scene, badElement, &dae );
+	
+	if ( badElement )
+	{
+		LL_INFOS()<<"Scene could not be parsed"<<LL_ENDL;
+		setLoadState( ERROR_PARSING );
+	}
+	
+	return true;
+}
+
+void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, domMesh* mesh, domSkin* skin)
+{
+	llassert(model && dae && mesh && skin);
+
+	if (model)
+	{
+		LLVector3 mesh_scale_vector;
+		LLVector3 mesh_translation_vector;
+		model->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector);
+
+		LLMatrix4 normalized_transformation;
+		normalized_transformation.setTranslation(mesh_translation_vector);
+
+		LLMatrix4 mesh_scale;
+		mesh_scale.initScale(mesh_scale_vector);
+		mesh_scale *= normalized_transformation;
+		normalized_transformation = mesh_scale;
+
+		glh::matrix4f inv_mat((F32*) normalized_transformation.mMatrix);
+		inv_mat = inv_mat.inverse();
+		LLMatrix4 inverse_normalized_transformation(inv_mat.m);
+
+		domSkin::domBind_shape_matrix* bind_mat = skin->getBind_shape_matrix();
+
+		if (bind_mat)
+		{ //get bind shape matrix
+			domFloat4x4& dom_value = bind_mat->getValue();
+
+			LLMeshSkinInfo& skin_info = model->mSkinInfo;
+
+			for (int i = 0; i < 4; i++)
+			{
+				for(int j = 0; j < 4; j++)
+				{
+					skin_info.mBindShapeMatrix.mMatrix[i][j] = dom_value[i + j*4];
+				}
+			}
+
+			LLMatrix4 trans = normalized_transformation;
+			trans *= skin_info.mBindShapeMatrix;
+			skin_info.mBindShapeMatrix = trans;							
+		}
+
+
+		//Some collada setup for accessing the skeleton
+		daeElement* pElement = 0;
+		dae->getDatabase()->getElement( &pElement, 0, 0, "skeleton" );
+
+		//Try to get at the skeletal instance controller
+		domInstance_controller::domSkeleton* pSkeleton = daeSafeCast<domInstance_controller::domSkeleton>( pElement );
+		bool missingSkeletonOrScene = false;
+
+		//If no skeleton, do a breadth-first search to get at specific joints
+		bool rootNode = false;
+
+		//Need to test for a skeleton that does not have a root node
+		//This occurs when your instance controller does not have an associated scene 
+		if ( pSkeleton )
+		{
+			daeElement* pSkeletonRootNode = pSkeleton->getValue().getElement();
+			if ( pSkeletonRootNode )
+			{
+				rootNode = true;
+			}
+
+		}
+		if ( !pSkeleton || !rootNode )
+		{
+			daeElement* pScene = root->getDescendant("visual_scene");
+			if ( !pScene )
+			{
+				LL_WARNS()<<"No visual scene - unable to parse bone offsets "<<LL_ENDL;
+				missingSkeletonOrScene = true;
+			}
+			else
+			{
+				//Get the children at this level
+				daeTArray< daeSmartRef<daeElement> > children = pScene->getChildren();
+				S32 childCount = children.getCount();
+
+				//Process any children that are joints
+				//Not all children are joints, some code be ambient lights, cameras, geometry etc..
+				for (S32 i = 0; i < childCount; ++i)
+				{
+					domNode* pNode = daeSafeCast<domNode>(children[i]);
+					if ( isNodeAJoint( pNode ) )
+					{
+						processJointNode( pNode, mJointList );
+					}
+				}
+			}
+		}
+		else
+			//Has Skeleton
+		{
+			//Get the root node of the skeleton
+			daeElement* pSkeletonRootNode = pSkeleton->getValue().getElement();
+			if ( pSkeletonRootNode )
+			{
+				//Once we have the root node - start acccessing it's joint components
+				const int jointCnt = mJointMap.size();
+				JointMap :: const_iterator jointIt = mJointMap.begin();
+
+				//Loop over all the possible joints within the .dae - using the allowed joint list in the ctor.
+				for ( int i=0; i<jointCnt; ++i, ++jointIt )
+				{
+					//Build a joint for the resolver to work with
+					char str[64]={0};
+					sprintf(str,"./%s",(*jointIt).first.c_str() );
+					//LL_WARNS()<<"Joint "<< str <<LL_ENDL;
+
+					//Setup the resolver
+					daeSIDResolver resolver( pSkeletonRootNode, str );
+
+					//Look for the joint
+					domNode* pJoint = daeSafeCast<domNode>( resolver.getElement() );
+					if ( pJoint )
+					{
+						//Pull out the translate id and store it in the jointTranslations map
+						daeSIDResolver jointResolverA( pJoint, "./translate" );
+						domTranslate* pTranslateA = daeSafeCast<domTranslate>( jointResolverA.getElement() );
+						daeSIDResolver jointResolverB( pJoint, "./location" );
+						domTranslate* pTranslateB = daeSafeCast<domTranslate>( jointResolverB.getElement() );
+
+						LLMatrix4 workingTransform;
+
+						//Translation via SID
+						if ( pTranslateA )
+						{
+							extractTranslation( pTranslateA, workingTransform );
+						}
+						else
+							if ( pTranslateB )
+							{
+								extractTranslation( pTranslateB, workingTransform );
+							}
+							else
+							{
+								//Translation via child from element
+								daeElement* pTranslateElement = getChildFromElement( pJoint, "translate" );
+								if ( pTranslateElement && pTranslateElement->typeID() != domTranslate::ID() )
+								{
+									LL_WARNS()<< "The found element is not a translate node" <<LL_ENDL;
+									missingSkeletonOrScene = true;
+								}
+								else
+									if ( pTranslateElement )
+									{
+										extractTranslationViaElement( pTranslateElement, workingTransform );
+									}
+									else
+									{
+										extractTranslationViaSID( pJoint, workingTransform );
+									}
+
+							}
+
+							//Store the joint transform w/respect to it's name.
+							mJointList[(*jointIt).second.c_str()] = workingTransform;
+					}
+				}
+
+				//If anything failed in regards to extracting the skeleton, joints or translation id,
+				//mention it
+				if ( missingSkeletonOrScene  )
+				{
+					LL_WARNS()<< "Partial jointmap found in asset - did you mean to just have a partial map?" << LL_ENDL;
+				}
+			}//got skeleton?
+		}
+
+
+		domSkin::domJoints* joints = skin->getJoints();
+
+		domInputLocal_Array& joint_input = joints->getInput_array();
+
+		for (size_t i = 0; i < joint_input.getCount(); ++i)
+		{
+			domInputLocal* input = joint_input.get(i);
+			xsNMTOKEN semantic = input->getSemantic();
+
+			if (strcmp(semantic, COMMON_PROFILE_INPUT_JOINT) == 0)
+			{ //found joint source, fill model->mJointMap and model->mSkinInfo.mJointNames
+				daeElement* elem = input->getSource().getElement();
+
+				domSource* source = daeSafeCast<domSource>(elem);
+				if (source)
+				{
+
+
+					domName_array* names_source = source->getName_array();
+
+					if (names_source)
+					{
+						domListOfNames &names = names_source->getValue();
+
+						for (size_t j = 0; j < names.getCount(); ++j)
+						{
+							std::string name(names.get(j));
+							if (mJointMap.find(name) != mJointMap.end())
+							{
+								name = mJointMap[name];
+							}
+							model->mSkinInfo.mJointNames.push_back(name);
+							model->mSkinInfo.mJointMap[name] = j;
+						}
+					}
+					else
+					{
+						domIDREF_array* names_source = source->getIDREF_array();
+						if (names_source)
+						{
+							xsIDREFS& names = names_source->getValue();
+
+							for (size_t j = 0; j < names.getCount(); ++j)
+							{
+								std::string name(names.get(j).getID());
+								if (mJointMap.find(name) != mJointMap.end())
+								{
+									name = mJointMap[name];
+								}
+								model->mSkinInfo.mJointNames.push_back(name);
+								model->mSkinInfo.mJointMap[name] = j;
+							}
+						}
+					}
+				}
+			}
+			else if (strcmp(semantic, COMMON_PROFILE_INPUT_INV_BIND_MATRIX) == 0)
+			{ //found inv_bind_matrix array, fill model->mInvBindMatrix
+				domSource* source = daeSafeCast<domSource>(input->getSource().getElement());
+				if (source)
+				{
+					domFloat_array* t = source->getFloat_array();
+					if (t)
+					{
+						domListOfFloats& transform = t->getValue();
+						S32 count = transform.getCount()/16;
+
+						for (S32 k = 0; k < count; ++k)
+						{
+							LLMatrix4 mat;
+
+							for (int i = 0; i < 4; i++)
+							{
+								for(int j = 0; j < 4; j++)
+								{
+									mat.mMatrix[i][j] = transform[k*16 + i + j*4];
+								}
+							}
+
+							model->mSkinInfo.mInvBindMatrix.push_back(mat);											
+						}
+					}
+				}
+			}
+		}
+
+		//Now that we've parsed the joint array, let's determine if we have a full rig
+		//(which means we have all the joint sthat are required for an avatar versus
+		//a skinned asset attached to a node in a file that contains an entire skeleton,
+		//but does not use the skeleton).						
+		buildJointToNodeMappingFromScene( root );
+		critiqueRigForUploadApplicability( model->mSkinInfo.mJointNames );
+
+		if ( !missingSkeletonOrScene )
+		{
+			//Set the joint translations on the avatar - if it's a full mapping
+			//The joints are reset in the dtor
+			if ( getRigWithSceneParity() )
+			{	
+				JointMap :: const_iterator masterJointIt = mJointMap.begin();
+				JointMap :: const_iterator masterJointItEnd = mJointMap.end();
+				for (;masterJointIt!=masterJointItEnd;++masterJointIt )
+				{
+					std::string lookingForJoint = (*masterJointIt).first.c_str();
+
+					if ( mJointList.find( lookingForJoint ) != mJointList.end() )
+					{
+						//LL_INFOS()<<"joint "<<lookingForJoint.c_str()<<LL_ENDL;
+						LLMatrix4 jointTransform = mJointList[lookingForJoint];
+						LLJoint* pJoint = mJointLookupFunc(lookingForJoint,mOpaqueData);
+						if ( pJoint )
+						{   
+							LLUUID fake_mesh_id;
+							fake_mesh_id.generate();
+							pJoint->addAttachmentPosOverride( jointTransform.getTranslation(), fake_mesh_id, "");
+						}
+						else
+						{
+							//Most likely an error in the asset.
+							LL_WARNS()<<"Tried to apply joint position from .dae, but it did not exist in the avatar rig." << LL_ENDL;
+						}
+					}
+				}
+			}
+		} //missingSkeletonOrScene
+
+		//We need to construct the alternate bind matrix (which contains the new joint positions)
+		//in the same order as they were stored in the joint buffer. The joints associated
+		//with the skeleton are not stored in the same order as they are in the exported joint buffer.
+		//This remaps the skeletal joints to be in the same order as the joints stored in the model.
+		std::vector<std::string> :: const_iterator jointIt  = model->mSkinInfo.mJointNames.begin();
+		const int jointCnt = model->mSkinInfo.mJointNames.size();
+		for ( int i=0; i<jointCnt; ++i, ++jointIt )
+		{
+			std::string lookingForJoint = (*jointIt).c_str();
+			//Look for the joint xform that we extracted from the skeleton, using the jointIt as the key
+			//and store it in the alternate bind matrix
+			if ( mJointList.find( lookingForJoint ) != mJointList.end() )
+			{
+				LLMatrix4 jointTransform = mJointList[lookingForJoint];
+				LLMatrix4 newInverse = model->mSkinInfo.mInvBindMatrix[i];
+				newInverse.setTranslation( mJointList[lookingForJoint].getTranslation() );
+				model->mSkinInfo.mAlternateBindMatrix.push_back( newInverse );
+			}
+			else
+			{
+				LL_WARNS()<<"Possibly misnamed/missing joint [" <<lookingForJoint.c_str()<<" ] "<<LL_ENDL;
+			}
+		}
+
+		//grab raw position array
+
+		domVertices* verts = mesh->getVertices();
+		if (verts)
+		{
+			domInputLocal_Array& inputs = verts->getInput_array();
+			for (size_t i = 0; i < inputs.getCount() && model->mPosition.empty(); ++i)
+			{
+				if (strcmp(inputs[i]->getSemantic(), COMMON_PROFILE_INPUT_POSITION) == 0)
+				{
+					domSource* pos_source = daeSafeCast<domSource>(inputs[i]->getSource().getElement());
+					if (pos_source)
+					{
+						domFloat_array* pos_array = pos_source->getFloat_array();
+						if (pos_array)
+						{
+							domListOfFloats& pos = pos_array->getValue();
+
+							for (size_t j = 0; j < pos.getCount(); j += 3)
+							{
+								if (pos.getCount() <= j+2)
+								{
+									LL_ERRS() << "Invalid position array size." << LL_ENDL;
+								}
+
+								LLVector3 v(pos[j], pos[j+1], pos[j+2]);
+
+								//transform from COLLADA space to volume space
+								v = v * inverse_normalized_transformation;
+
+								model->mPosition.push_back(v);
+							}
+						}
+					}
+				}
+			}
+		}
+
+		//grab skin weights array
+		domSkin::domVertex_weights* weights = skin->getVertex_weights();
+		if (weights)
+		{
+			domInputLocalOffset_Array& inputs = weights->getInput_array();
+			domFloat_array* vertex_weights = NULL;
+			for (size_t i = 0; i < inputs.getCount(); ++i)
+			{
+				if (strcmp(inputs[i]->getSemantic(), COMMON_PROFILE_INPUT_WEIGHT) == 0)
+				{
+					domSource* weight_source = daeSafeCast<domSource>(inputs[i]->getSource().getElement());
+					if (weight_source)
+					{
+						vertex_weights = weight_source->getFloat_array();
+					}
+				}
+			}
+
+			if (vertex_weights)
+			{
+				domListOfFloats& w = vertex_weights->getValue();
+				domListOfUInts& vcount = weights->getVcount()->getValue();
+				domListOfInts& v = weights->getV()->getValue();
+
+				U32 c_idx = 0;
+				for (size_t vc_idx = 0; vc_idx < vcount.getCount(); ++vc_idx)
+				{ //for each vertex
+					daeUInt count = vcount[vc_idx];
+
+					//create list of weights that influence this vertex
+					LLModel::weight_list weight_list;
+
+					for (daeUInt i = 0; i < count; ++i)
+					{ //for each weight
+						daeInt joint_idx = v[c_idx++];
+						daeInt weight_idx = v[c_idx++];
+
+						if (joint_idx == -1)
+						{
+							//ignore bindings to bind_shape_matrix
+							continue;
+						}
+
+						F32 weight_value = w[weight_idx];
+
+						weight_list.push_back(LLModel::JointWeight(joint_idx, weight_value));
+					}
+
+					//sort by joint weight
+					std::sort(weight_list.begin(), weight_list.end(), LLModel::CompareWeightGreater());
+
+					std::vector<LLModel::JointWeight> wght;
+
+					F32 total = 0.f;
+
+					for (U32 i = 0; i < llmin((U32) 4, (U32) weight_list.size()); ++i)
+					{ //take up to 4 most significant weights
+						if (weight_list[i].mWeight > 0.f)
+						{
+							wght.push_back( weight_list[i] );
+							total += weight_list[i].mWeight;
+						}
+					}
+
+					F32 scale = 1.f/total;
+					if (scale != 1.f)
+					{ //normalize weights
+						for (U32 i = 0; i < wght.size(); ++i)
+						{
+							wght[i].mWeight *= scale;
+						}
+					}
+
+					model->mSkinWeights[model->mPosition[vc_idx]] = wght;
+				}
+			}
+
+		}
+
+		//add instance to scene for this model
+
+		LLMatrix4 transformation;
+		transformation.initScale(mesh_scale_vector);
+		transformation.setTranslation(mesh_translation_vector);
+		transformation *= mTransform;
+
+		std::map<std::string, LLImportMaterial> materials;
+		for (U32 i = 0; i < model->mMaterialList.size(); ++i)
+		{
+			materials[model->mMaterialList[i]] = LLImportMaterial();
+		}
+		mScene[transformation].push_back(LLModelInstance(model, model->mLabel, transformation, materials));
+		stretch_extents(model, transformation, mExtents[0], mExtents[1], mFirstTransform);
+	}
+}
+
+//-----------------------------------------------------------------------------
+// buildJointToNodeMappingFromScene()
+//-----------------------------------------------------------------------------
+void LLDAELoader::buildJointToNodeMappingFromScene( daeElement* pRoot )
+{
+	daeElement* pScene = pRoot->getDescendant("visual_scene");
+	if ( pScene )
+	{
+		daeTArray< daeSmartRef<daeElement> > children = pScene->getChildren();
+		S32 childCount = children.getCount();
+		for (S32 i = 0; i < childCount; ++i)
+		{
+			domNode* pNode = daeSafeCast<domNode>(children[i]);
+			processJointToNodeMapping( pNode );			
+		}
+	}
+}
+//-----------------------------------------------------------------------------
+// processJointToNodeMapping()
+//-----------------------------------------------------------------------------
+void LLDAELoader::processJointToNodeMapping( domNode* pNode )
+{
+	if ( isNodeAJoint( pNode ) )
+	{
+		//1.Store the parent
+		std::string nodeName = pNode->getName();
+		if ( !nodeName.empty() )
+		{
+			mJointsFromNode.push_front( pNode->getName() );
+		}
+		//2. Handle the kiddo's
+		processChildJoints( pNode );
+	}
+	else
+	{
+		//Determine if the're any children wrt to this failed node.
+		//This occurs when an armature is exported and ends up being what essentially amounts to
+		//as the root for the visual_scene
+		if ( pNode ) 
+		{
+			processChildJoints( pNode );
+		}
+		else 
+		{
+			LL_INFOS()<<"Node is NULL"<<LL_ENDL;
+		}
+
+	}
+}
+//-----------------------------------------------------------------------------
+// processChildJoint()
+//-----------------------------------------------------------------------------
+void LLDAELoader::processChildJoints( domNode* pParentNode )
+{	
+	daeTArray< daeSmartRef<daeElement> > childOfChild = pParentNode->getChildren();
+	S32 childOfChildCount = childOfChild.getCount();
+	for (S32 i = 0; i < childOfChildCount; ++i)
+	{
+		domNode* pChildNode = daeSafeCast<domNode>( childOfChild[i] );
+		if ( pChildNode )
+		{
+			processJointToNodeMapping( pChildNode );
+		}
+	}
+}
+
+//-----------------------------------------------------------------------------
+// isNodeAJoint()
+//-----------------------------------------------------------------------------
+bool LLDAELoader::isNodeAJoint( domNode* pNode )
+{
+	if ( !pNode )
+	{
+		LL_INFOS()<<"Created node is NULL"<<LL_ENDL;
+		return false;
+	}
+	
+	return LLModelLoader::isNodeAJoint(pNode->getName());
+}
+//-----------------------------------------------------------------------------
+// verifyCount
+//-----------------------------------------------------------------------------
+bool LLDAELoader::verifyCount( int expected, int result )
+{
+	if ( expected != result )
+	{
+		LL_INFOS()<< "Error: (expected/got)"<<expected<<"/"<<result<<"verts"<<LL_ENDL;
+		return false;
+	}
+	return true;
+}
+//-----------------------------------------------------------------------------
+// verifyController
+//-----------------------------------------------------------------------------
+bool LLDAELoader::verifyController( domController* pController )
+{	
+
+	bool result = true;
+
+	domSkin* pSkin = pController->getSkin();
+
+	if ( pSkin )
+	{
+		xsAnyURI & uri = pSkin->getSource();
+		domElement* pElement = uri.getElement();
+
+		if ( !pElement )
+		{
+			LL_INFOS()<<"Can't resolve skin source"<<LL_ENDL;
+			return false;
+		}
+
+		daeString type_str = pElement->getTypeName();
+		if ( stricmp(type_str, "geometry") == 0 )
+		{	
+			//Skin is reference directly by geometry and get the vertex count from skin
+			domSkin::domVertex_weights* pVertexWeights = pSkin->getVertex_weights();
+			U32 vertexWeightsCount = pVertexWeights->getCount();
+			domGeometry* pGeometry = (domGeometry*) (domElement*) uri.getElement();
+			domMesh* pMesh = pGeometry->getMesh();				
+			
+			if ( pMesh )
+			{
+				//Get vertex count from geometry
+				domVertices* pVertices = pMesh->getVertices();
+				if ( !pVertices )
+				{ 
+					LL_INFOS()<<"No vertices!"<<LL_ENDL;
+					return false;
+				}
+
+				if ( pVertices )
+				{
+					xsAnyURI src = pVertices->getInput_array()[0]->getSource();
+					domSource* pSource = (domSource*) (domElement*) src.getElement();
+					U32 verticesCount = pSource->getTechnique_common()->getAccessor()->getCount();
+					result = verifyCount( verticesCount, vertexWeightsCount );
+					if ( !result )
+					{
+						return result;
+					}
+				}
+			}	
+
+			U32 vcountCount = (U32) pVertexWeights->getVcount()->getValue().getCount();
+			result = verifyCount( vcountCount, vertexWeightsCount );	
+			if ( !result )
+			{
+				return result;
+			}
+
+			domInputLocalOffset_Array& inputs = pVertexWeights->getInput_array();
+			U32 sum = 0;
+			for (size_t i=0; i<vcountCount; i++)
+			{
+				sum += pVertexWeights->getVcount()->getValue()[i];
+			}
+			result = verifyCount( sum * inputs.getCount(), (domInt) pVertexWeights->getV()->getValue().getCount() );
+		}
+	}
+	
+	return result;
+}
+
+//-----------------------------------------------------------------------------
+// extractTranslation()
+//-----------------------------------------------------------------------------
+void LLDAELoader::extractTranslation( domTranslate* pTranslate, LLMatrix4& transform )
+{
+	domFloat3 jointTrans = pTranslate->getValue();
+	LLVector3 singleJointTranslation( jointTrans[0], jointTrans[1], jointTrans[2] );
+	transform.setTranslation( singleJointTranslation );
+}
+//-----------------------------------------------------------------------------
+// extractTranslationViaElement()
+//-----------------------------------------------------------------------------
+void LLDAELoader::extractTranslationViaElement( daeElement* pTranslateElement, LLMatrix4& transform )
+{
+	if ( pTranslateElement )
+	{
+		domTranslate* pTranslateChild = dynamic_cast<domTranslate*>( pTranslateElement );
+		domFloat3 translateChild = pTranslateChild->getValue();
+		LLVector3 singleJointTranslation( translateChild[0], translateChild[1], translateChild[2] );
+		transform.setTranslation( singleJointTranslation );
+	}	
+}
+//-----------------------------------------------------------------------------
+// extractTranslationViaSID()
+//-----------------------------------------------------------------------------
+void LLDAELoader::extractTranslationViaSID( daeElement* pElement, LLMatrix4& transform )
+{
+	if ( pElement )
+	{	
+		daeSIDResolver resolver( pElement, "./transform" );
+		domMatrix* pMatrix = daeSafeCast<domMatrix>( resolver.getElement() );
+		//We are only extracting out the translational component atm
+		LLMatrix4 workingTransform;
+		if ( pMatrix )
+		{
+			domFloat4x4 domArray = pMatrix->getValue();									
+			for ( int i = 0; i < 4; i++ )
+			{
+				for( int j = 0; j < 4; j++ )
+				{
+					workingTransform.mMatrix[i][j] = domArray[i + j*4];
+				}
+			}
+			LLVector3 trans = workingTransform.getTranslation();
+			transform.setTranslation( trans );	
+		}
+	}
+	else
+	{
+		LL_WARNS()<<"Element is nonexistent - empty/unsupported node."<<LL_ENDL;
+	}
+}
+//-----------------------------------------------------------------------------
+// processJointNode()
+//-----------------------------------------------------------------------------
+void LLDAELoader::processJointNode( domNode* pNode, JointTransformMap& jointTransforms )
+{
+	if (pNode->getName() == NULL)
+	{
+		LL_WARNS() << "nameless node, can't process" << LL_ENDL;
+		return;
+	}
+
+	//LL_WARNS()<<"ProcessJointNode# Node:" <<pNode->getName()<<LL_ENDL;
+
+	//1. handle the incoming node - extract out translation via SID or element
+
+	LLMatrix4 workingTransform;
+
+	//Pull out the translate id and store it in the jointTranslations map
+	daeSIDResolver jointResolverA( pNode, "./translate" );
+	domTranslate* pTranslateA = daeSafeCast<domTranslate>( jointResolverA.getElement() );
+	daeSIDResolver jointResolverB( pNode, "./location" );
+	domTranslate* pTranslateB = daeSafeCast<domTranslate>( jointResolverB.getElement() );
+
+	//Translation via SID was successful
+	if ( pTranslateA )
+	{
+		extractTranslation( pTranslateA, workingTransform );
+	}
+	else
+	if ( pTranslateB )
+	{
+		extractTranslation( pTranslateB, workingTransform );
+	}
+	else
+	{
+		//Translation via child from element
+		daeElement* pTranslateElement = getChildFromElement( pNode, "translate" );
+		if ( !pTranslateElement || pTranslateElement->typeID() != domTranslate::ID() )
+		{
+			//LL_WARNS()<< "The found element is not a translate node" <<LL_ENDL;
+			daeSIDResolver jointResolver( pNode, "./matrix" );
+			domMatrix* pMatrix = daeSafeCast<domMatrix>( jointResolver.getElement() );
+			if ( pMatrix )
+			{
+				//LL_INFOS()<<"A matrix SID was however found!"<<LL_ENDL;
+				domFloat4x4 domArray = pMatrix->getValue();									
+				for ( int i = 0; i < 4; i++ )
+				{
+					for( int j = 0; j < 4; j++ )
+					{
+						workingTransform.mMatrix[i][j] = domArray[i + j*4];
+					}
+				}
+			}
+			else
+			{
+				LL_WARNS()<< "The found element is not translate or matrix node - most likely a corrupt export!" <<LL_ENDL;
+			}
+		}
+		else
+		{
+			extractTranslationViaElement( pTranslateElement, workingTransform );
+		}
+	}
+
+	//Store the working transform relative to the nodes name.
+	jointTransforms[ pNode->getName() ] = workingTransform;
+
+	//2. handle the nodes children
+
+	//Gather and handle the incoming nodes children
+	daeTArray< daeSmartRef<daeElement> > childOfChild = pNode->getChildren();
+	S32 childOfChildCount = childOfChild.getCount();
+
+	for (S32 i = 0; i < childOfChildCount; ++i)
+	{
+		domNode* pChildNode = daeSafeCast<domNode>( childOfChild[i] );
+		if ( pChildNode )
+		{
+			processJointNode( pChildNode, jointTransforms );
+		}
+	}
+}
+//-----------------------------------------------------------------------------
+// getChildFromElement()
+//-----------------------------------------------------------------------------
+daeElement* LLDAELoader::getChildFromElement( daeElement* pElement, std::string const & name )
+{
+    daeElement* pChildOfElement = pElement->getChild( name.c_str() );
+	if ( pChildOfElement )
+	{
+		return pChildOfElement;
+	}
+	LL_WARNS()<< "Could not find a child [" << name << "] for the element: \"" << pElement->getAttribute("id") << "\"" << LL_ENDL;
+    return NULL;
+}
+
+void LLDAELoader::processElement( daeElement* element, bool& badElement, DAE* dae )
+{
+	LLMatrix4 saved_transform;
+	bool pushed_mat = false;
+
+	domNode* node = daeSafeCast<domNode>(element);
+	if (node)
+	{
+		pushed_mat = true;
+		saved_transform = mTransform;
+	}
+
+	domTranslate* translate = daeSafeCast<domTranslate>(element);
+	if (translate)
+	{
+		domFloat3 dom_value = translate->getValue();
+
+		LLMatrix4 translation;
+		translation.setTranslation(LLVector3(dom_value[0], dom_value[1], dom_value[2]));
+
+		translation *= mTransform;
+		mTransform = translation;
+		mTransform.condition();
+	}
+
+	domRotate* rotate = daeSafeCast<domRotate>(element);
+	if (rotate)
+	{
+		domFloat4 dom_value = rotate->getValue();
+
+		LLMatrix4 rotation;
+		rotation.initRotTrans(dom_value[3] * DEG_TO_RAD, LLVector3(dom_value[0], dom_value[1], dom_value[2]), LLVector3(0, 0, 0));
+
+		rotation *= mTransform;
+		mTransform = rotation;
+		mTransform.condition();
+	}
+
+	domScale* scale = daeSafeCast<domScale>(element);
+	if (scale)
+	{
+		domFloat3 dom_value = scale->getValue();
+
+
+		LLVector3 scale_vector = LLVector3(dom_value[0], dom_value[1], dom_value[2]);
+		scale_vector.abs(); // Set all values positive, since we don't currently support mirrored meshes
+		LLMatrix4 scaling;
+		scaling.initScale(scale_vector);
+
+		scaling *= mTransform;
+		mTransform = scaling;
+		mTransform.condition();
+	}
+
+	domMatrix* matrix = daeSafeCast<domMatrix>(element);
+	if (matrix)
+	{
+		domFloat4x4 dom_value = matrix->getValue();
+
+		LLMatrix4 matrix_transform;
+
+		for (int i = 0; i < 4; i++)
+		{
+			for(int j = 0; j < 4; j++)
+			{
+				matrix_transform.mMatrix[i][j] = dom_value[i + j*4];
+			}
+		}
+
+		matrix_transform *= mTransform;
+		mTransform = matrix_transform;
+		mTransform.condition();
+	}
+
+	domInstance_geometry* instance_geo = daeSafeCast<domInstance_geometry>(element);
+	if (instance_geo)
+	{
+		domGeometry* geo = daeSafeCast<domGeometry>(instance_geo->getUrl().getElement());
+		if (geo)
+		{
+			domMesh* mesh = daeSafeCast<domMesh>(geo->getDescendant(daeElement::matchType(domMesh::ID())));
+			if (mesh)
+			{
+
+				std::vector< LLPointer< LLModel > >::iterator i = mModelsMap[mesh].begin();				
+				while (i != mModelsMap[mesh].end())
+				{
+					LLModel* model = *i;
+
+					LLMatrix4 transformation = mTransform;
+				
+					if (mTransform.determinant() < 0)
+					{ //negative scales are not supported
+						LL_INFOS() << "Negative scale detected, unsupported transform.  domInstance_geometry: " << getElementLabel(instance_geo) << LL_ENDL;
+						badElement = true;
+					}
+
+					LLModelLoader::material_map materials = getMaterials(model, instance_geo, dae);
+
+					// adjust the transformation to compensate for mesh normalization
+					LLVector3 mesh_scale_vector;
+					LLVector3 mesh_translation_vector;
+					model->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector);
+
+					LLMatrix4 mesh_translation;
+					mesh_translation.setTranslation(mesh_translation_vector);
+					mesh_translation *= transformation;
+					transformation = mesh_translation;
+					
+					LLMatrix4 mesh_scale;
+					mesh_scale.initScale(mesh_scale_vector);
+					mesh_scale *= transformation;
+					transformation = mesh_scale;
+
+					if (transformation.determinant() < 0)
+					{ //negative scales are not supported
+						LL_INFOS() << "Negative scale detected, unsupported post-normalization transform.  domInstance_geometry: " << getElementLabel(instance_geo) << LL_ENDL;
+						badElement = true;
+					}
+
+					std::string label;
+					
+					if (model->mLabel.empty())
+					{
+						label = getLodlessLabel(instance_geo);
+
+						llassert(!label.empty());
+
+						if (model->mSubmodelID)
+						{
+							label += (char)((int)'a' + model->mSubmodelID);
+						}
+
+						model->mLabel = label + lod_suffix[mLod];
+					}
+					else
+					{
+						// Don't change model's name if possible, it will play havoc with scenes that already use said model.
+						size_t ext_pos = getSuffixPosition(model->mLabel);
+						if (ext_pos != -1)
+						{
+							label = model->mLabel.substr(0, ext_pos);
+						}
+						else
+						{
+							label = model->mLabel;
+						}
+					}
+
+					mScene[transformation].push_back(LLModelInstance(model, label, transformation, materials));
+					stretch_extents(model, transformation, mExtents[0], mExtents[1], mFirstTransform);
+					i++;
+				}
+			}
+		}
+		else 
+		{
+			LL_INFOS()<<"Unable to resolve geometry URL."<<LL_ENDL;
+			badElement = true;			
+		}
+
+	}	
+
+	domInstance_node* instance_node = daeSafeCast<domInstance_node>(element);
+	if (instance_node)
+	{
+		daeElement* instance = instance_node->getUrl().getElement();
+		if (instance)
+		{
+			processElement(instance,badElement, dae);
+		}
+	}
+
+	//process children
+	daeTArray< daeSmartRef<daeElement> > children = element->getChildren();
+	int childCount = children.getCount();
+	for (S32 i = 0; i < childCount; i++)
+	{
+		processElement(children[i],badElement, dae);
+	}
+
+	if (pushed_mat)
+	{ //this element was a node, restore transform before processiing siblings
+		mTransform = saved_transform;
+	}
+}
+
+std::map<std::string, LLImportMaterial> LLDAELoader::getMaterials(LLModel* model, domInstance_geometry* instance_geo, DAE* dae)
+{
+	std::map<std::string, LLImportMaterial> materials;
+	for (int i = 0; i < model->mMaterialList.size(); i++)
+	{
+		LLImportMaterial import_material;
+
+		domInstance_material* instance_mat = NULL;
+
+		domBind_material::domTechnique_common* technique =
+		daeSafeCast<domBind_material::domTechnique_common>(instance_geo->getDescendant(daeElement::matchType(domBind_material::domTechnique_common::ID())));
+
+		if (technique)
+		{
+			daeTArray< daeSmartRef<domInstance_material> > inst_materials = technique->getChildrenByType<domInstance_material>();
+			for (int j = 0; j < inst_materials.getCount(); j++)
+			{
+				std::string symbol(inst_materials[j]->getSymbol());
+
+				if (symbol == model->mMaterialList[i]) // found the binding
+				{
+					instance_mat = inst_materials[j];
+					break;
+				}
+			}
+		}
+
+		if (instance_mat)
+		{
+			domMaterial* material = daeSafeCast<domMaterial>(instance_mat->getTarget().getElement());
+			if (material)
+			{
+				domInstance_effect* instance_effect =
+				daeSafeCast<domInstance_effect>(material->getDescendant(daeElement::matchType(domInstance_effect::ID())));
+				if (instance_effect)
+				{
+					domEffect* effect = daeSafeCast<domEffect>(instance_effect->getUrl().getElement());
+					if (effect)
+					{
+						domProfile_COMMON* profile =
+						daeSafeCast<domProfile_COMMON>(effect->getDescendant(daeElement::matchType(domProfile_COMMON::ID())));
+						if (profile)
+						{
+							import_material = profileToMaterial(profile, dae);
+						}
+					}
+				}
+			}
+		}
+
+		import_material.mBinding = model->mMaterialList[i];
+		materials[model->mMaterialList[i]] = import_material;
+	}
+
+	return materials;
+}
+
+LLImportMaterial LLDAELoader::profileToMaterial(domProfile_COMMON* material, DAE* dae)
+{
+	LLImportMaterial mat;
+	mat.mFullbright = FALSE;
+
+	daeElement* diffuse = material->getDescendant("diffuse");
+	if (diffuse)
+	{
+		domCommon_color_or_texture_type_complexType::domTexture* texture =
+		daeSafeCast<domCommon_color_or_texture_type_complexType::domTexture>(diffuse->getDescendant("texture"));
+		if (texture)
+		{
+			domCommon_newparam_type_Array newparams = material->getNewparam_array();
+			if (newparams.getCount())
+			{
+
+				for (S32 i = 0; i < newparams.getCount(); i++)
+				{
+					domFx_surface_common* surface = newparams[i]->getSurface();
+					if (surface)
+					{
+						domFx_surface_init_common* init = surface->getFx_surface_init_common();
+						if (init)
+						{
+							domFx_surface_init_from_common_Array init_from = init->getInit_from_array();
+
+							if (init_from.getCount() > i)
+							{
+								domImage* image = daeSafeCast<domImage>(init_from[i]->getValue().getElement());
+								if (image)
+								{
+									// we only support init_from now - embedded data will come later
+									domImage::domInit_from* init = image->getInit_from();
+									if (init)
+									{									
+										mat.mDiffuseMapFilename = cdom::uriToNativePath(init->getValue().str());
+										mat.mDiffuseMapLabel = getElementLabel(material);
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+			else if (texture->getTexture())
+			{
+				domImage* image = NULL;
+				dae->getDatabase()->getElement((daeElement**) &image, 0, texture->getTexture(), COLLADA_TYPE_IMAGE);
+				if (image)
+				{
+					// we only support init_from now - embedded data will come later
+					domImage::domInit_from* init = image->getInit_from();
+					if (init)
+					{
+						std::string image_path_value = cdom::uriToNativePath(init->getValue().str());
+
+#if LL_WINDOWS
+						// Work-around DOM tendency to resort to UNC names which are only confusing for downstream...
+						//
+						std::string::iterator i = image_path_value.begin();
+						while (*i == '\\')
+							i++;
+						mat.mDiffuseMapFilename.assign(i, image_path_value.end());
+#else
+						mat.mDiffuseMapFilename = image_path_value;
+#endif
+						mat.mDiffuseMapLabel = getElementLabel(material);
+					}
+				}
+			}
+		}
+
+		domCommon_color_or_texture_type_complexType::domColor* color =
+		daeSafeCast<domCommon_color_or_texture_type_complexType::domColor>(diffuse->getDescendant("color"));
+		if (color)
+		{
+			domFx_color_common domfx_color = color->getValue();
+			LLColor4 value = LLColor4(domfx_color[0], domfx_color[1], domfx_color[2], domfx_color[3]);
+			mat.mDiffuseColor = value;
+		}
+	}
+
+	daeElement* emission = material->getDescendant("emission");
+	if (emission)
+	{
+		LLColor4 emission_color = getDaeColor(emission);
+		if (((emission_color[0] + emission_color[1] + emission_color[2]) / 3.0) > 0.25)
+		{
+			mat.mFullbright = TRUE;
+		}
+	}
+
+	return mat;
+}
+
+// try to get a decent label for this element
+std::string LLDAELoader::getElementLabel(daeElement *element)
+{
+	// if we have a name attribute, use it
+	std::string name = element->getAttribute("name");
+	if (name.length())
+	{
+		return name;
+	}
+
+	// if we have an ID attribute, use it
+	if (element->getID())
+	{
+		return std::string(element->getID());
+	}
+
+	// if we have a parent, use it
+	daeElement* parent = element->getParent();
+	std::string index_string;
+	if (parent)
+	{
+		// retrieve index to distinguish items inside same parent
+		size_t ind = 0;
+		parent->getChildren().find(element, ind);
+		index_string = "_" + boost::lexical_cast<std::string>(ind);
+
+		// if parent has a name or ID, use it
+		std::string name = parent->getAttribute("name");
+		if (!name.length())
+		{
+			name = std::string(parent->getID());
+		}
+
+		if (name.length())
+		{
+			// make sure that index won't mix up with pre-named lod extensions
+			size_t ext_pos = getSuffixPosition(name);
+
+			if (ext_pos == -1)
+			{
+				return name + index_string;
+			}
+			else
+			{
+				return name.insert(ext_pos, index_string);
+			}
+		}
+	}
+
+	// try to use our type
+	daeString element_name = element->getElementName();
+	if (element_name)
+	{
+		return std::string(element_name) + index_string;
+	}
+
+	// if all else fails, use "object"
+	return std::string("object") + index_string;
+}
+
+// static
+size_t LLDAELoader::getSuffixPosition(std::string label)
+{
+	if ((label.find("_LOD") != -1) || (label.find("_PHYS") != -1))
+	{
+		return label.rfind('_');
+	}
+	return -1;
+}
+
+// static
+std::string LLDAELoader::getLodlessLabel(daeElement *element)
+{
+	std::string label = getElementLabel(element);
+	size_t ext_pos = getSuffixPosition(label);
+	if (ext_pos != -1)
+	{
+		return label.substr(0, ext_pos);
+	}
+	return label;
+}
+
+LLColor4 LLDAELoader::getDaeColor(daeElement* element)
+{
+	LLColor4 value;
+	domCommon_color_or_texture_type_complexType::domColor* color =
+	daeSafeCast<domCommon_color_or_texture_type_complexType::domColor>(element->getDescendant("color"));
+	if (color)
+	{
+		domFx_color_common domfx_color = color->getValue();
+		value = LLColor4(domfx_color[0], domfx_color[1], domfx_color[2], domfx_color[3]);
+	}
+
+	return value;
+}
+
+bool LLDAELoader::addVolumeFacesFromDomMesh(LLModel* pModel,domMesh* mesh)
+{
+	LLModel::EModelStatus status = LLModel::NO_ERRORS;
+	domTriangles_Array& tris = mesh->getTriangles_array();
+
+	for (U32 i = 0; i < tris.getCount(); ++i)
+	{
+		domTrianglesRef& tri = tris.get(i);
+
+		status = load_face_from_dom_triangles(pModel->getVolumeFaces(), pModel->getMaterialList(), tri);
+		pModel->mStatus = status;
+		if(status != LLModel::NO_ERRORS)
+		{
+			pModel->ClearFacesAndMaterials();
+			return false;
+		}
+	}
+
+	domPolylist_Array& polys = mesh->getPolylist_array();
+	for (U32 i = 0; i < polys.getCount(); ++i)
+	{
+		domPolylistRef& poly = polys.get(i);
+		status = load_face_from_dom_polylist(pModel->getVolumeFaces(), pModel->getMaterialList(), poly);
+
+		if(status != LLModel::NO_ERRORS)
+		{
+			pModel->ClearFacesAndMaterials();
+			return false;
+		}
+	}
+
+	domPolygons_Array& polygons = mesh->getPolygons_array();
+
+	for (U32 i = 0; i < polygons.getCount(); ++i)
+	{
+		domPolygonsRef& poly = polygons.get(i);
+		status = load_face_from_dom_polygons(pModel->getVolumeFaces(), pModel->getMaterialList(), poly);
+
+		if(status != LLModel::NO_ERRORS)
+		{
+			pModel->ClearFacesAndMaterials();
+			return false;
+		}
+	}
+
+	return (status == LLModel::NO_ERRORS);
+}
+
+//static 
+LLModel* LLDAELoader::loadModelFromDomMesh(domMesh *mesh)
+{
+	LLVolumeParams volume_params;
+	volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
+	LLModel* ret = new LLModel(volume_params, 0.f); 
+	createVolumeFacesFromDomMesh(ret, mesh);
+    if (ret->mLabel.empty())
+    {
+	    ret->mLabel = getElementLabel(mesh);
+    }
+    return ret;
+}
+
+//static diff version supports creating multiple models when material counts spill
+// over the 8 face server-side limit
+//
+bool LLDAELoader::loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& models_out, U32 submodel_limit)
+{
+
+	LLVolumeParams volume_params;
+	volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
+
+	models_out.clear();
+
+	LLModel* ret = new LLModel(volume_params, 0.f);
+
+	std::string model_name = getLodlessLabel(mesh);
+	ret->mLabel = model_name + lod_suffix[mLod];
+
+	llassert(!ret->mLabel.empty());
+
+	// Like a monkey, ready to be shot into space
+	//
+	ret->ClearFacesAndMaterials();
+
+	// Get the whole set of volume faces
+	//
+	addVolumeFacesFromDomMesh(ret, mesh);
+
+	U32 volume_faces = ret->getNumVolumeFaces();
+
+	// Side-steps all manner of issues when splitting models
+	// and matching lower LOD materials to base models
+	//
+	ret->sortVolumeFacesByMaterialName();
+
+	bool normalized = false;
+
+    int submodelID = 0;
+
+	// remove all faces that definitely won't fit into one model and submodel limit
+	U32 face_limit = (submodel_limit + 1) * LL_SCULPT_MESH_MAX_FACES;
+	if (face_limit < volume_faces)
+	{
+		ret->setNumVolumeFaces(face_limit);
+	}
+
+	LLVolume::face_list_t remainder;
+	do 
+	{
+		// Insure we do this once with the whole gang and not per-model
+		//
+		if (!normalized && !mNoNormalize)
+		{			
+			normalized = true;
+			ret->normalizeVolumeFaces();
+		}
+
+		ret->trimVolumeFacesToSize(LL_SCULPT_MESH_MAX_FACES, &remainder);
+
+		if (!mNoOptimize)
+		{
+			ret->optimizeVolumeFaces();
+		}
+
+		volume_faces = remainder.size();
+
+		models_out.push_back(ret);
+
+		// If we have left-over volume faces, create another model
+		// to absorb them...
+		//
+		if (volume_faces)
+		{
+			LLModel* next = new LLModel(volume_params, 0.f);
+			next->mSubmodelID = ++submodelID;
+			next->mLabel = model_name + (char)((int)'a' + next->mSubmodelID) + lod_suffix[mLod];
+			next->getVolumeFaces() = remainder;
+			next->mNormalizedScale = ret->mNormalizedScale;
+			next->mNormalizedTranslation = ret->mNormalizedTranslation;
+			if ( ret->mMaterialList.size() > LL_SCULPT_MESH_MAX_FACES)
+			{
+				next->mMaterialList.assign(ret->mMaterialList.begin() + LL_SCULPT_MESH_MAX_FACES, ret->mMaterialList.end());
+			}
+			ret = next;
+		}
+
+		remainder.clear();
+
+	} while (volume_faces);	
+
+	return true;
+}
+
+bool LLDAELoader::createVolumeFacesFromDomMesh(LLModel* pModel, domMesh* mesh)
+{
+	if (mesh)
+	{
+		pModel->ClearFacesAndMaterials();
+
+		addVolumeFacesFromDomMesh(pModel, mesh);
+
+		if (pModel->getNumVolumeFaces() > 0)
+		{
+			pModel->normalizeVolumeFaces();
+			pModel->optimizeVolumeFaces();
+
+			if (pModel->getNumVolumeFaces() > 0)
+			{
+				return true;
+			}
+		}
+	}
+	else
+	{	
+		LL_WARNS() << "no mesh found" << LL_ENDL;
+	}
+
+	return false;
+}
diff --git a/indra/llprimitive/lldaeloader.h b/indra/llprimitive/lldaeloader.h
new file mode 100644
index 0000000000000000000000000000000000000000..7d91a6063b4cad0e4a2ed78443ce288012f3ee3d
--- /dev/null
+++ b/indra/llprimitive/lldaeloader.h
@@ -0,0 +1,107 @@
+/**
+ * @file lldaeloader.h
+ * @brief LLDAELoader class definition
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2013, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLDAELOADER_H
+#define LL_LLDAELOADER_H
+
+#include "llmodelloader.h"
+
+class DAE;
+class daeElement;
+class domProfile_COMMON;
+class domInstance_geometry;
+class domNode;
+class domTranslate;
+class domController;
+class domSkin;
+class domMesh;
+
+class LLDAELoader : public LLModelLoader
+{
+public:
+	typedef std::map<std::string, LLImportMaterial>							material_map;
+	typedef std::map<daeElement*, std::vector<LLPointer<LLModel> > >	dae_model_map;
+	dae_model_map	mModelsMap;
+
+	LLDAELoader(
+		std::string									filename,
+		S32											lod, 
+		LLModelLoader::load_callback_t		load_cb,
+		LLModelLoader::joint_lookup_func_t	joint_lookup_func,
+		LLModelLoader::texture_load_func_t	texture_load_func,
+		LLModelLoader::state_callback_t		state_cb,
+		void*											opaque_userdata,
+		JointTransformMap&						jointMap,
+		JointSet&									jointsFromNodes,
+		U32									modelLimit);
+	virtual ~LLDAELoader() ;
+
+	virtual bool OpenFile(const std::string& filename);
+
+protected:
+
+	void processElement(daeElement* element, bool& badElement, DAE* dae);
+	void processDomModel(LLModel* model, DAE* dae, daeElement* pRoot, domMesh* mesh, domSkin* skin);
+
+	material_map getMaterials(LLModel* model, domInstance_geometry* instance_geo, DAE* dae);
+	LLImportMaterial profileToMaterial(domProfile_COMMON* material, DAE* dae);	
+	LLColor4 getDaeColor(daeElement* element);
+	
+	daeElement* getChildFromElement( daeElement* pElement, std::string const & name );
+	
+	bool isNodeAJoint( domNode* pNode );
+	void processJointNode( domNode* pNode, std::map<std::string,LLMatrix4>& jointTransforms );
+	void extractTranslation( domTranslate* pTranslate, LLMatrix4& transform );
+	void extractTranslationViaElement( daeElement* pTranslateElement, LLMatrix4& transform );
+	void extractTranslationViaSID( daeElement* pElement, LLMatrix4& transform );
+	void buildJointToNodeMappingFromScene( daeElement* pRoot );
+	void processJointToNodeMapping( domNode* pNode );
+	void processChildJoints( domNode* pParentNode );
+
+	bool verifyCount( int expected, int result );
+
+	//Verify that a controller matches vertex counts
+	bool verifyController( domController* pController );
+
+	static bool addVolumeFacesFromDomMesh(LLModel* model, domMesh* mesh);
+	static bool createVolumeFacesFromDomMesh(LLModel* model, domMesh *mesh);
+
+	static LLModel* loadModelFromDomMesh(domMesh* mesh);
+
+	// Loads a mesh breaking it into one or more models as necessary
+	// to get around volume face limitations while retaining >8 materials
+	//
+	bool loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& models_out, U32 submodel_limit);
+
+	static std::string getElementLabel(daeElement *element);
+	static size_t getSuffixPosition(std::string label);
+	static std::string getLodlessLabel(daeElement *element);
+
+private:
+	U32 mGeneratedModelLimit; // Attempt to limit amount of generated submodels
+
+};
+#endif  // LL_LLDAELLOADER_H
diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp
index ed903146ef3352887d0886451df58f04877af99d..e494c55250145c74704fbc35690bb67b465c78e4 100755
--- a/indra/llprimitive/llmodel.cpp
+++ b/indra/llprimitive/llmodel.cpp
@@ -31,867 +31,38 @@
 #include "llconvexdecomposition.h"
 #include "llsdserialize.h"
 #include "llvector4a.h"
-#if LL_MSVC
-#pragma warning (disable : 4263)
-#pragma warning (disable : 4264)
-#endif
-#include "dae.h"
-#include "dae/daeErrorHandler.h"
-#include "dom/domConstants.h"
-#include "dom/domMesh.h"
-#if LL_MSVC
-#pragma warning (default : 4263)
-#pragma warning (default : 4264)
-#endif
-
-#ifdef LL_USESYSTEMLIBS
-# include <zlib.h>
-#else
-# include "zlib/zlib.h"
-#endif
-
-
-
-std::string model_names[] =
-{
-	"lowest_lod",
-	"low_lod",
-	"medium_lod",
-	"high_lod",
-	"physics_mesh"
-};
-
-const int MODEL_NAMES_LENGTH = sizeof(model_names) / sizeof(std::string);
-
-LLModel::LLModel(LLVolumeParams& params, F32 detail)
-	: LLVolume(params, detail), mNormalizedScale(1,1,1), mNormalizedTranslation(0,0,0)
-	, mPelvisOffset( 0.0f ), mStatus(NO_ERRORS)
-{
-	mDecompID = -1;
-	mLocalID = -1;
-}
-
-LLModel::~LLModel()
-{
-	if (mDecompID >= 0)
-	{
-		LLConvexDecomposition::getInstance()->deleteDecomposition(mDecompID);
-	}
-}
-
-
-bool get_dom_sources(const domInputLocalOffset_Array& inputs, S32& pos_offset, S32& tc_offset, S32& norm_offset, S32 &idx_stride,
-					 domSource* &pos_source, domSource* &tc_source, domSource* &norm_source)
-{
-	idx_stride = 0;
-
-	for (U32 j = 0; j < inputs.getCount(); ++j)
-	{
-		idx_stride = llmax((S32) inputs[j]->getOffset(), idx_stride);
-
-		if (strcmp(COMMON_PROFILE_INPUT_VERTEX, inputs[j]->getSemantic()) == 0)
-		{ //found vertex array
-			const domURIFragmentType& uri = inputs[j]->getSource();
-			daeElementRef elem = uri.getElement();
-			domVertices* vertices = (domVertices*) elem.cast();
-			if ( !vertices )
-			{
-				return false;
-			}
-				
-			domInputLocal_Array& v_inp = vertices->getInput_array();
-			
-			
-			for (U32 k = 0; k < v_inp.getCount(); ++k)
-			{
-				if (strcmp(COMMON_PROFILE_INPUT_POSITION, v_inp[k]->getSemantic()) == 0)
-				{
-					pos_offset = inputs[j]->getOffset();
-
-					const domURIFragmentType& uri = v_inp[k]->getSource();
-					daeElementRef elem = uri.getElement();
-					pos_source = (domSource*) elem.cast();
-				}
-				
-				if (strcmp(COMMON_PROFILE_INPUT_NORMAL, v_inp[k]->getSemantic()) == 0)
-				{
-					norm_offset = inputs[j]->getOffset();
-
-					const domURIFragmentType& uri = v_inp[k]->getSource();
-					daeElementRef elem = uri.getElement();
-					norm_source = (domSource*) elem.cast();
-				}
-			}
-		}
-
-		if (strcmp(COMMON_PROFILE_INPUT_NORMAL, inputs[j]->getSemantic()) == 0)
-		{
-			//found normal array for this triangle list
-			norm_offset = inputs[j]->getOffset();
-			const domURIFragmentType& uri = inputs[j]->getSource();
-			daeElementRef elem = uri.getElement();
-			norm_source = (domSource*) elem.cast();
-		}
-		else if (strcmp(COMMON_PROFILE_INPUT_TEXCOORD, inputs[j]->getSemantic()) == 0)
-		{ //found texCoords
-			tc_offset = inputs[j]->getOffset();
-			const domURIFragmentType& uri = inputs[j]->getSource();
-			daeElementRef elem = uri.getElement();
-			tc_source = (domSource*) elem.cast();
-		}
-	}
-
-	idx_stride += 1;
-	
-	return true;
-}
-
-LLModel::EModelStatus load_face_from_dom_triangles(std::vector<LLVolumeFace>& face_list, std::vector<std::string>& materials, domTrianglesRef& tri)
-{
-	LLVolumeFace face;
-	std::vector<LLVolumeFace::VertexData> verts;
-	std::vector<U16> indices;
-	
-	const domInputLocalOffset_Array& inputs = tri->getInput_array();
-
-	S32 pos_offset = -1;
-	S32 tc_offset = -1;
-	S32 norm_offset = -1;
-
-	domSource* pos_source = NULL;
-	domSource* tc_source = NULL;
-	domSource* norm_source = NULL;
-
-	S32 idx_stride = 0;
-
-	if ( !get_dom_sources(inputs, pos_offset, tc_offset, norm_offset, idx_stride, pos_source, tc_source, norm_source) || !pos_source )
-	{
-		LL_WARNS() << "Could not find dom sources for basic geo data; invalid model." << LL_ENDL;
-		return LLModel::BAD_ELEMENT;
-	}
-
-	if (!pos_source)
-	{
-		LL_WARNS() << "Unable to process mesh without position data; invalid model;  invalid model." << LL_ENDL;
-		return LLModel::BAD_ELEMENT;
-	}
-	
-	domPRef p = tri->getP();
-	domListOfUInts& idx = p->getValue();
-	
-	domListOfFloats  dummy ;
-	domListOfFloats& v = (pos_source && pos_source->getFloat_array()) ? pos_source->getFloat_array()->getValue() : dummy ;
-	domListOfFloats& tc = (tc_source && tc_source->getFloat_array()) ? tc_source->getFloat_array()->getValue() : dummy ;
-	domListOfFloats& n = (norm_source && norm_source->getFloat_array()) ? norm_source->getFloat_array()->getValue() : dummy ;
-
-	LLVolumeFace::VertexMapData::PointMap point_map;
-		
-	U32 index_count  = idx.getCount();
-	U32 vertex_count = (pos_source &&  pos_source->getFloat_array())	? v.getCount()	: 0;
-	U32 tc_count     = (tc_source && tc_source->getFloat_array()) 		? tc.getCount()	: 0;
-	U32 norm_count   = (norm_source && norm_source->getFloat_array()) 	? n.getCount(): 0;
-
-	if (vertex_count == 0)
-	{
-		LL_WARNS() << "Unable to process mesh with empty position array; invalid model." << LL_ENDL;
-		return LLModel::BAD_ELEMENT;
-	}
-
-	face.mExtents[0].set(v[0], v[1], v[2]);
-	face.mExtents[1].set(v[0], v[1], v[2]);
-	
-	for (U32 i = 0; i < index_count; i += idx_stride)
-	{
-		LLVolumeFace::VertexData cv;
-		if (pos_source)
-		{
-			// guard against model data specifiying out of range indices or verts
-			//
-			if (((i + pos_offset) > index_count)
-			 || ((idx[i+pos_offset]*3+2) > vertex_count))
-			{
-				LL_WARNS() << "Out of range index data; invalid model." << LL_ENDL;
-				return LLModel::BAD_ELEMENT;
-			}
-
-			cv.setPosition(LLVector4a(v[idx[i+pos_offset]*3+0],
-								v[idx[i+pos_offset]*3+1],
-								v[idx[i+pos_offset]*3+2]));
-
-			if (!cv.getPosition().isFinite3())
-			{
-				LL_WARNS() << "Nan positional data, invalid model." << LL_ENDL;
-				return LLModel::BAD_ELEMENT;
-			}
-		}
-
-		if (tc_source)
-		{
-			// guard against model data specifiying out of range indices or tcs
-			//
-
-			if (((i + tc_offset) > index_count)
-			 || ((idx[i+tc_offset]*2+1) > tc_count))
-			{
-				LL_WARNS() << "Out of range TC indices." << LL_ENDL;
-				return LLModel::BAD_ELEMENT;
-			}
-
-			cv.mTexCoord.setVec(tc[idx[i+tc_offset]*2+0],
-								tc[idx[i+tc_offset]*2+1]);
-
-			if (!cv.mTexCoord.isFinite())
-			{
-				LL_WARNS() << "Found NaN while loading tex coords from DAE-Model, invalid model." << LL_ENDL;
-				return LLModel::BAD_ELEMENT;
-			}
-		}
-		
-		if (norm_source)
-		{
-			// guard against model data specifiying out of range indices or norms
-			//
-			if (((i + norm_offset) > index_count)
-				|| ((idx[i+norm_offset]*3+2) > norm_count))
-			{
-				LL_WARNS() << "Found out of range norm indices, invalid model." << LL_ENDL;
-				return LLModel::BAD_ELEMENT;
-			}
-
-			cv.setNormal(LLVector4a(n[idx[i+norm_offset]*3+0],
-								n[idx[i+norm_offset]*3+1],
-								n[idx[i+norm_offset]*3+2]));
-
-			if (!cv.getNormal().isFinite3())
-			{
-				LL_WARNS() << "Found NaN while loading normals from DAE-Model, invalid model." << LL_ENDL;
-				return LLModel::BAD_ELEMENT;
-			}
-		}
-		
-		BOOL found = FALSE;
-			
-		LLVolumeFace::VertexMapData::PointMap::iterator point_iter;
-		point_iter = point_map.find(LLVector3(cv.getPosition().getF32ptr()));
-		
-		if (point_iter != point_map.end())
-		{
-			for (U32 j = 0; j < point_iter->second.size(); ++j)
-			{
-				if ((point_iter->second)[j] == cv)
-				{
-					found = TRUE;
-					indices.push_back((point_iter->second)[j].mIndex);
-					break;
-				}
-			}
-		}
-
-		if (!found)
-		{
-			update_min_max(face.mExtents[0], face.mExtents[1], cv.getPosition());
-			verts.push_back(cv);
-			if (verts.size() >= 65535)
-			{
-				//LL_ERRS() << "Attempted to write model exceeding 16-bit index buffer limitation." << LL_ENDL;
-				return LLModel::VERTEX_NUMBER_OVERFLOW ;
-			}
-			U16 index = (U16) (verts.size()-1);
-			indices.push_back(index);
-
-			LLVolumeFace::VertexMapData d;
-			d.setPosition(cv.getPosition());
-			d.mTexCoord = cv.mTexCoord;
-			d.setNormal(cv.getNormal());
-			d.mIndex = index;
-			if (point_iter != point_map.end())
-			{
-				point_iter->second.push_back(d);
-			}
-			else
-			{
-				point_map[LLVector3(d.getPosition().getF32ptr())].push_back(d);
-			}
-		}
-
-		if (indices.size()%3 == 0 && verts.size() >= 65532)
-		{
-			std::string material;
-
-			if (tri->getMaterial())
-			{
-				material = std::string(tri->getMaterial());
-			}
-
-			materials.push_back(material);
-			face_list.push_back(face);
-			face_list.rbegin()->fillFromLegacyData(verts, indices);
-			LLVolumeFace& new_face = *face_list.rbegin();
-			if (!norm_source)
-			{
-				//ll_aligned_free_16(new_face.mNormals);
-				new_face.mNormals = NULL;
-			}
-
-			if (!tc_source)
-			{
-				//ll_aligned_free_16(new_face.mTexCoords);
-				new_face.mTexCoords = NULL;
-			}
-
-			face = LLVolumeFace();
-			point_map.clear();
-		}
-	}
-
-	if (!verts.empty())
-	{
-		std::string material;
-
-		if (tri->getMaterial())
-		{
-			material = std::string(tri->getMaterial());
-		}
-		
-		materials.push_back(material);
-		face_list.push_back(face);
-
-		face_list.rbegin()->fillFromLegacyData(verts, indices);
-		LLVolumeFace& new_face = *face_list.rbegin();
-		if (!norm_source)
-		{
-			//ll_aligned_free_16(new_face.mNormals);
-			new_face.mNormals = NULL;
-		}
-
-		if (!tc_source)
-		{
-			//ll_aligned_free_16(new_face.mTexCoords);
-			new_face.mTexCoords = NULL;
-		}
-	}
-
-	return LLModel::NO_ERRORS ;
-}
-
-LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& face_list, std::vector<std::string>& materials, domPolylistRef& poly)
-{
-	domPRef p = poly->getP();
-	domListOfUInts& idx = p->getValue();
-
-	if (idx.getCount() == 0)
-	{
-		return LLModel::NO_ERRORS ;
-	}
-
-	const domInputLocalOffset_Array& inputs = poly->getInput_array();
-
-
-	domListOfUInts& vcount = poly->getVcount()->getValue();
-	
-	S32 pos_offset = -1;
-	S32 tc_offset = -1;
-	S32 norm_offset = -1;
-
-	domSource* pos_source = NULL;
-	domSource* tc_source = NULL;
-	domSource* norm_source = NULL;
-
-	S32 idx_stride = 0;
-
-	if (!get_dom_sources(inputs, pos_offset, tc_offset, norm_offset, idx_stride, pos_source, tc_source, norm_source))
-	{
-		LL_WARNS() << "Could not get DOM sources for basic geo data, invalid model." << LL_ENDL;
-		return LLModel::BAD_ELEMENT;
-	}
-
-	LLVolumeFace face;
-
-	std::vector<U16> indices;
-	std::vector<LLVolumeFace::VertexData> verts;
-
-	domListOfFloats v;
-	domListOfFloats tc;
-	domListOfFloats n;
-
-	if (pos_source)
-	{
-		v = pos_source->getFloat_array()->getValue();
-		face.mExtents[0].set(v[0], v[1], v[2]);
-		face.mExtents[1].set(v[0], v[1], v[2]);
-	}
 
-	if (tc_source)
-	{
-		tc = tc_source->getFloat_array()->getValue();
-	}
-
-	if (norm_source)
-	{
-		n = norm_source->getFloat_array()->getValue();
-	}
-	
-	LLVolumeFace::VertexMapData::PointMap point_map;
-
-	U32 index_count  = idx.getCount();
-	U32 vertex_count = pos_source  ? v.getCount()  : 0;
-	U32 tc_count     = tc_source   ? tc.getCount() : 0;
-	U32 norm_count   = norm_source ? n.getCount()  : 0;
-
-	U32 cur_idx = 0;
-	for (U32 i = 0; i < vcount.getCount(); ++i)
-	{ //for each polygon
-		U32 first_index = 0;
-		U32 last_index = 0;
-		for (U32 j = 0; j < vcount[i]; ++j)
-		{ //for each vertex
-
-			LLVolumeFace::VertexData cv;
-
-			if (pos_source)
-			{
-				// guard against model data specifiying out of range indices or verts
-				//
-				if (((cur_idx + pos_offset) > index_count)
-				 || ((idx[cur_idx+pos_offset]*3+2) > vertex_count))
-				{
-					LL_WARNS() << "Out of range position indices, invalid model." << LL_ENDL;
-					return LLModel::BAD_ELEMENT;
-				}
-
-				cv.getPosition().set(v[idx[cur_idx+pos_offset]*3+0],
-									v[idx[cur_idx+pos_offset]*3+1],
-									v[idx[cur_idx+pos_offset]*3+2]);
-
-				if (!cv.getPosition().isFinite3())
-				{
-					LL_WARNS() << "Found NaN while loading positions from DAE-Model, invalid model." << LL_ENDL;
-					return LLModel::BAD_ELEMENT;
-				}
-
-			}
-
-			if (tc_source)
-			{
-				// guard against model data specifiying out of range indices or tcs
-				//
-				if (((cur_idx + tc_offset) > index_count)
-				 || ((idx[cur_idx+tc_offset]*2+1) > tc_count))
-				{
-					LL_WARNS() << "Out of range TC indices, invalid model." << LL_ENDL;
-					return LLModel::BAD_ELEMENT;
-				}
-
-				cv.mTexCoord.setVec(tc[idx[cur_idx+tc_offset]*2+0],
-									tc[idx[cur_idx+tc_offset]*2+1]);
-
-				if (!cv.mTexCoord.isFinite())
-				{
-					LL_WARNS() << "Found NaN while loading tex coords from DAE-Model, invalid model." << LL_ENDL;
-					return LLModel::BAD_ELEMENT;
-				}
-			}
-			
-			if (norm_source)
-			{
-				// guard against model data specifiying out of range indices or norms
-				//
-				if (((cur_idx + norm_offset) > index_count)
-				 || ((idx[cur_idx+norm_offset]*3+2) > norm_count))
-				{
-					LL_WARNS() << "Out of range norm indices, invalid model." << LL_ENDL;
-					return LLModel::BAD_ELEMENT;
-				}
-
-				cv.getNormal().set(n[idx[cur_idx+norm_offset]*3+0],
-									n[idx[cur_idx+norm_offset]*3+1],
-									n[idx[cur_idx+norm_offset]*3+2]);
-
-				if (!cv.getNormal().isFinite3())
-				{
-					LL_WARNS() << "Found NaN while loading normals from DAE-Model, invalid model." << LL_ENDL;
-					return LLModel::BAD_ELEMENT;
-				}
-			}
-			
-			cur_idx += idx_stride;
-			
-			BOOL found = FALSE;
-				
-			LLVolumeFace::VertexMapData::PointMap::iterator point_iter;
-			LLVector3 pos3(cv.getPosition().getF32ptr());
-			point_iter = point_map.find(pos3);
-			
-			if (point_iter != point_map.end())
-			{
-				for (U32 k = 0; k < point_iter->second.size(); ++k)
-				{
-					if ((point_iter->second)[k] == cv)
-					{
-						found = TRUE;
-						U32 index = (point_iter->second)[k].mIndex;
-						if (j == 0)
-						{
-							first_index = index;
-						}
-						else if (j == 1)
-						{
-							last_index = index;
-						}
-						else
-						{
-							indices.push_back(first_index);
-							indices.push_back(last_index);
-							indices.push_back(index);
-							last_index = index;
-						}
-
-						break;
-					}
-				}
-			}
-
-			if (!found)
-			{
-				update_min_max(face.mExtents[0], face.mExtents[1], cv.getPosition());
-				verts.push_back(cv);
-				if (verts.size() >= 65535)
-				{
-					//LL_ERRS() << "Attempted to write model exceeding 16-bit index buffer limitation." << LL_ENDL;
-					return LLModel::VERTEX_NUMBER_OVERFLOW ;
-				}
-				U16 index = (U16) (verts.size()-1);
-			
-				if (j == 0)
-				{
-					first_index = index;
-				}
-				else if (j == 1)
-				{
-					last_index = index;
-				}
-				else
-				{
-					indices.push_back(first_index);
-					indices.push_back(last_index);
-					indices.push_back(index);
-					last_index = index;
-				}	
-
-				LLVolumeFace::VertexMapData d;
-				d.setPosition(cv.getPosition());
-				d.mTexCoord = cv.mTexCoord;
-				d.setNormal(cv.getNormal());
-				d.mIndex = index;
-				if (point_iter != point_map.end())
-				{
-					point_iter->second.push_back(d);
-				}
-				else
-				{
-					point_map[pos3].push_back(d);
-				}
-			}
-
-			if (indices.size()%3 == 0 && indices.size() >= 65532)
-			{
-				std::string material;
-
-				if (poly->getMaterial())
-				{
-					material = std::string(poly->getMaterial());
-				}
-
-				materials.push_back(material);
-				face_list.push_back(face);
-				face_list.rbegin()->fillFromLegacyData(verts, indices);
-				LLVolumeFace& new_face = *face_list.rbegin();
-				if (!norm_source)
-				{
-					//ll_aligned_free_16(new_face.mNormals);
-					new_face.mNormals = NULL;
-				}
-
-				if (!tc_source)
-				{
-					//ll_aligned_free_16(new_face.mTexCoords);
-					new_face.mTexCoords = NULL;
-				}
-
-				face = LLVolumeFace();
-				verts.clear();
-				indices.clear();
-				point_map.clear();
-			}
-		}
-	}
-
-	if (!verts.empty())
-	{
-		std::string material;
-
-		if (poly->getMaterial())
-		{
-			material = std::string(poly->getMaterial());
-		}
-	
-		materials.push_back(material);
-		face_list.push_back(face);
-		face_list.rbegin()->fillFromLegacyData(verts, indices);
-
-		LLVolumeFace& new_face = *face_list.rbegin();
-		if (!norm_source)
-		{
-			//ll_aligned_free_16(new_face.mNormals);
-			new_face.mNormals = NULL;
-		}
-
-		if (!tc_source)
-		{
-			//ll_aligned_free_16(new_face.mTexCoords);
-			new_face.mTexCoords = NULL;
-		}
-	}
-
-	return LLModel::NO_ERRORS ;
-}
-
-LLModel::EModelStatus load_face_from_dom_polygons(std::vector<LLVolumeFace>& face_list, std::vector<std::string>& materials, domPolygonsRef& poly)
-{
-	LLVolumeFace face;
-	std::vector<U16> indices;
-	std::vector<LLVolumeFace::VertexData> verts;
-
-	const domInputLocalOffset_Array& inputs = poly->getInput_array();
-
-	S32 v_offset = -1;
-	S32 n_offset = -1;
-	S32 t_offset = -1;
-
-	domListOfFloats* v = NULL;
-	domListOfFloats* n = NULL;
-	domListOfFloats* t = NULL;
-	
-	U32 stride = 0;
-	for (U32 i = 0; i < inputs.getCount(); ++i)
-	{
-		stride = llmax((U32) inputs[i]->getOffset()+1, stride);
-
-		if (strcmp(COMMON_PROFILE_INPUT_VERTEX, inputs[i]->getSemantic()) == 0)
-		{ //found vertex array
-			v_offset = inputs[i]->getOffset();
-
-			const domURIFragmentType& uri = inputs[i]->getSource();
-			daeElementRef elem = uri.getElement();
-			domVertices* vertices = (domVertices*) elem.cast();
-			if (!vertices)
-			{
-				LL_WARNS() << "Could not find vertex source, invalid model." << LL_ENDL;
-				return LLModel::BAD_ELEMENT;
-			}
-			domInputLocal_Array& v_inp = vertices->getInput_array();
-
-			for (U32 k = 0; k < v_inp.getCount(); ++k)
-			{
-				if (strcmp(COMMON_PROFILE_INPUT_POSITION, v_inp[k]->getSemantic()) == 0)
-				{
-					const domURIFragmentType& uri = v_inp[k]->getSource();
-					daeElementRef elem = uri.getElement();
-					domSource* src = (domSource*) elem.cast();
-					if (!src)
-					{
-						LL_WARNS() << "Could not find DOM source, invalid model." << LL_ENDL;
-						return LLModel::BAD_ELEMENT;
-					}
-					v = &(src->getFloat_array()->getValue());
-				}
-			}
-		}
-		else if (strcmp(COMMON_PROFILE_INPUT_NORMAL, inputs[i]->getSemantic()) == 0)
-		{
-			n_offset = inputs[i]->getOffset();
-			//found normal array for this triangle list
-			const domURIFragmentType& uri = inputs[i]->getSource();
-			daeElementRef elem = uri.getElement();
-			domSource* src = (domSource*) elem.cast();
-			if (!src)
-			{
-				LL_WARNS() << "Could not find DOM source, invalid model." << LL_ENDL;
-				return LLModel::BAD_ELEMENT;
-			}
-			n = &(src->getFloat_array()->getValue());
-		}
-		else if (strcmp(COMMON_PROFILE_INPUT_TEXCOORD, inputs[i]->getSemantic()) == 0 && inputs[i]->getSet() == 0)
-		{ //found texCoords
-			t_offset = inputs[i]->getOffset();
-			const domURIFragmentType& uri = inputs[i]->getSource();
-			daeElementRef elem = uri.getElement();
-			domSource* src = (domSource*) elem.cast();
-			if (!src)
-			{
-				LL_WARNS() << "Could not find DOM source, invalid model." << LL_ENDL;
-				return LLModel::BAD_ELEMENT;
-			}
-			t = &(src->getFloat_array()->getValue());
-		}
-	}
-
-	domP_Array& ps = poly->getP_array();
-
-	//make a triangle list in <verts>
-	for (U32 i = 0; i < ps.getCount(); ++i)
-	{ //for each polygon
-		domListOfUInts& idx = ps[i]->getValue();
-		for (U32 j = 0; j < idx.getCount()/stride; ++j)
-		{ //for each vertex
-			if (j > 2)
-			{
-				U32 size = verts.size();
-				LLVolumeFace::VertexData v0 = verts[size-3];
-				LLVolumeFace::VertexData v1 = verts[size-1];
-
-				verts.push_back(v0);
-				verts.push_back(v1);
-			}
-
-			LLVolumeFace::VertexData vert;
-
-
-			if (v)
-			{
-				U32 v_idx = idx[j*stride+v_offset]*3;
-				v_idx = llclamp(v_idx, (U32) 0, (U32) v->getCount());
-				vert.getPosition().set(v->get(v_idx),
-								v->get(v_idx+1),
-								v->get(v_idx+2));
-
-				if (!vert.getPosition().isFinite3())
-				{
-					LL_WARNS() << "Found NaN while loading position data from DAE-Model, invalid model." << LL_ENDL;
-					return LLModel::BAD_ELEMENT;
-				}
-			}
-			
-			//bounds check n and t lookups because some FBX to DAE converters
-			//use negative indices and empty arrays to indicate data does not exist
-			//for a particular channel
-			if (n && n->getCount() > 0)
-			{
-				U32 n_idx = idx[j*stride+n_offset]*3;
-				n_idx = llclamp(n_idx, (U32) 0, (U32) n->getCount());
-				vert.getNormal().set(n->get(n_idx),
-								n->get(n_idx+1),
-								n->get(n_idx+2));
-
-				if (!vert.getNormal().isFinite3())
-				{
-					LL_WARNS() << "Found NaN while loading normals from DAE-Model, invalid model." << LL_ENDL;
-					return LLModel::BAD_ELEMENT;
-				}
-			}
-			else
-			{
-				vert.getNormal().clear();
-			}
-
-			
-			if (t && t->getCount() > 0)
-			{
-				U32 t_idx = idx[j*stride+t_offset]*2;
-				t_idx = llclamp(t_idx, (U32) 0, (U32) t->getCount());
-				vert.mTexCoord.setVec(t->get(t_idx),
-								t->get(t_idx+1));								
-
-				if (!vert.mTexCoord.isFinite())
-				{
-					LL_WARNS() << "Found NaN while loading tex coords from DAE-Model, invalid model." << LL_ENDL;
-					return LLModel::BAD_ELEMENT;
-				}
-			}
-			else
-			{
-				vert.mTexCoord.clear();
-			}
-
-						
-			verts.push_back(vert);
-		}
-	}
-
-	if (verts.empty())
-	{
-		return LLModel::NO_ERRORS;
-	}
-
-	face.mExtents[0] = verts[0].getPosition();
-	face.mExtents[1] = verts[0].getPosition();
-	
-	//create a map of unique vertices to indices
-	std::map<LLVolumeFace::VertexData, U32> vert_idx;
-
-	U32 cur_idx = 0;
-	for (U32 i = 0; i < verts.size(); ++i)
-	{
-		std::map<LLVolumeFace::VertexData, U32>::iterator iter = vert_idx.find(verts[i]);
-		if (iter == vert_idx.end())
-		{
-			vert_idx[verts[i]] = cur_idx++;
-		}
-	}
-
-	//build vertex array from map
-	std::vector<LLVolumeFace::VertexData> new_verts;
-	new_verts.resize(vert_idx.size());
-
-	for (std::map<LLVolumeFace::VertexData, U32>::iterator iter = vert_idx.begin(); iter != vert_idx.end(); ++iter)
-	{
-		new_verts[iter->second] = iter->first;
-		update_min_max(face.mExtents[0], face.mExtents[1], iter->first.getPosition());
-	}
-
-	//build index array from map
-	indices.resize(verts.size());
-
-	for (U32 i = 0; i < verts.size(); ++i)
-	{
-		indices[i] = vert_idx[verts[i]];
-	}
-
-	// DEBUG just build an expanded triangle list
-	/*for (U32 i = 0; i < verts.size(); ++i)
-	{
-		indices.push_back((U16) i);
-		update_min_max(face.mExtents[0], face.mExtents[1], verts[i].getPosition());
-	}*/
-
-    if (!new_verts.empty())
-	{
-		std::string material;
+#ifdef LL_USESYSTEMLIBS
+# include <zlib.h>
+#else
+# include "zlib/zlib.h"
+#endif
 
-		if (poly->getMaterial())
-		{
-			material = std::string(poly->getMaterial());
-		}
+std::string model_names[] =
+{
+	"lowest_lod",
+	"low_lod",
+	"medium_lod",
+	"high_lod",
+	"physics_mesh"
+};
 
-		materials.push_back(material);
-		face_list.push_back(face);
-		face_list.rbegin()->fillFromLegacyData(new_verts, indices);
+const int MODEL_NAMES_LENGTH = sizeof(model_names) / sizeof(std::string);
 
-		LLVolumeFace& new_face = *face_list.rbegin();
-		if (!n)
-		{
-			//ll_aligned_free_16(new_face.mNormals);
-			new_face.mNormals = NULL;
-		}
+LLModel::LLModel(LLVolumeParams& params, F32 detail)
+	: LLVolume(params, detail), mNormalizedScale(1,1,1), mNormalizedTranslation(0,0,0)
+	, mPelvisOffset( 0.0f ), mStatus(NO_ERRORS), mSubmodelID(0)
+{
+	mDecompID = -1;
+	mLocalID = -1;
+}
 
-		if (!t)
-		{
-			//ll_aligned_free_16(new_face.mTexCoords);
-			new_face.mTexCoords = NULL;
-		}
+LLModel::~LLModel()
+{
+	if (mDecompID >= 0)
+	{
+		LLConvexDecomposition::getInstance()->deleteDecomposition(mDecompID);
 	}
-
-	return LLModel::NO_ERRORS ;
 }
 
 //static
@@ -903,115 +74,103 @@ std::string LLModel::getStatusString(U32 status)
 	{
 		if(status_strings[status] == std::string())
 		{
-			LL_ERRS() << "No valid status string for this status: " << (U32)status << LL_ENDL ;
+			//LL_ERRS() << "No valid status string for this status: " << (U32)status << LL_ENDL();
 		}
 		return status_strings[status] ;
 	}
 
-	LL_ERRS() << "Invalid model status: " << (U32)status << LL_ENDL ;
+	//LL_ERRS() << "Invalid model status: " << (U32)status << LL_ENDL();
 
 	return std::string() ;
 }
 
-void LLModel::addVolumeFacesFromDomMesh(domMesh* mesh)
+
+void LLModel::offsetMesh( const LLVector3& pivotPoint )
 {
-	domTriangles_Array& tris = mesh->getTriangles_array();
-		
-	for (U32 i = 0; i < tris.getCount(); ++i)
+	LLVector4a pivot( pivotPoint[VX], pivotPoint[VY], pivotPoint[VZ] );
+	
+	for (std::vector<LLVolumeFace>::iterator faceIt = mVolumeFaces.begin(); faceIt != mVolumeFaces.end(); )
 	{
-		domTrianglesRef& tri = tris.get(i);
-
-		mStatus = load_face_from_dom_triangles(mVolumeFaces, mMaterialList, tri);
+		std::vector<LLVolumeFace>:: iterator currentFaceIt = faceIt++;
+		LLVolumeFace& face = *currentFaceIt;
+		LLVector4a *pos = (LLVector4a*) face.mPositions;
 		
-		if(mStatus != NO_ERRORS)
+		for (U32 i=0; i<face.mNumVertices; ++i )
 		{
-			mVolumeFaces.clear() ;
-			mMaterialList.clear() ;
-			return ; //abort
+			pos[i].add( pivot );
 		}
 	}
+}
 
-	domPolylist_Array& polys = mesh->getPolylist_array();
-	for (U32 i = 0; i < polys.getCount(); ++i)
-	{
-		domPolylistRef& poly = polys.get(i);
-		mStatus = load_face_from_dom_polylist(mVolumeFaces, mMaterialList, poly);
-
-		if(mStatus != NO_ERRORS)
-		{
-			mVolumeFaces.clear() ;
-			mMaterialList.clear() ;
-			return ; //abort
-		}
-	}
-	
-	domPolygons_Array& polygons = mesh->getPolygons_array();
-	
-	for (U32 i = 0; i < polygons.getCount(); ++i)
+void LLModel::optimizeVolumeFaces()
+{
+	for (U32 i = 0; i < getNumVolumeFaces(); ++i)
 	{
-		domPolygonsRef& poly = polygons.get(i);
-		mStatus = load_face_from_dom_polygons(mVolumeFaces, mMaterialList, poly);
-
-		if(mStatus != NO_ERRORS)
-		{
-			mVolumeFaces.clear() ;
-			mMaterialList.clear() ;
-			return ; //abort
-		}
+		mVolumeFaces[i].optimize();
 	}
- 
 }
 
-BOOL LLModel::createVolumeFacesFromDomMesh(domMesh* mesh)
+struct MaterialBinding
+{
+	int				index;
+	std::string		matName;
+};
+
+struct MaterialSort
 {
-	if (mesh)
+	bool operator()(const MaterialBinding& lhs, const MaterialBinding& rhs)
 	{
-		mVolumeFaces.clear();
-		mMaterialList.clear();
+		return LLStringUtil::compareInsensitive(lhs.matName, rhs.matName) < 0;
+	}
+};
 
-		addVolumeFacesFromDomMesh(mesh);
-		
-		if (getNumVolumeFaces() > 0)
+void LLModel::sortVolumeFacesByMaterialName()
+{
+	std::vector<MaterialBinding> bindings;
+	bindings.resize(mVolumeFaces.size());
+
+	for (int i = 0; i < bindings.size(); i++)
+	{
+		bindings[i].index = i;
+		if(i < mMaterialList.size())
 		{
-			normalizeVolumeFaces();
-			optimizeVolumeFaces();
-			
-			if (getNumVolumeFaces() > 0)
-			{
-				return TRUE;
-			}
+			bindings[i].matName = mMaterialList[i];
 		}
 	}
-	else
-	{	
-		LL_WARNS() << "no mesh found" << LL_ENDL;
-	}
-	
-	return FALSE;
-}
+	std::sort(bindings.begin(), bindings.end(), MaterialSort());
+	std::vector< LLVolumeFace > new_faces;
 
-void LLModel::offsetMesh( const LLVector3& pivotPoint )
-{
-	LLVector4a pivot( pivotPoint[VX], pivotPoint[VY], pivotPoint[VZ] );
-	
-	for (std::vector<LLVolumeFace>::iterator faceIt = mVolumeFaces.begin(); faceIt != mVolumeFaces.end(); )
+	// remap the faces to be in the same order the mats now are...
+	//
+	new_faces.resize(bindings.size());
+	for (int i = 0; i < bindings.size(); i++)
 	{
-		std::vector<LLVolumeFace>:: iterator currentFaceIt = faceIt++;
-		LLVolumeFace& face = *currentFaceIt;
-		LLVector4a *pos = (LLVector4a*) face.mPositions;
-		
-		for (U32 i=0; i<face.mNumVertices; ++i )
+		new_faces[i] = mVolumeFaces[bindings[i].index];
+		if(i < mMaterialList.size())
 		{
-			pos[i].add( pivot );
+			mMaterialList[i] = bindings[i].matName;
 		}
 	}
+
+	mVolumeFaces = new_faces;	
 }
 
-void LLModel::optimizeVolumeFaces()
+void LLModel::trimVolumeFacesToSize(U32 new_count, LLVolume::face_list_t* remainder)
 {
-	for (U32 i = 0; i < getNumVolumeFaces(); ++i)
+	llassert(new_count <= LL_SCULPT_MESH_MAX_FACES);
+
+	if (new_count && (getNumVolumeFaces() > new_count))
 	{
-		mVolumeFaces[i].optimize();
+		// Copy out remaining volume faces for alternative handling, if provided
+		//
+		if (remainder)
+		{
+			(*remainder).assign(mVolumeFaces.begin() + new_count, mVolumeFaces.end());
+		}		
+
+		// Trim down to the final set of volume faces (now stuffed to the gills!)
+		//
+		mVolumeFaces.resize(new_count);
 	}
 }
 
@@ -1025,11 +184,6 @@ void LLModel::optimizeVolumeFaces()
 // within the unit cube.
 void LLModel::normalizeVolumeFaces()
 {
-
-	// ensure we don't have too many faces
-	if (mVolumeFaces.size() > LL_SCULPT_MESH_MAX_FACES)
-		mVolumeFaces.resize(LL_SCULPT_MESH_MAX_FACES);
-	
 	if (!mVolumeFaces.empty())
 	{
 		LLVector4a min, max;
@@ -1496,68 +650,10 @@ void LLModel::generateNormals(F32 angle_cutoff)
 	}
 }
 
-//static
-std::string LLModel::getElementLabel(daeElement *element)
-{ // try to get a decent label for this element
-	// if we have a name attribute, use it
-	std::string name = element->getAttribute("name");
-	if (name.length())
-	{
-		return name;
-	}
-
-	// if we have an ID attribute, use it
-	if (element->getID())
-	{
-		return std::string(element->getID());
-	}
-
-	// if we have a parent, use it
-	daeElement* parent = element->getParent();
-	if (parent)
-	{
-		// if parent has a name, use it
-		std::string name = parent->getAttribute("name");
-		if (name.length())
-		{
-			return name;
-		}
-
-		// if parent has an ID, use it
-		if (parent->getID())
-		{
-			return std::string(parent->getID());
-		}
-	}
-
-	// try to use our type
-	daeString element_name = element->getElementName();
-	if (element_name)
-	{
-		return std::string(element_name);
-	}
-
-	// if all else fails, use "object"
-	return std::string("object");
-}
-
-//static 
-LLModel* LLModel::loadModelFromDomMesh(domMesh *mesh)
-{
-	LLVolumeParams volume_params;
-	volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
-	LLModel* ret = new LLModel(volume_params, 0.f); 
-	ret->createVolumeFacesFromDomMesh(mesh);
-	ret->mLabel = getElementLabel(mesh);
-	return ret;
-}
 
 std::string LLModel::getName() const
 {
-	if (!mRequestedLabel.empty())
-		return mRequestedLabel;
-	else
-		return mLabel;
+    return mRequestedLabel.empty() ? mLabel : mRequestedLabel;
 }
 
 //static
@@ -1572,7 +668,8 @@ LLSD LLModel::writeModel(
 	BOOL upload_skin,
 	BOOL upload_joints,
 	BOOL nowrite,
-	BOOL as_slm)
+	BOOL as_slm,
+	int submodel_id)
 {
 	LLSD mdl;
 
@@ -1601,6 +698,14 @@ LLSD LLModel::writeModel(
 			model[LLModel::LOD_PHYSICS] = NULL;
 		}
 	}
+	else if (submodel_id)
+	{
+		const LLModel::Decomposition fake_decomp;
+		mdl["secondary"] = true;
+        mdl["submodel_id"] = submodel_id;
+		mdl["physics_convex"] = fake_decomp.asLLSD();
+		model[LLModel::LOD_PHYSICS] = NULL;
+	}
 
 	if (as_slm)
 	{ //save material list names
@@ -1612,7 +717,7 @@ LLSD LLModel::writeModel(
 
 	for (U32 idx = 0; idx < MODEL_NAMES_LENGTH; ++idx)
 	{
-		if (model[idx] && model[idx]->getNumVolumeFaces() > 0)
+		if (model[idx] && (model[idx]->getNumVolumeFaces() > 0) && model[idx]->getVolumeFace(0).mPositions != NULL)
 		{
 			LLVector3 min_pos = LLVector3(model[idx]->getVolumeFace(0).mPositions[0].getF32ptr());
 			LLVector3 max_pos = min_pos;
@@ -1845,6 +950,11 @@ LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, BOOL nowrite, BO
 		}
 	}
 
+        if (mdl.has("submodel_id"))
+        { //write out submodel id
+        header["submodel_id"] = (LLSD::Integer)mdl["submodel_id"];
+        }
+
 	std::string out[MODEL_NAMES_LENGTH];
 
 	for (S32 i = 0; i < MODEL_NAMES_LENGTH; i++)
@@ -2028,7 +1138,9 @@ bool LLModel::loadModel(std::istream& is)
 		}
 	}
 
-	static const std::string nm[] = 
+	mSubmodelID = header.has("submodel_id") ? header["submodel_id"].asInteger() : false;
+
+	static const std::string lod_name[] = 
 	{
 		"lowest_lod",
 		"low_lod",
@@ -2041,8 +1153,8 @@ bool LLModel::loadModel(std::istream& is)
 
 	S32 lod = llclamp((S32) mDetail, 0, MODEL_LODS);
 
-	if (header[nm[lod]]["offset"].asInteger() == -1 || 
-		header[nm[lod]]["size"].asInteger() == 0 )
+	if (header[lod_name[lod]]["offset"].asInteger() == -1 || 
+		header[lod_name[lod]]["size"].asInteger() == 0 )
 	{ //cannot load requested LOD
 		LL_WARNS() << "LoD data is invalid!" << LL_ENDL;
 		return false;
@@ -2051,23 +1163,23 @@ bool LLModel::loadModel(std::istream& is)
 	bool has_skin = header["skin"]["offset"].asInteger() >=0 &&
 					header["skin"]["size"].asInteger() > 0;
 
-	if (lod == LLModel::LOD_HIGH)
+	if ((lod == LLModel::LOD_HIGH) && !mSubmodelID)
 	{ //try to load skin info and decomp info
 		std::ios::pos_type cur_pos = is.tellg();
 		loadSkinInfo(header, is);
 		is.seekg(cur_pos);
 	}
 
-	if (lod == LLModel::LOD_HIGH || lod == LLModel::LOD_PHYSICS)
+	if ((lod == LLModel::LOD_HIGH || lod == LLModel::LOD_PHYSICS) && !mSubmodelID)
 	{
 		std::ios::pos_type cur_pos = is.tellg();
 		loadDecomposition(header, is);
 		is.seekg(cur_pos);
 	}
 
-	is.seekg(header[nm[lod]]["offset"].asInteger(), std::ios_base::cur);
+	is.seekg(header[lod_name[lod]]["offset"].asInteger(), std::ios_base::cur);
 
-	if (unpackVolumeFaces(is, header[nm[lod]]["size"].asInteger()))
+	if (unpackVolumeFaces(is, header[lod_name[lod]]["size"].asInteger()))
 	{
 		if (has_skin)
 		{ 
@@ -2133,8 +1245,10 @@ bool LLModel::isMaterialListSubset( LLModel* ref )
 				break;
 			}										
 		}
+
 		if (!foundRef)
 		{
+            LL_INFOS() << "Could not find material " << mMaterialList[src] << " in reference model " << ref->mLabel << LL_ENDL;
 			return false;
 		}
 	}
@@ -2185,41 +1299,42 @@ bool LLModel::matchMaterialOrder(LLModel* ref, int& refFaceCnt, int& modelFaceCn
 	for (U32 i = 0; i < mMaterialList.size(); i++)
 	{
 		index_map[ref->mMaterialList[i]] = i;
-		if (!reorder)
-		{ //if any material name does not match reference, we need to reorder
-			reorder = ref->mMaterialList[i] != mMaterialList[i];
-		}
+		//if any material name does not match reference, we need to reorder
+		reorder |= ref->mMaterialList[i] != mMaterialList[i];
 		base_mat.insert(ref->mMaterialList[i]);
 		cur_mat.insert(mMaterialList[i]);
 	}
 
 
-	if (reorder && 
-		base_mat == cur_mat) //don't reorder if material name sets don't match
+	if (reorder &&  (base_mat == cur_mat)) //don't reorder if material name sets don't match
 	{
 		std::vector<LLVolumeFace> new_face_list;
-		new_face_list.resize(mVolumeFaces.size());
+		new_face_list.resize(mMaterialList.size());
 
 		std::vector<std::string> new_material_list;
-		new_material_list.resize(mVolumeFaces.size());
+		new_material_list.resize(mMaterialList.size());
 
 		//rebuild face list so materials have the same order 
 		//as the reference model
 		for (U32 i = 0; i < mMaterialList.size(); ++i)
 		{ 
 			U32 ref_idx = index_map[mMaterialList[i]];
-			new_face_list[ref_idx] = mVolumeFaces[i];
 
+			if (i < mVolumeFaces.size())
+			{
+				new_face_list[ref_idx] = mVolumeFaces[i];
+			}
 			new_material_list[ref_idx] = mMaterialList[i];
 		}
 
 		llassert(new_material_list == ref->mMaterialList);
 		
 		mVolumeFaces = new_face_list;
-	}
 
-	//override material list with reference model ordering
-	mMaterialList = ref->mMaterialList;
+		//override material list with reference model ordering
+		mMaterialList = ref->mMaterialList;
+	}
+	
 	return true;
 }
 
@@ -2250,7 +1365,7 @@ bool LLModel::loadDecomposition(LLSD& header, std::istream& is)
 	S32 offset = header["physics_convex"]["offset"].asInteger();
 	S32 size = header["physics_convex"]["size"].asInteger();
 
-	if (offset >= 0 && size > 0)
+	if (offset >= 0 && size > 0 && !mSubmodelID)
 	{
 		is.seekg(offset, std::ios_base::cur);
 
@@ -2658,3 +1773,227 @@ void LLModel::Decomposition::merge(const LLModel::Decomposition* rhs)
 	}
 }
 
+bool ll_is_degenerate(const LLVector4a& a, const LLVector4a& b, const LLVector4a& c, F32 tolerance)
+{
+	// small area check
+	{
+		LLVector4a edge1; edge1.setSub( a, b );
+		LLVector4a edge2; edge2.setSub( a, c );
+		//////////////////////////////////////////////////////////////////////////
+		/// Linden Modified
+		//////////////////////////////////////////////////////////////////////////
+
+		// If no one edge is more than 10x longer than any other edge, we weaken
+		// the tolerance by a factor of 1e-4f.
+
+		LLVector4a edge3; edge3.setSub( c, b );
+		const F32 len1sq = edge1.dot3(edge1).getF32();
+		const F32 len2sq = edge2.dot3(edge2).getF32();
+		const F32 len3sq = edge3.dot3(edge3).getF32();
+		bool abOK = (len1sq <= 100.f * len2sq) && (len1sq <= 100.f * len3sq);
+		bool acOK = (len2sq <= 100.f * len1sq) && (len1sq <= 100.f * len3sq);
+		bool cbOK = (len3sq <= 100.f * len1sq) && (len1sq <= 100.f * len2sq);
+		if ( abOK && acOK && cbOK )
+		{
+			tolerance *= 1e-4f;
+		}
+
+		//////////////////////////////////////////////////////////////////////////
+		/// End Modified
+		//////////////////////////////////////////////////////////////////////////
+
+		LLVector4a cross; cross.setCross3( edge1, edge2 );
+
+		LLVector4a edge1b; edge1b.setSub( b, a );
+		LLVector4a edge2b; edge2b.setSub( b, c );
+		LLVector4a crossb; crossb.setCross3( edge1b, edge2b );
+
+		if ( ( cross.dot3(cross).getF32() < tolerance ) || ( crossb.dot3(crossb).getF32() < tolerance ))
+		{
+			return true;
+		}
+	}
+
+	// point triangle distance check
+	{
+		LLVector4a Q; Q.setSub(a, b);
+		LLVector4a R; R.setSub(c, b);
+
+		const F32 QQ = dot3fpu(Q, Q);
+		const F32 RR = dot3fpu(R, R);
+		const F32 QR = dot3fpu(R, Q);
+
+		volatile F32 QQRR = QQ * RR;
+		volatile F32 QRQR = QR * QR;
+		F32 Det = (QQRR - QRQR);
+
+		if( Det == 0.0f )
+		{
+			return true;
+		}
+	}
+
+	return false;
+}
+
+bool validate_face(const LLVolumeFace& face)
+{
+	for (U32 i = 0; i < face.mNumIndices; ++i)
+	{
+		if (face.mIndices[i] >= face.mNumVertices)
+		{
+			LL_WARNS() << "Face has invalid index." << LL_ENDL;
+			return false;
+		}
+	}
+
+	if (face.mNumIndices % 3 != 0 || face.mNumIndices == 0)
+	{
+		LL_WARNS() << "Face has invalid number of indices." << LL_ENDL;
+		return false;
+	}
+
+	/*const LLVector4a scale(0.5f);
+
+	for (U32 i = 0; i < face.mNumIndices; i+=3)
+	{
+		U16 idx1 = face.mIndices[i];
+		U16 idx2 = face.mIndices[i+1];
+		U16 idx3 = face.mIndices[i+2];
+
+		LLVector4a v1; v1.setMul(face.mPositions[idx1], scale);
+		LLVector4a v2; v2.setMul(face.mPositions[idx2], scale);
+		LLVector4a v3; v3.setMul(face.mPositions[idx3], scale);
+
+		if (ll_is_degenerate(v1,v2,v3))
+		{
+			llwarns << "Degenerate face found!" << LL_ENDL;
+			return false;
+		}
+	}*/
+
+	return true;
+}
+
+bool validate_model(const LLModel* mdl)
+{
+	if (mdl->getNumVolumeFaces() == 0)
+	{
+		LL_WARNS() << "Model has no faces!" << LL_ENDL;
+		return false;
+	}
+
+	for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i)
+	{
+		if (mdl->getVolumeFace(i).mNumVertices == 0)
+		{
+			LL_WARNS() << "Face has no vertices." << LL_ENDL;
+			return false;
+		}
+
+		if (mdl->getVolumeFace(i).mNumIndices == 0)
+		{
+			LL_WARNS() << "Face has no indices." << LL_ENDL;
+			return false;
+		}
+
+		if (!validate_face(mdl->getVolumeFace(i)))
+		{
+			return false;
+		}
+	}
+
+	return true;
+}
+
+LLModelInstance::LLModelInstance(LLSD& data)
+	: LLModelInstanceBase()
+{
+	mLocalMeshID = data["mesh_id"].asInteger();
+	mLabel = data["label"].asString();
+	mTransform.setValue(data["transform"]);
+
+	for (U32 i = 0; i < data["material"].size(); ++i)
+	{
+		LLImportMaterial mat(data["material"][i]);
+		mMaterial[mat.mBinding] = mat;
+	}
+}
+
+
+LLSD LLModelInstance::asLLSD()
+{	
+	LLSD ret;
+
+	ret["mesh_id"] = mModel->mLocalID;
+	ret["label"] = mLabel;
+	ret["transform"] = mTransform.getValue();
+
+	U32 i = 0;
+	for (std::map<std::string, LLImportMaterial>::iterator iter = mMaterial.begin(); iter != mMaterial.end(); ++iter)
+	{
+		ret["material"][i++] = iter->second.asLLSD();
+	}
+
+	return ret;
+}
+
+
+LLImportMaterial::~LLImportMaterial()
+{
+}
+
+LLImportMaterial::LLImportMaterial(LLSD& data)
+{
+	mDiffuseMapFilename = data["diffuse"]["filename"].asString();
+	mDiffuseMapLabel = data["diffuse"]["label"].asString();
+	mDiffuseColor.setValue(data["diffuse"]["color"]);
+	mFullbright = data["fullbright"].asBoolean();
+	mBinding = data["binding"].asString();
+}
+
+
+LLSD LLImportMaterial::asLLSD()
+{
+	LLSD ret;
+
+	ret["diffuse"]["filename"] = mDiffuseMapFilename;
+	ret["diffuse"]["label"] = mDiffuseMapLabel;
+	ret["diffuse"]["color"] = mDiffuseColor.getValue();
+	ret["fullbright"] = mFullbright;
+	ret["binding"] = mBinding;
+
+	return ret;
+}
+
+bool LLImportMaterial::operator<(const LLImportMaterial &rhs) const
+{
+
+	if (mDiffuseMapID != rhs.mDiffuseMapID)
+	{
+		return mDiffuseMapID < rhs.mDiffuseMapID;
+	}
+
+	if (mDiffuseMapFilename != rhs.mDiffuseMapFilename)
+	{
+		return mDiffuseMapFilename < rhs.mDiffuseMapFilename;
+	}
+
+	if (mDiffuseMapLabel != rhs.mDiffuseMapLabel)
+	{
+		return mDiffuseMapLabel < rhs.mDiffuseMapLabel;
+	}
+
+	if (mDiffuseColor != rhs.mDiffuseColor)
+	{
+		return mDiffuseColor < rhs.mDiffuseColor;
+	}
+
+	if (mBinding != rhs.mBinding)
+	{
+		return mBinding < rhs.mBinding;
+	}
+
+	return mFullbright < rhs.mFullbright;
+}
+
diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h
index aaafc55258d51fac20b3016b7dea88c9d470fcb7..ae602c09df55b8098f0e3479e1e85e49f33c5b99 100755
--- a/indra/llprimitive/llmodel.h
+++ b/indra/llprimitive/llmodel.h
@@ -31,6 +31,7 @@
 #include "llvolume.h"
 #include "v4math.h"
 #include "m4math.h"
+#include <queue>
 
 class daeElement;
 class domMesh;
@@ -138,15 +139,16 @@ public:
 		BOOL upload_skin,
 		BOOL upload_joints,
 		BOOL nowrite = FALSE,
-		BOOL as_slm = FALSE);
+		BOOL as_slm = FALSE,
+		int submodel_id = 0);
 
 	static LLSD writeModelToStream(
 		std::ostream& ostr,
 		LLSD& mdl,
 		BOOL nowrite = FALSE, BOOL as_slm = FALSE);
+	
+	void ClearFacesAndMaterials() { mVolumeFaces.clear(); mMaterialList.clear(); }
 
-	static LLModel* loadModelFromDomMesh(domMesh* mesh);
-	static std::string getElementLabel(daeElement* element);
 	std::string getName() const;
 	std::string getMetric() const {return mMetric;}
 	EModelStatus getStatus() const {return mStatus;}
@@ -169,20 +171,25 @@ public:
 
 	void addFace(const LLVolumeFace& face);
 
+	void sortVolumeFacesByMaterialName();
 	void normalizeVolumeFaces();
+	void trimVolumeFacesToSize(U32 new_count = LL_SCULPT_MESH_MAX_FACES, LLVolume::face_list_t* remainder = NULL);
 	void optimizeVolumeFaces();
 	void offsetMesh( const LLVector3& pivotPoint );
 	void getNormalizedScaleTranslation(LLVector3& scale_out, LLVector3& translation_out);
 	LLVector3 getTransformedCenter(const LLMatrix4& mat);
-
+	
 	//reorder face list based on mMaterialList in this and reference so 
 	//order matches that of reference (material ordering touchup)
 	bool matchMaterialOrder(LLModel* ref, int& refFaceCnt, int& modelFaceCnt );
 	bool isMaterialListSubset( LLModel* ref );
 	bool needToAddFaces( LLModel* ref, int& refFaceCnt, int& modelFaceCnt );
 	
-	
-	std::vector<std::string> mMaterialList;
+	typedef std::vector<std::string> material_list;
+
+	material_list mMaterialList;
+
+	material_list& getMaterialList() { return mMaterialList; }
 
 	//data used for skin weights
 	class JointWeight
@@ -275,9 +282,115 @@ public:
 	Decomposition mPhysics;
 
 	EModelStatus mStatus ;
+
+	int mSubmodelID;
+};
+
+typedef std::vector<LLPointer<LLModel> >	model_list;
+typedef std::queue<LLPointer<LLModel> >	model_queue;
+
+class LLModelMaterialBase
+{
+public:	
+	std::string mDiffuseMapFilename;
+	std::string mDiffuseMapLabel;
+	std::string mBinding;
+	LLColor4		mDiffuseColor;
+	bool			mFullbright;
+
+	LLModelMaterialBase() 
+		: mFullbright(false) 
+	{ 
+		mDiffuseColor.set(1,1,1,1);
+	}
+};
+
+class LLImportMaterial : public LLModelMaterialBase
+{
+public:
+    friend class LLMeshUploadThread;
+    friend class LLModelPreview;
+    
+    bool operator<(const LLImportMaterial &params) const;
+    
+    LLImportMaterial() : LLModelMaterialBase()
+    {
+        mDiffuseColor.set(1,1,1,1);
+    }
+    
+    LLImportMaterial(LLSD& data);
+    virtual ~LLImportMaterial();
+    
+    LLSD asLLSD();
+    
+    const LLUUID&	getDiffuseMap() const					{ return mDiffuseMapID;		}
+    void				setDiffuseMap(const LLUUID& texId)	{ mDiffuseMapID = texId;	}
+    
 protected:
-	void addVolumeFacesFromDomMesh(domMesh* mesh);
-	virtual BOOL createVolumeFacesFromDomMesh(domMesh *mesh);
+    
+    LLUUID		mDiffuseMapID;
+    void*			mOpaqueData;	// allow refs to viewer/platform-specific structs for each material
+    // currently only stores an LLPointer< LLViewerFetchedTexture > > to
+    // maintain refs to textures associated with each material for free
+    // ref counting.
 };
 
+typedef std::map<std::string, LLImportMaterial> material_map;
+
+class LLModelInstanceBase
+{
+public:
+	LLPointer<LLModel> mModel;
+	LLPointer<LLModel> mLOD[5];
+	LLUUID mMeshID;
+
+	LLMatrix4 mTransform;
+	material_map mMaterial;
+
+	LLModelInstanceBase(LLModel* model, LLMatrix4& transform, material_map& materials)
+		: mModel(model), mTransform(transform), mMaterial(materials)
+	{
+	}
+
+	LLModelInstanceBase()
+		: mModel(NULL)
+	{
+	}
+};
+
+typedef std::vector<LLModelInstanceBase> model_instance_list;
+
+class LLModelInstance : public LLModelInstanceBase
+{
+public:
+	std::string mLabel;
+	LLUUID mMeshID;
+	S32 mLocalMeshID;
+
+	LLModelInstance(LLModel* model, const std::string& label, LLMatrix4& transform, material_map& materials)
+		: LLModelInstanceBase(model, transform, materials), mLabel(label)
+	{
+		mLocalMeshID = -1;
+	}
+
+	LLModelInstance(LLSD& data);
+
+	LLSD asLLSD();
+};
+
+#define LL_DEGENERACY_TOLERANCE  1e-7f
+
+inline F32 dot3fpu(const LLVector4a& a, const LLVector4a& b)
+{
+	volatile F32 p0 = a[0] * b[0];
+	volatile F32 p1 = a[1] * b[1];
+	volatile F32 p2 = a[2] * b[2];
+	return p0 + p1 + p2;
+}
+
+bool ll_is_degenerate(const LLVector4a& a, const LLVector4a& b, const LLVector4a& c, F32 tolerance = LL_DEGENERACY_TOLERANCE);
+
+bool validate_face(const LLVolumeFace& face);
+bool validate_model(const LLModel* mdl);
+
 #endif //LL_LLMODEL_H
diff --git a/indra/llprimitive/llmodelloader.cpp b/indra/llprimitive/llmodelloader.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..81d92d151b1e911055935100bbb003c8d618dced
--- /dev/null
+++ b/indra/llprimitive/llmodelloader.cpp
@@ -0,0 +1,640 @@
+/**
+ * @file llmodelloader.cpp
+ * @brief LLModelLoader class implementation
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "llmodelloader.h"
+#include "llsdserialize.h"
+#include "lljoint.h"
+#include "llcallbacklist.h"
+
+#include "glh/glh_linear.h"
+#include "llmatrix4a.h"
+#include <boost/bind.hpp>
+
+std::list<LLModelLoader*> LLModelLoader::sActiveLoaderList;
+
+void stretch_extents(LLModel* model, LLMatrix4a& mat, LLVector4a& min, LLVector4a& max, BOOL& first_transform)
+{
+	LLVector4a box[] =
+	{
+		LLVector4a(-1, 1,-1),
+		LLVector4a(-1, 1, 1),
+		LLVector4a(-1,-1,-1),
+		LLVector4a(-1,-1, 1),
+		LLVector4a( 1, 1,-1),
+		LLVector4a( 1, 1, 1),
+		LLVector4a( 1,-1,-1),
+		LLVector4a( 1,-1, 1),
+	};
+
+	for (S32 j = 0; j < model->getNumVolumeFaces(); ++j)
+	{
+		const LLVolumeFace& face = model->getVolumeFace(j);
+
+		LLVector4a center;
+		center.setAdd(face.mExtents[0], face.mExtents[1]);
+		center.mul(0.5f);
+		LLVector4a size;
+		size.setSub(face.mExtents[1],face.mExtents[0]);
+		size.mul(0.5f);
+
+		for (U32 i = 0; i < 8; i++)
+		{
+			LLVector4a t;
+			t.setMul(size, box[i]);
+			t.add(center);
+
+			LLVector4a v;
+
+			mat.affineTransform(t, v);
+
+			if (first_transform)
+			{
+				first_transform = FALSE;
+				min = max = v;
+			}
+			else
+			{
+				update_min_max(min, max, v);
+			}
+		}
+	}
+}
+
+void stretch_extents(LLModel* model, LLMatrix4& mat, LLVector3& min, LLVector3& max, BOOL& first_transform)
+{
+	LLVector4a mina, maxa;
+	LLMatrix4a mata;
+
+	mata.loadu(mat);
+	mina.load3(min.mV);
+	maxa.load3(max.mV);
+
+	stretch_extents(model, mata, mina, maxa, first_transform);
+
+	min.set(mina.getF32ptr());
+	max.set(maxa.getF32ptr());
+}
+
+//-----------------------------------------------------------------------------
+// LLModelLoader
+//-----------------------------------------------------------------------------
+LLModelLoader::LLModelLoader(
+	std::string				filename,
+	S32						lod,
+	load_callback_t		load_cb,
+	joint_lookup_func_t	joint_lookup_func,
+	texture_load_func_t	texture_load_func,
+	state_callback_t		state_cb,
+	void*						opaque_userdata,
+	JointTransformMap&	jointMap,
+	JointSet&				jointsFromNodes )
+: mJointList( jointMap )
+, mJointsFromNode( jointsFromNodes )
+, LLThread("Model Loader")
+, mFilename(filename)
+, mLod(lod)
+, mFirstTransform(TRUE)
+, mNumOfFetchingTextures(0)
+, mLoadCallback(load_cb)
+, mJointLookupFunc(joint_lookup_func)
+, mTextureLoadFunc(texture_load_func)
+, mStateCallback(state_cb)
+, mOpaqueData(opaque_userdata)
+, mNoNormalize(false)
+, mNoOptimize(false)
+, mCacheOnlyHitIfRigged(false)
+{
+	mJointMap["mPelvis"] = "mPelvis";
+	mJointMap["mTorso"] = "mTorso";
+	mJointMap["mChest"] = "mChest";
+	mJointMap["mNeck"] = "mNeck";
+	mJointMap["mHead"] = "mHead";
+	mJointMap["mSkull"] = "mSkull";
+	mJointMap["mEyeRight"] = "mEyeRight";
+	mJointMap["mEyeLeft"] = "mEyeLeft";
+	mJointMap["mCollarLeft"] = "mCollarLeft";
+	mJointMap["mShoulderLeft"] = "mShoulderLeft";
+	mJointMap["mElbowLeft"] = "mElbowLeft";
+	mJointMap["mWristLeft"] = "mWristLeft";
+	mJointMap["mCollarRight"] = "mCollarRight";
+	mJointMap["mShoulderRight"] = "mShoulderRight";
+	mJointMap["mElbowRight"] = "mElbowRight";
+	mJointMap["mWristRight"] = "mWristRight";
+	mJointMap["mHipRight"] = "mHipRight";
+	mJointMap["mKneeRight"] = "mKneeRight";
+	mJointMap["mAnkleRight"] = "mAnkleRight";
+	mJointMap["mFootRight"] = "mFootRight";
+	mJointMap["mToeRight"] = "mToeRight";
+	mJointMap["mHipLeft"] = "mHipLeft";
+	mJointMap["mKneeLeft"] = "mKneeLeft";
+	mJointMap["mAnkleLeft"] = "mAnkleLeft";
+	mJointMap["mFootLeft"] = "mFootLeft";
+	mJointMap["mToeLeft"] = "mToeLeft";
+
+	mJointMap["avatar_mPelvis"] = "mPelvis";
+	mJointMap["avatar_mTorso"] = "mTorso";
+	mJointMap["avatar_mChest"] = "mChest";
+	mJointMap["avatar_mNeck"] = "mNeck";
+	mJointMap["avatar_mHead"] = "mHead";
+	mJointMap["avatar_mSkull"] = "mSkull";
+	mJointMap["avatar_mEyeRight"] = "mEyeRight";
+	mJointMap["avatar_mEyeLeft"] = "mEyeLeft";
+	mJointMap["avatar_mCollarLeft"] = "mCollarLeft";
+	mJointMap["avatar_mShoulderLeft"] = "mShoulderLeft";
+	mJointMap["avatar_mElbowLeft"] = "mElbowLeft";
+	mJointMap["avatar_mWristLeft"] = "mWristLeft";
+	mJointMap["avatar_mCollarRight"] = "mCollarRight";
+	mJointMap["avatar_mShoulderRight"] = "mShoulderRight";
+	mJointMap["avatar_mElbowRight"] = "mElbowRight";
+	mJointMap["avatar_mWristRight"] = "mWristRight";
+	mJointMap["avatar_mHipRight"] = "mHipRight";
+	mJointMap["avatar_mKneeRight"] = "mKneeRight";
+	mJointMap["avatar_mAnkleRight"] = "mAnkleRight";
+	mJointMap["avatar_mFootRight"] = "mFootRight";
+	mJointMap["avatar_mToeRight"] = "mToeRight";
+	mJointMap["avatar_mHipLeft"] = "mHipLeft";
+	mJointMap["avatar_mKneeLeft"] = "mKneeLeft";
+	mJointMap["avatar_mAnkleLeft"] = "mAnkleLeft";
+	mJointMap["avatar_mFootLeft"] = "mFootLeft";
+	mJointMap["avatar_mToeLeft"] = "mToeLeft";
+
+
+	mJointMap["hip"] = "mPelvis";
+	mJointMap["abdomen"] = "mTorso";
+	mJointMap["chest"] = "mChest";
+	mJointMap["neck"] = "mNeck";
+	mJointMap["head"] = "mHead";
+	mJointMap["figureHair"] = "mSkull";
+	mJointMap["lCollar"] = "mCollarLeft";
+	mJointMap["lShldr"] = "mShoulderLeft";
+	mJointMap["lForeArm"] = "mElbowLeft";
+	mJointMap["lHand"] = "mWristLeft";
+	mJointMap["rCollar"] = "mCollarRight";
+	mJointMap["rShldr"] = "mShoulderRight";
+	mJointMap["rForeArm"] = "mElbowRight";
+	mJointMap["rHand"] = "mWristRight";
+	mJointMap["rThigh"] = "mHipRight";
+	mJointMap["rShin"] = "mKneeRight";
+	mJointMap["rFoot"] = "mFootRight";
+	mJointMap["lThigh"] = "mHipLeft";
+	mJointMap["lShin"] = "mKneeLeft";
+	mJointMap["lFoot"] = "mFootLeft";
+
+	//move into joint mapper class
+	//1. joints for joint offset verification
+	mMasterJointList.push_front("mPelvis");
+	mMasterJointList.push_front("mTorso");
+	mMasterJointList.push_front("mChest");
+	mMasterJointList.push_front("mNeck");
+	mMasterJointList.push_front("mHead");
+	mMasterJointList.push_front("mCollarLeft");
+	mMasterJointList.push_front("mShoulderLeft");
+	mMasterJointList.push_front("mElbowLeft");
+	mMasterJointList.push_front("mWristLeft");
+	mMasterJointList.push_front("mCollarRight");
+	mMasterJointList.push_front("mShoulderRight");
+	mMasterJointList.push_front("mElbowRight");
+	mMasterJointList.push_front("mWristRight");
+	mMasterJointList.push_front("mHipRight");
+	mMasterJointList.push_front("mKneeRight");
+	mMasterJointList.push_front("mFootRight");
+	mMasterJointList.push_front("mHipLeft");
+	mMasterJointList.push_front("mKneeLeft");
+	mMasterJointList.push_front("mFootLeft");
+	
+	//2. legacy joint list - used to verify rigs that will not be using joint offsets
+	mMasterLegacyJointList.push_front("mPelvis");
+	mMasterLegacyJointList.push_front("mTorso");
+	mMasterLegacyJointList.push_front("mChest");
+	mMasterLegacyJointList.push_front("mNeck");
+	mMasterLegacyJointList.push_front("mHead");
+	mMasterLegacyJointList.push_front("mHipRight");
+	mMasterLegacyJointList.push_front("mKneeRight");
+	mMasterLegacyJointList.push_front("mFootRight");
+	mMasterLegacyJointList.push_front("mHipLeft");
+	mMasterLegacyJointList.push_front("mKneeLeft");
+	mMasterLegacyJointList.push_front("mFootLeft");
+
+	assert_main_thread();
+	sActiveLoaderList.push_back(this) ;
+}
+
+LLModelLoader::~LLModelLoader()
+{
+	assert_main_thread();
+	sActiveLoaderList.remove(this);
+}
+
+void LLModelLoader::run()
+{
+	doLoadModel();
+	doOnIdleOneTime(boost::bind(&LLModelLoader::loadModelCallback,this));
+}
+
+bool LLModelLoader::doLoadModel()
+{
+	//first, look for a .slm file of the same name that was modified later
+	//than the .dae
+
+	if (mTrySLM)
+	{
+		std::string filename = mFilename;
+			
+		std::string::size_type i = filename.rfind(".");
+		if (i != std::string::npos)
+		{
+			filename.replace(i, filename.size()-1, ".slm");
+			llstat slm_status;
+			if (LLFile::stat(filename, &slm_status) == 0)
+			{ //slm file exists
+				llstat dae_status;
+				if (LLFile::stat(mFilename, &dae_status) != 0 ||
+					dae_status.st_mtime < slm_status.st_mtime)
+				{
+					if (loadFromSLM(filename))
+					{ //slm successfully loaded, if this fails, fall through and
+						//try loading from dae
+
+						mLod = -1; //successfully loading from an slm implicitly sets all 
+									//LoDs
+						return true;
+					}
+				}
+			}	
+		}
+	}
+
+	return OpenFile(mFilename);
+}
+
+void LLModelLoader::setLoadState(U32 state)
+{
+	mStateCallback(state, mOpaqueData);
+}
+
+bool LLModelLoader::loadFromSLM(const std::string& filename)
+{ 
+	//only need to populate mScene with data from slm
+	llstat stat;
+
+	if (LLFile::stat(filename, &stat))
+	{ //file does not exist
+		return false;
+	}
+
+	S32 file_size = (S32) stat.st_size;
+	
+	llifstream ifstream(filename.c_str(), std::ifstream::in | std::ifstream::binary);
+	LLSD data;
+	LLSDSerialize::fromBinary(data, ifstream, file_size);
+	ifstream.close();
+
+	//build model list for each LoD
+	model_list model[LLModel::NUM_LODS];
+
+	if (data["version"].asInteger() != SLM_SUPPORTED_VERSION)
+	{  //unsupported version
+		return false;
+	}
+
+	LLSD& mesh = data["mesh"];
+
+	LLVolumeParams volume_params;
+	volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
+
+	for (S32 lod = 0; lod < LLModel::NUM_LODS; ++lod)
+	{
+		for (U32 i = 0; i < mesh.size(); ++i)
+		{
+			std::stringstream str(mesh[i].asString());
+			LLPointer<LLModel> loaded_model = new LLModel(volume_params, (F32) lod);
+			if (loaded_model->loadModel(str))
+			{
+				loaded_model->mLocalID = i;
+				model[lod].push_back(loaded_model);
+
+				if (lod == LLModel::LOD_HIGH)
+				{
+					if (!loaded_model->mSkinInfo.mJointNames.empty())
+					{ 
+						//check to see if rig is valid					
+						critiqueRigForUploadApplicability( loaded_model->mSkinInfo.mJointNames );					
+					}
+					else if (mCacheOnlyHitIfRigged)
+					{
+						return false;
+					}
+				}
+			}
+		}
+	}	
+
+	if (model[LLModel::LOD_HIGH].empty())
+	{ //failed to load high lod
+		return false;
+	}
+
+	//load instance list
+	model_instance_list instance_list;
+
+	LLSD& instance = data["instance"];
+
+	for (U32 i = 0; i < instance.size(); ++i)
+	{
+		//deserialize instance list
+		instance_list.push_back(LLModelInstance(instance[i]));
+
+		//match up model instance pointers
+		S32 idx = instance_list[i].mLocalMeshID;
+		std::string instance_label = instance_list[i].mLabel;
+
+		for (U32 lod = 0; lod < LLModel::NUM_LODS; ++lod)
+		{
+			if (!model[lod].empty())
+			{
+				if (idx >= model[lod].size())
+				{
+					if (model[lod].size())
+					{
+						instance_list[i].mLOD[lod] = model[lod][0];
+					}
+					else
+					{
+						instance_list[i].mLOD[lod] = NULL;
+					}					
+					continue;
+				}
+
+				if (model[lod][idx]
+					&& model[lod][idx]->mLabel.empty()
+					&& !instance_label.empty())
+				{
+					// restore model names
+					std::string name = instance_label;
+					switch (lod)
+					{
+					case LLModel::LOD_IMPOSTOR: name += "_LOD0"; break;
+					case LLModel::LOD_LOW:      name += "_LOD1"; break;
+					case LLModel::LOD_MEDIUM:   name += "_LOD2"; break;
+					case LLModel::LOD_PHYSICS:  name += "_PHYS"; break;
+					case LLModel::LOD_HIGH:                      break;
+					}
+					model[lod][idx]->mLabel = name;
+				}
+
+				instance_list[i].mLOD[lod] = model[lod][idx];
+			}
+		}
+
+		if (!instance_list[i].mModel)
+			instance_list[i].mModel = model[LLModel::LOD_HIGH][idx];
+	}
+
+	// Set name for UI to use
+	std::string name = data["name"];
+	if (!name.empty())
+	{
+		model[LLModel::LOD_HIGH][0]->mRequestedLabel = name;
+	}
+
+
+	//convert instance_list to mScene
+	mFirstTransform = TRUE;
+	for (U32 i = 0; i < instance_list.size(); ++i)
+	{
+		LLModelInstance& cur_instance = instance_list[i];
+		mScene[cur_instance.mTransform].push_back(cur_instance);
+		stretch_extents(cur_instance.mModel, cur_instance.mTransform, mExtents[0], mExtents[1], mFirstTransform);
+	}
+	
+	setLoadState( DONE );
+
+	return true;
+}
+
+//static
+bool LLModelLoader::isAlive(LLModelLoader* loader)
+{
+	if(!loader)
+	{
+		return false ;
+	}
+
+	std::list<LLModelLoader*>::iterator iter = sActiveLoaderList.begin() ;
+	for(; iter != sActiveLoaderList.end() && (*iter) != loader; ++iter) ;
+	
+	return *iter == loader ;
+}
+
+void LLModelLoader::loadModelCallback()
+{
+	mLoadCallback(mScene,mModelList,mLod, mOpaqueData);
+
+	while (!isStopped())
+	{ //wait until this thread is stopped before deleting self
+		apr_sleep(100);
+	}
+
+	//double check if "this" is valid before deleting it, in case it is aborted during running.
+	if(!isAlive(this))
+	{
+		return ;
+	}
+
+	delete this;
+}
+
+//-----------------------------------------------------------------------------
+// critiqueRigForUploadApplicability()
+//-----------------------------------------------------------------------------
+void LLModelLoader::critiqueRigForUploadApplicability( const std::vector<std::string> &jointListFromAsset )
+{
+	critiqueJointToNodeMappingFromScene();
+	
+	//Determines the following use cases for a rig:
+	//1. It is suitable for upload with skin weights & joint positions, or
+	//2. It is suitable for upload as standard av with just skin weights
+	
+	bool isJointPositionUploadOK = isRigSuitableForJointPositionUpload( jointListFromAsset );
+	bool isRigLegacyOK			 = isRigLegacy( jointListFromAsset );
+
+	//It's OK that both could end up being true, both default to false
+	if ( isJointPositionUploadOK )
+	{
+		setRigValidForJointPositionUpload( true );
+	}
+
+	if ( isRigLegacyOK) 
+	{	
+		setLegacyRigValid( true );
+	}
+
+}
+//-----------------------------------------------------------------------------
+// critiqueJointToNodeMappingFromScene()
+//-----------------------------------------------------------------------------
+void LLModelLoader::critiqueJointToNodeMappingFromScene( void  )
+{
+	//Do the actual nodes back the joint listing from the dae?
+	//if yes then this is a fully rigged asset, otherwise it's just a partial rig
+	
+	JointSet::iterator jointsFromNodeIt = mJointsFromNode.begin();
+	JointSet::iterator jointsFromNodeEndIt = mJointsFromNode.end();
+	bool result = true;
+
+	if ( !mJointsFromNode.empty() )
+	{
+		for ( ;jointsFromNodeIt!=jointsFromNodeEndIt;++jointsFromNodeIt )
+		{
+			std::string name = *jointsFromNodeIt;
+			if ( mJointTransformMap.find( name ) != mJointTransformMap.end() )
+			{
+				continue;
+			}
+			else
+			{
+				LL_INFOS() <<"critiqueJointToNodeMappingFromScene is missing a: " << name << LL_ENDL;
+				result = false;				
+			}
+		}
+	}
+	else
+	{
+		result = false;
+	}
+
+	//Determines the following use cases for a rig:
+	//1. Full av rig  w/1-1 mapping from the scene and joint array
+	//2. Partial rig but w/o parity between the scene and joint array
+	if ( result )
+	{		
+		setRigWithSceneParity( true );
+	}	
+}
+//-----------------------------------------------------------------------------
+// isRigLegacy()
+//-----------------------------------------------------------------------------
+bool LLModelLoader::isRigLegacy( const std::vector<std::string> &jointListFromAsset )
+{
+	//No joints in asset
+	if ( jointListFromAsset.size() == 0 )
+	{
+		return false;
+	}
+
+	bool result = false;
+
+	JointSet :: const_iterator masterJointIt = mMasterLegacyJointList.begin();	
+	JointSet :: const_iterator masterJointEndIt = mMasterLegacyJointList.end();
+	
+	std::vector<std::string> :: const_iterator modelJointIt = jointListFromAsset.begin();	
+	std::vector<std::string> :: const_iterator modelJointItEnd = jointListFromAsset.end();
+	
+	for ( ;masterJointIt!=masterJointEndIt;++masterJointIt )
+	{
+		result = false;
+		modelJointIt = jointListFromAsset.begin();
+
+		for ( ;modelJointIt!=modelJointItEnd; ++modelJointIt )
+		{
+			if ( *masterJointIt == *modelJointIt )
+			{
+				result = true;
+				break;
+			}			
+		}		
+		if ( !result )
+		{
+			LL_INFOS() <<" Asset did not contain the joint (if you're u/l a fully rigged asset w/joint positions - it is required)." << *masterJointIt<< LL_ENDL;
+			break;
+		}
+	}	
+	return result;
+}
+//-----------------------------------------------------------------------------
+// isRigSuitableForJointPositionUpload()
+//-----------------------------------------------------------------------------
+bool LLModelLoader::isRigSuitableForJointPositionUpload( const std::vector<std::string> &jointListFromAsset )
+{
+	bool result = false;
+
+	JointSet :: const_iterator masterJointIt = mMasterJointList.begin();	
+	JointSet :: const_iterator masterJointEndIt = mMasterJointList.end();
+	
+	std::vector<std::string> :: const_iterator modelJointIt = jointListFromAsset.begin();	
+	std::vector<std::string> :: const_iterator modelJointItEnd = jointListFromAsset.end();
+	
+	for ( ;masterJointIt!=masterJointEndIt;++masterJointIt )
+	{
+		result = false;
+		modelJointIt = jointListFromAsset.begin();
+
+		for ( ;modelJointIt!=modelJointItEnd; ++modelJointIt )
+		{
+			if ( *masterJointIt == *modelJointIt )
+			{
+				result = true;
+				break;
+			}			
+		}		
+		if ( !result )
+		{
+			LL_INFOS() <<" Asset did not contain the joint (if you're u/l a fully rigged asset w/joint positions - it is required)." << *masterJointIt<< LL_ENDL;
+			break;
+		}
+	}	
+	return result;
+}
+
+
+//called in the main thread
+void LLModelLoader::loadTextures()
+{
+	BOOL is_paused = isPaused() ;
+	pause() ; //pause the loader 
+
+	for(scene::iterator iter = mScene.begin(); iter != mScene.end(); ++iter)
+	{
+		for(U32 i = 0 ; i < iter->second.size(); i++)
+		{
+			for(std::map<std::string, LLImportMaterial>::iterator j = iter->second[i].mMaterial.begin();
+				j != iter->second[i].mMaterial.end(); ++j)
+			{
+				LLImportMaterial& material = j->second;
+
+				if(!material.mDiffuseMapFilename.empty())
+				{
+					mNumOfFetchingTextures += mTextureLoadFunc(material, mOpaqueData);					
+				}
+			}
+		}
+	}
+
+	if(!is_paused)
+	{
+		unpause() ;
+	}
+}
diff --git a/indra/llprimitive/llmodelloader.h b/indra/llprimitive/llmodelloader.h
new file mode 100644
index 0000000000000000000000000000000000000000..bb4d06dca3c5f9a43de3b322d77d3f535c88207d
--- /dev/null
+++ b/indra/llprimitive/llmodelloader.h
@@ -0,0 +1,212 @@
+/**
+ * @file llmodelloader.h
+ * @brief LLModelLoader class definition
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLMODELLOADER_H
+#define LL_LLMODELLOADER_H
+
+#include "llmodel.h"
+#include "llthread.h"
+#include <boost/function.hpp>
+#include <list>
+
+class LLJoint;
+
+typedef std::map<std::string, LLMatrix4>					JointTransformMap;
+typedef std::map<std::string, LLMatrix4>:: iterator	JointTransformMapIt;
+typedef std::map<std::string, std::string>				JointMap;
+typedef std::deque<std::string>								JointSet;
+
+const S32 SLM_SUPPORTED_VERSION	= 3;
+const S32 NUM_LOD						= 4;
+
+class LLModelLoader : public LLThread
+{
+public:
+
+	typedef std::map<std::string, LLImportMaterial>			material_map;
+	typedef std::vector<LLPointer<LLModel > >					model_list;	
+	typedef std::vector<LLModelInstance>						model_instance_list;	
+	typedef std::map<LLMatrix4, model_instance_list >		scene;
+
+	// Callback with loaded model data and loaded LoD
+	// 
+	typedef boost::function<void (scene&,model_list&,S32,void*) >		load_callback_t;
+
+	// Function to provide joint lookup by name
+	// (within preview avi skeleton, for example)
+	//
+	typedef boost::function<LLJoint* (const std::string&,void*) >		joint_lookup_func_t;
+
+	// Func to load and associate material with all it's textures,
+	// returned value is the number of textures loaded
+	// intentionally non-const so func can modify material to
+	// store platform-specific data
+	//
+	typedef boost::function<U32 (LLImportMaterial&,void*) >				texture_load_func_t;
+
+	// Callback to inform client of state changes
+	// during loading process (errors will be reported
+	// as state changes here as well)
+	//
+	typedef boost::function<void (U32,void*) >								state_callback_t;
+
+	typedef enum
+	{
+		STARTING = 0,
+		READING_FILE,
+		CREATING_FACES,
+		GENERATING_VERTEX_BUFFERS,
+		GENERATING_LOD,
+		DONE,
+		ERROR_PARSING, //basically loading failed
+		ERROR_MATERIALS,
+		ERROR_PASSWORD_REQUIRED,
+		ERROR_NEED_MORE_MEMORY,
+		ERROR_INVALID_FILE,
+		ERROR_LOADER_SETUP,
+		ERROR_INVALID_PARAMETERS,
+		ERROR_OUT_OF_RANGE,
+		ERROR_FILE_VERSION_INVALID,
+		ERROR_MODEL // this error should always be last in this list, error code is passed as ERROR_MODEL+error_code
+	} eLoadState;
+
+	U32 mState;
+	std::string mFilename;
+	
+	S32 mLod;
+	
+	LLMatrix4 mTransform;
+	BOOL mFirstTransform;
+	LLVector3 mExtents[2];
+	
+	bool mTrySLM;
+	bool mCacheOnlyHitIfRigged; // ignore cached SLM if it does not contain rig info (and we want rig info)
+
+	model_list		mModelList;
+	scene				mScene;
+
+	typedef std::queue<LLPointer<LLModel> > model_queue;
+
+	//queue of models that need a physics rep
+	model_queue mPhysicsQ;
+
+	//map of avatar joints as named in COLLADA assets to internal joint names
+	JointMap			mJointMap;
+	JointTransformMap&	mJointList;	
+	JointSet&			mJointsFromNode;
+
+	LLModelLoader(
+		std::string									filename,
+		S32											lod, 
+		LLModelLoader::load_callback_t		load_cb,
+		LLModelLoader::joint_lookup_func_t	joint_lookup_func,
+		LLModelLoader::texture_load_func_t	texture_load_func,
+		LLModelLoader::state_callback_t		state_cb,
+		void*											opaque_userdata,
+		JointTransformMap&						jointMap,
+		JointSet&									jointsFromNodes);
+	virtual ~LLModelLoader() ;
+
+	virtual void setNoNormalize() { mNoNormalize = true; }
+	virtual void setNoOptimize() { mNoOptimize = true; }
+
+	virtual void run();
+	
+	// Will try SLM or derived class OpenFile as appropriate
+	//
+	virtual bool doLoadModel();
+
+	// Derived classes need to provide their parsing of files here
+	//
+	virtual bool OpenFile(const std::string& filename) = 0;
+
+	bool loadFromSLM(const std::string& filename);
+	
+	void loadModelCallback();
+	void loadTextures() ; //called in the main thread.
+	void setLoadState(U32 state);
+
+	
+
+	S32 mNumOfFetchingTextures ; //updated in the main thread
+	bool areTexturesReady() { return !mNumOfFetchingTextures; } //called in the main thread.
+
+	bool verifyCount( int expected, int result );
+
+	//Determines the viability of an asset to be used as an avatar rig (w or w/o joint upload caps)
+	void critiqueRigForUploadApplicability( const std::vector<std::string> &jointListFromAsset );
+	void critiqueJointToNodeMappingFromScene( void  );
+
+	//Determines if a rig is a legacy from the joint list
+	bool isRigLegacy( const std::vector<std::string> &jointListFromAsset );
+
+	//Determines if a rig is suitable for upload
+	bool isRigSuitableForJointPositionUpload( const std::vector<std::string> &jointListFromAsset );
+
+	void setRigWithSceneParity( bool state ) { mRigParityWithScene = state; }
+	const bool getRigWithSceneParity( void ) const { return mRigParityWithScene; }
+
+	const bool isRigValidForJointPositionUpload( void ) const { return mRigValidJointUpload; }
+	void setRigValidForJointPositionUpload( bool rigValid ) { mRigValidJointUpload = rigValid; }
+
+	const bool isLegacyRigValid( void ) const { return mLegacyRigValid; }
+	void setLegacyRigValid( bool rigValid ) { mLegacyRigValid = rigValid; }		
+
+	//-----------------------------------------------------------------------------
+	// isNodeAJoint()
+	//-----------------------------------------------------------------------------
+	bool isNodeAJoint(const char* name)
+	{
+		return mJointMap.find(name) != mJointMap.end();
+	}
+
+protected:
+
+	LLModelLoader::load_callback_t		mLoadCallback;
+	LLModelLoader::joint_lookup_func_t	mJointLookupFunc;
+	LLModelLoader::texture_load_func_t	mTextureLoadFunc;
+	LLModelLoader::state_callback_t		mStateCallback;
+	void*											mOpaqueData;
+
+	bool		mRigParityWithScene;
+	bool		mRigValidJointUpload;
+	bool		mLegacyRigValid;
+
+	bool		mNoNormalize;
+	bool		mNoOptimize;
+
+	JointSet				mMasterJointList;
+	JointSet				mMasterLegacyJointList;
+	JointTransformMap	mJointTransformMap;
+
+	static std::list<LLModelLoader*> sActiveLoaderList;
+	static bool isAlive(LLModelLoader* loader) ;
+};
+class LLMatrix4a;
+void stretch_extents(LLModel* model, LLMatrix4a& mat, LLVector4a& min, LLVector4a& max, BOOL& first_transform);
+void stretch_extents(LLModel* model, LLMatrix4& mat, LLVector3& min, LLVector3& max, BOOL& first_transform);
+
+#endif  // LL_LLMODELLOADER_H
diff --git a/indra/llprimitive/lltextureentry.h b/indra/llprimitive/lltextureentry.h
index 3fe23d6c9f282bd49a6fb57cfa4ea29bf242f94f..81e2e8a5a28708b7f735a233503279d4e9ce702d 100755
--- a/indra/llprimitive/lltextureentry.h
+++ b/indra/llprimitive/lltextureentry.h
@@ -89,6 +89,10 @@ public:
 
 	bool operator==(const LLTextureEntry &rhs) const;
 	bool operator!=(const LLTextureEntry &rhs) const;
+	
+	// Added to allow use with std::map
+	//
+	bool operator <(const LLTextureEntry &rhs) const;
 
 	LLSD asLLSD() const;
 	void asLLSD(LLSD& sd) const;
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 536c7740bbe80622994a10421d210ca7477e19e1..16877c345e82b94ab7725f1f083cd02505b50729 100755
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -140,7 +140,6 @@ set(viewer_SOURCE_FILES
     llbreadcrumbview.cpp
     llbrowsernotification.cpp
     llbuycurrencyhtml.cpp
-    llcallbacklist.cpp
     llcallingcard.cpp
     llcapabilitylistener.cpp
     llcaphttpsender.cpp
@@ -756,7 +755,6 @@ set(viewer_HEADER_FILES
     llbox.h
     llbreadcrumbview.h
     llbuycurrencyhtml.h
-    llcallbacklist.h
     llcallingcard.h
     llcapabilitylistener.h
     llcapabilityprovider.h
diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt
index ff313b8c212f86ecce5e9ce1db1b5a17b83d134a..0cbfaed0d9fe75138f385fed8aff02d7ca8a0880 100644
--- a/indra/newview/VIEWER_VERSION.txt
+++ b/indra/newview/VIEWER_VERSION.txt
@@ -1 +1 @@
-3.8.4
+3.8.5
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 4b5b7430cf948ec10d2295dd90b761f84ca0f81c..5f378c64e8774aafd47965ebf815d00be92aa84d 100755
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -2,6 +2,39 @@
 <llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:noNamespaceSchemaLocation="llsd.xsd">
 <map>
+  <key>ImporterDebug</key>
+  <map>
+    <key>Comment</key>
+    <string>Enable debug output to more precisely identify sources of import errors. Warning: the output can slow down import on many machines.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Boolean</string>
+    <key>Value</key>
+    <integer>0</integer>
+  </map>
+  <key>ImporterLegacyMatching</key>
+  <map>
+    <key>Comment</key>
+    <string>Enable index based model matching.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Boolean</string>
+    <key>Value</key>
+    <integer>0</integer>
+  </map>
+  <key>ImporterModelLimit</key>
+  <map>
+    <key>Comment</key>
+    <string>Limits amount of importer generated models for dae files</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>U32</string>
+    <key>Value</key>
+    <integer>768</integer>
+  </map>
     <key>IMShowTime</key>
     <map>
       <key>Comment</key>
diff --git a/indra/newview/llcallbacklist.cpp b/indra/newview/llcallbacklist.cpp
old mode 100755
new mode 100644
diff --git a/indra/newview/llcommandhandler.cpp b/indra/newview/llcommandhandler.cpp
index 19dba3f9173a0a210736d92f620a96f5ad6283c7..5ea7efc0454dbd26281f9d4bace2bf44dbbdd28e 100755
--- a/indra/newview/llcommandhandler.cpp
+++ b/indra/newview/llcommandhandler.cpp
@@ -30,6 +30,7 @@
 #include "llcommandhandler.h"
 #include "llnotificationsutil.h"
 #include "llcommanddispatcherlistener.h"
+#include "llstartup.h"
 #include "stringize.h"
 
 // system includes
@@ -116,7 +117,11 @@ bool LLCommandHandlerRegistry::dispatch(const std::string& cmd,
 			LL_WARNS_ONCE("SLURL") << "Blocked SLURL command from untrusted browser" << LL_ENDL;
 			if (! slurl_blocked)
 			{
-				LLNotificationsUtil::add("BlockedSLURL");
+				if (LLStartUp::getStartupState() >= STATE_BROWSER_INIT)
+				{
+					// Note: commands can arrive before we initialize everything we need for Notification.
+					LLNotificationsUtil::add("BlockedSLURL");
+				}
 				slurl_blocked = true;
 			}
 			return true;
@@ -138,7 +143,10 @@ bool LLCommandHandlerRegistry::dispatch(const std::string& cmd,
 				LL_WARNS_ONCE("SLURL") << "Throttled SLURL command from untrusted browser" << LL_ENDL;
 				if (! slurl_throttled)
 				{
-					LLNotificationsUtil::add("ThrottledSLURL");
+					if (LLStartUp::getStartupState() >= STATE_BROWSER_INIT)
+					{
+						LLNotificationsUtil::add("ThrottledSLURL");
+					}
 					slurl_throttled = true;
 				}
 				return true;
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 20760001fd0ccba081741b69abed7da30ecf3a02..41005144a7a002bd0c58239ce9549bbfcdd545e1 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -26,35 +26,8 @@
 
 #include "llviewerprecompiledheaders.h"
 
-#if LL_MSVC
-#pragma warning (disable : 4263)
-#pragma warning (disable : 4264)
-#endif
-#include "dae.h"
-//#include "dom.h"
-#include "dom/domAsset.h"
-#include "dom/domBind_material.h"
-#include "dom/domCOLLADA.h"
-#include "dom/domConstants.h"
-#include "dom/domController.h"
-#include "dom/domEffect.h"
-#include "dom/domGeometry.h"
-#include "dom/domInstance_geometry.h"
-#include "dom/domInstance_material.h"
-#include "dom/domInstance_node.h"
-#include "dom/domInstance_effect.h"
-#include "dom/domMaterial.h"
-#include "dom/domMatrix.h"
-#include "dom/domNode.h"
-#include "dom/domProfile_COMMON.h"
-#include "dom/domRotate.h"
-#include "dom/domScale.h"
-#include "dom/domTranslate.h"
-#include "dom/domVisual_scene.h"
-#if LL_MSVC
-#pragma warning (default : 4263)
-#pragma warning (default : 4264)
-#endif
+#include "llmodelloader.h"
+#include "lldaeloader.h"
 
 #include "llfloatermodelpreview.h"
 
@@ -112,14 +85,15 @@
 #include "llanimationstates.h"
 #include "llviewernetwork.h"
 #include "llviewershadermgr.h"
-#include "glod/glod.h"
 
-const S32 SLM_SUPPORTED_VERSION = 3;
+#include "glod/glod.h"
+#include <boost/algorithm/string.hpp>
 
 //static
 S32 LLFloaterModelPreview::sUploadAmount = 10;
 LLFloaterModelPreview* LLFloaterModelPreview::sInstance = NULL;
-std::list<LLModelLoader*> LLModelLoader::sActiveLoaderList;
+
+bool LLModelPreview::sIgnoreLoadedCallback = false;
 
 // "Retain%" decomp parameter has values from 0.0 to 1.0 by 0.01
 // But according to the UI spec for upload model floater, this parameter
@@ -199,190 +173,46 @@ std::string lod_label_name[NUM_LOD+1] =
 	"I went off the end of the lod_label_name array.  Me so smart."
 };
 
-std::string colladaVersion[VERSIONTYPE_COUNT+1] = 
-{
-	"1.4.0",
-	"1.4.1",
-	"Unsupported"
-};
-
-
-#define LL_DEGENERACY_TOLERANCE  1e-7f
-
-inline F32 dot3fpu(const LLVector4a& a, const LLVector4a& b)
-{
-    volatile F32 p0 = a[0] * b[0];
-    volatile F32 p1 = a[1] * b[1];
-    volatile F32 p2 = a[2] * b[2];
-    return p0 + p1 + p2;
-}
-
-bool ll_is_degenerate(const LLVector4a& a, const LLVector4a& b, const LLVector4a& c, F32 tolerance = LL_DEGENERACY_TOLERANCE)
-{
-        // small area check
-        {
-                LLVector4a edge1; edge1.setSub( a, b );
-                LLVector4a edge2; edge2.setSub( a, c );
-                //////////////////////////////////////////////////////////////////////////
-                /// Linden Modified
-                //////////////////////////////////////////////////////////////////////////
-
-                // If no one edge is more than 10x longer than any other edge, we weaken
-                // the tolerance by a factor of 1e-4f.
-
-                LLVector4a edge3; edge3.setSub( c, b );
-				const F32 len1sq = edge1.dot3(edge1).getF32();
-                const F32 len2sq = edge2.dot3(edge2).getF32();
-                const F32 len3sq = edge3.dot3(edge3).getF32();
-                bool abOK = (len1sq <= 100.f * len2sq) && (len1sq <= 100.f * len3sq);
-                bool acOK = (len2sq <= 100.f * len1sq) && (len1sq <= 100.f * len3sq);
-                bool cbOK = (len3sq <= 100.f * len1sq) && (len1sq <= 100.f * len2sq);
-                if ( abOK && acOK && cbOK )
-                {
-                        tolerance *= 1e-4f;
-                }
-
-                //////////////////////////////////////////////////////////////////////////
-                /// End Modified
-                //////////////////////////////////////////////////////////////////////////
-
-                LLVector4a cross; cross.setCross3( edge1, edge2 );
-
-                LLVector4a edge1b; edge1b.setSub( b, a );
-                LLVector4a edge2b; edge2b.setSub( b, c );
-                LLVector4a crossb; crossb.setCross3( edge1b, edge2b );
-
-                if ( ( cross.dot3(cross).getF32() < tolerance ) || ( crossb.dot3(crossb).getF32() < tolerance ))
-                {
-                        return true;
-                }
-        }
-
-        // point triangle distance check
-        {
-                LLVector4a Q; Q.setSub(a, b);
-                LLVector4a R; R.setSub(c, b);
-
-                const F32 QQ = dot3fpu(Q, Q);
-                const F32 RR = dot3fpu(R, R);
-                const F32 QR = dot3fpu(R, Q);
-
-                volatile F32 QQRR = QQ * RR;
-                volatile F32 QRQR = QR * QR;
-                F32 Det = (QQRR - QRQR);
-
-                if( Det == 0.0f )
-                {
-                        return true;
-                }
-        }
-
-        return false;
-}
-
-bool validate_face(const LLVolumeFace& face)
+BOOL stop_gloderror()
 {
+	GLuint error = glodGetError();
 
-	for (U32 v = 0; v < face.mNumVertices; v++)
-	{
-		if(face.mPositions && !face.mPositions[v].isFinite3())
-		{
-			LL_WARNS() << "NaN position data in face found!" << LL_ENDL;
-			return false;
-		}
-
-		if(face.mNormals && !face.mNormals[v].isFinite3())
-		{
-			LL_WARNS() << "NaN normal data in face found!" << LL_ENDL;
-			return false;
-		}
-	}
-
-	for (U32 i = 0; i < face.mNumIndices; ++i)
-	{
-		if (face.mIndices[i] >= face.mNumVertices)
-		{
-			LL_WARNS() << "Face has invalid index." << LL_ENDL;
-			return false;
-		}
-	}
-
-	if (face.mNumIndices % 3 != 0 || face.mNumIndices == 0)
+	if (error != GLOD_NO_ERROR)
 	{
-		LL_WARNS() << "Face has invalid number of indices." << LL_ENDL;
-		return false;
+		LL_WARNS() << "GLOD error detected, cannot generate LOD: " << std::hex << error << LL_ENDL;
+		return TRUE;
 	}
 
-
-	/*const LLVector4a scale(0.5f);
-
-
-	for (U32 i = 0; i < face.mNumIndices; i+=3)
-	{
-		U16 idx1 = face.mIndices[i];
-		U16 idx2 = face.mIndices[i+1];
-		U16 idx3 = face.mIndices[i+2];
-
-		LLVector4a v1; v1.setMul(face.mPositions[idx1], scale);
-		LLVector4a v2; v2.setMul(face.mPositions[idx2], scale);
-		LLVector4a v3; v3.setMul(face.mPositions[idx3], scale);
-
-		if (ll_is_degenerate(v1,v2,v3))
-		{
-			LL_WARNS() << "Degenerate face found!" << LL_ENDL;
-			return false;
-		}
-	}*/
-	return true;
+	return FALSE;
 }
 
-bool validate_model(const LLModel* mdl)
+LLViewerFetchedTexture* bindMaterialDiffuseTexture(const LLImportMaterial& material)
 {
-	if (mdl->getNumVolumeFaces() == 0)
-	{
-		LL_WARNS() << "Model has no faces!" << LL_ENDL;
-		return false;
-	}
+	LLViewerFetchedTexture *texture = LLViewerTextureManager::getFetchedTexture(material.getDiffuseMap(), FTT_DEFAULT, TRUE, LLGLTexture::BOOST_PREVIEW);
 
-	for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i)
+	if (texture)
 	{
-		if (mdl->getVolumeFace(i).mNumVertices == 0)
-		{
-			LL_WARNS() << "Face has no vertices." << LL_ENDL;
-			return false;
-		}
-
-		if (mdl->getVolumeFace(i).mNumIndices == 0)
-		{
-			LL_WARNS() << "Face has no indices." << LL_ENDL;
-			return false;
-		}
-
-		if (!validate_face(mdl->getVolumeFace(i)))
+		if (texture->getDiscardLevel() > -1)
 		{
-			return false;
+			gGL.getTexUnit(0)->bind(texture, true);
+			return texture;
 		}
 	}
 
-	return true;
+	return NULL;
 }
 
-BOOL stop_gloderror()
+std::string stripSuffix(std::string name)
 {
-	GLuint error = glodGetError();
-
-	if (error != GLOD_NO_ERROR)
+	if ((name.find("_LOD") != -1) || (name.find("_PHYS") != -1))
 	{
-		LL_WARNS() << "GLOD error detected, cannot generate LOD: " << std::hex << error << LL_ENDL;
-		return TRUE;
+		return name.substr(0, name.rfind('_'));
 	}
-
-	return FALSE;
+	return name;
 }
 
-
 LLMeshFilePicker::LLMeshFilePicker(LLModelPreview* mp, S32 lod)
-	: LLFilePickerThread(LLFilePicker::FFLOAD_COLLADA)
+: LLFilePickerThread(LLFilePicker::FFLOAD_COLLADA)
 	{
 		mMP = mp;
 		mLOD = lod;
@@ -393,6 +223,29 @@ void LLMeshFilePicker::notify(const std::string& filename)
 	mMP->loadModel(mFile, mLOD);
 }
 
+void FindModel(LLModelLoader::scene& scene, const std::string& name_to_match, LLModel*& baseModelOut, LLMatrix4& matOut)
+{
+    LLModelLoader::scene::iterator base_iter = scene.begin();
+    bool found = false;
+    while (!found && (base_iter != scene.end()))
+    {
+        matOut = base_iter->first;
+
+        LLModelLoader::model_instance_list::iterator base_instance_iter = base_iter->second.begin();
+        while (!found && (base_instance_iter != base_iter->second.end()))
+        {
+		    LLModelInstance& base_instance = *base_instance_iter++;					    		    
+            LLModel* base_model = base_instance.mModel;
+         
+            if (base_model && (base_model->mLabel == name_to_match))
+            {
+                baseModelOut = base_model;
+                return;
+            }
+        }
+        base_iter++;
+    }
+}
 
 //-----------------------------------------------------------------------------
 // LLFloaterModelPreview()
@@ -613,6 +466,11 @@ void LLFloaterModelPreview::disableViewOption(const std::string& option)
 void LLFloaterModelPreview::loadModel(S32 lod)
 {
 	mModelPreview->mLoading = true;
+	if (lod == LLModel::LOD_PHYSICS)
+	{
+		// loading physics from file
+		mModelPreview->mPhysicsSearchLOD = lod;
+	}
 
 	(new LLMeshFilePicker(mModelPreview, lod))->getFile();
 }
@@ -791,9 +649,9 @@ void LLFloaterModelPreview::draw()
 			childSetTextArg("status", "[STATUS]", getString("status_material_mismatch"));
 		}
 		else
-		if ( mModelPreview->getLoadState() > LLModelLoader::ERROR_PARSING )
-		{		
-			childSetTextArg("status", "[STATUS]", getString(LLModel::getStatusString(mModelPreview->getLoadState() - LLModelLoader::ERROR_PARSING)));
+		if ( mModelPreview->getLoadState() > LLModelLoader::ERROR_MODEL )
+		{
+			childSetTextArg("status", "[STATUS]", getString(LLModel::getStatusString(mModelPreview->getLoadState() - LLModelLoader::ERROR_MODEL)));
 		}
 		else
 		if ( mModelPreview->getLoadState() == LLModelLoader::ERROR_PARSING )
@@ -945,9 +803,16 @@ BOOL LLFloaterModelPreview::handleScrollWheel(S32 x, S32 y, S32 clicks)
 /*virtual*/
 void LLFloaterModelPreview::onOpen(const LLSD& key)
 {
+	LLModelPreview::sIgnoreLoadedCallback = false;
 	requestAgentUploadPermissions();
 }
 
+/*virtual*/
+void LLFloaterModelPreview::onClose(bool app_quitting)
+{
+	LLModelPreview::sIgnoreLoadedCallback = true;
+}
+
 //static
 void LLFloaterModelPreview::onPhysicsParamCommit(LLUICtrl* ctrl, void* data)
 {
@@ -1308,2038 +1173,197 @@ void LLFloaterModelPreview::onMouseCaptureLostModelPreview(LLMouseHandler* handl
 }
 
 //-----------------------------------------------------------------------------
-// LLModelLoader
+// LLModelPreview
 //-----------------------------------------------------------------------------
-LLModelLoader::LLModelLoader( std::string filename, S32 lod, LLModelPreview* preview, JointTransformMap& jointMap, 
-							  std::deque<std::string>& jointsFromNodes )
-: mJointList( jointMap )
-, mJointsFromNode( jointsFromNodes )
-, LLThread("Model Loader"), mFilename(filename), mLod(lod), mPreview(preview), mFirstTransform(TRUE), mNumOfFetchingTextures(0)
+
+LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)
+: LLViewerDynamicTexture(width, height, 3, ORDER_MIDDLE, FALSE), LLMutex(NULL)
+, mPelvisZOffset( 0.0f )
+, mLegacyRigValid( false )
+, mRigValidJointUpload( false )
+, mPhysicsSearchLOD( LLModel::LOD_PHYSICS )
+, mResetJoints( false )
+, mModelNoErrors( true )
+, mRigParityWithScene( false )
+, mLastJointUpdate( false )
 {
-	mJointMap["mPelvis"] = "mPelvis";
-	mJointMap["mTorso"] = "mTorso";
-	mJointMap["mChest"] = "mChest";
-	mJointMap["mNeck"] = "mNeck";
-	mJointMap["mHead"] = "mHead";
-	mJointMap["mSkull"] = "mSkull";
-	mJointMap["mEyeRight"] = "mEyeRight";
-	mJointMap["mEyeLeft"] = "mEyeLeft";
-	mJointMap["mCollarLeft"] = "mCollarLeft";
-	mJointMap["mShoulderLeft"] = "mShoulderLeft";
-	mJointMap["mElbowLeft"] = "mElbowLeft";
-	mJointMap["mWristLeft"] = "mWristLeft";
-	mJointMap["mCollarRight"] = "mCollarRight";
-	mJointMap["mShoulderRight"] = "mShoulderRight";
-	mJointMap["mElbowRight"] = "mElbowRight";
-	mJointMap["mWristRight"] = "mWristRight";
-	mJointMap["mHipRight"] = "mHipRight";
-	mJointMap["mKneeRight"] = "mKneeRight";
-	mJointMap["mAnkleRight"] = "mAnkleRight";
-	mJointMap["mFootRight"] = "mFootRight";
-	mJointMap["mToeRight"] = "mToeRight";
-	mJointMap["mHipLeft"] = "mHipLeft";
-	mJointMap["mKneeLeft"] = "mKneeLeft";
-	mJointMap["mAnkleLeft"] = "mAnkleLeft";
-	mJointMap["mFootLeft"] = "mFootLeft";
-	mJointMap["mToeLeft"] = "mToeLeft";
-
-	mJointMap["avatar_mPelvis"] = "mPelvis";
-	mJointMap["avatar_mTorso"] = "mTorso";
-	mJointMap["avatar_mChest"] = "mChest";
-	mJointMap["avatar_mNeck"] = "mNeck";
-	mJointMap["avatar_mHead"] = "mHead";
-	mJointMap["avatar_mSkull"] = "mSkull";
-	mJointMap["avatar_mEyeRight"] = "mEyeRight";
-	mJointMap["avatar_mEyeLeft"] = "mEyeLeft";
-	mJointMap["avatar_mCollarLeft"] = "mCollarLeft";
-	mJointMap["avatar_mShoulderLeft"] = "mShoulderLeft";
-	mJointMap["avatar_mElbowLeft"] = "mElbowLeft";
-	mJointMap["avatar_mWristLeft"] = "mWristLeft";
-	mJointMap["avatar_mCollarRight"] = "mCollarRight";
-	mJointMap["avatar_mShoulderRight"] = "mShoulderRight";
-	mJointMap["avatar_mElbowRight"] = "mElbowRight";
-	mJointMap["avatar_mWristRight"] = "mWristRight";
-	mJointMap["avatar_mHipRight"] = "mHipRight";
-	mJointMap["avatar_mKneeRight"] = "mKneeRight";
-	mJointMap["avatar_mAnkleRight"] = "mAnkleRight";
-	mJointMap["avatar_mFootRight"] = "mFootRight";
-	mJointMap["avatar_mToeRight"] = "mToeRight";
-	mJointMap["avatar_mHipLeft"] = "mHipLeft";
-	mJointMap["avatar_mKneeLeft"] = "mKneeLeft";
-	mJointMap["avatar_mAnkleLeft"] = "mAnkleLeft";
-	mJointMap["avatar_mFootLeft"] = "mFootLeft";
-	mJointMap["avatar_mToeLeft"] = "mToeLeft";
-
-
-	mJointMap["hip"] = "mPelvis";
-	mJointMap["abdomen"] = "mTorso";
-	mJointMap["chest"] = "mChest";
-	mJointMap["neck"] = "mNeck";
-	mJointMap["head"] = "mHead";
-	mJointMap["figureHair"] = "mSkull";
-	mJointMap["lCollar"] = "mCollarLeft";
-	mJointMap["lShldr"] = "mShoulderLeft";
-	mJointMap["lForeArm"] = "mElbowLeft";
-	mJointMap["lHand"] = "mWristLeft";
-	mJointMap["rCollar"] = "mCollarRight";
-	mJointMap["rShldr"] = "mShoulderRight";
-	mJointMap["rForeArm"] = "mElbowRight";
-	mJointMap["rHand"] = "mWristRight";
-	mJointMap["rThigh"] = "mHipRight";
-	mJointMap["rShin"] = "mKneeRight";
-	mJointMap["rFoot"] = "mFootRight";
-	mJointMap["lThigh"] = "mHipLeft";
-	mJointMap["lShin"] = "mKneeLeft";
-	mJointMap["lFoot"] = "mFootLeft";
-
-	if (mPreview)
-	{
-		//only try to load from slm if viewer is configured to do so and this is the 
-		//initial model load (not an LoD or physics shape)
-		mTrySLM = gSavedSettings.getBOOL("MeshImportUseSLM") && mPreview->mUploadData.empty();
-		mPreview->setLoadState(STARTING);
-	}
-	else
+	mNeedsUpdate = TRUE;
+	mCameraDistance = 0.f;
+	mCameraYaw = 0.f;
+	mCameraPitch = 0.f;
+	mCameraZoom = 1.f;
+	mTextureName = 0;
+	mPreviewLOD = 0;
+	mModelLoader = NULL;
+	mMaxTriangleLimit = 0;
+	mDirty = false;
+	mGenLOD = false;
+	mLoading = false;
+	mLoadState = LLModelLoader::STARTING;
+	mGroup = 0;
+	mLODFrozen = false;
+	mBuildShareTolerance = 0.f;
+	mBuildQueueMode = GLOD_QUEUE_GREEDY;
+	mBuildBorderMode = GLOD_BORDER_UNLOCK;
+	mBuildOperator = GLOD_OPERATOR_EDGE_COLLAPSE;
+
+	for (U32 i = 0; i < LLModel::NUM_LODS; ++i)
 	{
-		mTrySLM = false;
+		mRequestedTriangleCount[i] = 0;
+		mRequestedCreaseAngle[i] = -1.f;
+		mRequestedLoDMode[i] = 0;
+		mRequestedErrorThreshold[i] = 0.f;
+		mRequestedBuildOperator[i] = 0;
+		mRequestedQueueMode[i] = 0;
+		mRequestedBorderMode[i] = 0;
+		mRequestedShareTolerance[i] = 0.f;
 	}
 
-	assert_main_thread();
-	sActiveLoaderList.push_back(this) ;
+	mViewOption["show_textures"] = false;
+
+	mFMP = fmp;
+
+	mHasPivot = false;
+	mModelPivot = LLVector3( 0.0f, 0.0f, 0.0f );
+	
+	glodInit();
+
+	createPreviewAvatar();
 }
 
-LLModelLoader::~LLModelLoader()
+LLModelPreview::~LLModelPreview()
 {
-	assert_main_thread();
-	sActiveLoaderList.remove(this);
+	// glod apparently has internal mem alignment issues that are angering
+	// the heap-check code in windows, these should be hunted down in that
+	// TP code, if possible
+	//
+	// kernel32.dll!HeapFree()  + 0x14 bytes	
+	// msvcr100.dll!free(void * pBlock)  Line 51	C
+	// glod.dll!glodGetGroupParameteriv()  + 0x119 bytes	
+	// glod.dll!glodShutdown()  + 0x77 bytes	
+	//
+	//glodShutdown();
 }
 
-void stretch_extents(LLModel* model, LLMatrix4a& mat, LLVector4a& min, LLVector4a& max, BOOL& first_transform)
+U32 LLModelPreview::calcResourceCost()
 {
-	LLVector4a box[] =
-	{
-		LLVector4a(-1, 1,-1),
-		LLVector4a(-1, 1, 1),
-		LLVector4a(-1,-1,-1),
-		LLVector4a(-1,-1, 1),
-		LLVector4a( 1, 1,-1),
-		LLVector4a( 1, 1, 1),
-		LLVector4a( 1,-1,-1),
-		LLVector4a( 1,-1, 1),
-	};
-
-	for (S32 j = 0; j < model->getNumVolumeFaces(); ++j)
-	{
-		const LLVolumeFace& face = model->getVolumeFace(j);
+	assert_main_thread();
 
-		LLVector4a center;
-		center.setAdd(face.mExtents[0], face.mExtents[1]);
-		center.mul(0.5f);
-		LLVector4a size;
-		size.setSub(face.mExtents[1],face.mExtents[0]);
-		size.mul(0.5f);
+	rebuildUploadData();
 
-		for (U32 i = 0; i < 8; i++)
+	//Upload skin is selected BUT check to see if the joints coming in from the asset were malformed.
+	if ( mFMP && mFMP->childGetValue("upload_skin").asBoolean() )
+	{
+		bool uploadingJointPositions = mFMP->childGetValue("upload_joints").asBoolean();
+		if ( uploadingJointPositions && !isRigValidForJointPositionUpload() )
 		{
-			LLVector4a t;
-			t.setMul(size, box[i]);
-			t.add(center);
+			mFMP->childDisable("ok_btn");		
+		}		
+	}
+	
+	std::set<LLModel*> accounted;
+	U32 num_points = 0;
+	U32 num_hulls = 0;
 
-			LLVector4a v;
+	F32 debug_scale = mFMP ? mFMP->childGetValue("import_scale").asReal() : 1.f;
+	mPelvisZOffset = mFMP ? mFMP->childGetValue("pelvis_offset").asReal() : 3.0f;
+	
+	if ( mFMP && mFMP->childGetValue("upload_joints").asBoolean() )
+	{
+		// FIXME if preview avatar ever gets reused, this fake mesh ID stuff will fail.
+		// see also call to addAttachmentPosOverride.
+		LLUUID fake_mesh_id;
+		fake_mesh_id.generate();
+		getPreviewAvatar()->addPelvisFixup( mPelvisZOffset, fake_mesh_id );
+	}
 
-			mat.affineTransform(t, v);
+	F32 streaming_cost = 0.f;
+	F32 physics_cost = 0.f;
+	for (U32 i = 0; i < mUploadData.size(); ++i)
+	{
+		LLModelInstance& instance = mUploadData[i];
+		
+		if (accounted.find(instance.mModel) == accounted.end())
+		{
+			accounted.insert(instance.mModel);
 
-			if (first_transform)
-			{
-				first_transform = FALSE;
-				min = max = v;
+			LLModel::Decomposition& decomp =
+			instance.mLOD[LLModel::LOD_PHYSICS] ?
+			instance.mLOD[LLModel::LOD_PHYSICS]->mPhysics :
+			instance.mModel->mPhysics;
+			
+			//update instance skin info for each lods pelvisZoffset 
+			for ( int j=0; j<LLModel::NUM_LODS; ++j )
+			{	
+				if ( instance.mLOD[j] )
+				{
+					instance.mLOD[j]->mSkinInfo.mPelvisOffset = mPelvisZOffset;
+				}
 			}
-			else
+
+			std::stringstream ostr;
+			LLSD ret = LLModel::writeModel(ostr,
+					   instance.mLOD[4],
+					   instance.mLOD[3],
+					   instance.mLOD[2],
+					   instance.mLOD[1],
+					   instance.mLOD[0],
+					   decomp,
+					   mFMP->childGetValue("upload_skin").asBoolean(),
+					   mFMP->childGetValue("upload_joints").asBoolean(),
+					   TRUE,
+						FALSE,
+						instance.mModel->mSubmodelID);
+			
+			num_hulls += decomp.mHull.size();
+			for (U32 i = 0; i < decomp.mHull.size(); ++i)
 			{
-				update_min_max(min, max, v);
+				num_points += decomp.mHull[i].size();
 			}
+
+			//calculate streaming cost
+			LLMatrix4 transformation = instance.mTransform;
+
+			LLVector3 position = LLVector3(0, 0, 0) * transformation;
+
+			LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position;
+			LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position;
+			LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position;
+			F32 x_length = x_transformed.normalize();
+			F32 y_length = y_transformed.normalize();
+			F32 z_length = z_transformed.normalize();
+			LLVector3 scale = LLVector3(x_length, y_length, z_length);
+
+			F32 radius = scale.length()*0.5f*debug_scale;
+
+			streaming_cost += LLMeshRepository::getStreamingCost(ret, radius);
 		}
 	}
-}
 
-void stretch_extents(LLModel* model, LLMatrix4& mat, LLVector3& min, LLVector3& max, BOOL& first_transform)
-{
-	LLVector4a mina, maxa;
-	LLMatrix4a mata;
+	F32 scale = mFMP ? mFMP->childGetValue("import_scale").asReal()*2.f : 2.f;
 
-	mata.loadu(mat);
-	mina.load3(min.mV);
-	maxa.load3(max.mV);
+	mDetailsSignal(mPreviewScale[0]*scale, mPreviewScale[1]*scale, mPreviewScale[2]*scale, streaming_cost, physics_cost);
 
-	stretch_extents(model, mata, mina, maxa, first_transform);
+	updateStatusMessages();
 
-	min.set(mina.getF32ptr());
-	max.set(maxa.getF32ptr());
+	return (U32) streaming_cost;
 }
 
-void LLModelLoader::run()
+void LLFloaterModelPreview::setDetails(F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost)
 {
-	doLoadModel();
-	doOnIdleOneTime(boost::bind(&LLModelLoader::loadModelCallback,this));
+	assert_main_thread();
+	childSetTextArg("import_dimensions", "[X]", llformat("%.3f", x));
+	childSetTextArg("import_dimensions", "[Y]", llformat("%.3f", y));
+	childSetTextArg("import_dimensions", "[Z]", llformat("%.3f", z));
 }
 
-bool LLModelLoader::doLoadModel()
+
+void LLModelPreview::rebuildUploadData()
 {
-	//first, look for a .slm file of the same name that was modified later
-	//than the .dae
+	assert_main_thread();
 
-	if (mTrySLM)
-	{
-		std::string filename = mFilename;
-			
-		std::string::size_type i = filename.rfind(".");
-		if (i != std::string::npos)
-		{
-			filename.replace(i, filename.size()-1, ".slm");
-			llstat slm_status;
-			if (LLFile::stat(filename, &slm_status) == 0)
-			{ //slm file exists
-				llstat dae_status;
-				if (LLFile::stat(mFilename, &dae_status) != 0 ||
-					dae_status.st_mtime < slm_status.st_mtime)
-				{
-					if (loadFromSLM(filename))
-					{ //slm successfully loaded, if this fails, fall through and
-						//try loading from dae
-
-						mLod = -1; //successfully loading from an slm implicitly sets all 
-									//LoDs
-						return true;
-					}
-				}
-			}	
-		}
-	}
-
-	//no suitable slm exists, load from the .dae file
-	DAE dae;
-	domCOLLADA* dom = dae.open(mFilename);
-	
-	if (!dom)
-	{
-		LL_INFOS()<<" Error with dae - traditionally indicates a corrupt file."<<LL_ENDL;
-		setLoadState( ERROR_PARSING );
-		return false;
-	}
-	//Dom version
-	daeString domVersion = dae.getDomVersion();
-	std::string sldom(domVersion);
-	LL_INFOS()<<"Collada Importer Version: "<<sldom<<LL_ENDL;
-	//Dae version
-	domVersionType docVersion = dom->getVersion();
-	//0=1.4
-	//1=1.4.1
-	//2=Currently unsupported, however may work
-	if (docVersion > 1 ) 
-	{ 
-		docVersion = VERSIONTYPE_COUNT;
-	}
-	LL_INFOS()<<"Dae version "<<colladaVersion[docVersion]<<LL_ENDL;
-	
-	
-	daeDatabase* db = dae.getDatabase();
-	
-	daeInt count = db->getElementCount(NULL, COLLADA_TYPE_MESH);
-	
-	daeDocument* doc = dae.getDoc(mFilename);
-	if (!doc)
-	{
-		LL_WARNS() << "can't find internal doc" << LL_ENDL;
-		return false;
-	}
-	
-	daeElement* root = doc->getDomRoot();
-	if (!root)
-	{
-		LL_WARNS() << "document has no root" << LL_ENDL;
-		return false;
-	}
-	
-	//Verify some basic properties of the dae
-	//1. Basic validity check on controller 
-	U32 controllerCount = (int) db->getElementCount( NULL, "controller" );
-	bool result = false;
-	for ( int i=0; i<controllerCount; ++i )
-	{
-		domController* pController = NULL;
-		db->getElement( (daeElement**) &pController, i , NULL, "controller" );
-		result = mPreview->verifyController( pController );
-		if (!result)
-		{
-			setLoadState( ERROR_PARSING );
-			return true;
-		}
-	}
-
-
-	//get unit scale
-	mTransform.setIdentity();
-	
-	domAsset::domUnit* unit = daeSafeCast<domAsset::domUnit>(root->getDescendant(daeElement::matchType(domAsset::domUnit::ID())));
-	
-	if (unit)
-	{
-		F32 meter = unit->getMeter();
-		mTransform.mMatrix[0][0] = meter;
-		mTransform.mMatrix[1][1] = meter;
-		mTransform.mMatrix[2][2] = meter;
-	}
-	
-	//get up axis rotation
-	LLMatrix4 rotation;
-	
-	domUpAxisType up = UPAXISTYPE_Y_UP;  // default is Y_UP
-	domAsset::domUp_axis* up_axis =
-	daeSafeCast<domAsset::domUp_axis>(root->getDescendant(daeElement::matchType(domAsset::domUp_axis::ID())));
-	
-	if (up_axis)
-	{
-		up = up_axis->getValue();
-	}
-	
-	if (up == UPAXISTYPE_X_UP)
-	{
-		rotation.initRotation(0.0f, 90.0f * DEG_TO_RAD, 0.0f);
-	}
-	else if (up == UPAXISTYPE_Y_UP)
-	{
-		rotation.initRotation(90.0f * DEG_TO_RAD, 0.0f, 0.0f);
-	}
-	
-	rotation *= mTransform;
-	mTransform = rotation;
-	
-	
-	for (daeInt idx = 0; idx < count; ++idx)
-	{ //build map of domEntities to LLModel
-		domMesh* mesh = NULL;
-		db->getElement((daeElement**) &mesh, idx, NULL, COLLADA_TYPE_MESH);
-		
-		if (mesh)
-		{
-			LLPointer<LLModel> model = LLModel::loadModelFromDomMesh(mesh);
-			
-			if(model->getStatus() != LLModel::NO_ERRORS)
-			{
-				setLoadState(ERROR_PARSING + model->getStatus()) ;
-				return false; //abort
-			}
-
-			if (model.notNull() && validate_model(model))
-			{
-				mModelList.push_back(model);
-				mModel[mesh] = model;
-			}
-		}
-	}
-	
-	count = db->getElementCount(NULL, COLLADA_TYPE_SKIN);
-	for (daeInt idx = 0; idx < count; ++idx)
-	{ //add skinned meshes as instances
-		domSkin* skin = NULL;
-		db->getElement((daeElement**) &skin, idx, NULL, COLLADA_TYPE_SKIN);
-		
-		if (skin)
-		{
-			domGeometry* geom = daeSafeCast<domGeometry>(skin->getSource().getElement());
-			
-			if (geom)
-			{
-				domMesh* mesh = geom->getMesh();
-				if (mesh)
-				{
-					LLModel* model = mModel[mesh];
-					if (model)
-					{
-						LLVector3 mesh_scale_vector;
-						LLVector3 mesh_translation_vector;
-						model->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector);
-						
-						LLMatrix4 normalized_transformation;
-						normalized_transformation.setTranslation(mesh_translation_vector);
-						
-						LLMatrix4 mesh_scale;
-						mesh_scale.initScale(mesh_scale_vector);
-						mesh_scale *= normalized_transformation;
-						normalized_transformation = mesh_scale;
-						
-						glh::matrix4f inv_mat((F32*) normalized_transformation.mMatrix);
-						inv_mat = inv_mat.inverse();
-						LLMatrix4 inverse_normalized_transformation(inv_mat.m);
-						
-						domSkin::domBind_shape_matrix* bind_mat = skin->getBind_shape_matrix();
-						
-						if (bind_mat)
-						{ //get bind shape matrix
-							domFloat4x4& dom_value = bind_mat->getValue();
-							
-							LLMeshSkinInfo& skin_info = model->mSkinInfo;
-
-							for (int i = 0; i < 4; i++)
-							{
-								for(int j = 0; j < 4; j++)
-								{
-									skin_info.mBindShapeMatrix.mMatrix[i][j] = dom_value[i + j*4];
-								}
-							}
-							
-							LLMatrix4 trans = normalized_transformation;
-							trans *= skin_info.mBindShapeMatrix;
-							skin_info.mBindShapeMatrix = trans;							
-						}
-										
-											
-						//Some collada setup for accessing the skeleton
-						daeElement* pElement = 0;
-						dae.getDatabase()->getElement( &pElement, 0, 0, "skeleton" );
-						
-						//Try to get at the skeletal instance controller
-						domInstance_controller::domSkeleton* pSkeleton = daeSafeCast<domInstance_controller::domSkeleton>( pElement );
-						bool missingSkeletonOrScene = false;
-						
-						//If no skeleton, do a breadth-first search to get at specific joints
-						bool rootNode = false;
-						
-						//Need to test for a skeleton that does not have a root node
-						//This occurs when your instance controller does not have an associated scene 
-						if ( pSkeleton )
-						{
-							daeElement* pSkeletonRootNode = pSkeleton->getValue().getElement();
-							if ( pSkeletonRootNode )
-							{
-								rootNode = true;
-							}
-
-						}
-						if ( !pSkeleton || !rootNode )
-						{
-							daeElement* pScene = root->getDescendant("visual_scene");
-							if ( !pScene )
-							{
-								LL_WARNS()<<"No visual scene - unable to parse bone offsets "<<LL_ENDL;
-								missingSkeletonOrScene = true;
-							}
-							else
-							{
-								//Get the children at this level
-								daeTArray< daeSmartRef<daeElement> > children = pScene->getChildren();
-								S32 childCount = children.getCount();
-								
-								//Process any children that are joints
-								//Not all children are joints, some code be ambient lights, cameras, geometry etc..
-								for (S32 i = 0; i < childCount; ++i)
-								{
-									domNode* pNode = daeSafeCast<domNode>(children[i]);
-									if ( isNodeAJoint( pNode ) )
-									{
-										processJointNode( pNode, mJointList );
-									}
-								}
-							}
-						}
-						else
-							//Has Skeleton
-						{
-							//Get the root node of the skeleton
-							daeElement* pSkeletonRootNode = pSkeleton->getValue().getElement();
-							if ( pSkeletonRootNode )
-							{
-								//Once we have the root node - start acccessing it's joint components
-								const int jointCnt = mJointMap.size();
-								std::map<std::string, std::string> :: const_iterator jointIt = mJointMap.begin();
-								
-								//Loop over all the possible joints within the .dae - using the allowed joint list in the ctor.
-								for ( int i=0; i<jointCnt; ++i, ++jointIt )
-								{
-									//Build a joint for the resolver to work with
-									char str[64]={0};
-									sprintf(str,"./%s",(*jointIt).first.c_str() );
-									//LL_WARNS()<<"Joint "<< str <<LL_ENDL;
-									
-									//Setup the resolver
-                                    daeSIDResolver resolver( pSkeletonRootNode, str );
-									
-                                    //Look for the joint
-                                    domNode* pJoint = daeSafeCast<domNode>( resolver.getElement() );
-                                    if ( pJoint )
-                                    {
-										//Pull out the translate id and store it in the jointTranslations map
-										daeSIDResolver jointResolverA( pJoint, "./translate" );
-										domTranslate* pTranslateA = daeSafeCast<domTranslate>( jointResolverA.getElement() );
-										daeSIDResolver jointResolverB( pJoint, "./location" );
-										domTranslate* pTranslateB = daeSafeCast<domTranslate>( jointResolverB.getElement() );
-										
-										LLMatrix4 workingTransform;
-										
-										//Translation via SID
-										if ( pTranslateA )
-										{
-											extractTranslation( pTranslateA, workingTransform );
-										}
-										else
-										if ( pTranslateB )
-										{
-											extractTranslation( pTranslateB, workingTransform );
-										}
-										else
-										{
-											//Translation via child from element
-											daeElement* pTranslateElement = getChildFromElement( pJoint, "translate" );
-											if ( pTranslateElement && pTranslateElement->typeID() != domTranslate::ID() )
-											{
-												LL_WARNS()<< "The found element is not a translate node" <<LL_ENDL;
-												missingSkeletonOrScene = true;
-											}
-											else
-											if ( pTranslateElement )
-											{
-												extractTranslationViaElement( pTranslateElement, workingTransform );
-											}
-											else
-											{
-												extractTranslationViaSID( pJoint, workingTransform );
-											}
-
-										}
-										
-										//Store the joint transform w/respect to it's name.
-										mJointList[(*jointIt).second.c_str()] = workingTransform;
-                                    }
-								}
-								
-								//If anything failed in regards to extracting the skeleton, joints or translation id,
-								//mention it
-								if ( missingSkeletonOrScene  )
-								{
-									LL_WARNS()<< "Partial jointmap found in asset - did you mean to just have a partial map?" << LL_ENDL;
-								}
-							}//got skeleton?
-						}
-						
-						
-						domSkin::domJoints* joints = skin->getJoints();
-						
-						domInputLocal_Array& joint_input = joints->getInput_array();
-						
-						for (size_t i = 0; i < joint_input.getCount(); ++i)
-						{
-							domInputLocal* input = joint_input.get(i);
-							xsNMTOKEN semantic = input->getSemantic();
-							
-							if (strcmp(semantic, COMMON_PROFILE_INPUT_JOINT) == 0)
-							{ //found joint source, fill model->mJointMap and model->mSkinInfo.mJointNames
-								daeElement* elem = input->getSource().getElement();
-								
-								domSource* source = daeSafeCast<domSource>(elem);
-								if (source)
-								{
-									
-									
-									domName_array* names_source = source->getName_array();
-									
-									if (names_source)
-									{
-										domListOfNames &names = names_source->getValue();
-										
-										for (size_t j = 0; j < names.getCount(); ++j)
-										{
-											std::string name(names.get(j));
-											if (mJointMap.find(name) != mJointMap.end())
-											{
-												name = mJointMap[name];
-											}
-											model->mSkinInfo.mJointNames.push_back(name);
-											model->mSkinInfo.mJointMap[name] = j;
-										}
-									}
-									else
-									{
-										domIDREF_array* names_source = source->getIDREF_array();
-										if (names_source)
-										{
-											xsIDREFS& names = names_source->getValue();
-											
-											for (size_t j = 0; j < names.getCount(); ++j)
-											{
-												std::string name(names.get(j).getID());
-												if (mJointMap.find(name) != mJointMap.end())
-												{
-													name = mJointMap[name];
-												}
-												model->mSkinInfo.mJointNames.push_back(name);
-												model->mSkinInfo.mJointMap[name] = j;
-											}
-										}
-									}
-								}
-							}
-							else if (strcmp(semantic, COMMON_PROFILE_INPUT_INV_BIND_MATRIX) == 0)
-							{ //found inv_bind_matrix array, fill model->mInvBindMatrix
-								domSource* source = daeSafeCast<domSource>(input->getSource().getElement());
-								if (source)
-								{
-									domFloat_array* t = source->getFloat_array();
-									if (t)
-									{
-										domListOfFloats& transform = t->getValue();
-										S32 count = transform.getCount()/16;
-										
-										for (S32 k = 0; k < count; ++k)
-										{
-											LLMatrix4 mat;
-											
-											for (int i = 0; i < 4; i++)
-											{
-												for(int j = 0; j < 4; j++)
-												{
-													mat.mMatrix[i][j] = transform[k*16 + i + j*4];
-												}
-											}
-											
-											model->mSkinInfo.mInvBindMatrix.push_back(mat);											
-										}
-									}
-								}
-							}
-						}
-						
-						//Now that we've parsed the joint array, let's determine if we have a full rig
-						//(which means we have all the joint sthat are required for an avatar versus
-						//a skinned asset attached to a node in a file that contains an entire skeleton,
-						//but does not use the skeleton).						
-						buildJointToNodeMappingFromScene( root );
-						mPreview->critiqueRigForUploadApplicability( model->mSkinInfo.mJointNames );
-										
-						if ( !missingSkeletonOrScene )
-						{
-							//Set the joint translations on the avatar - if it's a full mapping
-							//The joints are reset in the dtor
-							if ( mPreview->getRigWithSceneParity() )
-							{	
-								std::map<std::string, std::string> :: const_iterator masterJointIt = mJointMap.begin();
-								std::map<std::string, std::string> :: const_iterator masterJointItEnd = mJointMap.end();
-								for (;masterJointIt!=masterJointItEnd;++masterJointIt )
-								{
-									std::string lookingForJoint = (*masterJointIt).first.c_str();
-									
-									if ( mJointList.find( lookingForJoint ) != mJointList.end() )
-									{
-										//LL_INFOS()<<"joint "<<lookingForJoint.c_str()<<LL_ENDL;
-										LLMatrix4 jointTransform = mJointList[lookingForJoint];
-										LLJoint* pJoint = mPreview->getPreviewAvatar()->getJoint( lookingForJoint );
-										if ( pJoint )
-										{   
-											LLUUID fake_mesh_id;
-											fake_mesh_id.generate();
-											pJoint->addAttachmentPosOverride( jointTransform.getTranslation(), fake_mesh_id, gAgentAvatarp->avString());
-										}
-										else
-										{
-											//Most likely an error in the asset.
-											LL_WARNS()<<"Tried to apply joint position from .dae, but it did not exist in the avatar rig." << LL_ENDL;
-										}
-									}
-								}
-							}
-						} //missingSkeletonOrScene
-						
-						
-						//We need to construct the alternate bind matrix (which contains the new joint positions)
-						//in the same order as they were stored in the joint buffer. The joints associated
-						//with the skeleton are not stored in the same order as they are in the exported joint buffer.
-						//This remaps the skeletal joints to be in the same order as the joints stored in the model.
-						std::vector<std::string> :: const_iterator jointIt  = model->mSkinInfo.mJointNames.begin();
-						const int jointCnt = model->mSkinInfo.mJointNames.size();
-						for ( int i=0; i<jointCnt; ++i, ++jointIt )
-						{
-							std::string lookingForJoint = (*jointIt).c_str();
-							//Look for the joint xform that we extracted from the skeleton, using the jointIt as the key
-							//and store it in the alternate bind matrix
-							if ( mJointList.find( lookingForJoint ) != mJointList.end() )
-							{
-								LLMatrix4 jointTransform = mJointList[lookingForJoint];
-								LLMatrix4 newInverse = model->mSkinInfo.mInvBindMatrix[i];
-								newInverse.setTranslation( mJointList[lookingForJoint].getTranslation() );
-								model->mSkinInfo.mAlternateBindMatrix.push_back( newInverse );
-							}
-							else
-							{
-								LL_WARNS()<<"Possibly misnamed/missing joint [" <<lookingForJoint.c_str()<<" ] "<<LL_ENDL;
-							}
-						}
-						
-						//grab raw position array
-						
-						domVertices* verts = mesh->getVertices();
-						if (verts)
-						{
-							domInputLocal_Array& inputs = verts->getInput_array();
-							for (size_t i = 0; i < inputs.getCount() && model->mPosition.empty(); ++i)
-							{
-								if (strcmp(inputs[i]->getSemantic(), COMMON_PROFILE_INPUT_POSITION) == 0)
-								{
-									domSource* pos_source = daeSafeCast<domSource>(inputs[i]->getSource().getElement());
-									if (pos_source)
-									{
-										domFloat_array* pos_array = pos_source->getFloat_array();
-										if (pos_array)
-										{
-											domListOfFloats& pos = pos_array->getValue();
-											
-											for (size_t j = 0; j < pos.getCount(); j += 3)
-											{
-												if (pos.getCount() <= j+2)
-												{
-													LL_ERRS() << "Invalid position array size." << LL_ENDL;
-												}
-												
-												LLVector3 v(pos[j], pos[j+1], pos[j+2]);
-												
-												//transform from COLLADA space to volume space
-												v = v * inverse_normalized_transformation;
-												
-												model->mPosition.push_back(v);
-											}
-										}
-									}
-								}
-							}
-						}
-						
-						//grab skin weights array
-						domSkin::domVertex_weights* weights = skin->getVertex_weights();
-						if (weights)
-						{
-							domInputLocalOffset_Array& inputs = weights->getInput_array();
-							domFloat_array* vertex_weights = NULL;
-							for (size_t i = 0; i < inputs.getCount(); ++i)
-							{
-								if (strcmp(inputs[i]->getSemantic(), COMMON_PROFILE_INPUT_WEIGHT) == 0)
-								{
-									domSource* weight_source = daeSafeCast<domSource>(inputs[i]->getSource().getElement());
-									if (weight_source)
-									{
-										vertex_weights = weight_source->getFloat_array();
-									}
-								}
-							}
-							
-							if (vertex_weights)
-							{
-								domListOfFloats& w = vertex_weights->getValue();
-								domListOfUInts& vcount = weights->getVcount()->getValue();
-								domListOfInts& v = weights->getV()->getValue();
-								
-								U32 c_idx = 0;
-								for (size_t vc_idx = 0; vc_idx < vcount.getCount(); ++vc_idx)
-								{ //for each vertex
-									daeUInt count = vcount[vc_idx];
-									
-									//create list of weights that influence this vertex
-									LLModel::weight_list weight_list;
-									
-									for (daeUInt i = 0; i < count; ++i)
-									{ //for each weight
-										daeInt joint_idx = v[c_idx++];
-										daeInt weight_idx = v[c_idx++];
-										
-										if (joint_idx == -1)
-										{
-											//ignore bindings to bind_shape_matrix
-											continue;
-										}
-										
-										F32 weight_value = w[weight_idx];
-										
-										weight_list.push_back(LLModel::JointWeight(joint_idx, weight_value));
-									}
-									
-									//sort by joint weight
-									std::sort(weight_list.begin(), weight_list.end(), LLModel::CompareWeightGreater());
-									
-									std::vector<LLModel::JointWeight> wght;
-									
-									F32 total = 0.f;
-									
-									for (U32 i = 0; i < llmin((U32) 4, (U32) weight_list.size()); ++i)
-									{ //take up to 4 most significant weights
-										if (weight_list[i].mWeight > 0.f)
-										{
-											wght.push_back( weight_list[i] );
-											total += weight_list[i].mWeight;
-										}
-									}
-									
-									F32 scale = 1.f/total;
-									if (scale != 1.f)
-									{ //normalize weights
-										for (U32 i = 0; i < wght.size(); ++i)
-										{
-											wght[i].mWeight *= scale;
-										}
-									}
-									
-									model->mSkinWeights[model->mPosition[vc_idx]] = wght;
-								}
-								
-								//add instance to scene for this model
-								
-								LLMatrix4 transformation = mTransform;
-								// adjust the transformation to compensate for mesh normalization
-								
-								LLMatrix4 mesh_translation;
-								mesh_translation.setTranslation(mesh_translation_vector);
-								mesh_translation *= transformation;
-								transformation = mesh_translation;
-								
-								LLMatrix4 mesh_scale;
-								mesh_scale.initScale(mesh_scale_vector);
-								mesh_scale *= transformation;
-								transformation = mesh_scale;
-								
-								std::map<std::string, LLImportMaterial> materials;
-								for (U32 i = 0; i < model->mMaterialList.size(); ++i)
-								{
-									materials[model->mMaterialList[i]] = LLImportMaterial();
-								}
-								mScene[transformation].push_back(LLModelInstance(model, model->mLabel, transformation, materials));
-								stretch_extents(model, transformation, mExtents[0], mExtents[1], mFirstTransform);
-							}
-						}
-					}
-				}
-			}
-		}
-	}
-	
-	daeElement* scene = root->getDescendant("visual_scene");
-	
-	if (!scene)
-	{
-		LL_WARNS() << "document has no visual_scene" << LL_ENDL;
-		setLoadState( ERROR_PARSING );
-		return true;
-	}
-	
-	setLoadState( DONE );
-
-	bool badElement = false;
-	
-	processElement( scene, badElement );
-	
-	if ( badElement )
-	{
-		setLoadState( ERROR_PARSING );
-	}
-	
-	return true;
-}
-
-void LLModelLoader::setLoadState(U32 state)
-{
-	if (mPreview)
-	{
-		mPreview->setLoadState(state);
-	}
-}
-
-bool LLModelLoader::loadFromSLM(const std::string& filename)
-{ 
-	//only need to populate mScene with data from slm
-	llstat stat;
-
-	if (LLFile::stat(filename, &stat))
-	{ //file does not exist
-		return false;
-	}
-
-	S32 file_size = (S32) stat.st_size;
-	
-	llifstream ifstream(filename.c_str(), std::ifstream::in | std::ifstream::binary);
-	LLSD data;
-	LLSDSerialize::fromBinary(data, ifstream, file_size);
-	ifstream.close();
-
-	//build model list for each LoD
-	model_list model[LLModel::NUM_LODS];
-
-	if (data["version"].asInteger() != SLM_SUPPORTED_VERSION)
-	{  //unsupported version
-		return false;
-	}
-
-	LLSD& mesh = data["mesh"];
-
-	LLVolumeParams volume_params;
-	volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
-
-	for (S32 lod = 0; lod < LLModel::NUM_LODS; ++lod)
-	{
-		for (U32 i = 0; i < mesh.size(); ++i)
-		{
-			std::stringstream str(mesh[i].asString());
-			LLPointer<LLModel> loaded_model = new LLModel(volume_params, (F32) lod);
-			if (loaded_model->loadModel(str))
-			{
-				loaded_model->mLocalID = i;
-				model[lod].push_back(loaded_model);
-
-				if (lod == LLModel::LOD_HIGH && !loaded_model->mSkinInfo.mJointNames.empty())
-				{ 
-					//check to see if rig is valid					
-					mPreview->critiqueRigForUploadApplicability( loaded_model->mSkinInfo.mJointNames );					
-				}
-			}
-		}
-	}	
-
-	if (model[LLModel::LOD_HIGH].empty())
-	{ //failed to load high lod
-		return false;
-	}
-
-	// Set name.
-	std::string name = data["name"];
-	if (!name.empty())
-	{
-		model[LLModel::LOD_HIGH][0]->mLabel = name;
-	}
-	
-
-	//load instance list
-	model_instance_list instance_list;
-
-	LLSD& instance = data["instance"];
-
-	for (U32 i = 0; i < instance.size(); ++i)
-	{
-		//deserialize instance list
-		instance_list.push_back(LLModelInstance(instance[i]));
-
-		//match up model instance pointers
-		S32 idx = instance_list[i].mLocalMeshID;
-
-		for (U32 lod = 0; lod < LLModel::NUM_LODS; ++lod)
-		{
-			if (model[lod].size() > idx)
-			{
-				instance_list[i].mLOD[lod] = model[lod][idx];
-			}
-			else if (!model[lod].empty())
-			{
-				// slm load failed - indexes are corrupted
-				return false;
-			}
-		}
-
-		instance_list[i].mModel = model[LLModel::LOD_HIGH][idx];
-	}
-
-
-	//convert instance_list to mScene
-	mFirstTransform = TRUE;
-	for (U32 i = 0; i < instance_list.size(); ++i)
-	{
-		LLModelInstance& cur_instance = instance_list[i];
-		mScene[cur_instance.mTransform].push_back(cur_instance);
-		stretch_extents(cur_instance.mModel, cur_instance.mTransform, mExtents[0], mExtents[1], mFirstTransform);
-	}
-	
-	setLoadState( DONE );
-
-	return true;
-}
-
-//static
-bool LLModelLoader::isAlive(LLModelLoader* loader)
-{
-	if(!loader)
-	{
-		return false ;
-	}
-
-	std::list<LLModelLoader*>::iterator iter = sActiveLoaderList.begin() ;
-	for(; iter != sActiveLoaderList.end() && (*iter) != loader; ++iter) ;
-	
-	return *iter == loader ;
-}
-
-void LLModelLoader::loadModelCallback()
-{
-	assert_main_thread();
-
-	if (mPreview)
-	{
-		mPreview->loadModelCallback(mLod);	
-	}
-
-	while (!isStopped())
-	{ //wait until this thread is stopped before deleting self
-		apr_sleep(100);
-	}
-
-	//doubel check if "this" is valid before deleting it, in case it is aborted during running.
-	if(!isAlive(this))
-	{
-		return ;
-	}
-
-	//cleanup model loader
-	if (mPreview)
-	{
-		mPreview->mModelLoader = NULL;
-	}
-
-	delete this;
-}
-//-----------------------------------------------------------------------------
-// buildJointToNodeMappingFromScene()
-//-----------------------------------------------------------------------------
-void LLModelLoader::buildJointToNodeMappingFromScene( daeElement* pRoot )
-{
-	daeElement* pScene = pRoot->getDescendant("visual_scene");
-	if ( pScene )
-	{
-		daeTArray< daeSmartRef<daeElement> > children = pScene->getChildren();
-		S32 childCount = children.getCount();
-		for (S32 i = 0; i < childCount; ++i)
-		{
-			domNode* pNode = daeSafeCast<domNode>(children[i]);
-			processJointToNodeMapping( pNode );			
-		}
-	}
-}
-//-----------------------------------------------------------------------------
-// processJointToNodeMapping()
-//-----------------------------------------------------------------------------
-void LLModelLoader::processJointToNodeMapping( domNode* pNode )
-{
-	if ( isNodeAJoint( pNode ) )
-	{
-		//1.Store the parent
-		std::string nodeName = pNode->getName();
-		if ( !nodeName.empty() )
-		{
-			mJointsFromNode.push_front( pNode->getName() );
-		}
-		//2. Handle the kiddo's
-		processChildJoints( pNode );
-	}
-	else
-	{
-		//Determine if the're any children wrt to this failed node.
-		//This occurs when an armature is exported and ends up being what essentially amounts to
-		//as the root for the visual_scene
-		if ( pNode ) 
-		{
-			processChildJoints( pNode );
-		}
-		else 
-		{
-			LL_INFOS()<<"Node is NULL"<<LL_ENDL;
-		}
-
-	}
-}
-//-----------------------------------------------------------------------------
-// processChildJoint()
-//-----------------------------------------------------------------------------
-void LLModelLoader::processChildJoints( domNode* pParentNode )
-{	
-	daeTArray< daeSmartRef<daeElement> > childOfChild = pParentNode->getChildren();
-	S32 childOfChildCount = childOfChild.getCount();
-	for (S32 i = 0; i < childOfChildCount; ++i)
-	{
-		domNode* pChildNode = daeSafeCast<domNode>( childOfChild[i] );
-		if ( pChildNode )
-		{
-			processJointToNodeMapping( pChildNode );
-		}
-	}
-}
-
-//-----------------------------------------------------------------------------
-// critiqueRigForUploadApplicability()
-//-----------------------------------------------------------------------------
-void LLModelPreview::critiqueRigForUploadApplicability( const std::vector<std::string> &jointListFromAsset )
-{
-	critiqueJointToNodeMappingFromScene();
-	
-	//Determines the following use cases for a rig:
-	//1. It is suitable for upload with skin weights & joint positions, or
-	//2. It is suitable for upload as standard av with just skin weights
-	
-	bool isJointPositionUploadOK = isRigSuitableForJointPositionUpload( jointListFromAsset );
-	bool isRigLegacyOK			 = isRigLegacy( jointListFromAsset );
-
-	//It's OK that both could end up being true, both default to false
-	if ( isJointPositionUploadOK )
-	{
-		setRigValidForJointPositionUpload( true );
-	}
-
-	if ( isRigLegacyOK) 
-	{	
-		setLegacyRigValid( true );
-	}
-
-}
-//-----------------------------------------------------------------------------
-// critiqueJointToNodeMappingFromScene()
-//-----------------------------------------------------------------------------
-void LLModelPreview::critiqueJointToNodeMappingFromScene( void  )
-{
-	//Do the actual nodes back the joint listing from the dae?
-	//if yes then this is a fully rigged asset, otherwise it's just a partial rig
-	
-	std::deque<std::string>::iterator jointsFromNodeIt = mJointsFromNode.begin();
-	std::deque<std::string>::iterator jointsFromNodeEndIt = mJointsFromNode.end();
-	bool result = true;
-
-	if ( !mJointsFromNode.empty() )
-	{
-		for ( ;jointsFromNodeIt!=jointsFromNodeEndIt;++jointsFromNodeIt )
-		{
-			std::string name = *jointsFromNodeIt;
-			if ( mJointTransformMap.find( name ) != mJointTransformMap.end() )
-			{
-				continue;
-			}
-			else
-			{
-				LL_INFOS()<<"critiqueJointToNodeMappingFromScene is missing a: "<<name<<LL_ENDL;
-				result = false;				
-			}
-		}
-	}
-	else
-	{
-		result = false;
-	}
-
-	//Determines the following use cases for a rig:
-	//1. Full av rig  w/1-1 mapping from the scene and joint array
-	//2. Partial rig but w/o parity between the scene and joint array
-	if ( result )
-	{		
-		setRigWithSceneParity( true );
-	}	
-}
-//-----------------------------------------------------------------------------
-// isRigLegacy()
-//-----------------------------------------------------------------------------
-bool LLModelPreview::isRigLegacy( const std::vector<std::string> &jointListFromAsset )
-{
-	//No joints in asset
-	if ( jointListFromAsset.size() == 0 )
-	{
-		return false;
-	}
-
-	bool result = false;
-
-	std::deque<std::string> :: const_iterator masterJointIt = mMasterLegacyJointList.begin();	
-	std::deque<std::string> :: const_iterator masterJointEndIt = mMasterLegacyJointList.end();
-	
-	std::vector<std::string> :: const_iterator modelJointIt = jointListFromAsset.begin();	
-	std::vector<std::string> :: const_iterator modelJointItEnd = jointListFromAsset.end();
-	
-	for ( ;masterJointIt!=masterJointEndIt;++masterJointIt )
-	{
-		result = false;
-		modelJointIt = jointListFromAsset.begin();
-
-		for ( ;modelJointIt!=modelJointItEnd; ++modelJointIt )
-		{
-			if ( *masterJointIt == *modelJointIt )
-			{
-				result = true;
-				break;
-			}			
-		}		
-		if ( !result )
-		{
-			LL_INFOS()<<" Asset did not contain the joint (if you're u/l a fully rigged asset w/joint positions - it is required)." << *masterJointIt<< LL_ENDL;
-			break;
-		}
-	}	
-	return result;
-}
-//-----------------------------------------------------------------------------
-// isRigSuitableForJointPositionUpload()
-//-----------------------------------------------------------------------------
-bool LLModelPreview::isRigSuitableForJointPositionUpload( const std::vector<std::string> &jointListFromAsset )
-{
-	bool result = false;
-
-	std::deque<std::string> :: const_iterator masterJointIt = mMasterJointList.begin();	
-	std::deque<std::string> :: const_iterator masterJointEndIt = mMasterJointList.end();
-	
-	std::vector<std::string> :: const_iterator modelJointIt = jointListFromAsset.begin();	
-	std::vector<std::string> :: const_iterator modelJointItEnd = jointListFromAsset.end();
-	
-	for ( ;masterJointIt!=masterJointEndIt;++masterJointIt )
-	{
-		result = false;
-		modelJointIt = jointListFromAsset.begin();
-
-		for ( ;modelJointIt!=modelJointItEnd; ++modelJointIt )
-		{
-			if ( *masterJointIt == *modelJointIt )
-			{
-				result = true;
-				break;
-			}			
-		}		
-		if ( !result )
-		{
-			LL_INFOS()<<" Asset did not contain the joint (if you're u/l a fully rigged asset w/joint positions - it is required)." << *masterJointIt<< LL_ENDL;
-			break;
-		}
-	}	
-	return result;
-}
-
-
-//called in the main thread
-void LLModelLoader::loadTextures()
-{
-	BOOL is_paused = isPaused() ;
-	pause() ; //pause the loader 
-
-	for(scene::iterator iter = mScene.begin(); iter != mScene.end(); ++iter)
-	{
-		for(U32 i = 0 ; i < iter->second.size(); i++)
-		{
-			for(std::map<std::string, LLImportMaterial>::iterator j = iter->second[i].mMaterial.begin();
-				j != iter->second[i].mMaterial.end(); ++j)
-			{
-				LLImportMaterial& material = j->second;
-
-				if(!material.mDiffuseMapFilename.empty())
-				{
-					material.mDiffuseMap = 
-						LLViewerTextureManager::getFetchedTextureFromUrl("file://" + material.mDiffuseMapFilename, FTT_LOCAL_FILE, TRUE, LLGLTexture::BOOST_PREVIEW);
-					material.mDiffuseMap->setLoadedCallback(LLModelPreview::textureLoadedCallback, 0, TRUE, FALSE, mPreview, NULL, FALSE);
-					material.mDiffuseMap->forceToSaveRawImage(0, F32_MAX);
-					mNumOfFetchingTextures++ ;
-				}
-			}
-		}
-	}
-
-	if(!is_paused)
-	{
-		unpause() ;
-	}
-}
-
-//-----------------------------------------------------------------------------
-// isNodeAJoint()
-//-----------------------------------------------------------------------------
-bool LLModelLoader::isNodeAJoint( domNode* pNode )
-{
-	if ( !pNode )
-	{
-		LL_INFOS()<<"Created node is NULL"<<LL_ENDL;
-		return false;
-	}
-	
-	if ( pNode->getName() == NULL )
-	{
-		LL_INFOS()<<"Parsed node has no name "<<LL_ENDL;
-		//Attempt to write the node id, if possible (aids in debugging the visual scene)
-		if ( pNode->getId() )
-		{
-			LL_INFOS()<<"Parsed node ID: "<<pNode->getId()<<LL_ENDL;
-		}
-		return false;
-	}
-
-	if ( mJointMap.find( pNode->getName() )  != mJointMap.end() )
-	{
-		return true;
-	}
-
-	return false;
-}
-//-----------------------------------------------------------------------------
-// verifyCount
-//-----------------------------------------------------------------------------
-bool LLModelPreview::verifyCount( int expected, int result )
-{
-	if ( expected != result )
-	{
-		LL_INFOS()<< "Error: (expected/got)"<<expected<<"/"<<result<<"verts"<<LL_ENDL;
-		return false;
-	}
-	return true;
-}
-//-----------------------------------------------------------------------------
-// verifyController
-//-----------------------------------------------------------------------------
-bool LLModelPreview::verifyController( domController* pController )
-{	
-
-	bool result = true;
-
-	domSkin* pSkin = pController->getSkin();
-
-	if ( pSkin )
-	{
-		xsAnyURI & uri = pSkin->getSource();
-		domElement* pElement = uri.getElement();
-
-		if ( !pElement )
-		{
-			LL_INFOS()<<"Can't resolve skin source"<<LL_ENDL;
-			return false;
-		}
-
-		daeString type_str = pElement->getTypeName();
-		if ( stricmp(type_str, "geometry") == 0 )
-		{	
-			//Skin is reference directly by geometry and get the vertex count from skin
-			domSkin::domVertex_weights* pVertexWeights = pSkin->getVertex_weights();
-			U32 vertexWeightsCount = pVertexWeights->getCount();
-			domGeometry* pGeometry = (domGeometry*) (domElement*) uri.getElement();
-			domMesh* pMesh = pGeometry->getMesh();				
-			
-			if ( pMesh )
-			{
-				//Get vertex count from geometry
-				domVertices* pVertices = pMesh->getVertices();
-				if ( !pVertices )
-				{ 
-					LL_INFOS()<<"No vertices!"<<LL_ENDL;
-					return false;
-				}
-
-				if ( pVertices )
-				{
-					xsAnyURI src = pVertices->getInput_array()[0]->getSource();
-					domSource* pSource = (domSource*) (domElement*) src.getElement();
-					U32 verticesCount = pSource->getTechnique_common()->getAccessor()->getCount();
-					result = verifyCount( verticesCount, vertexWeightsCount );
-					if ( !result )
-					{
-						return result;
-					}
-				}
-			}	
-
-			U32 vcountCount = (U32) pVertexWeights->getVcount()->getValue().getCount();
-			result = verifyCount( vcountCount, vertexWeightsCount );	
-			if ( !result )
-			{
-				return result;
-			}
-
-			domInputLocalOffset_Array& inputs = pVertexWeights->getInput_array();
-			U32 sum = 0;
-			for (size_t i=0; i<vcountCount; i++)
-			{
-				sum += pVertexWeights->getVcount()->getValue()[i];
-			}
-			result = verifyCount( sum * inputs.getCount(), (domInt) pVertexWeights->getV()->getValue().getCount() );
-		}
-	}
-	
-	return result;
-}
-
-//-----------------------------------------------------------------------------
-// extractTranslation()
-//-----------------------------------------------------------------------------
-void LLModelLoader::extractTranslation( domTranslate* pTranslate, LLMatrix4& transform )
-{
-	domFloat3 jointTrans = pTranslate->getValue();
-	LLVector3 singleJointTranslation( jointTrans[0], jointTrans[1], jointTrans[2] );
-	transform.setTranslation( singleJointTranslation );
-}
-//-----------------------------------------------------------------------------
-// extractTranslationViaElement()
-//-----------------------------------------------------------------------------
-void LLModelLoader::extractTranslationViaElement( daeElement* pTranslateElement, LLMatrix4& transform )
-{
-	if ( pTranslateElement )
-	{
-		domTranslate* pTranslateChild = dynamic_cast<domTranslate*>( pTranslateElement );
-		domFloat3 translateChild = pTranslateChild->getValue();
-		LLVector3 singleJointTranslation( translateChild[0], translateChild[1], translateChild[2] );
-		transform.setTranslation( singleJointTranslation );
-	}	
-}
-//-----------------------------------------------------------------------------
-// extractTranslationViaSID()
-//-----------------------------------------------------------------------------
-void LLModelLoader::extractTranslationViaSID( daeElement* pElement, LLMatrix4& transform )
-{
-	if ( pElement )
-	{	
-		daeSIDResolver resolver( pElement, "./transform" );
-		domMatrix* pMatrix = daeSafeCast<domMatrix>( resolver.getElement() );
-		//We are only extracting out the translational component atm
-		LLMatrix4 workingTransform;
-		if ( pMatrix )
-		{
-			domFloat4x4 domArray = pMatrix->getValue();									
-			for ( int i = 0; i < 4; i++ )
-			{
-				for( int j = 0; j < 4; j++ )
-				{
-					workingTransform.mMatrix[i][j] = domArray[i + j*4];
-				}
-			}
-			LLVector3 trans = workingTransform.getTranslation();
-			transform.setTranslation( trans );	
-		}
-	}
-	else
-	{
-		LL_WARNS()<<"Element is nonexistent - empty/unsupported node."<<LL_ENDL;
-	}
-}
-//-----------------------------------------------------------------------------
-// processJointNode()
-//-----------------------------------------------------------------------------
-void LLModelLoader::processJointNode( domNode* pNode, JointTransformMap& jointTransforms )
-{
-	if (pNode->getName() == NULL)
-	{
-		LL_WARNS() << "nameless node, can't process" << LL_ENDL;
-		return;
-	}
-
-	//LL_WARNS()<<"ProcessJointNode# Node:" <<pNode->getName()<<LL_ENDL;
-
-	//1. handle the incoming node - extract out translation via SID or element
-
-	LLMatrix4 workingTransform;
-
-	//Pull out the translate id and store it in the jointTranslations map
-	daeSIDResolver jointResolverA( pNode, "./translate" );
-	domTranslate* pTranslateA = daeSafeCast<domTranslate>( jointResolverA.getElement() );
-	daeSIDResolver jointResolverB( pNode, "./location" );
-	domTranslate* pTranslateB = daeSafeCast<domTranslate>( jointResolverB.getElement() );
-
-	//Translation via SID was successful
-	if ( pTranslateA )
-	{
-		extractTranslation( pTranslateA, workingTransform );
-	}
-	else
-	if ( pTranslateB )
-	{
-		extractTranslation( pTranslateB, workingTransform );
-	}
-	else
-	{
-		//Translation via child from element
-		daeElement* pTranslateElement = getChildFromElement( pNode, "translate" );
-		if ( !pTranslateElement || pTranslateElement->typeID() != domTranslate::ID() )
-		{
-			//LL_WARNS()<< "The found element is not a translate node" <<LL_ENDL;
-			daeSIDResolver jointResolver( pNode, "./matrix" );
-			domMatrix* pMatrix = daeSafeCast<domMatrix>( jointResolver.getElement() );
-			if ( pMatrix )
-			{
-				//LL_INFOS()<<"A matrix SID was however found!"<<LL_ENDL;
-				domFloat4x4 domArray = pMatrix->getValue();									
-				for ( int i = 0; i < 4; i++ )
-				{
-					for( int j = 0; j < 4; j++ )
-					{
-						workingTransform.mMatrix[i][j] = domArray[i + j*4];
-					}
-				}
-			}
-			else
-			{
-				LL_WARNS()<< "The found element is not translate or matrix node - most likely a corrupt export!" <<LL_ENDL;
-			}
-		}
-		else
-		{
-			extractTranslationViaElement( pTranslateElement, workingTransform );
-		}
-	}
-
-	//Store the working transform relative to the nodes name.
-	jointTransforms[ pNode->getName() ] = workingTransform;
-
-	//2. handle the nodes children
-
-	//Gather and handle the incoming nodes children
-	daeTArray< daeSmartRef<daeElement> > childOfChild = pNode->getChildren();
-	S32 childOfChildCount = childOfChild.getCount();
-
-	for (S32 i = 0; i < childOfChildCount; ++i)
-	{
-		domNode* pChildNode = daeSafeCast<domNode>( childOfChild[i] );
-		if ( pChildNode )
-		{
-			processJointNode( pChildNode, jointTransforms );
-		}
-	}
-}
-//-----------------------------------------------------------------------------
-// getChildFromElement()
-//-----------------------------------------------------------------------------
-daeElement* LLModelLoader::getChildFromElement( daeElement* pElement, std::string const & name )
-{
-    daeElement* pChildOfElement = pElement->getChild( name.c_str() );
-	if ( pChildOfElement )
-	{
-		return pChildOfElement;
-	}
-	LL_WARNS()<< "Could not find a child [" << name << "] for the element: \"" << pElement->getAttribute("id") << "\"" << LL_ENDL;
-    return NULL;
-}
-
-void LLModelLoader::processElement( daeElement* element, bool& badElement )
-{
-	LLMatrix4 saved_transform = mTransform;
-
-	domTranslate* translate = daeSafeCast<domTranslate>(element);
-	if (translate)
-	{
-		domFloat3 dom_value = translate->getValue();
-
-		LLMatrix4 translation;
-		translation.setTranslation(LLVector3(dom_value[0], dom_value[1], dom_value[2]));
-
-		translation *= mTransform;
-		mTransform = translation;
-	}
-
-	domRotate* rotate = daeSafeCast<domRotate>(element);
-	if (rotate)
-	{
-		domFloat4 dom_value = rotate->getValue();
-
-		LLMatrix4 rotation;
-		rotation.initRotTrans(dom_value[3] * DEG_TO_RAD, LLVector3(dom_value[0], dom_value[1], dom_value[2]), LLVector3(0, 0, 0));
-
-		rotation *= mTransform;
-		mTransform = rotation;
-	}
-
-	domScale* scale = daeSafeCast<domScale>(element);
-	if (scale)
-	{
-		domFloat3 dom_value = scale->getValue();
-
-
-		LLVector3 scale_vector = LLVector3(dom_value[0], dom_value[1], dom_value[2]);
-		scale_vector.abs(); // Set all values positive, since we don't currently support mirrored meshes
-		LLMatrix4 scaling;
-		scaling.initScale(scale_vector);
-
-		scaling *= mTransform;
-		mTransform = scaling;
-	}
-
-	domMatrix* matrix = daeSafeCast<domMatrix>(element);
-	if (matrix)
-	{
-		domFloat4x4 dom_value = matrix->getValue();
-
-		LLMatrix4 matrix_transform;
-
-		for (int i = 0; i < 4; i++)
-		{
-			for(int j = 0; j < 4; j++)
-			{
-				matrix_transform.mMatrix[i][j] = dom_value[i + j*4];
-			}
-		}
-
-		matrix_transform *= mTransform;
-		mTransform = matrix_transform;
-	}
-
-	domInstance_geometry* instance_geo = daeSafeCast<domInstance_geometry>(element);
-	if (instance_geo)
-	{
-		domGeometry* geo = daeSafeCast<domGeometry>(instance_geo->getUrl().getElement());
-		if (geo)
-		{
-			domMesh* mesh = daeSafeCast<domMesh>(geo->getDescendant(daeElement::matchType(domMesh::ID())));
-			if (mesh)
-			{
-				LLModel* model = mModel[mesh];
-				if (model)
-				{
-					LLMatrix4 transformation = mTransform;
-
-					if (mTransform.determinant() < 0)
-					{ //negative scales are not supported
-						LL_INFOS() << "Negative scale detected, unsupported transform.  domInstance_geometry: " << LLModel::getElementLabel(instance_geo) << LL_ENDL;
-						badElement = true;
-					}
-					
-					std::map<std::string, LLImportMaterial> materials = getMaterials(model, instance_geo);
-
-					// adjust the transformation to compensate for mesh normalization
-					LLVector3 mesh_scale_vector;
-					LLVector3 mesh_translation_vector;
-					model->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector);
-
-					LLMatrix4 mesh_translation;
-					mesh_translation.setTranslation(mesh_translation_vector);
-					mesh_translation *= transformation;
-					transformation = mesh_translation;
-
-					LLMatrix4 mesh_scale;
-					mesh_scale.initScale(mesh_scale_vector);
-					mesh_scale *= transformation;
-					transformation = mesh_scale;
-
-					std::string label = getElementLabel(instance_geo);
-					mScene[transformation].push_back(LLModelInstance(model, label, transformation, materials));
-
-					stretch_extents(model, transformation, mExtents[0], mExtents[1], mFirstTransform);
-				}
-			}
-		}
-		else 
-		{
-			LL_INFOS()<<"Unable to resolve geometry URL."<<LL_ENDL;
-			badElement = true;			
-		}
-
-	}
-
-	domInstance_node* instance_node = daeSafeCast<domInstance_node>(element);
-	if (instance_node)
-	{
-		daeElement* instance = instance_node->getUrl().getElement();
-		if (instance)
-		{
-			processElement(instance,badElement);
-		}
-	}
-
-	//process children
-	daeTArray< daeSmartRef<daeElement> > children = element->getChildren();
-	int childCount = children.getCount();
-	for (S32 i = 0; i < childCount; i++)
-	{
-		processElement(children[i],badElement);
-	}
-
-	domNode* node = daeSafeCast<domNode>(element);
-	if (node)
-	{ //this element was a node, restore transform before processiing siblings
-		mTransform = saved_transform;
-	}
-}
-
-std::map<std::string, LLImportMaterial> LLModelLoader::getMaterials(LLModel* model, domInstance_geometry* instance_geo)
-{
-	std::map<std::string, LLImportMaterial> materials;
-	for (int i = 0; i < model->mMaterialList.size(); i++)
-	{
-		LLImportMaterial import_material;
-
-		domInstance_material* instance_mat = NULL;
-
-		domBind_material::domTechnique_common* technique =
-		daeSafeCast<domBind_material::domTechnique_common>(instance_geo->getDescendant(daeElement::matchType(domBind_material::domTechnique_common::ID())));
-
-		if (technique)
-		{
-			daeTArray< daeSmartRef<domInstance_material> > inst_materials = technique->getChildrenByType<domInstance_material>();
-			for (int j = 0; j < inst_materials.getCount(); j++)
-			{
-				std::string symbol(inst_materials[j]->getSymbol());
-
-				if (symbol == model->mMaterialList[i]) // found the binding
-				{
-					instance_mat = inst_materials[j];
-				}
-			}
-		}
-
-		if (instance_mat)
-		{
-			domMaterial* material = daeSafeCast<domMaterial>(instance_mat->getTarget().getElement());
-			if (material)
-			{
-				domInstance_effect* instance_effect =
-				daeSafeCast<domInstance_effect>(material->getDescendant(daeElement::matchType(domInstance_effect::ID())));
-				if (instance_effect)
-				{
-					domEffect* effect = daeSafeCast<domEffect>(instance_effect->getUrl().getElement());
-					if (effect)
-					{
-						domProfile_COMMON* profile =
-						daeSafeCast<domProfile_COMMON>(effect->getDescendant(daeElement::matchType(domProfile_COMMON::ID())));
-						if (profile)
-						{
-							import_material = profileToMaterial(profile);
-						}
-					}
-				}
-			}
-		}
-
-		import_material.mBinding = model->mMaterialList[i];
-		materials[model->mMaterialList[i]] = import_material;
-	}
-
-	return materials;
-}
-
-LLImportMaterial LLModelLoader::profileToMaterial(domProfile_COMMON* material)
-{
-	LLImportMaterial mat;
-	mat.mFullbright = FALSE;
-
-	daeElement* diffuse = material->getDescendant("diffuse");
-	if (diffuse)
-	{
-		domCommon_color_or_texture_type_complexType::domTexture* texture =
-		daeSafeCast<domCommon_color_or_texture_type_complexType::domTexture>(diffuse->getDescendant("texture"));
-		if (texture)
-		{
-			domCommon_newparam_type_Array newparams = material->getNewparam_array();
-			for (S32 i = 0; i < newparams.getCount(); i++)
-			{
-				domFx_surface_common* surface = newparams[i]->getSurface();
-				if (surface)
-				{
-					domFx_surface_init_common* init = surface->getFx_surface_init_common();
-					if (init)
-					{
-						domFx_surface_init_from_common_Array init_from = init->getInit_from_array();
-
-						if (init_from.getCount() > i)
-						{
-							domImage* image = daeSafeCast<domImage>(init_from[i]->getValue().getElement());
-							if (image)
-							{
-								// we only support init_from now - embedded data will come later
-								domImage::domInit_from* init = image->getInit_from();
-								if (init)
-								{									
-									mat.mDiffuseMapFilename = cdom::uriToNativePath(init->getValue().str());
-									mat.mDiffuseMapLabel = getElementLabel(material);
-								}
-							}
-						}
-					}
-				}
-			}
-		}
-
-		domCommon_color_or_texture_type_complexType::domColor* color =
-		daeSafeCast<domCommon_color_or_texture_type_complexType::domColor>(diffuse->getDescendant("color"));
-		if (color)
-		{
-			domFx_color_common domfx_color = color->getValue();
-			LLColor4 value = LLColor4(domfx_color[0], domfx_color[1], domfx_color[2], domfx_color[3]);
-			mat.mDiffuseColor = value;
-		}
-	}
-
-	daeElement* emission = material->getDescendant("emission");
-	if (emission)
-	{
-		LLColor4 emission_color = getDaeColor(emission);
-		if (((emission_color[0] + emission_color[1] + emission_color[2]) / 3.0) > 0.25)
-		{
-			mat.mFullbright = TRUE;
-		}
-	}
-
-	return mat;
-}
-
-// try to get a decent label for this element
-std::string LLModelLoader::getElementLabel(daeElement *element)
-{
-	// if we have a name attribute, use it
-	std::string name = element->getAttribute("name");
-	if (name.length())
-	{
-		return name;
-	}
-
-	// if we have an ID attribute, use it
-	if (element->getID())
-	{
-		return std::string(element->getID());
-	}
-
-	// if we have a parent, use it
-	daeElement* parent = element->getParent();
-	if (parent)
-	{
-		// if parent has a name, use it
-		std::string name = parent->getAttribute("name");
-		if (name.length())
-		{
-			return name;
-		}
-
-		// if parent has an ID, use it
-		if (parent->getID())
-		{
-			return std::string(parent->getID());
-		}
-	}
-
-	// try to use our type
-	daeString element_name = element->getElementName();
-	if (element_name)
-	{
-		return std::string(element_name);
-	}
-
-	// if all else fails, use "object"
-	return std::string("object");
-}
-
-LLColor4 LLModelLoader::getDaeColor(daeElement* element)
-{
-	LLColor4 value;
-	domCommon_color_or_texture_type_complexType::domColor* color =
-	daeSafeCast<domCommon_color_or_texture_type_complexType::domColor>(element->getDescendant("color"));
-	if (color)
-	{
-		domFx_color_common domfx_color = color->getValue();
-		value = LLColor4(domfx_color[0], domfx_color[1], domfx_color[2], domfx_color[3]);
-	}
-
-	return value;
-}
-
-//-----------------------------------------------------------------------------
-// LLModelPreview
-//-----------------------------------------------------------------------------
-
-LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)
-: LLViewerDynamicTexture(width, height, 3, ORDER_MIDDLE, FALSE), LLMutex(NULL)
-, mPelvisZOffset( 0.0f )
-, mLegacyRigValid( false )
-, mRigValidJointUpload( false )
-, mResetJoints( false )
-, mRigParityWithScene( false )
-, mLastJointUpdate( false )
-{
-	mNeedsUpdate = TRUE;
-	mCameraDistance = 0.f;
-	mCameraYaw = 0.f;
-	mCameraPitch = 0.f;
-	mCameraZoom = 1.f;
-	mTextureName = 0;
-	mPreviewLOD = 0;
-	mModelLoader = NULL;
-	mMaxTriangleLimit = 0;
-	mDirty = false;
-	mGenLOD = false;
-	mLoading = false;
-	mLoadState = LLModelLoader::STARTING;
-	mGroup = 0;
-	mLODFrozen = false;
-	mBuildShareTolerance = 0.f;
-	mBuildQueueMode = GLOD_QUEUE_GREEDY;
-	mBuildBorderMode = GLOD_BORDER_UNLOCK;
-	mBuildOperator = GLOD_OPERATOR_EDGE_COLLAPSE;
-
-	for (U32 i = 0; i < LLModel::NUM_LODS; ++i)
-	{
-		mRequestedTriangleCount[i] = 0;
-		mRequestedCreaseAngle[i] = -1.f;
-		mRequestedLoDMode[i] = 0;
-		mRequestedErrorThreshold[i] = 0.f;
-		mRequestedBuildOperator[i] = 0;
-		mRequestedQueueMode[i] = 0;
-		mRequestedBorderMode[i] = 0;
-		mRequestedShareTolerance[i] = 0.f;
-	}
-
-	mViewOption["show_textures"] = false;
-
-	mFMP = fmp;
-
-	mHasPivot = false;
-	mModelPivot = LLVector3( 0.0f, 0.0f, 0.0f );
-	
-	glodInit();
-
-	//move into joint mapper class
-	//1. joints for joint offset verification
-	mMasterJointList.push_front("mPelvis");
-	mMasterJointList.push_front("mTorso");
-	mMasterJointList.push_front("mChest");
-	mMasterJointList.push_front("mNeck");
-	mMasterJointList.push_front("mHead");
-	mMasterJointList.push_front("mCollarLeft");
-	mMasterJointList.push_front("mShoulderLeft");
-	mMasterJointList.push_front("mElbowLeft");
-	mMasterJointList.push_front("mWristLeft");
-	mMasterJointList.push_front("mCollarRight");
-	mMasterJointList.push_front("mShoulderRight");
-	mMasterJointList.push_front("mElbowRight");
-	mMasterJointList.push_front("mWristRight");
-	mMasterJointList.push_front("mHipRight");
-	mMasterJointList.push_front("mKneeRight");
-	mMasterJointList.push_front("mFootRight");
-	mMasterJointList.push_front("mHipLeft");
-	mMasterJointList.push_front("mKneeLeft");
-	mMasterJointList.push_front("mFootLeft");
-	//2. legacy joint list - used to verify rigs that will not be using joint offsets
-	mMasterLegacyJointList.push_front("mPelvis");
-	mMasterLegacyJointList.push_front("mTorso");
-	mMasterLegacyJointList.push_front("mChest");
-	mMasterLegacyJointList.push_front("mNeck");
-	mMasterLegacyJointList.push_front("mHead");
-	mMasterLegacyJointList.push_front("mHipRight");
-	mMasterLegacyJointList.push_front("mKneeRight");
-	mMasterLegacyJointList.push_front("mFootRight");
-	mMasterLegacyJointList.push_front("mHipLeft");
-	mMasterLegacyJointList.push_front("mKneeLeft");
-	mMasterLegacyJointList.push_front("mFootLeft");
-
-	createPreviewAvatar();
-}
-
-LLModelPreview::~LLModelPreview()
-{
-	if (mModelLoader)
-	{
-		mModelLoader->mPreview = NULL;
-		mModelLoader = NULL;
-	}
-	//*HACK : *TODO : turn this back on when we understand why this crashes
-	//glodShutdown();
-}
-
-U32 LLModelPreview::calcResourceCost()
-{
-	assert_main_thread();
-
-	rebuildUploadData();
-
-	//Upload skin is selected BUT check to see if the joints coming in from the asset were malformed.
-	if ( mFMP && mFMP->childGetValue("upload_skin").asBoolean() )
-	{
-		bool uploadingJointPositions = mFMP->childGetValue("upload_joints").asBoolean();
-		if ( uploadingJointPositions && !isRigValidForJointPositionUpload() )
-		{
-			mFMP->childDisable("ok_btn");		
-		}		
-	}
-	
-	std::set<LLModel*> accounted;
-	U32 num_points = 0;
-	U32 num_hulls = 0;
-
-	F32 debug_scale = mFMP ? mFMP->childGetValue("import_scale").asReal() : 1.f;
-	mPelvisZOffset = mFMP ? mFMP->childGetValue("pelvis_offset").asReal() : 3.0f;
-	
-	if ( mFMP && mFMP->childGetValue("upload_joints").asBoolean() )
-	{
-		// FIXME if preview avatar ever gets reused, this fake mesh ID stuff will fail.
-		// see also call to addAttachmentPosOverride.
-		LLUUID fake_mesh_id;
-		fake_mesh_id.generate();
-		getPreviewAvatar()->addPelvisFixup( mPelvisZOffset, fake_mesh_id );
-	}
-
-	F32 streaming_cost = 0.f;
-	F32 physics_cost = 0.f;
-	for (U32 i = 0; i < mUploadData.size(); ++i)
-	{
-		LLModelInstance& instance = mUploadData[i];
-		
-		if (accounted.find(instance.mModel) == accounted.end())
-		{
-			accounted.insert(instance.mModel);
-
-			LLModel::Decomposition& decomp =
-			instance.mLOD[LLModel::LOD_PHYSICS] ?
-			instance.mLOD[LLModel::LOD_PHYSICS]->mPhysics :
-			instance.mModel->mPhysics;
-			
-			//update instance skin info for each lods pelvisZoffset 
-			for ( int j=0; j<LLModel::NUM_LODS; ++j )
-			{	
-				if ( instance.mLOD[j] )
-				{
-					instance.mLOD[j]->mSkinInfo.mPelvisOffset = mPelvisZOffset;
-				}
-			}
-
-			std::stringstream ostr;
-			LLSD ret = LLModel::writeModel(ostr,
-					   instance.mLOD[4],
-					   instance.mLOD[3],
-					   instance.mLOD[2],
-					   instance.mLOD[1],
-					   instance.mLOD[0],
-					   decomp,
-					   mFMP->childGetValue("upload_skin").asBoolean(),
-					   mFMP->childGetValue("upload_joints").asBoolean(),
-					   TRUE);
-			
-			num_hulls += decomp.mHull.size();
-			for (U32 i = 0; i < decomp.mHull.size(); ++i)
-			{
-				num_points += decomp.mHull[i].size();
-			}
-
-			//calculate streaming cost
-			LLMatrix4 transformation = instance.mTransform;
-
-			LLVector3 position = LLVector3(0, 0, 0) * transformation;
-
-			LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position;
-			LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position;
-			LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position;
-			F32 x_length = x_transformed.normalize();
-			F32 y_length = y_transformed.normalize();
-			F32 z_length = z_transformed.normalize();
-			LLVector3 scale = LLVector3(x_length, y_length, z_length);
-
-			F32 radius = scale.length()*0.5f*debug_scale;
-
-			streaming_cost += LLMeshRepository::getStreamingCost(ret, radius);
-		}
-	}
-
-	F32 scale = mFMP ? mFMP->childGetValue("import_scale").asReal()*2.f : 2.f;
-
-	mDetailsSignal(mPreviewScale[0]*scale, mPreviewScale[1]*scale, mPreviewScale[2]*scale, streaming_cost, physics_cost);
-
-	updateStatusMessages();
-
-	return (U32) streaming_cost;
-}
-
-void LLFloaterModelPreview::setDetails(F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost)
-{
-	assert_main_thread();
-	childSetTextArg("import_dimensions", "[X]", llformat("%.3f", x));
-	childSetTextArg("import_dimensions", "[Y]", llformat("%.3f", y));
-	childSetTextArg("import_dimensions", "[Z]", llformat("%.3f", z));
-}
-
-
-void LLModelPreview::rebuildUploadData()
-{
-	assert_main_thread();
-
-	mUploadData.clear();
-	mTextureSet.clear();
+	mUploadData.clear();
+	mTextureSet.clear();
 
 	//fill uploaddata instance vectors from scene data
 
@@ -3356,29 +1380,12 @@ void LLModelPreview::rebuildUploadData()
 
 	F32 max_scale = 0.f;
 
-	//reorder materials to match mBaseModel
-	for (U32 i = 0; i < LLModel::NUM_LODS-1; i++)
-	{
-		if (mBaseModel.size() == mModel[i].size())
-		{
-			for (U32 j = 0; j < mBaseModel.size(); ++j)
-			{
-				
-				int refFaceCnt = 0;
-				int modelFaceCnt = 0;
-				
-				if ( !mModel[i][j]->matchMaterialOrder(mBaseModel[j], refFaceCnt, modelFaceCnt ) )
-				{
-					setLoadState( LLModelLoader::ERROR_MATERIALS );
-					mFMP->childDisable( "calculate_btn" );
-				}
-			}
-		}
-	}
+	BOOL importerDebug = gSavedSettings.getBOOL("ImporterDebug");
+	BOOL legacyMatching = gSavedSettings.getBOOL("ImporterLegacyMatching");
 
 	for (LLModelLoader::scene::iterator iter = mBaseScene.begin(); iter != mBaseScene.end(); ++iter)
 	{ //for each transform in scene
-		LLMatrix4 mat = iter->first;
+		LLMatrix4 mat		= iter->first;
 
 		// compute position
 		LLVector3 position = LLVector3(0, 0, 0) * mat;
@@ -3395,43 +1402,204 @@ void LLModelPreview::rebuildUploadData()
 
 		mat *= scale_mat;
 
-		for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter)
-		{ //for each instance with said transform applied
-			LLModelInstance instance = *model_iter;
+		for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end();)
+		{ //for each instance with said transform applied 
+			LLModelInstance instance = *model_iter++;
 
 			LLModel* base_model = instance.mModel;
 			
-			if (base_model)
+			if (base_model && !requested_name.empty())
 			{
 				base_model->mRequestedLabel = requested_name;
 				base_model->mMetric = metric;
 			}
 
-			S32 idx = 0;
-			for (idx = 0; idx < mBaseModel.size(); ++idx)
-			{  //find reference instance for this model
-				if (mBaseModel[idx] == base_model)
+			for (int i = LLModel::NUM_LODS - 1; i >= LLModel::LOD_IMPOSTOR; i--)
+			{
+				LLModel* lod_model = NULL;
+				if (!legacyMatching)
+				{
+					// Fill LOD slots by finding matching meshes by label with name extensions
+					// in the appropriate scene for each LOD. This fixes all kinds of issues
+					// where the indexed method below fails in spectacular fashion.
+					// If you don't take the time to name your LOD and PHYS meshes
+					// with the name of their corresponding mesh in the HIGH LOD,
+					// then the indexed method will be attempted below.
+
+					LLMatrix4 transform;
+
+					std::string name_to_match = instance.mLabel;
+					llassert(!name_to_match.empty());
+
+					int extensionLOD;
+					if (i != LLModel::LOD_PHYSICS || mModel[LLModel::LOD_PHYSICS].empty())
+					{
+						extensionLOD = i;
+					}
+					else
+					{
+						//Physics can be inherited from other LODs or loaded, so we need to adjust what extension we are searching for
+						extensionLOD = mPhysicsSearchLOD;
+					}
+
+					std::string toAdd;
+					switch (extensionLOD)
+					{
+					case LLModel::LOD_IMPOSTOR: toAdd = "_LOD0"; break;
+					case LLModel::LOD_LOW:      toAdd = "_LOD1"; break;
+					case LLModel::LOD_MEDIUM:   toAdd = "_LOD2"; break;
+					case LLModel::LOD_PHYSICS:  toAdd = "_PHYS"; break;
+					case LLModel::LOD_HIGH:                      break;
+					}
+
+					if (name_to_match.find(toAdd) == -1)
+					{
+						name_to_match += toAdd;
+					}
+
+					FindModel(mScene[i], name_to_match, lod_model, transform);
+
+					if (!lod_model && i != LLModel::LOD_PHYSICS)
+					{
+						if (importerDebug)
+						{
+							LL_INFOS() << "Search of" << name_to_match << " in LOD" << i << " list failed. Searching for alternative among LOD lists." << LL_ENDL;
+						}
+
+						int searchLOD = (i > LLModel::LOD_HIGH) ? LLModel::LOD_HIGH : i;
+						while ((searchLOD <= LLModel::LOD_HIGH) && !lod_model)
+						{
+							std::string name_to_match = instance.mLabel;
+							llassert(!name_to_match.empty());
+
+							std::string toAdd;
+							switch (searchLOD)
+							{
+							case LLModel::LOD_IMPOSTOR: toAdd = "_LOD0"; break;
+							case LLModel::LOD_LOW:      toAdd = "_LOD1"; break;
+							case LLModel::LOD_MEDIUM:   toAdd = "_LOD2"; break;
+							case LLModel::LOD_PHYSICS:  toAdd = "_PHYS"; break;
+							case LLModel::LOD_HIGH:                      break;
+							}
+
+							if (name_to_match.find(toAdd) == -1)
+							{
+								name_to_match += toAdd;
+							}
+
+							// See if we can find an appropriately named model in LOD 'searchLOD'
+							//
+							FindModel(mScene[searchLOD], name_to_match, lod_model, transform);
+							searchLOD++;
+						}
+					}
+				}
+				else
+				{
+					// Use old method of index-based association
+					U32 idx = 0;
+					for (idx = 0; idx < mBaseModel.size(); ++idx)
+					{
+						// find reference instance for this model
+						if (mBaseModel[idx] == base_model)
+						{
+							if (importerDebug)
+							{
+								LL_INFOS() << "Attempting to use model index " << idx << " for LOD " << i << " of " << instance.mLabel << LL_ENDL;
+							}
+							break;
+						}
+					}
+
+					// If the model list for the current LOD includes that index...
+					//
+					if (mModel[i].size() > idx)
+					{
+						// Assign that index from the model list for our LOD as the LOD model for this instance
+						//
+						lod_model = mModel[i][idx];
+						if (importerDebug)
+						{
+							LL_INFOS() << "Indexed match of model index " << idx << " at LOD " << i << " to model named " << lod_model->mLabel << LL_ENDL;
+						}
+					}
+					else if (importerDebug)
+					{
+						LL_INFOS() << "List of models does not include index " << idx << LL_ENDL;
+					}
+				}
+
+				if (lod_model)
+				{
+					if (importerDebug)
+					{
+						if (i == LLModel::LOD_PHYSICS)
+						{
+							LL_INFOS() << "Assigning collision for " << instance.mLabel << " to match " << lod_model->mLabel << LL_ENDL;
+						}
+						else
+						{
+							LL_INFOS() << "Assigning LOD" << i << " for " << instance.mLabel << " to found match " << lod_model->mLabel << LL_ENDL;
+						}
+					}
+					instance.mLOD[i] = lod_model;
+				}
+				else if (importerDebug)
+				{
+					LL_INFOS() << "List of models does not include " << instance.mLabel << LL_ENDL;
+				}
+			}
+
+			LLModel* high_lod_model = instance.mLOD[LLModel::LOD_HIGH];
+			if (!high_lod_model)
+			{
+				setLoadState( LLModelLoader::ERROR_MATERIALS );
+				mFMP->childDisable( "calculate_btn" );
+			}
+			else
+			{
+				for (U32 i = 0; i < LLModel::NUM_LODS-1; i++)
+				{				
+					int refFaceCnt = 0;
+					int modelFaceCnt = 0;
+					llassert(instance.mLOD[i]);
+					if (instance.mLOD[i] && !instance.mLOD[i]->matchMaterialOrder(high_lod_model, refFaceCnt, modelFaceCnt ) )
+					{
+						setLoadState( LLModelLoader::ERROR_MATERIALS );
+						mFMP->childDisable( "calculate_btn" );
+					}
+				}
+			}
+			instance.mTransform = mat;
+			mUploadData.push_back(instance);
+		}
+	}
+
+	for (U32 lod = 0; lod < LLModel::NUM_LODS-1; lod++)
+	{
+		// Search for models that are not included into upload data
+		// If we found any, that means something we loaded is not a sub-model.
+		for (U32 model_ind = 0; model_ind < mModel[lod].size(); ++model_ind)
+		{
+			bool found_model = false;
+			for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)
+			{
+				LLModelInstance& instance = *iter;
+				if (instance.mLOD[lod] == mModel[lod][model_ind])
 				{
+					found_model = true;
 					break;
 				}
 			}
-
-			if(idx < mBaseModel.size())
+			if (!found_model && mModel[lod][model_ind] && !mModel[lod][model_ind]->mSubmodelID)
 			{
-				for (U32 i = 0; i < LLModel::NUM_LODS; i++)
-				{ //fill LOD slots based on reference model index
-					if (mModel[i].size() > idx)
-					{
-						instance.mLOD[i] = mModel[i][idx];
-					}
-					else
-					{
-						instance.mLOD[i] = NULL;
-					}
+				if (importerDebug)
+				{
+					LL_INFOS() << "Model " << mModel[lod][model_ind]->mLabel << " was not used - mismatching lod models." <<  LL_ENDL;
 				}
+				setLoadState( LLModelLoader::ERROR_MATERIALS );
+				mFMP->childDisable( "calculate_btn" );
 			}
-			instance.mTransform = mat;
-			mUploadData.push_back(instance);
 		}
 	}
 
@@ -3501,7 +1669,6 @@ void LLModelPreview::saveUploadData(const std::string& filename, bool save_skinw
 			meshes.insert(instance.mModel);
 
 			std::stringstream str;
-
 			LLModel::Decomposition& decomp =
 				instance.mLOD[LLModel::LOD_PHYSICS].notNull() ? 
 				instance.mLOD[LLModel::LOD_PHYSICS]->mPhysics : 
@@ -3514,8 +1681,8 @@ void LLModelPreview::saveUploadData(const std::string& filename, bool save_skinw
 				instance.mLOD[LLModel::LOD_LOW], 
 				instance.mLOD[LLModel::LOD_IMPOSTOR], 
 				decomp, 
-				save_skinweights, save_joint_positions, FALSE, TRUE);
-
+				save_skinweights, save_joint_positions,
+                FALSE, TRUE, instance.mModel->mSubmodelID);
 			
 			data["mesh"][instance.mModel->mLocalID] = str.str();
 		}
@@ -3583,13 +1750,28 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable
 		clearGLODGroup();
 	}
 
-	mModelLoader = new LLModelLoader(filename, lod, this, mJointTransformMap, mJointsFromNode );
+	mModelLoader = new LLDAELoader(
+		filename,
+		lod, 
+		&LLModelPreview::loadedCallback,
+		&LLModelPreview::lookupJointByName,
+		&LLModelPreview::loadTextures,
+		&LLModelPreview::stateChangedCallback,
+		this,
+		mJointTransformMap,
+		mJointsFromNode,
+		gSavedSettings.getU32("ImporterModelLimit"));
 
 	if (force_disable_slm)
 	{
 		mModelLoader->mTrySLM = false;
 	}
-
+	else
+	{
+		//only try to load from slm if viewer is configured to do so and this is the 
+		//initial model load (not an LoD or physics shape)
+		mModelLoader->mTrySLM = gSavedSettings.getBOOL("MeshImportUseSLM") && mUploadData.empty();
+	}
 	mModelLoader->start();
 
 	mFMP->childSetTextArg("status", "[STATUS]", mFMP->getString("status_reading_file"));
@@ -3620,6 +1802,7 @@ void LLModelPreview::setPhysicsFromLOD(S32 lod)
 
 	if (lod >= 0 && lod <= 3)
 	{
+		mPhysicsSearchLOD = lod;
 		mModel[LLModel::LOD_PHYSICS] = mModel[lod];
 		mScene[LLModel::LOD_PHYSICS] = mScene[lod];
 		mLODFile[LLModel::LOD_PHYSICS].clear();
@@ -3639,11 +1822,14 @@ void LLModelPreview::clearIncompatible(S32 lod)
 		return;
 	}
 
+	// at this point we don't care about sub-models,
+	// different amount of sub-models means face count mismatch, not incompatibility
+	U32 lod_size = countRootModels(mModel[lod]);
 	for (U32 i = 0; i <= LLModel::LOD_HIGH; i++)
 	{ //clear out any entries that aren't compatible with this model
 		if (i != lod)
 		{
-			if (mModel[i].size() != mModel[lod].size())
+			if (countRootModels(mModel[i]) != lod_size)
 			{
 				mModel[i].clear();
 				mScene[i].clear();
@@ -3678,7 +1864,7 @@ void LLModelPreview::clearGLODGroup()
 	}
 }
 
-void LLModelPreview::loadModelCallback(S32 lod)
+void LLModelPreview::loadModelCallback(S32 loaded_lod)
 {
 	assert_main_thread();
 
@@ -3691,12 +1877,18 @@ void LLModelPreview::loadModelCallback(S32 lod)
 	if(getLoadState() >= LLModelLoader::ERROR_PARSING)
 	{
 		mLoading = false ;
+		mModelLoader = NULL;
 		return ;
 	}
 
+	// Copy determinations about rig so UI will reflect them
+	//
+	setRigValidForJointPositionUpload(mModelLoader->isRigValidForJointPositionUpload());
+	setLegacyRigValid(mModelLoader->isLegacyRigValid());
+
 	mModelLoader->loadTextures() ;
 
-	if (lod == -1)
+	if (loaded_lod == -1)
 	{ //populate all LoDs from model loader scene
 		mBaseModel.clear();
 		mBaseScene.clear();
@@ -3728,6 +1920,11 @@ void LLModelPreview::loadModelCallback(S32 lod)
 						//override displayed model with current LoD
 						list_iter->mModel = list_iter->mLOD[lod];
 
+						if (!list_iter->mModel)
+						{
+							continue;
+						}
+
 						//add current model to current LoD's model list (LLModel::mLocalID makes a good vector index)
 						S32 idx = list_iter->mModel->mLocalID;
 
@@ -3736,7 +1933,7 @@ void LLModelPreview::loadModelCallback(S32 lod)
 							mModel[lod].resize(idx+1);
 						}
 
-						mModel[lod][idx] = list_iter->mModel;	
+						mModel[lod][idx] = list_iter->mModel;
 						if (!list_iter->mModel->mSkinWeights.empty())
 						{
 							skin_weights = true;
@@ -3779,31 +1976,108 @@ void LLModelPreview::loadModelCallback(S32 lod)
 	}
 	else
 	{ //only replace given LoD
-		mModel[lod] = mModelLoader->mModelList;
-		mScene[lod] = mModelLoader->mScene;
-		mVertexBuffer[lod].clear();
+		mModel[loaded_lod] = mModelLoader->mModelList;
+		mScene[loaded_lod] = mModelLoader->mScene;
+		mVertexBuffer[loaded_lod].clear();
 
-		setPreviewLOD(lod);
+		setPreviewLOD(loaded_lod);
 
-		if (lod == LLModel::LOD_HIGH)
+		if (loaded_lod == LLModel::LOD_HIGH)
 		{ //save a copy of the highest LOD for automatic LOD manipulation
 			if (mBaseModel.empty())
 			{ //first time we've loaded a model, auto-gen LoD
 				mGenLOD = true;
 			}
 
-			mBaseModel = mModel[lod];
+			mBaseModel = mModel[loaded_lod];
 			clearGLODGroup();
 
-			mBaseScene = mScene[lod];
+			mBaseScene = mScene[loaded_lod];
 			mVertexBuffer[5].clear();
 		}
+		else
+		{
+			BOOL importerDebug = gSavedSettings.getBOOL("ImporterDebug");
+			BOOL legacyMatching = gSavedSettings.getBOOL("ImporterLegacyMatching");
+			if (!legacyMatching)
+			{
+				if (!mBaseModel.empty())
+				{ 
+					BOOL name_based = FALSE;
+					BOOL has_submodels = FALSE;
+					for (U32 idx = 0; idx < mBaseModel.size(); ++idx)
+					{
+						if (mBaseModel[idx]->mSubmodelID)
+						{ // don't do index-based renaming when the base model has submodels
+							has_submodels = TRUE;
+							if (importerDebug)
+							{
+								LL_INFOS() << "High LOD has submodels" << LL_ENDL;
+							}
+							break;
+						}
+					}
+
+					for (U32 idx = 0; idx < mModel[loaded_lod].size(); ++idx)
+					{
+						std::string loaded_name = stripSuffix(mModel[loaded_lod][idx]->mLabel);
+
+						LLModel* found_model = NULL;
+						LLMatrix4 transform;
+						FindModel(mBaseScene, loaded_name, found_model, transform);
+						if (found_model)
+						{ // don't rename correctly named models (even if they are placed in a wrong order)
+							name_based = TRUE;
+						}
+
+						if (mModel[loaded_lod][idx]->mSubmodelID)
+						{ // don't rename the models when loaded LOD model has submodels
+							has_submodels = TRUE;
+						}
+					}
+
+					if (importerDebug)
+					{
+						LL_INFOS() << "Loaded LOD " << loaded_lod << ": correct names" << (name_based ? "" : "NOT ") << "found; submodels " << (has_submodels ? "" : "NOT ") << "found" << LL_ENDL;
+					}
+
+					if (!name_based && !has_submodels)
+					{ // replace the name of the model loaded for any non-HIGH LOD to match the others (MAINT-5601)
+					  // this actually works like "ImporterLegacyMatching" for this particular LOD
+						for (U32 idx = 0; idx < mModel[loaded_lod].size() && idx < mBaseModel.size(); ++idx)
+						{ 
+							std::string name = mBaseModel[idx]->mLabel;
+							std::string loaded_name = stripSuffix(mModel[loaded_lod][idx]->mLabel);
+
+							if (loaded_name != name)
+							{
+								switch (loaded_lod)
+								{
+								case LLModel::LOD_IMPOSTOR: name += "_LOD0"; break;
+								case LLModel::LOD_LOW:      name += "_LOD1"; break;
+								case LLModel::LOD_MEDIUM:   name += "_LOD2"; break;
+								case LLModel::LOD_PHYSICS:  name += "_PHYS"; break;
+								case LLModel::LOD_HIGH:                      break;
+								}
+
+								if (importerDebug)
+								{
+									LL_WARNS() << "Loded model name " << mModel[loaded_lod][idx]->mLabel << " for LOD " << loaded_lod << " doesn't match the base model. Renaming to " << name << LL_ENDL;
+								}
+
+								mModel[loaded_lod][idx]->mLabel = name;
+							}
+						}
+					}
+				}
+			}
+		}
 
-		clearIncompatible(lod);
+		clearIncompatible(loaded_lod);
 
 		mDirty = true;
 
-		if (lod == LLModel::LOD_HIGH)
+		if (loaded_lod == LLModel::LOD_HIGH)
 		{
 			resetPreviewTarget();
 		}
@@ -3822,6 +2096,8 @@ void LLModelPreview::loadModelCallback(S32 lod)
 	refresh();
 
 	mModelLoadedSignal();
+
+	mModelLoader = NULL;
 }
 
 void LLModelPreview::resetPreviewTarget()
@@ -4170,6 +2446,20 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim
 			volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
 			mModel[lod][mdl_idx] = new LLModel(volume_params, 0.f);
 
+            std::string name = base->mLabel;
+
+            switch (lod)
+            {
+                case LLModel::LOD_IMPOSTOR: name += "_LOD0"; break;
+                case LLModel::LOD_LOW:      name += "_LOD1"; break;
+		        case LLModel::LOD_MEDIUM:   name += "_LOD2"; break;
+                case LLModel::LOD_PHYSICS:  name += "_PHYS"; break;
+                case LLModel::LOD_HIGH:                      break;
+            }
+
+            mModel[lod][mdl_idx]->mLabel = name;
+			mModel[lod][mdl_idx]->mSubmodelID = base->mSubmodelID;
+            
 			GLint* sizes = new GLint[patch_count*2];
 			glodGetObjectParameteriv(mObject[base], GLOD_PATCH_SIZES, sizes);
 			stop_gloderror();
@@ -4282,17 +2572,6 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim
 	{
 		shader->bind();
 	}
-
-	/*if (which_lod == -1 && mScene[LLModel::LOD_PHYSICS].empty())
-	 { //build physics scene
-	 mScene[LLModel::LOD_PHYSICS] = mScene[LLModel::LOD_LOW];
-	 mModel[LLModel::LOD_PHYSICS] = mModel[LLModel::LOD_LOW];
-
-	 for (U32 i = 1; i < mModel[LLModel::LOD_PHYSICS].size(); ++i)
-	 {
-	 mPhysicsQ.push(mModel[LLModel::LOD_PHYSICS][i]);
-	 }
-	 }*/
 }
 
 void LLModelPreview::updateStatusMessages()
@@ -4309,43 +2588,89 @@ void LLModelPreview::updateStatusMessages()
 	S32 total_verts[LLModel::NUM_LODS];
 	S32 total_submeshes[LLModel::NUM_LODS];
 
-	for (S32 lod = 0; lod < LLModel::NUM_LODS; ++lod)
+    for (U32 i = 0; i < LLModel::NUM_LODS-1; i++)
+    {
+        total_tris[i] = 0;
+	    total_verts[i] = 0;
+	    total_submeshes[i] = 0;
+    }
+
+    for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)
 	{
-		//initialize total for this lod to 0
-		total_tris[lod] = total_verts[lod] = total_submeshes[lod] = 0;
+		LLModelInstance& instance = *iter;
 
-		for (LLModelLoader::scene::iterator iter = mScene[lod].begin(), endIter = mScene[lod].end(); iter != endIter; ++iter)
+        LLModel* model_high_lod = instance.mLOD[LLModel::LOD_HIGH];
+        if (!model_high_lod)
 		{
-			for (LLModelLoader::model_instance_list::iterator instance = iter->second.begin(), end_instance = iter->second.end(); instance != end_instance; ++instance)
-			{
-				LLModel* model = instance->mModel;
-				if (model)
-				{
-					 //for each model in the lod
-					S32 cur_tris = 0;
-					S32 cur_verts = 0;
-					S32 cur_submeshes = model->getNumVolumeFaces();
+			setLoadState( LLModelLoader::ERROR_MATERIALS );
+			mFMP->childDisable( "calculate_btn" );
+			continue;
+		}
 
-					for (S32 j = 0; j < cur_submeshes; ++j)
-					{ //for each submesh (face), add triangles and vertices to current total
-						const LLVolumeFace& face = model->getVolumeFace(j);
-						cur_tris += face.mNumIndices/3;
-						cur_verts += face.mNumVertices;
-					}
+        for (U32 i = 0; i < LLModel::NUM_LODS-1; i++)
+		{
+            LLModel* lod_model = instance.mLOD[i];
+            if (!lod_model)
+            {
+                setLoadState( LLModelLoader::ERROR_MATERIALS );
+                mFMP->childDisable( "calculate_btn" );
+            }
+
+            int refFaceCnt = 0;
+            int modelFaceCnt = 0;
+
+            if (!lod_model->matchMaterialOrder(model_high_lod, refFaceCnt, modelFaceCnt ) )
+			{
+                setLoadState( LLModelLoader::ERROR_MATERIALS );
+				mFMP->childDisable( "calculate_btn" );
+			}
 
-					//add this model to the lod total
-					total_tris[lod] += cur_tris;
-					total_verts[lod] += cur_verts;
-					total_submeshes[lod] += cur_submeshes;
+            if (lod_model)
+			{
+					//for each model in the lod
+				S32 cur_tris = 0;
+				S32 cur_verts = 0;
+				S32 cur_submeshes = lod_model->getNumVolumeFaces();
 
-					//store this model's counts to asset data
-					tris[lod].push_back(cur_tris);
-					verts[lod].push_back(cur_verts);
-					submeshes[lod].push_back(cur_submeshes);
+				for (S32 j = 0; j < cur_submeshes; ++j)
+				{ //for each submesh (face), add triangles and vertices to current total
+					const LLVolumeFace& face = lod_model->getVolumeFace(j);
+					cur_tris += face.mNumIndices/3;
+					cur_verts += face.mNumVertices;
 				}
+
+                std::string instance_name = instance.mLabel;
+
+                BOOL importerDebug = gSavedSettings.getBOOL("ImporterDebug");
+                if (importerDebug)
+                {
+                    // Useful for debugging generalized complaints below about total submeshes which don't have enough
+                    // context to address exactly what needs to be fixed to move towards compliance with the rules.
+                    //
+                    LL_INFOS() << "Instance " << lod_model->mLabel << " LOD " << i << " Verts: "   << cur_verts     << LL_ENDL;
+                    LL_INFOS() << "Instance " << lod_model->mLabel << " LOD " << i << " Tris:  "   << cur_tris      << LL_ENDL;
+                    LL_INFOS() << "Instance " << lod_model->mLabel << " LOD " << i << " Faces: "   << cur_submeshes << LL_ENDL;
+
+                    LLModel::material_list::iterator mat_iter = lod_model->mMaterialList.begin();
+                    while (mat_iter != lod_model->mMaterialList.end())
+                    {
+                        LL_INFOS() << "Instance " << lod_model->mLabel << " LOD " << i << " Material " << *(mat_iter) << LL_ENDL;
+                        mat_iter++;
+                    }
+                }
+
+                //add this model to the lod total
+				total_tris[i] += cur_tris;
+				total_verts[i] += cur_verts;
+				total_submeshes[i] += cur_submeshes;
+
+				//store this model's counts to asset data
+				tris[i].push_back(cur_tris);
+				verts[i].push_back(cur_verts);
+				submeshes[i].push_back(cur_submeshes);
 			}
 		}
-	}
+    }
 
 	if (mMaxTriangleLimit == 0)
 	{
@@ -4359,37 +2684,48 @@ void LLModelPreview::updateStatusMessages()
 		const LLVector4a scale(0.5f);
 		for (U32 i = 0; i < mModel[lod].size() && !has_degenerate; ++i)
 		{ //for each model in the lod
-			if (mModel[lod][i]->mPhysics.mHull.empty())
+			if (mModel[lod][i] && mModel[lod][i]->mPhysics.mHull.empty())
 			{ //no decomp exists
 				S32 cur_submeshes = mModel[lod][i]->getNumVolumeFaces();
 				for (S32 j = 0; j < cur_submeshes && !has_degenerate; ++j)
 				{ //for each submesh (face), add triangles and vertices to current total
-					const LLVolumeFace& face = mModel[lod][i]->getVolumeFace(j);
-					for (S32 k = 0; k < face.mNumIndices && !has_degenerate; )
+					LLVolumeFace& face = mModel[lod][i]->getVolumeFace(j);
+					for (S32 k = 0; (k < face.mNumIndices) && !has_degenerate; )
 					{
-						LLVector4a v1; v1.setMul(face.mPositions[face.mIndices[k++]], scale);
-						LLVector4a v2; v2.setMul(face.mPositions[face.mIndices[k++]], scale);
-						LLVector4a v3; v3.setMul(face.mPositions[face.mIndices[k++]], scale);
+						U16 index_a = face.mIndices[k+0];
+						U16 index_b = face.mIndices[k+1];
+						U16 index_c = face.mIndices[k+2];
+
+						LLVector4a v1; v1.setMul(face.mPositions[index_a], scale);
+						LLVector4a v2; v2.setMul(face.mPositions[index_b], scale);
+						LLVector4a v3; v3.setMul(face.mPositions[index_c], scale);
 
 						if (ll_is_degenerate(v1,v2,v3))
 						{
 							has_degenerate = true;
 						}
+						else
+						{
+							k += 3;
+						}
 					}
 				}
 			}
 		}
 	}
-	
+
 	mFMP->childSetTextArg("submeshes_info", "[SUBMESHES]", llformat("%d", total_submeshes[LLModel::LOD_HIGH]));
 
 	std::string mesh_status_na = mFMP->getString("mesh_status_na");
 
 	S32 upload_status[LLModel::LOD_HIGH+1];
 
-	bool upload_ok = true;
+	mModelNoErrors = true;
 
-	for (S32 lod = 0; lod <= LLModel::LOD_HIGH; ++lod)
+	const U32 lod_high = LLModel::LOD_HIGH;
+	U32 high_submodel_count = mModel[lod_high].size() - countRootModels(mModel[lod_high]);
+
+	for (S32 lod = 0; lod <= lod_high; ++lod)
 	{
 		upload_status[lod] = 0;
 
@@ -4402,7 +2738,7 @@ void LLModelPreview::updateStatusMessages()
 		}
 		else
 		{
-			if (lod == LLModel::LOD_HIGH)
+			if (lod == lod_high)
 			{
 				upload_status[lod] = 2;
 				message = "mesh_status_missing_lod";
@@ -4423,8 +2759,6 @@ void LLModelPreview::updateStatusMessages()
 			mFMP->childSetValue(lod_vertices_name[lod], mesh_status_na);
 		}
 
-		const U32 lod_high = LLModel::LOD_HIGH;
-
 		if (lod != lod_high)
 		{
 			if (total_submeshes[lod] && total_submeshes[lod] != total_submeshes[lod_high])
@@ -4432,6 +2766,13 @@ void LLModelPreview::updateStatusMessages()
 				message = "mesh_status_submesh_mismatch";
 				upload_status[lod] = 2;
 			}
+			else if (mModel[lod].size() - countRootModels(mModel[lod]) != high_submodel_count)
+			{//number of submodels is different, not all faces are matched correctly.
+				message = "mesh_status_submesh_mismatch";
+				upload_status[lod] = 2;
+				// Note: Submodels in instance were loaded from higher LOD and as result face count
+				// returns same value and total_submeshes[lod] is identical to high_lod one.
+			}
 			else if (!tris[lod].empty() && tris[lod].size() != tris[lod_high].size())
 			{ //number of meshes is different
 				message = "mesh_status_mesh_mismatch";
@@ -4452,7 +2793,7 @@ void LLModelPreview::updateStatusMessages()
 				{
 					//too many vertices in this lod
 					message = "mesh_status_too_many_vertices";
-					upload_status[lod] = 2;
+					upload_status[lod] = 1;
 				}
 			}
 		}
@@ -4464,7 +2805,7 @@ void LLModelPreview::updateStatusMessages()
 
 		if (upload_status[lod] >= 2)
 		{
-			upload_ok = false;
+			mModelNoErrors = false;
 		}
 
 		if (lod == mPreviewLOD)
@@ -4478,23 +2819,41 @@ void LLModelPreview::updateStatusMessages()
 	}
 
 
-	//make sure no hulls have more than 256 points in them
-	for (U32 i = 0; upload_ok && i < mModel[LLModel::LOD_PHYSICS].size(); ++i)
+	//warn if hulls have more than 256 points in them
+	BOOL physExceededVertexLimit = FALSE;
+	for (U32 i = 0; mModelNoErrors && i < mModel[LLModel::LOD_PHYSICS].size(); ++i)
 	{
 		LLModel* mdl = mModel[LLModel::LOD_PHYSICS][i];
 
-		for (U32 j = 0; upload_ok && j < mdl->mPhysics.mHull.size(); ++j)
+		if (mdl)
 		{
-			if (mdl->mPhysics.mHull[j].size() > 256)
+			for (U32 j = 0; j < mdl->mPhysics.mHull.size(); ++j)
 			{
-				upload_ok = false;
+				if (mdl->mPhysics.mHull[j].size() > 256)
+				{
+					physExceededVertexLimit = TRUE;
+					LL_INFOS() << "Physical model " << mdl->mLabel << " exceeds vertex per hull limitations." << LL_ENDL;
+					break;
+				}
 			}
 		}
 	}
+	mFMP->childSetVisible("physics_status_message_text", physExceededVertexLimit);
+	LLIconCtrl* physStatusIcon = mFMP->getChild<LLIconCtrl>("physics_status_message_icon");
+	physStatusIcon->setVisible(physExceededVertexLimit);
+	if (physExceededVertexLimit)
+	{
+		mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_vertex_limit_exceeded"));
+		LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Warning");
+		physStatusIcon->setImage(img);
+	}
 
-	bool errorStateFromLoader = getLoadState() >= LLModelLoader::ERROR_PARSING ? true : false;
+	if (getLoadState() >= LLModelLoader::ERROR_PARSING)
+	{
+		mModelNoErrors = false;
+		LL_INFOS() << "Loader returned errors, model can't be uploaded" << LL_ENDL;
+	}
 
-	bool skinAndRigOk = true;
 	bool uploadingSkin		     = mFMP->childGetValue("upload_skin").asBoolean();
 	bool uploadingJointPositions = mFMP->childGetValue("upload_joints").asBoolean();
 
@@ -4502,19 +2861,23 @@ void LLModelPreview::updateStatusMessages()
 	{
 		if ( uploadingJointPositions && !isRigValidForJointPositionUpload() )
 		{
-			skinAndRigOk = false;
-		}	
+			mModelNoErrors = false;
+			LL_INFOS() << "Invalid rig, there might be issues with uploading Joint positions" << LL_ENDL;
+		}
 	}
-	
-	if(upload_ok && mModelLoader)
+
+	if(mModelNoErrors && mModelLoader)
 	{
 		if(!mModelLoader->areTexturesReady() && mFMP->childGetValue("upload_textures").asBoolean())
 		{
-			upload_ok = false ;
+			// Some textures are still loading, prevent upload until they are done
+			mModelNoErrors = false;
 		}
 	}
 
-	if (!upload_ok || errorStateFromLoader || !skinAndRigOk || has_degenerate)
+	// Todo: investigate use of has_degenerate and include into mModelNoErrors upload blocking mechanics
+	// current use of has_degenerate won't block upload permanently - later checks will restore the button
+	if (!mModelNoErrors || has_degenerate)
 	{
 		mFMP->childDisable("ok_btn");
 	}
@@ -4856,7 +3219,8 @@ void LLModelPreview::genBuffers(S32 lod, bool include_skin_weights)
 		LLModel* base_mdl = *base_iter;
 		base_iter++;
 
-		for (S32 i = 0, e = mdl->getNumVolumeFaces(); i < e; ++i)
+		S32 num_faces = mdl->getNumVolumeFaces();
+		for (S32 i = 0; i < num_faces; ++i)
 		{
 			const LLVolumeFace &vf = mdl->getVolumeFace(i);
 			U32 num_vertices = vf.mNumVertices;
@@ -4951,23 +3315,23 @@ void LLModelPreview::genBuffers(S32 lod, bool include_skin_weights)
 
 void LLModelPreview::update()
 {
-	if (mDirty)
+    if (mGenLOD)
 	{
-		mDirty = false;
-		mResourceCost = calcResourceCost();
+		mGenLOD = false;
+		genLODs();
 		refresh();
 		updateStatusMessages();
 	}
 
-	if (mGenLOD)
+	if (mDirty)
 	{
-		mGenLOD = false;
-		genLODs();
+		mDirty = false;
+		mResourceCost = calcResourceCost();
 		refresh();
 		updateStatusMessages();
 	}
-
 }
+
 //-----------------------------------------------------------------------------
 // getTranslationForJointOffset()
 //-----------------------------------------------------------------------------
@@ -5001,8 +3365,77 @@ void LLModelPreview::createPreviewAvatar( void )
 	}
 	else
 	{
-		LL_INFOS()<<"Failed to create preview avatar for upload model window"<<LL_ENDL;
+		LL_INFOS() << "Failed to create preview avatar for upload model window" << LL_ENDL;
+	}
+}
+
+//static
+U32 LLModelPreview::countRootModels(LLModelLoader::model_list models)
+{
+	U32 root_models = 0;
+	model_list::iterator model_iter = models.begin();
+	while (model_iter != models.end())
+	{
+		LLModel* mdl = *model_iter;
+		if (mdl && mdl->mSubmodelID == 0)
+		{
+			root_models++;
+		}
+		model_iter++;
+	}
+	return root_models;
+}
+
+void LLModelPreview::loadedCallback(
+	LLModelLoader::scene& scene,
+	LLModelLoader::model_list& model_list,
+	S32 lod,
+	void* opaque)
+{
+	LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque);
+	if (pPreview && !LLModelPreview::sIgnoreLoadedCallback)
+	{
+		pPreview->loadModelCallback(lod);
+	}	
+}
+
+void LLModelPreview::stateChangedCallback(U32 state,void* opaque)
+{
+	LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque);
+	if (pPreview)
+	{
+	 pPreview->setLoadState(state);
+	}
+}
+
+LLJoint* LLModelPreview::lookupJointByName(const std::string& str, void* opaque)
+{
+	LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque);
+	if (pPreview)
+	{
+		return pPreview->getPreviewAvatar()->getJoint(str);
 	}
+	return NULL;
+}
+
+U32 LLModelPreview::loadTextures(LLImportMaterial& material,void* opaque)
+{
+	(void)opaque;
+
+	if (material.mDiffuseMapFilename.size())
+	{
+		material.mOpaqueData = new LLPointer< LLViewerFetchedTexture >;
+		LLPointer< LLViewerFetchedTexture >& tex = (*reinterpret_cast< LLPointer< LLViewerFetchedTexture > * >(material.mOpaqueData));
+
+		tex = LLViewerTextureManager::getFetchedTextureFromUrl("file://" + material.mDiffuseMapFilename, FTT_LOCAL_FILE, TRUE, LLGLTexture::BOOST_PREVIEW);
+		tex->setLoadedCallback(LLModelPreview::textureLoadedCallback, 0, TRUE, FALSE, opaque, NULL, FALSE);
+		tex->forceToSaveRawImage(0, F32_MAX);
+		material.setDiffuseMap(tex->getID()); // record tex ID
+		return 1;
+	}
+
+	material.mOpaqueData = NULL;
+	return 0;	
 }
 
 void LLModelPreview::addEmptyFace( LLModel* pTarget )
@@ -5258,23 +3691,9 @@ BOOL LLModelPreview::render()
 		if (regen)
 		{
 			genBuffers(mPreviewLOD, skin_weight);
-		}
-
-		//make sure material lists all match
-		for (U32 i = 0; i < LLModel::NUM_LODS-1; i++)
-		{
-			if (mBaseModel.size() == mModel[i].size())
 			{
-				for (U32 j = 0; j < mBaseModel.size(); ++j)
-				{
-					int refFaceCnt = 0;
-					int modelFaceCnt = 0;
-										
-					if ( !mModel[i][j]->matchMaterialOrder(mBaseModel[j], refFaceCnt, modelFaceCnt ) )
-					{
-						mFMP->childDisable( "calculate_btn" );
-					}
-				}
+				LL_INFOS() << "Vertex Buffer[" << mPreviewLOD << "]" << " is EMPTY!!!" << LL_ENDL;
+				regen = TRUE;
 			}
 		}
 
@@ -5286,62 +3705,63 @@ BOOL LLModelPreview::render()
 
 				LLModel* model = instance.mLOD[mPreviewLOD];
 
-				if (!model)
-				{
-					continue;
-				}
+					if (!model)
+					{
+						continue;
+					}
 
-				gGL.pushMatrix();
-				LLMatrix4 mat = instance.mTransform;
+					gGL.pushMatrix();
+					LLMatrix4 mat = instance.mTransform;
 
-				gGL.multMatrix((GLfloat*) mat.mMatrix);
+					gGL.multMatrix((GLfloat*) mat.mMatrix);
 
-				for (U32 i = 0, e = mVertexBuffer[mPreviewLOD][model].size(); i < e; ++i)
-				{
-					LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i];
-				
-					buffer->setBuffer(type_mask & buffer->getTypeMask());
 
-					if (textures)
+					U32 num_models = mVertexBuffer[mPreviewLOD][model].size();
+					for (U32 i = 0; i < num_models; ++i)
 					{
-						int materialCnt = instance.mModel->mMaterialList.size();
-						if ( i < materialCnt )
+						LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i];
+				
+						buffer->setBuffer(type_mask & buffer->getTypeMask());
+
+						if (textures)
 						{
-							const std::string& binding = instance.mModel->mMaterialList[i];						
-							const LLImportMaterial& material = instance.mMaterial[binding];
+							int materialCnt = instance.mModel->mMaterialList.size();
+							if ( i < materialCnt )
+							{
+								const std::string& binding = instance.mModel->mMaterialList[i];						
+								const LLImportMaterial& material = instance.mMaterial[binding];
 
-							gGL.diffuseColor4fv(material.mDiffuseColor.mV);
+								gGL.diffuseColor4fv(material.mDiffuseColor.mV);
 
-							if (material.mDiffuseMap.notNull())
-							{
-								if (material.mDiffuseMap->getDiscardLevel() > -1)
+								// Find the tex for this material, bind it, and add it to our set
+								//
+								LLViewerFetchedTexture* tex = bindMaterialDiffuseTexture(material);
+								if (tex)
 								{
-									gGL.getTexUnit(0)->bind(material.mDiffuseMap, true);
-									mTextureSet.insert(material.mDiffuseMap.get());
+									mTextureSet.insert(tex);
 								}
 							}
 						}
-					}
-					else
-					{
-						gGL.diffuseColor4f(1,1,1,1);
-					}
-
-					buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);
-					gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-					gGL.diffuseColor3f(0.4f, 0.4f, 0.4f);
+						else
+						{
+							gGL.diffuseColor4f(1,1,1,1);
+						}
 
-					if (edges)
-					{
-						glLineWidth(3.f);
-						glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
 						buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);
-						glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-						glLineWidth(1.f);
+						gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+						gGL.diffuseColor3f(0.4f, 0.4f, 0.4f);
+
+						if (edges)
+						{
+							glLineWidth(3.f);
+							glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+							buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);
+							glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+							glLineWidth(1.f);
+						}
 					}
+					gGL.popMatrix();
 				}
-				gGL.popMatrix();
-			}
 
 			if (physics)
 			{
@@ -5369,97 +3789,99 @@ BOOL LLModelPreview::render()
 
 						LLModel* model = instance.mLOD[LLModel::LOD_PHYSICS];
 
-						if (!model)
-						{
-							continue;
-						}
+							if (!model)
+							{
+								continue;
+							}
 
-						gGL.pushMatrix();
-						LLMatrix4 mat = instance.mTransform;
+							gGL.pushMatrix();
+							LLMatrix4 mat = instance.mTransform;
 
 						gGL.multMatrix((GLfloat*) mat.mMatrix);
 
 
-						bool render_mesh = true;
+							bool render_mesh = true;
 
-						LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread;
-						if (decomp)
-						{
-							LLMutexLock(decomp->mMutex);
+							LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread;
+							if (decomp)
+							{
+								LLMutexLock(decomp->mMutex);
 
-							LLModel::Decomposition& physics = model->mPhysics;
+								LLModel::Decomposition& physics = model->mPhysics;
 
-							if (!physics.mHull.empty())
-							{
-								render_mesh = false;
+								if (!physics.mHull.empty())
+								{
+									render_mesh = false;
 
-								if (physics.mMesh.empty())
-								{ //build vertex buffer for physics mesh
-									gMeshRepo.buildPhysicsMesh(physics);
-								}
+									if (physics.mMesh.empty())
+									{ //build vertex buffer for physics mesh
+										gMeshRepo.buildPhysicsMesh(physics);
+									}
 						
-								if (!physics.mMesh.empty())
-								{ //render hull instead of mesh
-									for (U32 i = 0; i < physics.mMesh.size(); ++i)
-									{
-										if (explode > 0.f)
+									if (!physics.mMesh.empty())
+									{ //render hull instead of mesh
+										for (U32 i = 0; i < physics.mMesh.size(); ++i)
 										{
-											gGL.pushMatrix();
+											if (explode > 0.f)
+											{
+												gGL.pushMatrix();
 
-											LLVector3 offset = model->mHullCenter[i]-model->mCenterOfHullCenters;
-											offset *= explode;
+												LLVector3 offset = model->mHullCenter[i]-model->mCenterOfHullCenters;
+												offset *= explode;
 
-											gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]);
-										}
+												gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]);
+											}
 
-										static std::vector<LLColor4U> hull_colors;
+											static std::vector<LLColor4U> hull_colors;
 
-										if (i+1 >= hull_colors.size())
-										{
-											hull_colors.push_back(LLColor4U(rand()%128+127, rand()%128+127, rand()%128+127, 128));
-										}
+											if (i+1 >= hull_colors.size())
+											{
+												hull_colors.push_back(LLColor4U(rand()%128+127, rand()%128+127, rand()%128+127, 128));
+											}
 
-										gGL.diffuseColor4ubv(hull_colors[i].mV);
-										LLVertexBuffer::drawArrays(LLRender::TRIANGLES, physics.mMesh[i].mPositions, physics.mMesh[i].mNormals);
+											gGL.diffuseColor4ubv(hull_colors[i].mV);
+											LLVertexBuffer::drawArrays(LLRender::TRIANGLES, physics.mMesh[i].mPositions, physics.mMesh[i].mNormals);
 
-										if (explode > 0.f)
-										{
-											gGL.popMatrix();
+											if (explode > 0.f)
+											{
+												gGL.popMatrix();
+											}
 										}
 									}
 								}
 							}
-						}
-
-						if (render_mesh)
-						{
-							if (mVertexBuffer[LLModel::LOD_PHYSICS].empty())
-							{
-								genBuffers(LLModel::LOD_PHYSICS, false);
-							}
-							for (U32 i = 0; i < mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); ++i)
+						
+							if (render_mesh)
 							{
-								LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i];
+								if (mVertexBuffer[LLModel::LOD_PHYSICS].empty())
+								{
+									genBuffers(LLModel::LOD_PHYSICS, false);
+								}
 
-								gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-								gGL.diffuseColor4f(0.4f, 0.4f, 0.0f, 0.4f);
+								U32 num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size();
+								for (U32 i = 0; i < num_models; ++i)
+								{
+									LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i];
 
-								buffer->setBuffer(type_mask & buffer->getTypeMask());
-								buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);
+									gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+									gGL.diffuseColor4f(0.4f, 0.4f, 0.0f, 0.4f);
 
-								gGL.diffuseColor3f(1.f, 1.f, 0.f);
+									buffer->setBuffer(type_mask & buffer->getTypeMask());
+									buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);
 
-								glLineWidth(2.f);
-								glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-								buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);
+									gGL.diffuseColor3f(1.f, 1.f, 0.f);
 
-								glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-								glLineWidth(1.f);
+									glLineWidth(2.f);
+									glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+									buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);
+
+									glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+									glLineWidth(1.f);
+								}
 							}
-						}
 
-						gGL.popMatrix();
-					}
+							gGL.popMatrix();
+						}
 
 					glLineWidth(3.f);
 					glPointSize(8.f);
@@ -5640,20 +4062,20 @@ BOOL LLModelPreview::render()
 								position[j] = v;
 							}
 
-							llassert(model->mMaterialList.size() > i);
+							llassert(model->mMaterialList.size() > i); 
 							const std::string& binding = instance.mModel->mMaterialList[i];
 							const LLImportMaterial& material = instance.mMaterial[binding];
 
 							buffer->setBuffer(type_mask & buffer->getTypeMask());
 							gGL.diffuseColor4fv(material.mDiffuseColor.mV);
 							gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-							if (material.mDiffuseMap.notNull())
+
+							// Find the tex for this material, bind it, and add it to our set
+							//
+							LLViewerFetchedTexture* tex = bindMaterialDiffuseTexture(material);
+							if (tex)
 							{
-								if (material.mDiffuseMap->getDiscardLevel() > -1)
-								{
-									gGL.getTexUnit(0)->bind(material.mDiffuseMap, true);
-									mTextureSet.insert(material.mDiffuseMap.get());
-								}
+								mTextureSet.insert(tex);
 							}
 						
 							buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0);
@@ -5768,14 +4190,14 @@ void LLFloaterModelPreview::onReset(void* user_data)
 	LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) user_data;
 	fmp->childDisable("reset_btn");
 	LLModelPreview* mp = fmp->mModelPreview;
-	std::string filename = mp->mLODFile[3]; 
+	std::string filename = mp->mLODFile[LLModel::LOD_HIGH]; 
 
 	fmp->resetDisplayOptions();
 	//reset model preview
 	fmp->initModelPreview();
 
 	mp = fmp->mModelPreview;
-	mp->loadModel(filename,3,true);
+	mp->loadModel(filename,LLModel::LOD_HIGH,true);
 }
 
 //static
@@ -5869,7 +4291,7 @@ void LLFloaterModelPreview::toggleCalculateButton(bool visible)
 	}
 	
 	mUploadBtn->setVisible(!visible);
-	mUploadBtn->setEnabled(mHasUploadPerm && !mUploadModelUrl.empty());
+	mUploadBtn->setEnabled(isModelUploadAllowed());
 
 	if (visible)
 	{
@@ -5935,7 +4357,7 @@ void LLFloaterModelPreview::handleModelPhysicsFeeReceived()
 	childSetTextArg("price_breakdown", "[MODEL]", llformat("%d", result["upload_price_breakdown"]["model"].asInteger()));
 	childSetVisible("upload_fee", true);
 	childSetVisible("price_breakdown", true);
-	mUploadBtn->setEnabled(mHasUploadPerm && !mUploadModelUrl.empty());
+	mUploadBtn->setEnabled(isModelUploadAllowed());
 }
 
 void LLFloaterModelPreview::setModelPhysicsFeeErrorStatus(S32 status, const std::string& reason)
@@ -5959,6 +4381,16 @@ void LLFloaterModelPreview::onModelUploadFailure()
 	mUploadBtn->setEnabled(true);
 }
 
+bool LLFloaterModelPreview::isModelUploadAllowed()
+{
+	bool allow_upload = mHasUploadPerm && !mUploadModelUrl.empty();
+	if (mModelPreview)
+	{
+		allow_upload &= mModelPreview->mModelNoErrors;
+	}
+	return allow_upload;
+}
+
 S32 LLFloaterModelPreview::DecompRequest::statusCallback(const char* status, S32 p1, S32 p2)
 {
 	if (mContinue)
@@ -6008,8 +4440,8 @@ void LLFloaterModelPreview::onPermissionsReceived(const LLSD& result)
 	// BAP HACK: handle "" for case that  MeshUploadFlag cap is broken.
 	mHasUploadPerm = (("" == upload_status) || ("valid" == upload_status));
 
-	//mUploadBtn->setEnabled(mHasUploadPerm);
-	mUploadBtn->setEnabled(mHasUploadPerm && !mUploadModelUrl.empty());
+	// isModelUploadAllowed() includes mHasUploadPerm
+	mUploadBtn->setEnabled(isModelUploadAllowed());
 	getChild<LLTextBox>("warning_title")->setVisible(!mHasUploadPerm);
 	getChild<LLTextBox>("warning_message")->setVisible(!mHasUploadPerm);
 }
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index 618748bd4ea417e2d3d6504cdc9057b6f52d0939..7a518c798b17977cb96d15ff596ec2d5b3a08864 100755
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -37,6 +37,8 @@
 #include "llviewermenufile.h"
 #include "llfloatermodeluploadbase.h"
 
+#include "lldaeloader.h"
+
 class LLComboBox;
 class LLJoint;
 class LLViewerJointMesh;
@@ -45,103 +47,18 @@ class LLTextBox;
 class LLVertexBuffer;
 class LLModelPreview;
 class LLFloaterModelPreview;
+class DAE;
 class daeElement;
 class domProfile_COMMON;
 class domInstance_geometry;
 class domNode;
 class domTranslate;
 class domController;
+class domSkin;
+class domMesh;
 class LLMenuButton;
 class LLToggleableMenu;
 
-typedef std::map<std::string, LLMatrix4> JointTransformMap;
-typedef std::map<std::string, LLMatrix4>:: iterator JointTransformMapIt;
-
-const S32 NUM_LOD = 4;
-
-class LLModelLoader : public LLThread
-{
-public:
-	typedef enum
-	{
-		STARTING = 0,
-		READING_FILE,
-		CREATING_FACES,
-		GENERATING_VERTEX_BUFFERS,
-		GENERATING_LOD,
-		DONE,
-		ERROR_PARSING, //basically loading failed
-		ERROR_MATERIALS,
-	} eLoadState;
-
-	U32 mState;
-	std::string mFilename;
-	S32 mLod;
-	LLModelPreview* mPreview;
-	LLMatrix4 mTransform;
-	BOOL mFirstTransform;
-	LLVector3 mExtents[2];
-	bool mTrySLM;
-	
-	std::map<daeElement*, LLPointer<LLModel> > mModel;
-	
-	typedef std::vector<LLPointer<LLModel> > model_list;
-	model_list mModelList;
-
-	typedef std::vector<LLModelInstance> model_instance_list;
-	
-	typedef std::map<LLMatrix4, model_instance_list > scene;
-
-	scene mScene;
-
-	typedef std::queue<LLPointer<LLModel> > model_queue;
-
-	//queue of models that need a physics rep
-	model_queue mPhysicsQ;
-
-	LLModelLoader( std::string filename, S32 lod, LLModelPreview* preview, JointTransformMap& jointMap, 
-				   std::deque<std::string>& jointsFromNodes );
-	~LLModelLoader() ;
-
-	virtual void run();
-	bool doLoadModel();
-	bool loadFromSLM(const std::string& filename);
-	void loadModelCallback();
-
-	void loadTextures() ; //called in the main thread.
-	void processElement(daeElement* element, bool& badElement);
-	std::map<std::string, LLImportMaterial> getMaterials(LLModel* model, domInstance_geometry* instance_geo);
-	LLImportMaterial profileToMaterial(domProfile_COMMON* material);
-	std::string getElementLabel(daeElement *element);
-	LLColor4 getDaeColor(daeElement* element);
-	
-	daeElement* getChildFromElement( daeElement* pElement, std::string const & name );
-	
-	bool isNodeAJoint( domNode* pNode );
-	void processJointNode( domNode* pNode, std::map<std::string,LLMatrix4>& jointTransforms );
-	void extractTranslation( domTranslate* pTranslate, LLMatrix4& transform );
-	void extractTranslationViaElement( daeElement* pTranslateElement, LLMatrix4& transform );
-	void extractTranslationViaSID( daeElement* pElement, LLMatrix4& transform );
-
-	void setLoadState(U32 state);
-
-	void buildJointToNodeMappingFromScene( daeElement* pRoot );
-	void processJointToNodeMapping( domNode* pNode );
-	void processChildJoints( domNode* pParentNode );
-
-	//map of avatar joints as named in COLLADA assets to internal joint names
-	std::map<std::string, std::string> mJointMap;
-	JointTransformMap& mJointList;	
-	std::deque<std::string>& mJointsFromNode;
-
-	S32 mNumOfFetchingTextures ; //updated in the main thread
-	bool areTexturesReady() { return !mNumOfFetchingTextures; } //called in the main thread.
-
-private:
-	static std::list<LLModelLoader*> sActiveLoaderList;
-	static bool isAlive(LLModelLoader* loader) ;
-};
-
 class LLFloaterModelPreview : public LLFloaterModelUploadBase
 {
 public:
@@ -172,6 +89,7 @@ public:
 	BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); 
 	
 	/*virtual*/ void onOpen(const LLSD& key);
+	/*virtual*/ void onClose(bool app_quitting);
 
 	static void onMouseCaptureLostModelPreview(LLMouseHandler*);
 	static void setUploadAmount(S32 amount) { sUploadAmount = amount; }
@@ -210,6 +128,8 @@ public:
 
 	/*virtual*/ void onModelUploadFailure();
 
+	bool isModelUploadAllowed();
+
 protected:
 	friend class LLModelPreview;
 	friend class LLMeshFilePicker;
@@ -359,21 +279,14 @@ public:
 	void setHasPivot( bool val ) { mHasPivot = val; }
 	void setModelPivot( const LLVector3& pivot ) { mModelPivot = pivot; }
 
-	//Determines the viability of an asset to be used as an avatar rig (w or w/o joint upload caps)
-	void critiqueRigForUploadApplicability( const std::vector<std::string> &jointListFromAsset );
-	void critiqueJointToNodeMappingFromScene( void  );
 	//Is a rig valid so that it can be used as a criteria for allowing for uploading of joint positions
 	//Accessors for joint position upload friendly rigs
 	const bool isRigValidForJointPositionUpload( void ) const { return mRigValidJointUpload; }
 	void setRigValidForJointPositionUpload( bool rigValid ) { mRigValidJointUpload = rigValid; }
-	bool isRigSuitableForJointPositionUpload( const std::vector<std::string> &jointListFromAsset );
-	//Determines if a rig is a legacy from the joint list
-	bool isRigLegacy( const std::vector<std::string> &jointListFromAsset );	
+
 	//Accessors for the legacy rigs
 	const bool isLegacyRigValid( void ) const { return mLegacyRigValid; }
-	void setLegacyRigValid( bool rigValid ) { mLegacyRigValid = rigValid; }	
-	//Verify that a controller matches vertex counts
-	bool verifyController( domController* pController );
+	void setLegacyRigValid( bool rigValid ) { mLegacyRigValid = rigValid; }		
 
 	static void	textureLoadedCallback( BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* src_aux, S32 discard_level, BOOL final, void* userdata );
 	
@@ -388,6 +301,16 @@ public:
 	
 	LLVector3 getTranslationForJointOffset( std::string joint );
 
+	static bool 		sIgnoreLoadedCallback;
+
+protected:
+
+	static void			loadedCallback(LLModelLoader::scene& scene,LLModelLoader::model_list& model_list, S32 lod, void* opaque);
+	static void			stateChangedCallback(U32 state, void* opaque);
+
+	static LLJoint*	lookupJointByName(const std::string&, void* opaque);
+	static U32			loadTextures(LLImportMaterial& material, void* opaque);
+
 private:
 	//Utility function for controller vertex compare
 	bool verifyCount( int expected, int result );
@@ -395,6 +318,8 @@ private:
 	void		createPreviewAvatar( void );
 	//Accessor for the dummy avatar
 	LLVOAvatar* getPreviewAvatar( void ) { return mPreviewAvatar; }
+	// Count amount of original models, excluding sub-models
+	static U32 countRootModels(LLModelLoader::model_list models);
 
  protected:
 	friend class LLModelLoader;
@@ -416,13 +341,15 @@ private:
 	LLVector3	mPreviewTarget;
 	LLVector3	mPreviewScale;
 	S32			mPreviewLOD;
+	S32			mPhysicsSearchLOD;
 	U32			mResourceCost;
 	std::string mLODFile[LLModel::NUM_LODS];
 	bool		mLoading;
 	U32			mLoadState;
 	bool		mResetJoints;
 	bool		mRigParityWithScene;
-	
+	bool		mModelNoErrors;
+
 	std::map<std::string, bool> mViewOption;
 
 	//GLOD object parameters (must rebuild object if these change)
@@ -459,7 +386,7 @@ private:
 	U32 mMaxTriangleLimit;
 	
 	LLMeshUploadThread::instance_list mUploadData;
-	std::set<LLViewerFetchedTexture* > mTextureSet;
+	std::set<LLViewerFetchedTexture * > mTextureSet;
 
 	//map of vertex buffers to models (one vertex buffer in vector per face in model
 	std::map<LLModel*, std::vector<LLPointer<LLVertexBuffer> > > mVertexBuffer[LLModel::NUM_LODS+1];
@@ -478,10 +405,9 @@ private:
 
 	bool		mLastJointUpdate;
 
-	std::deque<std::string> mMasterJointList;
-	std::deque<std::string> mMasterLegacyJointList;
-	std::deque<std::string> mJointsFromNode;
-	JointTransformMap		mJointTransformMap;
+	JointSet				mJointsFromNode;
+	JointTransformMap	mJointTransformMap;
+
 	LLPointer<LLVOAvatar>	mPreviewAvatar;
 };
 
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 9a0bd9d1bc53977a7905bb65481894d805d68c56..55b94aa1412c51ad3eabab0769fcbb90b7f999f4 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -493,6 +493,12 @@ void get_vertex_buffer_from_mesh(LLCDMeshData& mesh, LLModel::PhysicsMesh& res,
 	}
 }
 
+LLViewerFetchedTexture* LLMeshUploadThread::FindViewerTexture(const LLImportMaterial& material)
+{
+	LLPointer< LLViewerFetchedTexture > * ppTex = static_cast< LLPointer< LLViewerFetchedTexture > * >(material.mOpaqueData);
+	return ppTex ? (*ppTex).get() : NULL;
+}
+
 volatile S32 LLMeshRepoThread::sActiveHeaderRequests = 0;
 volatile S32 LLMeshRepoThread::sActiveLODRequests = 0;
 U32	LLMeshRepoThread::sMaxConcurrentRequests = 1;
@@ -587,16 +593,16 @@ public:
 	LLMeshLODHandler(const LLVolumeParams & mesh_params, S32 lod, U32 offset, U32 requested_bytes)
 		: LLMeshHandlerBase(offset, requested_bytes),
 		  mLOD(lod)
-		{
+	{
 			mMeshParams = mesh_params;
 			LLMeshRepoThread::incActiveLODRequests();
 		}
 	virtual ~LLMeshLODHandler();
-
+	
 protected:
 	LLMeshLODHandler(const LLMeshLODHandler &);					// Not defined
 	void operator=(const LLMeshLODHandler &);					// Not defined
-
+	
 public:
 	virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size);
 	virtual void processFailure(LLCore::HttpStatus status);
@@ -796,7 +802,7 @@ LLMeshRepoThread::LLMeshRepoThread()
 	mHttpLargePolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_LARGE_MESH);
 }
 
-			
+
 LLMeshRepoThread::~LLMeshRepoThread()
 {
 	LL_INFOS(LOG_MESH) << "Small GETs issued:  " << LLMeshRepository::sHTTPRequestCount
@@ -865,16 +871,16 @@ void LLMeshRepoThread::run()
 		{
 			break;
 		}
-
+		
 		if (! mHttpRequestSet.empty())
 		{
 			// Dispatch all HttpHandler notifications
 			mHttpRequest->update(0L);
 		}
 		sRequestWaterLevel = mHttpRequestSet.size();			// Stats data update
-
-		// NOTE: order of queue processing intentionally favors LOD requests over header requests
 			
+		// NOTE: order of queue processing intentionally favors LOD requests over header requests
+
 		while (!mLODReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater)
 		{
 			if (! mMutex)
@@ -938,7 +944,7 @@ void LLMeshRepoThread::run()
 					mSkinRequests.erase(iter);
 					mMutex->unlock();
 
-					if (!fetchMeshSkinInfo(mesh_id))
+					if (! fetchMeshSkinInfo(mesh_id))
 					{
 						incomplete.insert(mesh_id);
 					}
@@ -967,7 +973,7 @@ void LLMeshRepoThread::run()
 					mDecompositionRequests.erase(iter);
 					mMutex->unlock();
 					
-					if (!fetchMeshDecomposition(mesh_id))
+					if (! fetchMeshDecomposition(mesh_id))
 					{
 						incomplete.insert(mesh_id);
 					}
@@ -993,7 +999,7 @@ void LLMeshRepoThread::run()
 					mPhysicsShapeRequests.erase(iter);
 					mMutex->unlock();
 					
-					if (!fetchMeshPhysicsShape(mesh_id))
+					if (! fetchMeshPhysicsShape(mesh_id))
 					{
 						incomplete.insert(mesh_id);
 					}
@@ -1219,7 +1225,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
 	}
 
 	++LLMeshRepository::sMeshRequestCount;
-	bool ret = true ;
+	bool ret = true;
 	U32 header_size = mMeshHeaderSize[mesh_id];
 	
 	if (header_size > 0)
@@ -1267,7 +1273,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
 			constructUrl(mesh_id, &http_url, &cap_version);
 
 			if (!http_url.empty())
-			{				
+			{
 				LLMeshSkinInfoHandler * handler = new LLMeshSkinInfoHandler(mesh_id, offset, size);
 				LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler);
 				if (LLCORE_HTTP_HANDLE_INVALID == handle)
@@ -1313,7 +1319,7 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
 
 	++LLMeshRepository::sMeshRequestCount;
 	U32 header_size = mMeshHeaderSize[mesh_id];
-	bool ret = true ;
+	bool ret = true;
 	
 	if (header_size > 0)
 	{
@@ -1359,9 +1365,9 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
 			int cap_version(2);
 			std::string http_url;
 			constructUrl(mesh_id, &http_url, &cap_version);
-
+			
 			if (!http_url.empty())
-			{				
+			{
 				LLMeshDecompositionHandler * handler = new LLMeshDecompositionHandler(mesh_id, offset, size);
 				LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler);
 				if (LLCORE_HTTP_HANDLE_INVALID == handle)
@@ -1407,7 +1413,7 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
 
 	++LLMeshRepository::sMeshRequestCount;
 	U32 header_size = mMeshHeaderSize[mesh_id];
-	bool ret = true ;
+	bool ret = true;
 
 	if (header_size > 0)
 	{
@@ -1452,9 +1458,9 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
 			int cap_version(2);
 			std::string http_url;
 			constructUrl(mesh_id, &http_url, &cap_version);
-
+			
 			if (!http_url.empty())
-			{				
+			{
 				LLMeshPhysicsShapeHandler * handler = new LLMeshPhysicsShapeHandler(mesh_id, offset, size);
 				LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler);
 				if (LLCORE_HTTP_HANDLE_INVALID == handle)
@@ -1543,11 +1549,11 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params)
 	}
 
 	//either cache entry doesn't exist or is corrupt, request header from simulator	
-	bool retval = true ;
+	bool retval = true;
 	int cap_version(2);
 	std::string http_url;
 	constructUrl(mesh_params.getSculptID(), &http_url, &cap_version);
-
+	
 	if (!http_url.empty())
 	{
 		//grab first 4KB if we're going to bother with a fetch.  Cache will prevent future fetches if a full mesh fits
@@ -1635,9 +1641,9 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
 			int cap_version(2);
 			std::string http_url;
 			constructUrl(mesh_id, &http_url, &cap_version);
-
+			
 			if (!http_url.empty())
-			{				
+			{
 				LLMeshLODHandler * handler = new LLMeshLODHandler(mesh_params, lod, offset, size);
 				LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler);
 				if (LLCORE_HTTP_HANDLE_INVALID == handle)
@@ -1909,7 +1915,7 @@ LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data,
 
 	mOrigin += gAgent.getAtAxis() * scale.magVec();
 
-	mMeshUploadTimeOut = gSavedSettings.getS32("MeshUploadTimeOut") ;
+	mMeshUploadTimeOut = gSavedSettings.getS32("MeshUploadTimeOut");
 
 	mHttpRequest = new LLCore::HttpRequest;
 	mHttpOptions = new LLCore::HttpOptions;
@@ -1977,14 +1983,14 @@ void LLMeshUploadThread::preStart()
 
 void LLMeshUploadThread::discard()
 {
-	LLMutexLock lock(mMutex) ;
+	LLMutexLock lock(mMutex);
 	mDiscarded = true;
 }
 
 bool LLMeshUploadThread::isDiscarded() const
 {
-	LLMutexLock lock(mMutex) ;
-	return mDiscarded ;
+	LLMutexLock lock(mMutex);
+	return mDiscarded;
 }
 
 void LLMeshUploadThread::run()
@@ -2049,6 +2055,14 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
 	{
 		LLMeshUploadData data;
 		data.mBaseModel = iter->first;
+
+		if (data.mBaseModel->mSubmodelID)
+		{
+			// These are handled below to insure correct parenting order on creation
+			// due to map walking being based on model address (aka random)
+			continue;
+		}
+
 		LLModelInstance& first_instance = *(iter->second.begin());
 		for (S32 i = 0; i < 5; i++)
 		{
@@ -2086,7 +2100,10 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
 				data.mModel[LLModel::LOD_IMPOSTOR], 
 				decomp,
 				mUploadSkin,
-				mUploadJoints);
+				mUploadJoints,
+				FALSE,
+				FALSE,
+				data.mBaseModel->mSubmodelID);
 
 			data.mAssetData = ostr.str();
 			std::string str = ostr.str();
@@ -2120,17 +2137,26 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
 			instance_entry["scale"] = ll_sd_from_vector3(scale);
 		
 			instance_entry["material"] = LL_MCODE_WOOD;
-			instance_entry["physics_shape_type"] = (U8)(LLViewerObject::PHYSICS_SHAPE_CONVEX_HULL);
+			instance_entry["physics_shape_type"] = data.mModel[LLModel::LOD_PHYSICS].notNull() ? (U8)(LLViewerObject::PHYSICS_SHAPE_PRIM) : (U8)(LLViewerObject::PHYSICS_SHAPE_CONVEX_HULL);
 			instance_entry["mesh"] = mesh_index[data.mBaseModel];
 
 			instance_entry["face_list"] = LLSD::emptyArray();
 
-			S32 end = llmin((S32)data.mBaseModel->mMaterialList.size(), data.mBaseModel->getNumVolumeFaces()) ;
+			// We want to be able to allow more than 8 materials...
+			//
+			S32 end = llmin((S32)instance.mMaterial.size(), instance.mModel->getNumVolumeFaces()) ;
+
 			for (S32 face_num = 0; face_num < end; face_num++)
 			{
 				LLImportMaterial& material = instance.mMaterial[data.mBaseModel->mMaterialList[face_num]];
 				LLSD face_entry = LLSD::emptyMap();
-				LLViewerFetchedTexture *texture = material.mDiffuseMap.get();
+
+				LLViewerFetchedTexture *texture = NULL;
+
+				if (material.mDiffuseMapFilename.size())
+				{
+					texture = FindViewerTexture(material);
+				}
 				
 				if ((texture != NULL) &&
 					(textures.find(texture) == textures.end()))
@@ -2145,9 +2171,171 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
 					{											
 						LLPointer<LLImageJ2C> upload_file =
 							LLViewerTextureList::convertToUploadFile(texture->getSavedRawImage());
+
+						if (!upload_file.isNull() && upload_file->getDataSize())
+						{
+						texture_str.write((const char*) upload_file->getData(), upload_file->getDataSize());
+					}
+				}
+				}
+
+				if (texture != NULL &&
+					mUploadTextures &&
+					texture_index.find(texture) == texture_index.end())
+				{
+					texture_index[texture] = texture_num;
+					std::string str = texture_str.str();
+					res["texture_list"][texture_num] = LLSD::Binary(str.begin(),str.end());
+					texture_num++;
+				}
+
+				// Subset of TextureEntry fields.
+				if (texture != NULL && mUploadTextures)
+				{
+					face_entry["image"] = texture_index[texture];
+					face_entry["scales"] = 1.0;
+					face_entry["scalet"] = 1.0;
+					face_entry["offsets"] = 0.0;
+					face_entry["offsett"] = 0.0;
+					face_entry["imagerot"] = 0.0;
+				}
+				face_entry["diffuse_color"] = ll_sd_from_color4(material.mDiffuseColor);
+				face_entry["fullbright"] = material.mFullbright;
+				instance_entry["face_list"][face_num] = face_entry;
+		    }
+
+			res["instance_list"][instance_num] = instance_entry;
+			instance_num++;
+		}
+	}
+
+	for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter)
+	{
+		LLMeshUploadData data;
+		data.mBaseModel = iter->first;
+
+		if (!data.mBaseModel->mSubmodelID)
+		{
+			// These were handled above already...
+			//
+			continue;
+		}
+
+		LLModelInstance& first_instance = *(iter->second.begin());
+		for (S32 i = 0; i < 5; i++)
+		{
+			data.mModel[i] = first_instance.mLOD[i];
+		}
+
+		if (mesh_index.find(data.mBaseModel) == mesh_index.end())
+		{
+			// Have not seen this model before - create a new mesh_list entry for it.
+			if (model_name.empty())
+			{
+				model_name = data.mBaseModel->getName();
+			}
+
+			if (model_metric.empty())
+			{
+				model_metric = data.mBaseModel->getMetric();
+			}
+
+			std::stringstream ostr;
+			
+			LLModel::Decomposition& decomp =
+				data.mModel[LLModel::LOD_PHYSICS].notNull() ? 
+				data.mModel[LLModel::LOD_PHYSICS]->mPhysics : 
+				data.mBaseModel->mPhysics;
+
+			decomp.mBaseHull = mHullMap[data.mBaseModel];
+
+			LLSD mesh_header = LLModel::writeModel(
+				ostr,  
+				data.mModel[LLModel::LOD_PHYSICS],
+				data.mModel[LLModel::LOD_HIGH],
+				data.mModel[LLModel::LOD_MEDIUM],
+				data.mModel[LLModel::LOD_LOW],
+				data.mModel[LLModel::LOD_IMPOSTOR], 
+				decomp,
+				mUploadSkin,
+				mUploadJoints,
+				FALSE,
+				FALSE,
+				data.mBaseModel->mSubmodelID);
+
+			data.mAssetData = ostr.str();
+			std::string str = ostr.str();
+
+			res["mesh_list"][mesh_num] = LLSD::Binary(str.begin(),str.end()); 
+			mesh_index[data.mBaseModel] = mesh_num;
+			mesh_num++;
+		}
+
+		// For all instances that use this model
+		for (instance_list::iterator instance_iter = iter->second.begin();
+			 instance_iter != iter->second.end();
+			 ++instance_iter)
+		{
+
+			LLModelInstance& instance = *instance_iter;
+		
+			LLSD instance_entry;
+		
+			for (S32 i = 0; i < 5; i++)
+			{
+				data.mModel[i] = instance.mLOD[i];
+			}
+		
+			LLVector3 pos, scale;
+			LLQuaternion rot;
+			LLMatrix4 transformation = instance.mTransform;
+			decomposeMeshMatrix(transformation,pos,rot,scale);
+			instance_entry["position"] = ll_sd_from_vector3(pos);
+			instance_entry["rotation"] = ll_sd_from_quaternion(rot);
+			instance_entry["scale"] = ll_sd_from_vector3(scale);
+		
+			instance_entry["material"] = LL_MCODE_WOOD;
+			instance_entry["physics_shape_type"] = (U8)(LLViewerObject::PHYSICS_SHAPE_NONE);
+			instance_entry["mesh"] = mesh_index[data.mBaseModel];
+
+			instance_entry["face_list"] = LLSD::emptyArray();
+
+			// We want to be able to allow more than 8 materials...
+			//
+			S32 end = llmin((S32)instance.mMaterial.size(), instance.mModel->getNumVolumeFaces()) ;
+
+			for (S32 face_num = 0; face_num < end; face_num++)
+			{
+				LLImportMaterial& material = instance.mMaterial[data.mBaseModel->mMaterialList[face_num]];
+				LLSD face_entry = LLSD::emptyMap();
+
+				LLViewerFetchedTexture *texture = NULL;
+
+				if (material.mDiffuseMapFilename.size())
+				{
+					texture = FindViewerTexture(material);
+				}
+
+				if ((texture != NULL) &&
+					(textures.find(texture) == textures.end()))
+				{
+					textures.insert(texture);
+				}
+
+				std::stringstream texture_str;
+				if (texture != NULL && include_textures && mUploadTextures)
+				{
+					if(texture->hasSavedRawImage())
+					{											
+						LLPointer<LLImageJ2C> upload_file =
+							LLViewerTextureList::convertToUploadFile(texture->getSavedRawImage());
+
+						if (!upload_file.isNull() && upload_file->getDataSize())
+						{
 						texture_str.write((const char*) upload_file->getData(), upload_file->getDataSize());
 					}
 				}
+				}
 
 				if (texture != NULL &&
 					mUploadTextures &&
@@ -2235,7 +2423,7 @@ void LLMeshUploadThread::generateHulls()
 		}
 	}
 		
-	if(has_valid_requests)
+	if (has_valid_requests)
 	{
 		// *NOTE:  Interesting livelock condition on shutdown.  If there
 		// is an upload request in generateHulls() when shutdown starts,
@@ -2340,7 +2528,7 @@ void LLMeshUploadThread::requestWholeModelFee()
 	else
 	{
 		U32 sleep_time(10);
-
+		
 		mHttpRequest->update(0);
 		while (! LLApp::isQuitting() && ! finished() && ! isDiscarded())
 		{
@@ -2435,7 +2623,7 @@ void LLMeshUploadThread::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResp
 		// model fee case
 		LLWholeModelFeeObserver* observer(mFeeObserverHandle.get());
 		mWholeModelUploadURL.clear();
-
+		
 		if (! status)
 		{
 			LL_WARNS(LOG_MESH) << "Fee request failed.  Reason:  " << reason
@@ -2465,7 +2653,7 @@ void LLMeshUploadThread::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResp
 				LLCoreHttpUtil::responseToLLSD(response, true, body);
 			}
 			dump_llsd_to_file(body, make_dump_name("whole_model_fee_response_", dump_num));
-
+		
 			if (body["state"].asString() == "upload")
 			{
 				mWholeModelUploadURL = body["uploader"].asString();
@@ -2536,7 +2724,7 @@ void LLMeshRepoThread::notifyLoadedMeshes()
 		LODRequest req = mUnavailableQ.front();
 		mUnavailableQ.pop();
 		mMutex->unlock();
-		
+
 		update_metrics = true;
 		gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD);
 	}
@@ -2672,7 +2860,7 @@ void LLMeshRepository::cacheOutgoingMesh(LLMeshUploadData& data, LLSD& header)
 void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response)
 {
 	mProcessed = true;
-	
+
 	unsigned int retries(0U);
 	response->getRetries(NULL, &retries);
 	LLMeshRepository::sHTTPRetryCount += retries;
@@ -2728,7 +2916,7 @@ void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespo
 				// 200 case, typically
 				offset = 0;
 			}
-
+		
 			// *DEBUG:  To test validation below
 			// offset += 1;
 
@@ -2844,7 +3032,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b
 				const std::string & lod_name = header_lod[i];
 				lod_bytes = llmax(lod_bytes, header[lod_name]["offset"].asInteger()+header[lod_name]["size"].asInteger());
 			}
-
+		
 			// just in case skin info or decomposition is at the end of the file (which it shouldn't be)
 			lod_bytes = llmax(lod_bytes, header["skin"]["offset"].asInteger() + header["skin"]["size"].asInteger());
 			lod_bytes = llmax(lod_bytes, header["physics_convex"]["offset"].asInteger() + header["physics_convex"]["size"].asInteger());
@@ -2852,7 +3040,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b
 			S32 header_bytes = (S32) gMeshRepo.mThread->mMeshHeaderSize[mesh_id];
 			S32 bytes = lod_bytes + header_bytes; 
 
-
+		
 			// It's possible for the remote asset to have more data than is needed for the local cache
 			// only allocate as much space in the VFS as is needed for the local cache
 			data_size = llmin(data_size, bytes);
@@ -2864,11 +3052,11 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b
 				++LLMeshRepository::sCacheWrites;
 
 				file.write(data, data_size);
-
+			
 				// zero out the rest of the file 
 				U8 block[MESH_HEADER_SIZE];
 				memset(block, 0, sizeof(block));
-	
+
 				while (bytes-file.tell() > sizeof(block))
 				{
 					file.write(block, sizeof(block));
@@ -2894,8 +3082,8 @@ LLMeshLODHandler::~LLMeshLODHandler()
 			gMeshRepo.mThread->lockAndLoadMeshLOD(mMeshParams, mLOD);
 		}
 		LLMeshRepoThread::decActiveLODRequests();
-		}
 	}
+}
 
 void LLMeshLODHandler::processFailure(LLCore::HttpStatus status)
 {
@@ -2913,7 +3101,7 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body
 {
 	if ((! MESH_LOD_PROCESS_FAILED) && gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size))
 	{
-		//good fetch from sim, write to VFS for caching
+		// good fetch from sim, write to VFS for caching
 		LLVFile file(gVFS, mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLVFile::WRITE);
 
 		S32 offset = mOffset;
@@ -2958,7 +3146,7 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /*
 {
 	if ((! MESH_SKIN_INFO_PROCESS_FAILED) && gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size))
 	{
-		//good fetch from sim, write to VFS for caching
+		// good fetch from sim, write to VFS for caching
 		LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE);
 
 		S32 offset = mOffset;
@@ -3282,7 +3470,7 @@ void LLMeshRepository::notifyLoadedMeshes()
 													 REQUEST2_LOW_WATER_MIN,
 													 REQUEST2_LOW_WATER_MAX);
 	}
-
+	
 	//clean up completed upload threads
 	for (std::vector<LLMeshUploadThread*>::iterator iter = mUploads.begin(); iter != mUploads.end(); )
 	{
@@ -3359,7 +3547,7 @@ void LLMeshRepository::notifyLoadedMeshes()
 
 	//call completed callbacks on finished decompositions
 	mDecompThread->notifyCompleted();
-	
+
 	// For major operations, attempt to get the required locks
 	// without blocking and punt if they're not available.  The
 	// longest run of holdoffs is kept in sMaxLockHoldoffs just
@@ -3378,12 +3566,12 @@ void LLMeshRepository::notifyLoadedMeshes()
 			return;
 		}
 		hold_offs = 0;
-
+		
 		if (gAgent.getRegion())
 		{
 			// Update capability urls
 			static std::string region_name("never name a region this");
-
+			
 			if (gAgent.getRegion()->getName() != region_name && gAgent.getRegion()->capabilitiesReceived())
 			{
 				region_name = gAgent.getRegion()->getName();
@@ -3399,7 +3587,7 @@ void LLMeshRepository::notifyLoadedMeshes()
 									<< LL_ENDL;
 			}
 		}
-
+		
 		//popup queued error messages from background threads
 		while (!mUploadErrorQ.empty())
 		{
@@ -3754,7 +3942,7 @@ LLSD& LLMeshRepoThread::getMeshHeader(const LLUUID& mesh_id)
 
 
 void LLMeshRepository::uploadModel(std::vector<LLModelInstance>& data, LLVector3& scale, bool upload_textures,
-									bool upload_skin, bool upload_joints, std::string upload_url, bool do_upload,
+								   bool upload_skin, bool upload_joints, std::string upload_url, bool do_upload,
 								   LLHandle<LLWholeModelFeeObserver> fee_observer, LLHandle<LLWholeModelUploadObserver> upload_observer)
 {
 	LLMeshUploadThread* thread = new LLMeshUploadThread(data, scale, upload_textures, upload_skin, upload_joints, upload_url, 
@@ -3825,37 +4013,6 @@ void LLMeshUploadThread::decomposeMeshMatrix(LLMatrix4& transformation,
 	result_rot = quat_rotation; 
 }
 
-bool LLImportMaterial::operator<(const LLImportMaterial &rhs) const
-{
-	if (mDiffuseMap != rhs.mDiffuseMap)
-	{
-		return mDiffuseMap < rhs.mDiffuseMap;
-	}
-
-	if (mDiffuseMapFilename != rhs.mDiffuseMapFilename)
-	{
-		return mDiffuseMapFilename < rhs.mDiffuseMapFilename;
-	}
-
-	if (mDiffuseMapLabel != rhs.mDiffuseMapLabel)
-	{
-		return mDiffuseMapLabel < rhs.mDiffuseMapLabel;
-	}
-
-	if (mDiffuseColor != rhs.mDiffuseColor)
-	{
-		return mDiffuseColor < rhs.mDiffuseColor;
-	}
-
-	if (mBinding != rhs.mBinding)
-	{
-		return mBinding < rhs.mBinding;
-	}
-
-	return mFullbright < rhs.mFullbright;
-}
-
-
 void LLMeshRepository::updateInventory(inventory_data data)
 {
 	LLMutexLock lock(mMeshMutex);
@@ -4253,7 +4410,7 @@ void LLPhysicsDecomp::doDecompositionSingleHull()
 	setMeshData(mesh, true);
 
 	LLCDResult ret = decomp->buildSingleHull() ;
-	if(ret)
+	if (ret)
 	{
 		LL_WARNS(LOG_MESH) << "Could not execute decomposition stage when attempting to create single hull." << LL_ENDL;
 		make_box(mCurRequest);
@@ -4441,60 +4598,6 @@ void LLPhysicsDecomp::Request::setStatusMessage(const std::string& msg)
 	mStatusMessage = msg;
 }
 
-LLModelInstance::LLModelInstance(LLSD& data)
-{
-	mLocalMeshID = data["mesh_id"].asInteger();
-	mLabel = data["label"].asString();
-	mTransform.setValue(data["transform"]);
-
-	for (U32 i = 0; i < data["material"].size(); ++i)
-	{
-		LLImportMaterial mat(data["material"][i]);
-		mMaterial[mat.mBinding] = mat;
-	}
-}
-
-
-LLSD LLModelInstance::asLLSD()
-{	
-	LLSD ret;
-
-	ret["mesh_id"] = mModel->mLocalID;
-	ret["label"] = mLabel;
-	ret["transform"] = mTransform.getValue();
-	
-	U32 i = 0;
-	for (std::map<std::string, LLImportMaterial>::iterator iter = mMaterial.begin(); iter != mMaterial.end(); ++iter)
-	{
-		ret["material"][i++] = iter->second.asLLSD();
-	}
-
-	return ret;
-}
-
-LLImportMaterial::LLImportMaterial(LLSD& data)
-{
-	mDiffuseMapFilename = data["diffuse"]["filename"].asString();
-	mDiffuseMapLabel = data["diffuse"]["label"].asString();
-	mDiffuseColor.setValue(data["diffuse"]["color"]);
-	mFullbright = data["fullbright"].asBoolean();
-	mBinding = data["binding"].asString();
-}
-
-
-LLSD LLImportMaterial::asLLSD()
-{
-	LLSD ret;
-
-	ret["diffuse"]["filename"] = mDiffuseMapFilename;
-	ret["diffuse"]["label"] = mDiffuseMapLabel;
-	ret["diffuse"]["color"] = mDiffuseColor.getValue();
-	ret["fullbright"] = mFullbright;
-	ret["binding"] = mBinding;
-
-	return ret;
-}
-
 void LLMeshRepository::buildPhysicsMesh(LLModel::Decomposition& decomp)
 {
 	decomp.mMesh.resize(decomp.mHull.size());
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index 39280bea3a5bd4665461af7169c0e1af0e501fb7..688cd01a87c7626dfe8390a86c0705d93a817777 100755
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -90,54 +90,6 @@ public:
 	}
 };
 
-class LLImportMaterial
-{
-public:
-	LLPointer<LLViewerFetchedTexture> mDiffuseMap;
-	std::string mDiffuseMapFilename;
-	std::string mDiffuseMapLabel;
-	std::string mBinding;
-	LLColor4 mDiffuseColor;
-	bool mFullbright;
-
-	bool operator<(const LLImportMaterial &params) const;
-
-	LLImportMaterial() 
-		: mFullbright(false) 
-	{ 
-		mDiffuseColor.set(1,1,1,1);
-	}
-
-	LLImportMaterial(LLSD& data);
-
-	LLSD asLLSD();
-};
-
-class LLModelInstance 
-{
-public:
-	LLPointer<LLModel> mModel;
-	LLPointer<LLModel> mLOD[5];
-	
-	std::string mLabel;
-
-	LLUUID mMeshID;
-	S32 mLocalMeshID;
-
-	LLMatrix4 mTransform;
-	std::map<std::string, LLImportMaterial> mMaterial;
-
-	LLModelInstance(LLModel* model, const std::string& label, LLMatrix4& transform, std::map<std::string, LLImportMaterial>& materials)
-		: mModel(model), mLabel(label), mTransform(transform), mMaterial(materials)
-	{
-		mLocalMeshID = -1;
-	}
-
-	LLModelInstance(LLSD& data);
-
-	LLSD asLLSD();
-};
-
 class LLPhysicsDecomp : public LLThread
 {
 public:
@@ -483,6 +435,8 @@ public:
 	// Inherited from LLCore::HttpHandler
 	virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response);
 
+        LLViewerFetchedTexture* FindViewerTexture(const LLImportMaterial& material);
+
 private:
 	LLHandle<LLWholeModelFeeObserver> mFeeObserverHandle;
 	LLHandle<LLWholeModelUploadObserver> mUploadObserverHandle;
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 a4acd1df78448e3893d0d822a0d27577e9171881..7183b2f1f9d5d0f6081390fca477258f1cf59fcf 100755
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -31,6 +31,7 @@
   <string name="mesh_status_too_many_vertices">Level of detail has too many vertices.</string>
   <string name="mesh_status_missing_lod">Missing required level of detail.</string>
   <string name="mesh_status_invalid_material_list">LOD materials are not a subset of reference model.</string>
+  <string name="phys_status_vertex_limit_exceeded">Some physical hulls exceed vertex limitations.</string>
   <string name="layer_all">All</string> <!-- Text to display in physics layer combo box for "all layers" -->
   <string name="decomposing">Analyzing...</string>
   <string name="simplifying">Simplifying...</string>
@@ -66,7 +67,7 @@
           follows="top|left"
           layout="topleft"
           height="19"
-          max_length_bytes="64"
+          max_length_bytes="63"
           name="description_form"
           prevalidate_callback="ascii"
           top_pad="5"
@@ -1027,19 +1028,19 @@
                   bg_alpha_color="0 0 0 0"
                   bg_opaque_color="0 0 0 0.3"
                   follows="left|top"
-                  height="16"
+                  height="19"
                   layout="topleft"
                   left="18"
                   name="physics info"
-                  top_pad="15"
-                  width="589">
+                  top_pad="12"
+                  width="319">
                     <text
                       follows="top|left"
                       height="15"
                       layout="topleft"
                       left="0"
                       text_color="White"
-                      top_pad="0"
+                      top_pad="3"
                       name="results_text"
                       width="50">
                       Results:
@@ -1077,6 +1078,33 @@
                       Hulls: [HULLS]
                     </text>
                 </panel>
+                <panel
+                 bg_alpha_color="0 0 0 0"
+                 bg_opaque_color="0 0 0 0.3"
+                 follows="left|top"
+                 height="19"
+                 layout="topleft"
+                 left_pad="5"
+                 top_delta="0"
+                 name="physics message"
+                 width="270">
+                     <icon
+                      follows="left|top"
+                      height="16"
+                      left="0"
+                      layout="topleft"
+                      name="physics_status_message_icon"
+                      top_pad="0"
+                      width="16" />
+                     <text
+                      follows="left|top"
+                      height="15"
+                      layout="topleft"
+                      left_pad="2"
+                      name="physics_status_message_text"
+                      width="252"
+                      top_delta="3"/>
+                </panel>
         </panel>
       <!-- MODIFIERS PANEL -->
      <panel