From ae035a0d66604e25b1277c4fa303aea8d798e719 Mon Sep 17 00:00:00 2001
From: Graham Linden <graham@lindenlab.com>
Date: Tue, 22 Apr 2014 08:58:38 -0700
Subject: Modify importer to (optionally) improve debug output, perform
 name-based LOD association, and handle models with many materials.

---
 indra/llcommon/CMakeLists.txt           |    2 +
 indra/llcommon/llcallbacklist.cpp       |  230 +++
 indra/llcommon/llcallbacklist.h         |   79 +
 indra/llcommon/lldate.h                 |    1 +
 indra/llcommon/llinstancetracker.h      |    1 -
 indra/llcommon/llstl.h                  |    4 +-
 indra/llmath/llvolume.h                 |    5 +
 indra/llmath/m4math.cpp                 |   13 +
 indra/llmath/m4math.h                   |    5 +
 indra/llprimitive/CMakeLists.txt        |    8 +-
 indra/llprimitive/llmodel.cpp           | 1338 ++++-----------
 indra/llprimitive/llmodel.h             |  128 +-
 indra/llprimitive/lltextureentry.cpp    |    9 +-
 indra/llprimitive/lltextureentry.h      |    4 +
 indra/newview/CMakeLists.txt            |    2 -
 indra/newview/app_settings/settings.xml |   22 +
 indra/newview/llcallbacklist.cpp        |  305 ----
 indra/newview/llcallbacklist.h          |   72 -
 indra/newview/llfloatermodelpreview.cpp | 2846 ++++++-------------------------
 indra/newview/llfloatermodelpreview.h   |  121 +-
 indra/newview/llmeshrepository.cpp      |  281 ++-
 indra/newview/llmeshrepository.h        |   50 +-
 22 files changed, 1594 insertions(+), 3932 deletions(-)
 create mode 100644 indra/llcommon/llcallbacklist.cpp
 create mode 100644 indra/llcommon/llcallbacklist.h
 delete mode 100755 indra/newview/llcallbacklist.cpp
 delete mode 100755 indra/newview/llcallbacklist.h

diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 8767616a70..aad3bd64a2 100755
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -36,6 +36,7 @@ set(llcommon_SOURCE_FILES
     llavatarname.cpp
     llbase32.cpp
     llbase64.cpp
+    llcallbacklist.cpp
     llcommon.cpp
     llcommonutils.cpp
     llcoros.cpp
@@ -139,6 +140,7 @@ set(llcommon_HEADER_FILES
     llboost.h
     llchat.h
     llclickaction.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 0000000000..541ff75ee4
--- /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/llcommon/llcallbacklist.h b/indra/llcommon/llcallbacklist.h
new file mode 100644
index 0000000000..89716cd74c
--- /dev/null
+++ b/indra/llcommon/llcallbacklist.h
@@ -0,0 +1,79 @@
+/** 
+ * @file llcallbacklist.h
+ * @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$
+ */
+
+#ifndef LL_LLCALLBACKLIST_H
+#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 deleteAllFunctions();
+
+	static void test();
+
+protected:
+
+	inline callback_list_t::iterator find(callback_t func, void *data);
+
+	callback_list_t	mCallbackList;
+};
+
+typedef boost::function<void ()> nullary_func_t;
+typedef boost::function<bool ()> bool_func_t;
+
+// Call a given callable once in idle loop.
+void doOnIdleOneTime(nullary_func_t callable);
+
+// Repeatedly call a callable in idle loop until it returns true.
+void doOnIdleRepeating(bool_func_t callable);
+
+// Call a given callable once after specified interval.
+void doAfterInterval(nullary_func_t callable, F32 seconds);
+
+// Call a given callable every specified number of seconds, until it returns true.
+void doPeriodically(bool_func_t callable, F32 seconds);
+
+extern LLCallbackList gIdleCallbacks;
+
+#endif
diff --git a/indra/llcommon/lldate.h b/indra/llcommon/lldate.h
index 7ff8b550ad..47635b30bc 100755
--- a/indra/llcommon/lldate.h
+++ b/indra/llcommon/lldate.h
@@ -33,6 +33,7 @@
 #include <string>
 
 #include "stdtypes.h"
+#include "llpreprocessor.h"
 
 /** 
  * @class LLDate
diff --git a/indra/llcommon/llinstancetracker.h b/indra/llcommon/llinstancetracker.h
index 55187d8325..b039290037 100755
--- a/indra/llcommon/llinstancetracker.h
+++ b/indra/llcommon/llinstancetracker.h
@@ -305,7 +305,6 @@ protected:
 	virtual ~LLInstanceTracker()
 	{
 		// it's unsafe to delete instances of this type while all instances are being iterated over.
-		llassert_always(getStatic().sIterationNestDepth == 0);
 		getSet_().erase(static_cast<T*>(this));
 	}
 
diff --git a/indra/llcommon/llstl.h b/indra/llcommon/llstl.h
index 0a39288f5a..6a539c2b91 100755
--- a/indra/llcommon/llstl.h
+++ b/indra/llcommon/llstl.h
@@ -27,6 +27,8 @@
 #ifndef LL_LLSTL_H
 #define LL_LLSTL_H
 
+#include "stdtypes.h"
+
 #include <functional>
 #include <algorithm>
 #include <map>
@@ -489,7 +491,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.h b/indra/llmath/llvolume.h
index 975227ea58..d2cb052043 100755
--- a/indra/llmath/llvolume.h
+++ b/indra/llmath/llvolume.h
@@ -969,6 +969,7 @@ protected:
 	~LLVolume(); // use unref
 
 public:
+	typedef std::vector<LLVolumeFace> face_list_t;
 		
 	struct FaceParams
 	{
@@ -1041,6 +1042,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
 	
diff --git a/indra/llmath/m4math.cpp b/indra/llmath/m4math.cpp
index 6a1b4143cf..d89c482804 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 a7dce10397..a77c5bc76d 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 0dd13916bf..590620e1b1 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
@@ -41,13 +45,14 @@ set(llprimitive_SOURCE_FILES
 
 set(llprimitive_HEADER_FILES
     CMakeLists.txt
-
+    lldaeloader.h
     legacy_object_types.h
     llmaterial.h
     llmaterialid.h
     llmaterialtable.h
     llmediaentry.h
     llmodel.h
+    llmodelloader.h
     llprimitive.h
     llprimtexturelist.h
     lltextureanim.h
@@ -73,6 +78,7 @@ target_link_libraries(llprimitive
     ${LLMESSAGE_LIBRARIES}
     ${LLXML_LIBRARIES}
     ${LLPHYSICSEXTENSIONS_LIBRARIES}
+    ${LLCHARACTER_LIBRARIES}
     )
 
 
diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp
index 34e0483a83..5cc5dc5b97 100755
--- a/indra/llprimitive/llmodel.cpp
+++ b/indra/llprimitive/llmodel.cpp
@@ -31,843 +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_STANDALONE
 # 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 )
-	{
-		llwarns << "Could not find dom sources for basic geo data; invalid model." << llendl;
-		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)
-	{
-		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;
-	
-	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;
-
-	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))
-			{
-				llwarns << "Out of range index data; invalid model." << llendl;
-				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())
-			{
-				llwarns << "Nan positional data, invalid model." << llendl;
-				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))
-			{
-				llwarns << "Out of range TC indices." << llendl;
-				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())
-			{
-				llwarns << "Found NaN while loading tex coords from DAE-Model, invalid model." << llendl;
-				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))
-			{
-				llwarns << "Found out of range norm indices, invalid model." << llendl;
-				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())
-			{
-				llwarns << "Found NaN while loading normals from DAE-Model, invalid model." << llendl;
-				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)
-			{
-				//llerrs << "Attempted to write model exceeding 16-bit index buffer limitation." << llendl;
-				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)
-		{
-			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))
-	{
-		llwarns << "Could not get DOM sources for basic geo data, invalid model." << llendl;
-		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))
-				{
-					llwarns << "Out of range position indices, invalid model." << llendl;
-					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())
-				{
-					llwarns << "Found NaN while loading positions from DAE-Model, invalid model." << llendl;
-					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))
-				{
-					llwarns << "Out of range TC indices, invalid model." << llendl;
-					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())
-				{
-					llwarns << "Found NaN while loading tex coords from DAE-Model, invalid model." << llendl;
-					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))
-				{
-					llwarns << "Out of range norm indices, invalid model." << llendl;
-					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())
-				{
-					llwarns << "Found NaN while loading normals from DAE-Model, invalid model." << llendl;
-					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)
-				{
-					//llerrs << "Attempted to write model exceeding 16-bit index buffer limitation." << llendl;
-					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)
-			{
-				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)
-			{
-				llwarns << "Could not find vertex source, invalid model." << llendl;
-				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)
-					{
-						llwarns << "Could not find DOM source, invalid model." << llendl;
-						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)
-			{
-				llwarns << "Could not find DOM source, invalid model." << llendl;
-				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)
-			{
-				llwarns << "Could not find DOM source, invalid model." << llendl;
-				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())
-				{
-					llwarns << "Found NaN while loading position data from DAE-Model, invalid model." << llendl;
-					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())
-				{
-					llwarns << "Found NaN while loading normals from DAE-Model, invalid model." << llendl;
-					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())
-				{
-					llwarns << "Found NaN while loading tex coords from DAE-Model, invalid model." << llendl;
-					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;
+#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
@@ -889,82 +84,6 @@ std::string LLModel::getStatusString(U32 status)
 	return std::string() ;
 }
 
-void LLModel::addVolumeFacesFromDomMesh(domMesh* mesh)
-{
-	domTriangles_Array& tris = mesh->getTriangles_array();
-		
-	for (U32 i = 0; i < tris.getCount(); ++i)
-	{
-		domTrianglesRef& tri = tris.get(i);
-
-		mStatus = load_face_from_dom_triangles(mVolumeFaces, mMaterialList, tri);
-		
-		if(mStatus != NO_ERRORS)
-		{
-			mVolumeFaces.clear() ;
-			mMaterialList.clear() ;
-			return ; //abort
-		}
-	}
-
-	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)
-	{
-		domPolygonsRef& poly = polygons.get(i);
-		mStatus = load_face_from_dom_polygons(mVolumeFaces, mMaterialList, poly);
-
-		if(mStatus != NO_ERRORS)
-		{
-			mVolumeFaces.clear() ;
-			mMaterialList.clear() ;
-			return ; //abort
-		}
-	}
- 
-}
-
-BOOL LLModel::createVolumeFacesFromDomMesh(domMesh* mesh)
-{
-	if (mesh)
-	{
-		mVolumeFaces.clear();
-		mMaterialList.clear();
-
-		addVolumeFacesFromDomMesh(mesh);
-		
-		if (getNumVolumeFaces() > 0)
-		{
-			normalizeVolumeFaces();
-			optimizeVolumeFaces();
-			
-			if (getNumVolumeFaces() > 0)
-			{
-				return TRUE;
-			}
-		}
-	}
-	else
-	{	
-		llwarns << "no mesh found" << llendl;
-	}
-	
-	return FALSE;
-}
 
 void LLModel::offsetMesh( const LLVector3& pivotPoint )
 {
@@ -991,6 +110,63 @@ void LLModel::optimizeVolumeFaces()
 	}
 }
 
+struct MaterialBinding
+{
+	int				index;
+	std::string		matName;
+};
+
+struct MaterialSort
+{
+	bool operator()(const MaterialBinding& lhs, const MaterialBinding& rhs)
+	{
+		return LLStringUtil::compareInsensitive(lhs.matName, rhs.matName) < 0;
+	}
+};
+
+void LLModel::sortVolumeFacesByMaterialName()
+{
+	std::vector<MaterialBinding> bindings;
+	bindings.resize(mVolumeFaces.size());
+	for (int i = 0; i < bindings.size(); i++)
+	{
+		bindings[i].index = i;
+		bindings[i].matName = mMaterialList[i];
+	}
+	std::sort(bindings.begin(), bindings.end(), MaterialSort());
+	std::vector< LLVolumeFace > new_faces;
+
+	// 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++)
+	{
+		new_faces[i] = mVolumeFaces[bindings[i].index];
+		mMaterialList[i] = bindings[i].matName;
+	}
+
+	mVolumeFaces = new_faces;	
+}
+
+void LLModel::trimVolumeFacesToSize(U32 new_count, LLVolume::face_list_t* remainder)
+{
+	llassert(new_count <= LL_SCULPT_MESH_MAX_FACES);
+
+	if (new_count && (getNumVolumeFaces() > new_count))
+	{
+		// 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);
+	}
+}
+
 // Shrink the model to fit
 // on a 1x1x1 cube centered at the origin.
 // The positions and extents
@@ -1001,11 +177,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;
@@ -1472,68 +643,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
@@ -1548,7 +661,8 @@ LLSD LLModel::writeModel(
 	BOOL upload_skin,
 	BOOL upload_joints,
 	BOOL nowrite,
-	BOOL as_slm)
+	BOOL as_slm,
+	int submodel_id)
 {
 	LLSD mdl;
 
@@ -1577,6 +691,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
@@ -1588,7 +710,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;
@@ -1821,6 +943,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++)
@@ -2004,7 +1131,9 @@ bool LLModel::loadModel(std::istream& is)
 		}
 	}
 
-	std::string nm[] = 
+	mSubmodelID = header.has("submodel_id") ? header["submodel_id"].asInteger() : false;
+
+	std::string lod_name[] = 
 	{
 		"lowest_lod",
 		"low_lod",
@@ -2017,8 +1146,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
 		llwarns << "LoD data is invalid!" << llendl;
 		return false;
@@ -2027,23 +1156,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)
 		{ 
@@ -2109,8 +1238,10 @@ bool LLModel::isMaterialListSubset( LLModel* ref )
 				break;
 			}										
 		}
+
 		if (!foundRef)
 		{
+            llinfos << "Could not find material " << mMaterialList[src] << " in reference model " << ref->mLabel << llendl;
 			return false;
 		}
 	}
@@ -2161,41 +1292,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;
 }
 
@@ -2226,7 +1358,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);
 
@@ -2634,3 +1766,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)
+		{
+			llwarns << "Face has invalid index." << llendl;
+			return false;
+		}
+	}
+
+	if (face.mNumIndices % 3 != 0 || face.mNumIndices == 0)
+	{
+		llwarns << "Face has invalid number of indices." << llendl;
+		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!" << llendl;
+			return false;
+		}
+	}*/
+
+	return true;
+}
+
+bool validate_model(const LLModel* mdl)
+{
+	if (mdl->getNumVolumeFaces() == 0)
+	{
+		llwarns << "Model has no faces!" << llendl;
+		return false;
+	}
+
+	for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i)
+	{
+		if (mdl->getVolumeFace(i).mNumVertices == 0)
+		{
+			llwarns << "Face has no vertices." << llendl;
+			return false;
+		}
+
+		if (mdl->getVolumeFace(i).mNumIndices == 0)
+		{
+			llwarns << "Face has no indices." << llendl;
+			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 aaafc55258..d1570c16f5 100755
--- a/indra/llprimitive/llmodel.h
+++ b/indra/llprimitive/llmodel.h
@@ -138,15 +138,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 +170,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 +281,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/lltextureentry.cpp b/indra/llprimitive/lltextureentry.cpp
index 0db75a0e82..9e822e495c 100755
--- a/indra/llprimitive/lltextureentry.cpp
+++ b/indra/llprimitive/lltextureentry.cpp
@@ -191,6 +191,13 @@ bool LLTextureEntry::operator==(const LLTextureEntry &rhs) const
 	return(true);
 }
 
+bool LLTextureEntry::operator <(const LLTextureEntry &rhs) const
+{
+	if (mID < rhs.mID) return(true);
+	if (mMaterialID < rhs.mMaterialID) return (true);
+	return(false);
+}
+
 LLSD LLTextureEntry::asLLSD() const
 {
 	LLSD sd;
@@ -545,7 +552,7 @@ S32 LLTextureEntry::setMaterialID(const LLMaterialID& pMaterialID)
 		{
 			mMaterialUpdatePending = true;
 			mMaterialID = pMaterialID;
-			return TEM_CHANGE_TEXTURE;
+			return TEM_CHANGE_NONE;
 		}
 
 		mMaterialUpdatePending = false;
diff --git a/indra/llprimitive/lltextureentry.h b/indra/llprimitive/lltextureentry.h
index 19edcaa27d..a40c3988f2 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 0bf0152b30..889e26b532 100755
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -132,7 +132,6 @@ set(viewer_SOURCE_FILES
     llbreadcrumbview.cpp
     llbrowsernotification.cpp
     llbuycurrencyhtml.cpp
-    llcallbacklist.cpp
     llcallingcard.cpp
     llcapabilitylistener.cpp
     llcaphttpsender.cpp
@@ -720,7 +719,6 @@ set(viewer_HEADER_FILES
     llbox.h
     llbreadcrumbview.h
     llbuycurrencyhtml.h
-    llcallbacklist.h
     llcallingcard.h
     llcapabilitylistener.h
     llcapabilityprovider.h
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index de11309394..4343c7f70c 100755
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -2,6 +2,28 @@
 <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>0</integer>
+    <key>Type</key>
+    <string>Integer</string>
+    <key>Value</key>
+    <integer>0</integer>
+  </map>
+  <key>IMShowTime</key>
+  <map>
+    <key>Comment</key>
+    <string>Enable(disable) timestamp showing in the chat.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Boolean</string>
+    <key>Value</key>
+    <integer>1</integer>
+  </map>
     <key>IMShowTime</key>
     <map>
       <key>Comment</key>
diff --git a/indra/newview/llcallbacklist.cpp b/indra/newview/llcallbacklist.cpp
deleted file mode 100755
index 79ec43dfe9..0000000000
--- a/indra/newview/llcallbacklist.cpp
+++ /dev/null
@@ -1,305 +0,0 @@
-/** 
- * @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 "llviewerprecompiledheaders.h"
-
-#include "llcallbacklist.h"
-#include "lleventtimer.h"
-
-// Library includes
-#include "llerror.h"
-
-
-//
-// Globals
-//
-LLCallbackList gIdleCallbacks;
-
-//
-// Member functions
-//
-
-LLCallbackList::LLCallbackList()
-{
-	// nothing
-}
-
-LLCallbackList::~LLCallbackList()
-{
-}
-
-
-void LLCallbackList::addFunction( callback_t func, void *data)
-{
-	if (!func)
-	{
-		llerrs << "LLCallbackList::addFunction - function is NULL" << llendl;
-		return;
-	}
-
-	// only add one callback per func/data pair
-	callback_pair_t t(func, data);
-	callback_list_t::iterator iter = std::find(mCallbackList.begin(), mCallbackList.end(), t);
-	if (iter == mCallbackList.end())
-	{
-		mCallbackList.push_back(t);
-	}
-}
-
-
-BOOL LLCallbackList::containsFunction( callback_t func, void *data)
-{
-	callback_pair_t t(func, data);
-	callback_list_t::iterator iter = std::find(mCallbackList.begin(), mCallbackList.end(), t);
-	if (iter != mCallbackList.end())
-	{
-		return TRUE;
-	}
-	else
-	{
-		return FALSE;
-	}
-}
-
-
-BOOL LLCallbackList::deleteFunction( callback_t func, void *data)
-{
-	callback_pair_t t(func, data);
-	callback_list_t::iterator iter = std::find(mCallbackList.begin(), mCallbackList.end(), t);
-	if (iter != mCallbackList.end())
-	{
-		mCallbackList.erase(iter);
-		return TRUE;
-	}
-	else
-	{
-		return FALSE;
-	}
-}
-
-
-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);
-}
-
-#ifdef _DEBUG
-
-void test1(void *data)
-{
-	S32 *s32_data = (S32 *)data;
-	llinfos << "testfunc1 " << *s32_data << llendl;
-}
-
-
-void test2(void *data)
-{
-	S32 *s32_data = (S32 *)data;
-	llinfos << "testfunc2 " << *s32_data << llendl;
-}
-
-
-void
-LLCallbackList::test()
-{
-	S32 a = 1;
-	S32 b = 2;
-	LLCallbackList *list = new LLCallbackList;
-
-	llinfos << "Testing LLCallbackList" << llendl;
-
-	if (!list->deleteFunction(NULL))
-	{
-		llinfos << "passed 1" << llendl;
-	}
-	else
-	{
-		llinfos << "error, removed function from empty list" << llendl;
-	}
-
-	// llinfos << "This should crash" << llendl;
-	// list->addFunction(NULL);
-
-	list->addFunction(&test1, &a);
-	list->addFunction(&test1, &a);
-
-	llinfos << "Expect: test1 1, test1 1" << llendl;
-	list->callFunctions();
-
-	list->addFunction(&test1, &b);
-	list->addFunction(&test2, &b);
-
-	llinfos << "Expect: test1 1, test1 1, test1 2, test2 2" << llendl;
-	list->callFunctions();
-
-	if (list->deleteFunction(&test1, &b))
-	{
-		llinfos << "passed 3" << llendl;
-	}
-	else
-	{
-		llinfos << "error removing function" << llendl;
-	}
-
-	llinfos << "Expect: test1 1, test1 1, test2 2" << llendl;
-	list->callFunctions();
-
-	list->deleteAllFunctions();
-
-	llinfos << "Expect nothing" << llendl;
-	list->callFunctions();
-
-	llinfos << "nothing :-)" << llendl;
-
-	delete list;
-
-	llinfos << "test complete" << llendl;
-}
-
-#endif  // _DEBUG
diff --git a/indra/newview/llcallbacklist.h b/indra/newview/llcallbacklist.h
deleted file mode 100755
index 0516c9cdb4..0000000000
--- a/indra/newview/llcallbacklist.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/** 
- * @file llcallbacklist.h
- * @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$
- */
-
-#ifndef LL_LLCALLBACKLIST_H
-#define LL_LLCALLBACKLIST_H
-
-#include "llstl.h"
-
-class LLCallbackList
-{
-public:
-	typedef void (*callback_t)(void*);
-	
-	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 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;
-	callback_list_t	mCallbackList;
-};
-
-typedef boost::function<void ()> nullary_func_t;
-typedef boost::function<bool ()> bool_func_t;
-
-// Call a given callable once in idle loop.
-void doOnIdleOneTime(nullary_func_t callable);
-
-// Repeatedly call a callable in idle loop until it returns true.
-void doOnIdleRepeating(bool_func_t callable);
-
-// Call a given callable once after specified interval.
-void doAfterInterval(nullary_func_t callable, F32 seconds);
-
-// Call a given callable every specified number of seconds, until it returns true.
-void doPeriodically(bool_func_t callable, F32 seconds);
-
-extern LLCallbackList gIdleCallbacks;
-
-#endif
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 855836af7a..01809d38c1 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,16 +85,13 @@
 #include "llanimationstates.h"
 #include "llviewernetwork.h"
 #include "llviewershadermgr.h"
+
 #include "glod/glod.h"
 #include <boost/algorithm/string.hpp>
 
-
-const S32 SLM_SUPPORTED_VERSION = 3;
-
 //static
 S32 LLFloaterModelPreview::sUploadAmount = 10;
 LLFloaterModelPreview* LLFloaterModelPreview::sInstance = NULL;
-std::list<LLModelLoader*> LLModelLoader::sActiveLoaderList;
 
 const S32 PREVIEW_BORDER_WIDTH = 2;
 const S32 PREVIEW_RESIZE_HANDLE_SIZE = S32(RESIZE_HANDLE_WIDTH * OO_SQRT2) + PREVIEW_BORDER_WIDTH;
@@ -207,190 +177,37 @@ 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())
-		{
-			llwarns << "NaN position data in face found!" << llendl;
-			return false;
-		}
-
-		if(face.mNormals && !face.mNormals[v].isFinite3())
-		{
-			llwarns << "NaN normal data in face found!" << llendl;
-			return false;
-		}
-	}
-
-	for (U32 i = 0; i < face.mNumIndices; ++i)
-	{
-		if (face.mIndices[i] >= face.mNumVertices)
-		{
-			llwarns << "Face has invalid index." << llendl;
-			return false;
-		}
-	}
-
-	if (face.mNumIndices % 3 != 0 || face.mNumIndices == 0)
+	if (error != GLOD_NO_ERROR)
 	{
-		llwarns << "Face has invalid number of indices." << llendl;
-		return false;
+		llwarns << "GLOD error detected, cannot generate LOD: " << std::hex << error << llendl;
+		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))
-		{
-			llwarns << "Degenerate face found!" << llendl;
-			return false;
-		}
-	}*/
-	return true;
+	return FALSE;
 }
 
-bool validate_model(const LLModel* mdl)
+LLViewerFetchedTexture* bindMaterialDiffuseTexture(const LLImportMaterial& material)
 {
-	if (mdl->getNumVolumeFaces() == 0)
-	{
-		llwarns << "Model has no faces!" << llendl;
-		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)
-		{
-			llwarns << "Face has no vertices." << llendl;
-			return false;
-		}
-
-		if (mdl->getVolumeFace(i).mNumIndices == 0)
-		{
-			llwarns << "Face has no indices." << llendl;
-			return false;
-		}
-
-		if (!validate_face(mdl->getVolumeFace(i)))
+		if (texture->getDiscardLevel() > -1)
 		{
-			return false;
+			gGL.getTexUnit(0)->bind(texture, true);
+			return texture;
 		}
 	}
 
-	return true;
-}
-
-BOOL stop_gloderror()
-{
-	GLuint error = glodGetError();
-
-	if (error != GLOD_NO_ERROR)
-	{
-		llwarns << "GLOD error detected, cannot generate LOD: " << std::hex << error << llendl;
-		return TRUE;
-	}
-
-	return FALSE;
+	return NULL;
 }
 
-
 LLMeshFilePicker::LLMeshFilePicker(LLModelPreview* mp, S32 lod)
-	: LLFilePickerThread(LLFilePicker::FFLOAD_COLLADA)
+: LLFilePickerThread(LLFilePicker::FFLOAD_COLLADA)
 	{
 		mMP = mp;
 		mLOD = lod;
@@ -401,6 +218,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()
@@ -535,16 +375,9 @@ BOOL LLFloaterModelPreview::postBuild()
 	mUploadBtn = getChild<LLButton>("ok_btn");
 	mCalculateBtn = getChild<LLButton>("calculate_btn");
 
-	if (LLConvexDecomposition::getInstance() != NULL)
-	{
-		mCalculateBtn->setClickedCallback(boost::bind(&LLFloaterModelPreview::onClickCalculateBtn, this));
+	mCalculateBtn->setClickedCallback(boost::bind(&LLFloaterModelPreview::onClickCalculateBtn, this));
 
-		toggleCalculateButton(true);
-	}
-	else
-	{
-		mCalculateBtn->setEnabled(false);
-	}
+	toggleCalculateButton(true);
 
 	return TRUE;
 }
@@ -801,7 +634,7 @@ void LLFloaterModelPreview::draw()
 		else
 		if ( mModelPreview->getLoadState() == LLModelLoader::ERROR_PARSING )
 		{
-			childSetTextArg("status", "[STATUS]", getString("status_parse_error"));
+			childSetTextArg("status", "[STATUS]", getString("status_parse_error_dae"));
 			toggleCalculateButton(false);
 		}
 		else
@@ -1262,1859 +1095,52 @@ void LLFloaterModelPreview::initDecompControls()
 					LLComboBox* combo_box = getChild<LLComboBox>(name);
 					for (S32 k = 0; k < param[i].mDetails.mEnumValues.mNumEnums; ++k)
 					{
-						//llinfos << param[i].mDetails.mEnumValues.mEnumsArray[k].mValue
-						//	<< " - " << param[i].mDetails.mEnumValues.mEnumsArray[k].mName << llendl;
-
-						std::string name(param[i].mDetails.mEnumValues.mEnumsArray[k].mName);
-						std::string localized_name;
-						bool is_localized = LLTrans::findString(localized_name, name);
-
-						combo_box->add(is_localized ? localized_name : name,
-							LLSD::Integer(param[i].mDetails.mEnumValues.mEnumsArray[k].mValue));
-					}
-					combo_box->setValue(param[i].mDefault.mIntOrEnumValue);
-					combo_box->setCommitCallback(onPhysicsParamCommit, (void*) &param[i]);
-				}
-
-				//llinfos << "----" << llendl;
-			}
-			//llinfos << "-----------------------------" << llendl;
-		}
-	}
-
-	childSetCommitCallback("physics_explode", LLFloaterModelPreview::onExplodeCommit, this);
-}
-
-void LLFloaterModelPreview::createSmoothComboBox(LLComboBox* combo_box, float min, float max)
-{
-	float delta = (max - min) / SMOOTH_VALUES_NUMBER;
-	int ilabel = 0;
-
-	combo_box->add("0 (none)", ADD_BOTTOM, true);
-
-	for(float value = min + delta; value < max; value += delta)
-	{
-		std::string label = (++ilabel == SMOOTH_VALUES_NUMBER) ? "10 (max)" : llformat("%.1d", ilabel);
-		combo_box->add(label, value, ADD_BOTTOM, true);
-	}
-
-
-}
-
-//-----------------------------------------------------------------------------
-// onMouseCaptureLost()
-//-----------------------------------------------------------------------------
-// static
-void LLFloaterModelPreview::onMouseCaptureLostModelPreview(LLMouseHandler* handler)
-{
-	gViewerWindow->showCursor();
-}
-
-//-----------------------------------------------------------------------------
-// LLModelLoader
-//-----------------------------------------------------------------------------
-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)
-{
-	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
-	{
-		mTrySLM = false;
-	}
-
-	assert_main_thread();
-	sActiveLoaderList.push_back(this) ;
-}
-
-LLModelLoader::~LLModelLoader()
-{
-	assert_main_thread();
-	sActiveLoaderList.remove(this);
-}
-
-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());
-}
-
-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;
-					}
-				}
-			}	
-		}
-	}
-
-	//no suitable slm exists, load from the .dae file
-	DAE dae;
-	domCOLLADA* dom = dae.open(mFilename);
-	
-	if (!dom)
-	{
-		llinfos<<" Error with dae - traditionally indicates a corrupt file."<<llendl;
-		setLoadState( ERROR_PARSING );
-		return false;
-	}
-	//Dom version
-	daeString domVersion = dae.getDomVersion();
-	std::string sldom(domVersion);
-	llinfos<<"Collada Importer Version: "<<sldom<<llendl;
-	//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;
-	}
-	llinfos<<"Dae version "<<colladaVersion[docVersion]<<llendl;
-	
-	
-	daeDatabase* db = dae.getDatabase();
-	
-	daeInt count = db->getElementCount(NULL, COLLADA_TYPE_MESH);
-	
-	daeDocument* doc = dae.getDoc(mFilename);
-	if (!doc)
-	{
-		llwarns << "can't find internal doc" << llendl;
-		return false;
-	}
-	
-	daeElement* root = doc->getDomRoot();
-	if (!root)
-	{
-		llwarns << "document has no root" << llendl;
-		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 )
-							{
-								llwarns<<"No visual scene - unable to parse bone offsets "<<llendl;
-								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() );
-									//llwarns<<"Joint "<< str <<llendl;
-									
-									//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() )
-											{
-												llwarns<< "The found element is not a translate node" <<llendl;
-												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  )
-								{
-									llwarns<< "Partial jointmap found in asset - did you mean to just have a partial map?" << llendl;
-								}
-							}//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() )
-									{
-										//llinfos<<"joint "<<lookingForJoint.c_str()<<llendl;
-										LLMatrix4 jointTransform = mJointList[lookingForJoint];
-										LLJoint* pJoint = mPreview->getPreviewAvatar()->getJoint( lookingForJoint );
-										if ( pJoint )
-										{   
-											pJoint->storeCurrentXform( jointTransform.getTranslation() );												
-										}
-										else
-										{
-											//Most likely an error in the asset.
-											llwarns<<"Tried to apply joint position from .dae, but it did not exist in the avatar rig." << llendl;
-										}
-									}
-								}
-							}
-						} //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
-							{
-								llwarns<<"Possibly misnamed/missing joint [" <<lookingForJoint.c_str()<<" ] "<<llendl;
-							}
-						}
-						
-						//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)
-												{
-													llerrs << "Invalid position array size." << llendl;
-												}
-												
-												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)
-	{
-		llwarns << "document has no visual_scene" << llendl;
-		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, 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].empty())
-			{
-				instance_list[i].mLOD[lod] = model[lod][idx];
-			}
-		}
-
-		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 
-		{
-			llinfos<<"Node is NULL"<<llendl;
-		}
-
-	}
-}
-//-----------------------------------------------------------------------------
-// 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
-			{
-				llinfos<<"critiqueJointToNodeMappingFromScene is missing a: "<<name<<llendl;
-				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 )
-		{
-			llinfos<<" Asset did not contain the joint (if you're u/l a fully rigged asset w/joint positions - it is required)." << *masterJointIt<< llendl;
-			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 )
-		{
-			llinfos<<" Asset did not contain the joint (if you're u/l a fully rigged asset w/joint positions - it is required)." << *masterJointIt<< llendl;
-			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 )
-	{
-		llinfos<<"Created node is NULL"<<llendl;
-		return false;
-	}
-	
-	if ( pNode->getName() == NULL )
-	{
-		llinfos<<"Parsed node has no name "<<llendl;
-		//Attempt to write the node id, if possible (aids in debugging the visual scene)
-		if ( pNode->getId() )
-		{
-			llinfos<<"Parsed node ID: "<<pNode->getId()<<llendl;
-		}
-		return false;
-	}
-
-	if ( mJointMap.find( pNode->getName() )  != mJointMap.end() )
-	{
-		return true;
-	}
-
-	return false;
-}
-//-----------------------------------------------------------------------------
-// verifyCount
-//-----------------------------------------------------------------------------
-bool LLModelPreview::verifyCount( int expected, int result )
-{
-	if ( expected != result )
-	{
-		llinfos<< "Error: (expected/got)"<<expected<<"/"<<result<<"verts"<<llendl;
-		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 )
-		{
-			llinfos<<"Can't resolve skin source"<<llendl;
-			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 )
-				{ 
-					llinfos<<"No vertices!"<<llendl;
-					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
-	{
-		llwarns<<"Element is nonexistent - empty/unsupported node."<<llendl;
-	}
-}
-//-----------------------------------------------------------------------------
-// processJointNode()
-//-----------------------------------------------------------------------------
-void LLModelLoader::processJointNode( domNode* pNode, JointTransformMap& jointTransforms )
-{
-	if (pNode->getName() == NULL)
-	{
-		llwarns << "nameless node, can't process" << llendl;
-		return;
-	}
-
-	//llwarns<<"ProcessJointNode# Node:" <<pNode->getName()<<llendl;
-
-	//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() )
-		{
-			//llwarns<< "The found element is not a translate node" <<llendl;
-			daeSIDResolver jointResolver( pNode, "./matrix" );
-			domMatrix* pMatrix = daeSafeCast<domMatrix>( jointResolver.getElement() );
-			if ( pMatrix )
-			{
-				//llinfos<<"A matrix SID was however found!"<<llendl;
-				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
-			{
-				llwarns<< "The found element is not translate or matrix node - most likely a corrupt export!" <<llendl;
-			}
-		}
-		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;
-	}
-	llwarns<< "Could not find a child [" << name << "] for the element: \"" << pElement->getAttribute("id") << "\"" << llendl;
-    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
-						llinfos << "Negative scale detected, unsupported transform.  domInstance_geometry: " << LLModel::getElementLabel(instance_geo) << llendl;
-						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 
-		{
-			llinfos<<"Unable to resolve geometry URL."<<llendl;
-			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();
+						//llinfos << param[i].mDetails.mEnumValues.mEnumsArray[k].mValue
+						//	<< " - " << param[i].mDetails.mEnumValues.mEnumsArray[k].mName << llendl;
 
-						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);
-								}
-							}
-						}
+						std::string name(param[i].mDetails.mEnumValues.mEnumsArray[k].mName);
+						std::string localized_name;
+						bool is_localized = LLTrans::findString(localized_name, name);
+
+						combo_box->add(is_localized ? localized_name : name,
+							LLSD::Integer(param[i].mDetails.mEnumValues.mEnumsArray[k].mValue));
 					}
+					combo_box->setValue(param[i].mDefault.mIntOrEnumValue);
+					combo_box->setCommitCallback(onPhysicsParamCommit, (void*) &param[i]);
 				}
-			}
-		}
-
-		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;
+				//llinfos << "----" << llendl;
+			}
+			//llinfos << "-----------------------------" << llendl;
 		}
 	}
 
-	return mat;
+	childSetCommitCallback("physics_explode", LLFloaterModelPreview::onExplodeCommit, this);
 }
 
-// try to get a decent label for this element
-std::string LLModelLoader::getElementLabel(daeElement *element)
+void LLFloaterModelPreview::createSmoothComboBox(LLComboBox* combo_box, float min, float max)
 {
-	// if we have a name attribute, use it
-	std::string name = element->getAttribute("name");
-	if (name.length())
-	{
-		return name;
-	}
+	float delta = (max - min) / SMOOTH_VALUES_NUMBER;
+	int ilabel = 0;
 
-	// if we have an ID attribute, use it
-	if (element->getID())
-	{
-		return std::string(element->getID());
-	}
+	combo_box->add("0 (none)", ADD_BOTTOM, true);
 
-	// if we have a parent, use it
-	daeElement* parent = element->getParent();
-	if (parent)
+	for(float value = min + delta; value < max; value += delta)
 	{
-		// 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());
-		}
+		std::string label = (++ilabel == SMOOTH_VALUES_NUMBER) ? "10 (max)" : llformat("%.1d", ilabel);
+		combo_box->add(label, value, ADD_BOTTOM, true);
 	}
 
-	// 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)
+//-----------------------------------------------------------------------------
+// onMouseCaptureLost()
+//-----------------------------------------------------------------------------
+// static
+void LLFloaterModelPreview::onMouseCaptureLostModelPreview(LLMouseHandler* handler)
 {
-	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;
+	gViewerWindow->showCursor();
 }
 
 //-----------------------------------------------------------------------------
@@ -3171,51 +1197,20 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)
 	
 	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
+	// 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();
 }
 
@@ -3281,7 +1276,9 @@ U32 LLModelPreview::calcResourceCost()
 					   decomp,
 					   mFMP->childGetValue("upload_skin").asBoolean(),
 					   mFMP->childGetValue("upload_joints").asBoolean(),
-					   TRUE);
+					   TRUE,
+						FALSE,
+						instance.mModel->mSubmodelID);
 			
 			num_hulls += decomp.mHull.size();
 			for (U32 i = 0; i < decomp.mHull.size(); ++i)
@@ -3348,29 +1345,9 @@ 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" );
-				}
-			}
-		}
-	}
-
 	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;
@@ -3387,41 +1364,176 @@ 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)
-				{
-					break;
-				}
+            for (int i = LLModel::NUM_LODS - 1; i >= LLModel::LOD_IMPOSTOR; i--)
+			{ // 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.
+
+                LLModel* lod_model = NULL;
+                LLMatrix4 transform;
+
+                std::string name_to_match = instance.mLabel;
+                llassert(!name_to_match.empty());
+
+                switch (i)
+                {
+                    case LLModel::LOD_IMPOSTOR: name_to_match += "_LOD0"; break;
+                    case LLModel::LOD_LOW:      name_to_match += "_LOD1"; break;
+		            case LLModel::LOD_MEDIUM:   name_to_match += "_LOD2"; break;
+                    case LLModel::LOD_PHYSICS:  name_to_match += "_PHYS"; break;
+                    case LLModel::LOD_HIGH:                               break;
+                }
+
+                FindModel(mScene[i], name_to_match, lod_model, transform);
+
+                S32 importerDebug = gSavedSettings.getS32("ImporterDebug");                    
+
+                // Fall back to old method of index-based association if
+                // we could not find a match based on the mesh names
+                //
+                if (lod_model)
+                {
+                    
+                    if (i == LLModel::LOD_PHYSICS)
+                    {
+                        if (importerDebug > 0)
+                        {
+                            llinfos << "Assigning collision for " << instance.mLabel << " to match " << lod_model->mLabel << llendl;
+                        }
+                        instance.mLOD[i] = lod_model;
+                    }
+                    else
+                    {
+                        if (importerDebug > 0)
+                        {
+                            llinfos << "Assigning LOD" << i << " for " << instance.mLabel << " to found match " << lod_model->mLabel << llendl;
+                        }
+                        instance.mLOD[i] = lod_model;
+                    }
+                }
+                else
+                {
+                    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());
+
+                        switch (searchLOD)
+                        {
+                            case LLModel::LOD_IMPOSTOR: name_to_match += "_LOD0"; break;
+                            case LLModel::LOD_LOW:      name_to_match += "_LOD1"; break;
+		                    case LLModel::LOD_MEDIUM:   name_to_match += "_LOD2"; break;
+                            case LLModel::LOD_PHYSICS:  name_to_match += "_PHYS"; break;
+                            case LLModel::LOD_HIGH:                               break;
+                        }
+
+                        // See if we can find an appropriately named model in LOD 'searchLOD'
+                        //
+                        FindModel(mScene[searchLOD], name_to_match, lod_model, transform);
+                        searchLOD++;
+                    }
+
+                    // Fall back to old method of index-based association if
+                    // we could not find a match based on the mesh names at all.
+                    //
+                    if (lod_model)
+                    {
+                        if (i == LLModel::LOD_PHYSICS)
+                        {
+                            if (importerDebug > 0)
+                            {                         
+                                llinfos << "Falling back collision for " << instance.mLabel << " to " << lod_model->mLabel << llendl;
+                            }
+                            instance.mLOD[i] = lod_model;
+                        }
+                        else
+                        {
+                            if (importerDebug > 0)
+                            {
+                                llinfos << "Falling back LOD" << i << " for " << instance.mLabel << " to found " << lod_model->mLabel << llendl;
+                            }
+                            instance.mLOD[i] = lod_model;
+                        }
+                    }
+                    else
+                    {
+                        S32 idx = 0;
+			            for (idx = 0; idx < mBaseModel.size(); ++idx)
+			            {  //find reference instance for this model
+				            if (mBaseModel[idx] == base_model)
+				            {
+                                if (importerDebug > 0)
+                                {
+                                    llinfos << "Falling back to model index " << idx << " for LOD " << i << " of " << instance.mLabel << llendl;
+                                }
+					            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];
+						    instance.mLOD[i] = lod_model;
+                            if (i == LLModel::LOD_PHYSICS)
+                            {
+                                if (importerDebug > 0)
+                                {
+                                    llinfos << "Indexed fallback to model index " << idx << ": LOD " << i << " named " << lod_model->mLabel << " for collision for " << instance.mLabel <<  llendl;
+                                }
+                            }
+                            else
+                            {
+                                if (importerDebug > 0)
+                                {
+                                    llinfos << "Indexed fallback to model index " << idx << " LOD " << i << " named " << lod_model->mLabel << " for LOD " << i << " for " << instance.mLabel << llendl;
+                                }
+                            }
+					    }
+                        else
+                        {
+                            if (importerDebug > 0)
+                            {
+                                llinfos << "List of models for LOD " << i << " did not include index " << idx <<  llendl;
+                            }
+                        }
+                    }
+                }
 			}
 
-			if(idx < mBaseModel.size())
-			{
-				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;
-					}
+            LLModel* high_lod_model = instance.mLOD[LLModel::LOD_HIGH];
+            llassert(high_lod_model);
+
+            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);
 		}
@@ -3493,7 +1605,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 : 
@@ -3506,8 +1617,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();
 		}
@@ -3575,7 +1686,16 @@ 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 );	
 
 	if (force_disable_slm)
 	{
@@ -3683,9 +1803,15 @@ 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)
@@ -3720,6 +1846,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;
 
@@ -3728,7 +1859,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;
@@ -3817,6 +1948,8 @@ void LLModelPreview::loadModelCallback(S32 lod)
 	refresh();
 
 	mModelLoadedSignal();
+
+	mModelLoader = NULL;
 }
 
 void LLModelPreview::resetPreviewTarget()
@@ -4104,6 +2237,19 @@ 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;
+            
 			GLint* sizes = new GLint[patch_count*2];
 			glodGetObjectParameteriv(mObject[base], GLOD_PATCH_SIZES, sizes);
 			stop_gloderror();
@@ -4216,17 +2362,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()
@@ -4243,43 +2378,88 @@ 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;
+
+        LLModel* model_high_lod = instance.mLOD[LLModel::LOD_HIGH];
+        llassert(model_high_lod);
 
-		for (LLModelLoader::scene::iterator iter = mScene[lod].begin(), endIter = mScene[lod].end(); iter != endIter; ++iter)
+        for (U32 i = 0; i < LLModel::NUM_LODS-1; i++)
 		{
-			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();
+            LLModel* lod_model = instance.mLOD[i];
+            llassert(lod_model);
+            if (!lod_model)
+            {
+                setLoadState( LLModelLoader::ERROR_MATERIALS );
+                mFMP->childDisable( "calculate_btn" );
+            }
 
-					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;
-					}
+            int refFaceCnt = 0;
+            int modelFaceCnt = 0;
 
-					//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->matchMaterialOrder(model_high_lod, refFaceCnt, modelFaceCnt ) )
+			{
+                setLoadState( LLModelLoader::ERROR_MATERIALS );
+				mFMP->childDisable( "calculate_btn" );
+			}
+
+            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;
+
+                S32 importerDebug = gSavedSettings.getS32("ImporterDebug");
+                if (importerDebug > 0)
+                {
+                    // 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.
+                    //
+                    llinfos << "Instance " << lod_model->mLabel << " LOD " << i << " Verts: "   << cur_verts     << llendl;
+                    llinfos << "Instance " << lod_model->mLabel << " LOD " << i << " Tris:  "   << cur_tris      << llendl;
+                    llinfos << "Instance " << lod_model->mLabel << " LOD " << i << " Faces: "   << cur_submeshes << llendl;
+
+                    if (importerDebug > 1)
+                    {
+                        LLModel::material_list::iterator mat_iter = lod_model->mMaterialList.begin();
+                        while (mat_iter != lod_model->mMaterialList.end())
+                        {
+                            llinfos << "Instance " << lod_model->mLabel << " LOD " << i << " Material " << *(mat_iter) << llendl;
+                            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)
 	{
@@ -4293,28 +2473,36 @@ 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");
@@ -4417,13 +2605,16 @@ void LLModelPreview::updateStatusMessages()
 	{
 		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; upload_ok && j < mdl->mPhysics.mHull.size(); ++j)
 			{
-				upload_ok = false;
+				if (mdl->mPhysics.mHull[j].size() > 256)
+				{
+					upload_ok = false;
+				}
 			}
-		}
+		}		
 	}
 
 	bool errorStateFromLoader = getLoadState() >= LLModelLoader::ERROR_PARSING ? true : false;
@@ -4939,6 +3130,58 @@ void LLModelPreview::createPreviewAvatar( void )
 	}
 }
 
+void LLModelPreview::loadedCallback(
+	LLModelLoader::scene& scene,
+	LLModelLoader::model_list& model_list,
+	S32 lod,
+	void* opaque)
+{
+	LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque);
+	if (pPreview)
+	{
+		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 )
 {
 	U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0;
@@ -5181,24 +3424,6 @@ BOOL LLModelPreview::render()
 			}
 		}
 
-		//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" );
-					}
-				}
-			}
-		}
-
 		if (regen)
 		{
 			genBuffers(mPreviewLOD, skin_weight);
@@ -5212,62 +3437,61 @@ 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; i < mVertexBuffer[mPreviewLOD][model].size(); ++i)
-				{
-					LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i];
+					for (U32 i = 0; i < mVertexBuffer[mPreviewLOD][model].size(); ++i)
+					{
+						LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i];
 				
-					buffer->setBuffer(type_mask & buffer->getTypeMask());
+						buffer->setBuffer(type_mask & buffer->getTypeMask());
 
-					if (textures)
-					{
-						int materialCnt = instance.mModel->mMaterialList.size();
-						if ( i < materialCnt )
+						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)
 			{
@@ -5295,97 +3519,97 @@ 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);
+								}
+								for (U32 i = 0; i < mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); ++i)
+								{
+									LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i];
 
-								gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-								gGL.diffuseColor4f(0.4f, 0.4f, 0.0f, 0.4f);
+									gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+									gGL.diffuseColor4f(0.4f, 0.4f, 0.0f, 0.4f);
 
-								buffer->setBuffer(type_mask & buffer->getTypeMask());
-								buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);
+									buffer->setBuffer(type_mask & buffer->getTypeMask());
+									buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);
 
-								gGL.diffuseColor3f(1.f, 1.f, 0.f);
+									gGL.diffuseColor3f(1.f, 1.f, 0.f);
 
-								glLineWidth(2.f);
-								glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-								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);
 
-								glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-								glLineWidth(1.f);
+									glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+									glLineWidth(1.f);
+								}
 							}
-						}
 
-						gGL.popMatrix();
-					}
+							gGL.popMatrix();
+						}
 
 					glLineWidth(3.f);
 					glPointSize(8.f);
@@ -5572,13 +3796,13 @@ BOOL LLModelPreview::render()
 							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);
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index e588418f7b..9948bb35a8 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:
@@ -358,21 +275,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 );
 	
@@ -387,6 +297,14 @@ public:
 	
 	LLVector3 getTranslationForJointOffset( std::string joint );
 
+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 );
@@ -452,7 +370,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];
@@ -470,11 +388,10 @@ private:
 	bool		mLegacyRigValid;
 
 	bool		mLastJointUpdate;
+	
+	JointSet				mJointsFromNode;
+	JointTransformMap	mJointTransformMap;
 
-	std::deque<std::string> mMasterJointList;
-	std::deque<std::string> mMasterLegacyJointList;
-	std::deque<std::string> mJointsFromNode;
-	JointTransformMap		mJointTransformMap;
 	LLPointer<LLVOAvatar>	mPreviewAvatar;
 };
 
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 5afd2cb329..f36fb971b5 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -498,6 +498,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;
@@ -2027,6 +2033,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++)
 		{
@@ -2064,7 +2078,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();
@@ -2098,17 +2115,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()))
@@ -2123,9 +2149,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 &&
@@ -3756,37 +3944,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);
@@ -4372,60 +4529,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 39280bea3a..688cd01a87 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;
-- 
cgit v1.2.3


From 428f2aa84e4cb33e49e58ef115e88d057be664c1 Mon Sep 17 00:00:00 2001
From: Graham Linden <graham@lindenlab.com>
Date: Tue, 22 Apr 2014 13:57:56 -0700
Subject: Added missing new files

---
 indra/llprimitive/lldaeloader.cpp | 2326 +++++++++++++++++++++++++++++++++++++
 indra/llprimitive/lldaeloader.h   |  103 ++
 2 files changed, 2429 insertions(+)
 create mode 100644 indra/llprimitive/lldaeloader.cpp
 create mode 100644 indra/llprimitive/lldaeloader.h

diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
new file mode 100644
index 0000000000..740ecf31c7
--- /dev/null
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -0,0 +1,2326 @@
+/**
+ * @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 "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"
+};
+
+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 )
+	{
+		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)
+	{
+		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." << llendl;
+				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)
+			{
+				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 (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]);
+			}
+			
+			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." << llendl;
+					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)
+			{
+				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 )
+: LLModelLoader(
+		filename,
+		lod,
+		load_cb,
+		joint_lookup_func,
+		texture_load_func,
+		state_cb,
+		opaque_userdata,
+		jointMap,
+		jointsFromNodes)
+{
+}
+
+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)
+	{
+		llinfos<<" Error with dae - traditionally indicates a corrupt file."<<llendl;
+		setLoadState( ERROR_PARSING );
+		return false;
+	}
+	//Dom version
+	daeString domVersion = dae.getDomVersion();
+	std::string sldom(domVersion);
+	llinfos<<"Collada Importer Version: "<<sldom<<llendl;
+	//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;
+	}
+	llinfos<<"Dae version "<<colladaVersion[docVersion]<<llendl;
+	
+	
+	daeDatabase* db = dae.getDatabase();
+	
+	daeInt count = db->getElementCount(NULL, COLLADA_TYPE_MESH);
+	
+	daeDocument* doc = dae.getDoc(mFilename);
+	if (!doc)
+	{
+		llwarns << "can't find internal doc" << llendl;
+		return false;
+	}
+	
+	daeElement* root = doc->getDomRoot();
+	if (!root)
+	{
+		llwarns << "document has no root" << llendl;
+		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)
+		{
+			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();	
+
+	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);
+
+			std::vector<LLModel*>::iterator i;
+			i = models.begin();
+			while (i != models.end())
+			{
+				LLModel* mdl = *i;
+				if(mdl->getStatus() != LLModel::NO_ERRORS)
+				{
+					setLoadState(ERROR_PARSING + 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())
+	{
+		llinfos << "Importing " << (*model_iter)->mLabel << llendl;
+		std::vector<std::string>::iterator mat_iter = (*model_iter)->mMaterialList.begin();
+		while (mat_iter != (*model_iter)->mMaterialList.end())
+		{
+			llinfos << (*model_iter)->mLabel << " references " << (*mat_iter) << llendl;
+			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++;
+					}
+				}
+			}
+		}
+	}
+
+	daeElement* scene = root->getDescendant("visual_scene");
+	
+	if (!scene)
+	{
+		llwarns << "document has no visual_scene" << llendl;
+		setLoadState( ERROR_PARSING );
+		return true;
+	}
+	
+	setLoadState( DONE );
+
+	bool badElement = false;
+	
+	processElement( scene, badElement, &dae );
+	
+	if ( badElement )
+	{
+		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 )
+			{
+				llwarns<<"No visual scene - unable to parse bone offsets "<<llendl;
+				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() );
+					//llwarns<<"Joint "<< str <<llendl;
+
+					//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() )
+								{
+									llwarns<< "The found element is not a translate node" <<llendl;
+									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  )
+				{
+					llwarns<< "Partial jointmap found in asset - did you mean to just have a partial map?" << llendl;
+				}
+			}//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() )
+					{
+						//llinfos<<"joint "<<lookingForJoint.c_str()<<llendl;
+						LLMatrix4 jointTransform = mJointList[lookingForJoint];
+						LLJoint* pJoint = mJointLookupFunc(lookingForJoint,mOpaqueData);
+						if ( pJoint )
+						{   
+							pJoint->storeCurrentXform( jointTransform.getTranslation() );												
+						}
+						else
+						{
+							//Most likely an error in the asset.
+							llwarns<<"Tried to apply joint position from .dae, but it did not exist in the avatar rig." << llendl;
+						}
+					}
+				}
+			}
+		} //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
+			{
+				llwarns<<"Possibly misnamed/missing joint [" <<lookingForJoint.c_str()<<" ] "<<llendl;
+			}
+		}
+
+		//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)
+								{
+									llerrs << "Invalid position array size." << llendl;
+								}
+
+								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 
+		{
+			llinfos<<"Node is NULL"<<llendl;
+		}
+
+	}
+}
+//-----------------------------------------------------------------------------
+// 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 )
+	{
+		llinfos<<"Created node is NULL"<<llendl;
+		return false;
+	}
+	
+	return LLModelLoader::isNodeAJoint(pNode->getName());
+}
+//-----------------------------------------------------------------------------
+// verifyCount
+//-----------------------------------------------------------------------------
+bool LLDAELoader::verifyCount( int expected, int result )
+{
+	if ( expected != result )
+	{
+		llinfos<< "Error: (expected/got)"<<expected<<"/"<<result<<"verts"<<llendl;
+		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 )
+		{
+			llinfos<<"Can't resolve skin source"<<llendl;
+			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 )
+				{ 
+					llinfos<<"No vertices!"<<llendl;
+					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
+	{
+		llwarns<<"Element is nonexistent - empty/unsupported node."<<llendl;
+	}
+}
+//-----------------------------------------------------------------------------
+// processJointNode()
+//-----------------------------------------------------------------------------
+void LLDAELoader::processJointNode( domNode* pNode, JointTransformMap& jointTransforms )
+{
+	if (pNode->getName() == NULL)
+	{
+		llwarns << "nameless node, can't process" << llendl;
+		return;
+	}
+
+	//llwarns<<"ProcessJointNode# Node:" <<pNode->getName()<<llendl;
+
+	//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() )
+		{
+			//llwarns<< "The found element is not a translate node" <<llendl;
+			daeSIDResolver jointResolver( pNode, "./matrix" );
+			domMatrix* pMatrix = daeSafeCast<domMatrix>( jointResolver.getElement() );
+			if ( pMatrix )
+			{
+				//llinfos<<"A matrix SID was however found!"<<llendl;
+				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
+			{
+				llwarns<< "The found element is not translate or matrix node - most likely a corrupt export!" <<llendl;
+			}
+		}
+		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;
+	}
+	llwarns<< "Could not find a child [" << name << "] for the element: \"" << pElement->getAttribute("id") << "\"" << llendl;
+    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];
+			}
+		}
+
+		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
+						llinfos << "Negative scale detected, unsupported transform.  domInstance_geometry: " << getElementLabel(instance_geo) << llendl;
+						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
+						llinfos << "Negative scale detected, unsupported post-normalization transform.  domInstance_geometry: " << getElementLabel(instance_geo) << llendl;
+						badElement = true;
+					}
+
+					std::string label = getElementLabel(instance_geo);
+                   
+                    llassert(!label.empty());
+
+                    if (model->mSubmodelID)
+                    {
+                        // CHECK FOR _LODX and _PHYS here to ensure we bolt the submodel 'salt' at the right loc
+                        //
+                        if ((label.find("_LOD") != -1) || (label.find("_PHYS") != -1))
+                        {
+                            std::string labelStart;
+                            std::string markup;
+                            size_t underscore_offset = label.rfind('_');
+                            if (underscore_offset != -1)
+                            {
+                                markup = label.substr(underscore_offset + 1, 4);
+                                label.erase(label.begin() + underscore_offset, label.end());
+                                label +=(char)((int)'a' + model->mSubmodelID);
+                                label += "_";
+                                label += markup;
+                            }
+                            else
+                            {
+                                label +=(char)((int)'a' + model->mSubmodelID);
+                            }                            
+                        }
+                        else
+                        {
+                            label += (char)((int)'a' + model->mSubmodelID);
+                        }
+                    }
+
+                    model->mLabel = label;
+                   
+					mScene[transformation].push_back(LLModelInstance(model, label, transformation, materials));
+					stretch_extents(model, transformation, mExtents[0], mExtents[1], mFirstTransform);
+					i++;
+				}
+			}
+		}
+		else 
+		{
+			llinfos<<"Unable to resolve geometry URL."<<llendl;
+			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();
+	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 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);
+
+		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)
+{
+
+	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); 
+
+    ret->mLabel = getElementLabel(mesh);    
+
+    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;
+	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->getVolumeFaces() = remainder;
+			next->mNormalizedScale = ret->mNormalizedScale;
+			next->mNormalizedTranslation = ret->mNormalizedTranslation;
+			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
+	{	
+		llwarns << "no mesh found" << llendl;
+	}
+
+	return false;
+}
diff --git a/indra/llprimitive/lldaeloader.h b/indra/llprimitive/lldaeloader.h
new file mode 100644
index 0000000000..e71c407909
--- /dev/null
+++ b/indra/llprimitive/lldaeloader.h
@@ -0,0 +1,103 @@
+/**
+ * @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);
+	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);
+
+	static std::string getElementLabel(daeElement *element);	
+
+private:
+
+};
+#endif  // LL_LLDAELLOADER_H
-- 
cgit v1.2.3


From 99952dc3583c48a3da8bbfdf1861c876bb6656d0 Mon Sep 17 00:00:00 2001
From: Graham Linden <graham@lindenlab.com>
Date: Tue, 22 Apr 2014 19:08:29 -0700
Subject: More missing bits

---
 indra/llprimitive/llmodelloader.cpp | 620 ++++++++++++++++++++++++++++++++++++
 indra/llprimitive/llmodelloader.h   | 209 ++++++++++++
 2 files changed, 829 insertions(+)
 create mode 100644 indra/llprimitive/llmodelloader.cpp
 create mode 100644 indra/llprimitive/llmodelloader.h

diff --git a/indra/llprimitive/llmodelloader.cpp b/indra/llprimitive/llmodelloader.cpp
new file mode 100644
index 0000000000..5138167979
--- /dev/null
+++ b/indra/llprimitive/llmodelloader.cpp
@@ -0,0 +1,620 @@
+/**
+ * @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"
+
+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)
+{
+	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, 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;
+	}
+
+	// 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].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;
+				}
+				instance_list[i].mLOD[lod] = model[lod][idx];
+			}
+		}
+
+		if (!instance_list[i].mModel)
+			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()
+{
+	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
+			{
+				llinfos<<"critiqueJointToNodeMappingFromScene is missing a: "<<name<<llendl;
+				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 )
+		{
+			llinfos<<" Asset did not contain the joint (if you're u/l a fully rigged asset w/joint positions - it is required)." << *masterJointIt<< llendl;
+			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 )
+		{
+			llinfos<<" Asset did not contain the joint (if you're u/l a fully rigged asset w/joint positions - it is required)." << *masterJointIt<< llendl;
+			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 0000000000..6dac0d5a34
--- /dev/null
+++ b/indra/llprimitive/llmodelloader.h
@@ -0,0 +1,209 @@
+/**
+ * @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"
+
+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
+	} 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
-- 
cgit v1.2.3


From 5cbcff7b4045e5246eee5c86f789394510cb0669 Mon Sep 17 00:00:00 2001
From: Graham Linden <graham@lindenlab.com>
Date: Tue, 22 Apr 2014 22:19:39 -0700
Subject: Appease GCC 4.6.1 syntax problems with uintptr_t in alignment asserts

---
 indra/llcommon/llmemory.h | 10 ++++++----
 indra/llmath/llvolume.h   |  1 -
 2 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h
index 61e30f11cc..9ff884ca2a 100755
--- a/indra/llcommon/llmemory.h
+++ b/indra/llcommon/llmemory.h
@@ -27,6 +27,8 @@
 #define LLMEMORY_H
 
 #include "linden_common.h"
+#include "stdtypes.h"
+#include <stdint.h>
 
 class LLMutex ;
 
@@ -39,7 +41,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
@@ -49,13 +51,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
@@ -81,7 +83,7 @@ inline void* ll_aligned_malloc( size_t size, int align )
 #else
 	void* mem = malloc( size + (align - 1) + sizeof(void*) );
 	char* aligned = ((char*)mem) + sizeof(void*);
-	aligned += align - ((uintptr_t)aligned & (align - 1));
+	aligned += align - (uintptr_t(aligned) & (align - 1));
 
 	((void**)aligned)[-1] = mem;
 	return aligned;
diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h
index d2cb052043..3d7d4b40d1 100755
--- a/indra/llmath/llvolume.h
+++ b/indra/llmath/llvolume.h
@@ -1083,7 +1083,6 @@ public:
 	
 	
 	BOOL mGenerateSingleFace;
-	typedef std::vector<LLVolumeFace> face_list_t;
 	face_list_t mVolumeFaces;
 
 public:
-- 
cgit v1.2.3


From b703a612d7ca96e9c79f40a33208757bba6e9d7e Mon Sep 17 00:00:00 2001
From: Graham Linden <graham@lindenlab.com>
Date: Wed, 23 Apr 2014 05:41:55 -0700
Subject: Put back assert so tests checking for fail will find fail where
 expected

---
 indra/llcommon/llinstancetracker.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/indra/llcommon/llinstancetracker.h b/indra/llcommon/llinstancetracker.h
index b039290037..fc51fbd2f9 100755
--- a/indra/llcommon/llinstancetracker.h
+++ b/indra/llcommon/llinstancetracker.h
@@ -38,6 +38,8 @@
 #include <boost/iterator/transform_iterator.hpp>
 #include <boost/iterator/indirect_iterator.hpp>
 
+#include "llerror.h"
+
 /**
  * Base class manages "class-static" data that must actually have singleton
  * semantics: one instance per process, rather than one instance per module as
@@ -305,6 +307,7 @@ protected:
 	virtual ~LLInstanceTracker()
 	{
 		// it's unsafe to delete instances of this type while all instances are being iterated over.
+        llassert_always(getStatic().sIterationNestDepth == 0);
 		getSet_().erase(static_cast<T*>(this));
 	}
 
-- 
cgit v1.2.3


From cf143bd4610dc279f1aa790e0b37fbad5c86f654 Mon Sep 17 00:00:00 2001
From: Graham Linden <graham@lindenlab.com>
Date: Thu, 29 May 2014 04:59:41 -0700
Subject: Fix files apparently missed by whomever did the great log macro
 renaming (pox be upon them)

---
 indra/newview/llinventoryobserver.cpp | 2 +-
 indra/newview/lllogchat.cpp           | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/indra/newview/llinventoryobserver.cpp b/indra/newview/llinventoryobserver.cpp
index 2cbf9bb8b6..26643d8671 100755
--- a/indra/newview/llinventoryobserver.cpp
+++ b/indra/newview/llinventoryobserver.cpp
@@ -702,7 +702,7 @@ void LLInventoryCategoriesObserver::changed(U32 mask)
 		LLViewerInventoryCategory* category = gInventory.getCategory(cat_id);
 		if (!category)
         {
-            llwarns << "Category : Category id = " << cat_id << " disappeared" << llendl;
+            LL_WARNS() << "Category : Category id = " << cat_id << " disappeared" << LL_ENDL;
 			cat_data.mCallback();
             // Keep track of those deleted categories so we can remove them
             deleted_categories_ids.push_back(cat_id);
diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp
index 06e517a861..cadbc16f1e 100755
--- a/indra/newview/lllogchat.cpp
+++ b/indra/newview/lllogchat.cpp
@@ -1022,7 +1022,7 @@ void LLLoadHistoryThread::run()
 	{
 		loadHistory(mFileName, mMessages, mLoadParams);
 		int count = mMessages->size();
-		llinfos << "mMessages->size(): " << count << llendl;
+		LL_INFOS() << "mMessages->size(): " << count << LL_ENDL;
 		setFinished();
 	}
 }
-- 
cgit v1.2.3


From 3501487bfc9463bf2b732d790819bc2938816287 Mon Sep 17 00:00:00 2001
From: Graham Linden <none@none>
Date: Thu, 28 Aug 2014 11:25:17 -0700
Subject: Make ImporterDebug facilities work again

---
 indra/newview/app_settings/settings.xml |  6 +++---
 indra/newview/llfloatermodelpreview.cpp | 33 +++++++++++++++------------------
 2 files changed, 18 insertions(+), 21 deletions(-)

diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 8895bba472..b63cba75ec 100755
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -7,11 +7,11 @@
     <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>0</integer>
+    <integer>1</integer>
     <key>Type</key>
-    <string>Integer</string>
+    <string>Boolean</string>
     <key>Value</key>
-    <integer>0</integer>
+    <integer>1</integer>
   </map>
   <key>IMShowTime</key>
   <map>
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 78a087b050..685048ace7 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -1408,7 +1408,7 @@ void LLModelPreview::rebuildUploadData()
 
                 FindModel(mScene[i], name_to_match, lod_model, transform);
 
-                S32 importerDebug = gSavedSettings.getS32("ImporterDebug");                    
+                BOOL importerDebug = gSavedSettings.getBOOL("ImporterDebug");                    
 
                 // Fall back to old method of index-based association if
                 // we could not find a match based on the mesh names
@@ -1418,7 +1418,7 @@ void LLModelPreview::rebuildUploadData()
                     
                     if (i == LLModel::LOD_PHYSICS)
                     {
-                        if (importerDebug > 0)
+                        if (importerDebug)
                         {
                             LL_INFOS() << "Assigning collision for " << instance.mLabel << " to match " << lod_model->mLabel << LL_ENDL;
                         }
@@ -1426,7 +1426,7 @@ void LLModelPreview::rebuildUploadData()
                     }
                     else
                     {
-                        if (importerDebug > 0)
+                        if (importerDebug)
                         {
                             LL_INFOS() << "Assigning LOD" << i << " for " << instance.mLabel << " to found match " << lod_model->mLabel << LL_ENDL;
                         }
@@ -1463,7 +1463,7 @@ void LLModelPreview::rebuildUploadData()
                     {
                         if (i == LLModel::LOD_PHYSICS)
                         {
-                            if (importerDebug > 0)
+                            if (importerDebug)
                             {                         
                                 LL_INFOS() << "Falling back collision for " << instance.mLabel << " to " << lod_model->mLabel << LL_ENDL;
                             }
@@ -1471,7 +1471,7 @@ void LLModelPreview::rebuildUploadData()
                         }
                         else
                         {
-                            if (importerDebug > 0)
+                            if (importerDebug)
                             {
                                 LL_INFOS() << "Falling back LOD" << i << " for " << instance.mLabel << " to found " << lod_model->mLabel << LL_ENDL;
                             }
@@ -1485,7 +1485,7 @@ void LLModelPreview::rebuildUploadData()
 			            {  //find reference instance for this model
 				            if (mBaseModel[idx] == base_model)
 				            {
-                                if (importerDebug > 0)
+                                if (importerDebug)
                                 {
                                     LL_INFOS() << "Falling back to model index " << idx << " for LOD " << i << " of " << instance.mLabel << LL_ENDL;
                                 }
@@ -1503,14 +1503,14 @@ void LLModelPreview::rebuildUploadData()
 						    instance.mLOD[i] = lod_model;
                             if (i == LLModel::LOD_PHYSICS)
                             {
-                                if (importerDebug > 0)
+                                if (importerDebug)
                                 {
                                     LL_INFOS() << "Indexed fallback to model index " << idx << ": LOD " << i << " named " << lod_model->mLabel << " for collision for " << instance.mLabel <<  LL_ENDL;
                                 }
                             }
                             else
                             {
-                                if (importerDebug > 0)
+                                if (importerDebug)
                                 {
                                     LL_INFOS() << "Indexed fallback to model index " << idx << " LOD " << i << " named " << lod_model->mLabel << " for LOD " << i << " for " << instance.mLabel << LL_ENDL;
                                 }
@@ -1518,7 +1518,7 @@ void LLModelPreview::rebuildUploadData()
 					    }
                         else
                         {
-                            if (importerDebug > 0)
+                            if (importerDebug)
                             {
                                 LL_INFOS() << "List of models for LOD " << i << " did not include index " << idx <<  LL_ENDL;
                             }
@@ -2434,8 +2434,8 @@ void LLModelPreview::updateStatusMessages()
 
                 std::string instance_name = instance.mLabel;
 
-                S32 importerDebug = gSavedSettings.getS32("ImporterDebug");
-                if (importerDebug > 0)
+                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.
@@ -2444,14 +2444,11 @@ void LLModelPreview::updateStatusMessages()
                     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;
 
-                    if (importerDebug > 1)
+                    LLModel::material_list::iterator mat_iter = lod_model->mMaterialList.begin();
+                    while (mat_iter != lod_model->mMaterialList.end())
                     {
-                        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++;
-                        }
+                        LL_INFOS() << "Instance " << lod_model->mLabel << " LOD " << i << " Material " << *(mat_iter) << LL_ENDL;
+                        mat_iter++;
                     }
                 }
 
-- 
cgit v1.2.3


From da5bd17afd1b78fbd41b247986567204d9470275 Mon Sep 17 00:00:00 2001
From: Graham Linden <graham@lindenlab.com>
Date: Fri, 29 Aug 2014 04:59:14 -0700
Subject: Fix issue with double-addition of LOD/PHYS modifiers in some
 codepaths

---
 indra/newview/llfloatermodelpreview.cpp | 32 ++++++++++++++++++++++----------
 1 file changed, 22 insertions(+), 10 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 685048ace7..52f9f5af66 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -1397,13 +1397,19 @@ void LLModelPreview::rebuildUploadData()
                 std::string name_to_match = instance.mLabel;
                 llassert(!name_to_match.empty());
 
+                std::string toAdd;
                 switch (i)
                 {
-                    case LLModel::LOD_IMPOSTOR: name_to_match += "_LOD0"; break;
-                    case LLModel::LOD_LOW:      name_to_match += "_LOD1"; break;
-		            case LLModel::LOD_MEDIUM:   name_to_match += "_LOD2"; break;
-                    case LLModel::LOD_PHYSICS:  name_to_match += "_PHYS"; break;
-                    case LLModel::LOD_HIGH:                               break;
+                    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);
@@ -1441,13 +1447,19 @@ void LLModelPreview::rebuildUploadData()
                         std::string name_to_match = instance.mLabel;
                         llassert(!name_to_match.empty());
 
+                        std::string toAdd;
                         switch (searchLOD)
                         {
-                            case LLModel::LOD_IMPOSTOR: name_to_match += "_LOD0"; break;
-                            case LLModel::LOD_LOW:      name_to_match += "_LOD1"; break;
-		                    case LLModel::LOD_MEDIUM:   name_to_match += "_LOD2"; break;
-                            case LLModel::LOD_PHYSICS:  name_to_match += "_PHYS"; break;
-                            case LLModel::LOD_HIGH:                               break;
+                            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'
-- 
cgit v1.2.3


From 297aefd933c1f9646268556eb81132770fd3ce70 Mon Sep 17 00:00:00 2001
From: Graham Linden <graham@lindenlab.com>
Date: Fri, 29 Aug 2014 11:29:37 -0700
Subject: Merge up to 3.7.15 and fix many deprecated logging statements skipped
 by flickrfolk

---
 indra/llprimitive/llmodel.cpp           |  6 ++---
 indra/newview/llfloaterflickr.cpp       |  6 ++---
 indra/newview/llfloatergroupbulkban.cpp |  2 +-
 indra/newview/llfloatermodelpreview.cpp | 42 ++++++++++++++++-----------------
 indra/newview/llpanelgroupbulk.cpp      |  2 +-
 indra/newview/llpanelgroupinvite.cpp    |  2 +-
 indra/newview/llpanelgrouproles.cpp     |  5 ++--
 indra/newview/llsnapshotlivepreview.cpp | 14 +++++------
 8 files changed, 39 insertions(+), 40 deletions(-)

diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp
index f17f233525..0801dc96bf 100755
--- a/indra/llprimitive/llmodel.cpp
+++ b/indra/llprimitive/llmodel.cpp
@@ -943,10 +943,10 @@ LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, BOOL nowrite, BO
 		}
 	}
 
-    if (mdl.has("submodel_id"))
-	{ //write out submodel id
+        if (mdl.has("submodel_id"))
+        { //write out submodel id
         header["submodel_id"] = (LLSD::Integer)mdl["submodel_id"];
-	}
+        }
 
 	std::string out[MODEL_NAMES_LENGTH];
 
diff --git a/indra/newview/llfloaterflickr.cpp b/indra/newview/llfloaterflickr.cpp
index c4cf9cc8f0..5d44e9619c 100644
--- a/indra/newview/llfloaterflickr.cpp
+++ b/indra/newview/llfloaterflickr.cpp
@@ -457,13 +457,13 @@ void LLFlickrPhotoPanel::updateResolution(BOOL do_update)
 		if (width == 0 || height == 0)
 		{
 			// take resolution from current window size
-			lldebugs << "Setting preview res from window: " << gViewerWindow->getWindowWidthRaw() << "x" << gViewerWindow->getWindowHeightRaw() << llendl;
+			LL_DEBUGS() << "Setting preview res from window: " << gViewerWindow->getWindowWidthRaw() << "x" << gViewerWindow->getWindowHeightRaw() << LL_ENDL;
 			previewp->setSize(gViewerWindow->getWindowWidthRaw(), gViewerWindow->getWindowHeightRaw());
 		}
 		else
 		{
 			// use the resolution from the selected pre-canned drop-down choice
-			lldebugs << "Setting preview res selected from combo: " << width << "x" << height << llendl;
+			LL_DEBUGS() << "Setting preview res selected from combo: " << width << "x" << height << LL_ENDL;
 			previewp->setSize(width, height);
 		}
 
@@ -726,7 +726,7 @@ void LLFloaterFlickr::showPhotoPanel()
 	LLTabContainer* parent = dynamic_cast<LLTabContainer*>(mFlickrPhotoPanel->getParent());
 	if (!parent)
 	{
-		llwarns << "Cannot find panel container" << llendl;
+		LL_WARNS() << "Cannot find panel container" << LL_ENDL;
 		return;
 	}
 
diff --git a/indra/newview/llfloatergroupbulkban.cpp b/indra/newview/llfloatergroupbulkban.cpp
index 54a2283b13..44074047a7 100644
--- a/indra/newview/llfloatergroupbulkban.cpp
+++ b/indra/newview/llfloatergroupbulkban.cpp
@@ -101,7 +101,7 @@ void LLFloaterGroupBulkBan::showForGroup(const LLUUID& group_id, uuid_vec_t* age
 	// Make sure group_id isn't null
 	if (group_id.isNull())
 	{
-		llwarns << "LLFloaterGroupInvite::showForGroup with null group_id!" << llendl;
+		LL_WARNS() << "LLFloaterGroupInvite::showForGroup with null group_id!" << LL_ENDL;
 		return;
 	}
 
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 52f9f5af66..6aa41ef768 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -1809,7 +1809,7 @@ void LLModelPreview::clearGLODGroup()
 	}
 }
 
-void LLModelPreview::loadModelCallback(S32 lod)
+void LLModelPreview::loadModelCallback(S32 loaded_lod)
 {
 	assert_main_thread();
 
@@ -1833,7 +1833,7 @@ void LLModelPreview::loadModelCallback(S32 lod)
 
 	mModelLoader->loadTextures() ;
 
-	if (lod == -1)
+	if (loaded_lod == -1)
 	{ //populate all LoDs from model loader scene
 		mBaseModel.clear();
 		mBaseScene.clear();
@@ -1849,7 +1849,7 @@ void LLModelPreview::loadModelCallback(S32 lod)
 			mModel[lod].clear();
 			mVertexBuffer[lod].clear();
 			
-			if (mModelLoader->mScene.begin()->second[0].mLOD[lod].notNull())
+			if (mModelLoader->mScene.begin()->second[0].mLOD[loaded_lod].notNull())
 			{ //if this LoD exists in the loaded scene
 
 				//copy scene to current LoD
@@ -1921,31 +1921,31 @@ 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();
 		}
 
-		clearIncompatible(lod);
+		clearIncompatible(loaded_lod);
 
 		mDirty = true;
 
-		if (lod == LLModel::LOD_HIGH)
+		if (loaded_lod == LLModel::LOD_HIGH)
 		{
 			resetPreviewTarget();
 		}
@@ -3092,23 +3092,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()
 //-----------------------------------------------------------------------------
@@ -3933,14 +3933,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
diff --git a/indra/newview/llpanelgroupbulk.cpp b/indra/newview/llpanelgroupbulk.cpp
index 1eafc5bd64..76792cc6fd 100644
--- a/indra/newview/llpanelgroupbulk.cpp
+++ b/indra/newview/llpanelgroupbulk.cpp
@@ -387,7 +387,7 @@ void LLPanelGroupBulk::addUsers(uuid_vec_t& agent_ids)
 			} 
 			else 
 			{
-				llwarns << "llPanelGroupBulk: Selected avatar has no name: " << dest->getID() << llendl;
+				LL_WARNS() << "llPanelGroupBulk: Selected avatar has no name: " << dest->getID() << LL_ENDL;
 				names.push_back("(Unknown)");
 			}
 		}
diff --git a/indra/newview/llpanelgroupinvite.cpp b/indra/newview/llpanelgroupinvite.cpp
index 236ad861a5..e662a05dfc 100755
--- a/indra/newview/llpanelgroupinvite.cpp
+++ b/indra/newview/llpanelgroupinvite.cpp
@@ -492,7 +492,7 @@ void LLPanelGroupInvite::addUsers(uuid_vec_t& agent_ids)
 			} 
 			else 
 			{
-				llwarns << "llPanelGroupInvite: Selected avatar has no name: " << dest->getID() << llendl;
+				LL_WARNS() << "llPanelGroupInvite: Selected avatar has no name: " << dest->getID() << LL_ENDL;
 				names.push_back("(Unknown)");
 			}
 		}
diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp
index 1d7ba4d741..2e747bc4da 100755
--- a/indra/newview/llpanelgrouproles.cpp
+++ b/indra/newview/llpanelgrouproles.cpp
@@ -1961,7 +1961,7 @@ bool LLPanelGroupRolesSubTab::needsApply(std::string& mesg)
 	LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
 	if(!gdatap)
 	{
-		llwarns << "Unable to get group data for group " << mGroupID << llendl;
+		LL_WARNS() << "Unable to get group data for group " << mGroupID << LL_ENDL;
 		return false;
 	}
 
@@ -2389,8 +2389,7 @@ void LLPanelGroupRolesSubTab::handleActionCheck(LLUICtrl* ctrl, bool force)
 		}
 		else
 		{
-			llwarns << "Unable to look up role information for role id: "
-				<< role_id << llendl;
+			LL_WARNS() << "Unable to look up role information for role id: " << role_id << LL_ENDL;
 		}
 		
 		//////////////////////////////////////////////////////////////////////////
diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp
index f61db77169..bc5993ec76 100644
--- a/indra/newview/llsnapshotlivepreview.cpp
+++ b/indra/newview/llsnapshotlivepreview.cpp
@@ -151,7 +151,7 @@ F32 LLSnapshotLivePreview::getImageAspect()
 
 void LLSnapshotLivePreview::updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail, F32 delay)
 {
-	lldebugs << "updateSnapshot: mSnapshotUpToDate = " << getSnapshotUpToDate() << llendl;
+	LL_DEBUGS() << "updateSnapshot: mSnapshotUpToDate = " << getSnapshotUpToDate() << LL_ENDL;
 
 	// Update snapshot if requested.
 	if (new_snapshot)
@@ -594,7 +594,7 @@ void LLSnapshotLivePreview::generateThumbnailImage(BOOL force_update)
             }
             else
             {
-                llwarns << "Couldn't find a path to the following filter : " << getFilter() << llendl;
+                LL_WARNS() << "Couldn't find a path to the following filter : " << getFilter() << LL_ENDL;
             }
         }
         // Scale to a power of 2 so it can be mapped to a texture
@@ -642,7 +642,7 @@ LLViewerTexture* LLSnapshotLivePreview::getBigThumbnailImage()
             }
             else
             {
-                llwarns << "Couldn't find a path to the following filter : " << getFilter() << llendl;
+                LL_WARNS() << "Couldn't find a path to the following filter : " << getFilter() << LL_ENDL;
             }
         }
         // Scale to a power of 2 so it can be mapped to a texture
@@ -695,7 +695,7 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview )
 	// time to produce a snapshot
 	if(!previewp->getSnapshotUpToDate())
     {
-        lldebugs << "producing snapshot" << llendl;
+        LL_DEBUGS() << "producing snapshot" << LL_ENDL;
         if (!previewp->mPreviewImage)
         {
             previewp->mPreviewImage = new LLImageRaw;
@@ -775,7 +775,7 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview )
         previewp->setVisible(gSavedSettings.getBOOL("UseFreezeFrame") && previewp->mAllowFullScreenPreview); // only show fullscreen preview when in freeze frame mode
         previewp->mSnapshotDelayTimer.stop();
         previewp->mSnapshotActive = FALSE;
-        lldebugs << "done creating snapshot" << llendl;
+        LL_DEBUGS() << "done creating snapshot" << LL_ENDL;
     }
     
     if (!previewp->getThumbnailUpToDate())
@@ -910,13 +910,13 @@ LLPointer<LLImageFormatted>	LLSnapshotLivePreview::getFormattedImage()
             }
             else
             {
-                llwarns << "Couldn't find a path to the following filter : " << getFilter() << llendl;
+                LL_WARNS() << "Couldn't find a path to the following filter : " << getFilter() << LL_ENDL;
             }
         }
         
         // Create the new formatted image of the appropriate format.
         LLFloaterSnapshot::ESnapshotFormat format = getSnapshotFormat();
-        lldebugs << "Encoding new image of format " << format << llendl;
+        LL_DEBUGS() << "Encoding new image of format " << format << LL_ENDL;
             
         switch (format)
         {
-- 
cgit v1.2.3


From 6004ad167458914c3b85438b96e81ea8796e368b Mon Sep 17 00:00:00 2001
From: Graham Linden <graham@lindenlab.com>
Date: Fri, 29 Aug 2014 14:03:09 -0700
Subject: Fix for degen phys tris from falling back to non-hull geo

---
 indra/newview/llfloatermodelpreview.cpp | 63 +++++++++++++++------------------
 1 file changed, 28 insertions(+), 35 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 6aa41ef768..cfe2ea0307 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -1441,31 +1441,35 @@ void LLModelPreview::rebuildUploadData()
                 }
                 else
                 {
-                    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)
+                    if  (i != LLModel::LOD_PHYSICS)
+                    {
+                        int searchLOD = (i > LLModel::LOD_HIGH) ? LLModel::LOD_HIGH : i;
+                        while ((searchLOD <= LLModel::LOD_HIGH) && !lod_model)
                         {
-                            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;
-                        }
+                            std::string name_to_match = instance.mLabel;
+                            llassert(!name_to_match.empty());
 
-                        if (name_to_match.find(toAdd) == -1)
-                        {
-                            name_to_match += toAdd;
-                        }
+                            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;
+                            }
 
-                        // See if we can find an appropriately named model in LOD 'searchLOD'
-                        //
-                        FindModel(mScene[searchLOD], name_to_match, lod_model, transform);
-                        searchLOD++;
+                            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++;
+                        }
                     }
 
                     // Fall back to old method of index-based association if
@@ -1473,22 +1477,11 @@ void LLModelPreview::rebuildUploadData()
                     //
                     if (lod_model)
                     {
-                        if (i == LLModel::LOD_PHYSICS)
-                        {
-                            if (importerDebug)
-                            {                         
-                                LL_INFOS() << "Falling back collision for " << instance.mLabel << " to " << lod_model->mLabel << LL_ENDL;
-                            }
-                            instance.mLOD[i] = lod_model;
-                        }
-                        else
+                        if (importerDebug)
                         {
-                            if (importerDebug)
-                            {
-                                LL_INFOS() << "Falling back LOD" << i << " for " << instance.mLabel << " to found " << lod_model->mLabel << LL_ENDL;
-                            }
-                            instance.mLOD[i] = lod_model;
+                            LL_INFOS() << "Falling back LOD" << i << " for " << instance.mLabel << " to found " << lod_model->mLabel << LL_ENDL;
                         }
+                        instance.mLOD[i] = lod_model;
                     }
                     else
                     {
-- 
cgit v1.2.3


From 8f8055996d18b030195b6fc617aef948aae11ba7 Mon Sep 17 00:00:00 2001
From: Graham Linden <none@none>
Date: Thu, 25 Sep 2014 10:40:40 -0700
Subject: Maint 4470 fix material subset crash in the face of bogus content

---
 indra/newview/llfloatermodelpreview.cpp | 40 ++++++++++++++++++++-------------
 1 file changed, 25 insertions(+), 15 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index cfe2ea0307..3aa191cf51 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -1533,19 +1533,25 @@ void LLModelPreview::rebuildUploadData()
 			}
 
             LLModel* high_lod_model = instance.mLOD[LLModel::LOD_HIGH];
-            llassert(high_lod_model);
-
-            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" );
-				}
-	        }
+            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);
 		}
@@ -2402,12 +2408,16 @@ void LLModelPreview::updateStatusMessages()
 		LLModelInstance& instance = *iter;
 
         LLModel* model_high_lod = instance.mLOD[LLModel::LOD_HIGH];
-        llassert(model_high_lod);
+        if (!model_high_lod)
+		{
+			setLoadState( LLModelLoader::ERROR_MATERIALS );
+			mFMP->childDisable( "calculate_btn" );
+			continue;
+		}
 
         for (U32 i = 0; i < LLModel::NUM_LODS-1; i++)
 		{
             LLModel* lod_model = instance.mLOD[i];
-            llassert(lod_model);
             if (!lod_model)
             {
                 setLoadState( LLModelLoader::ERROR_MATERIALS );
-- 
cgit v1.2.3


From 7e4ec481aee7287e98e44767b10968302426c3ff Mon Sep 17 00:00:00 2001
From: Mnikolenko ProductEngine <mnikolenko@productengine.com>
Date: Thu, 16 Oct 2014 16:38:11 +0300
Subject: MAINT-4405 FIXED ImporterDebug defaults to false now

---
 indra/newview/app_settings/settings.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 6a905eabb1..b33166bfcf 100755
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -11,7 +11,7 @@
     <key>Type</key>
     <string>Boolean</string>
     <key>Value</key>
-    <integer>1</integer>
+    <integer>0</integer>
   </map>
   <key>IMShowTime</key>
   <map>
-- 
cgit v1.2.3


From 79212e759b19761726baad29845a505e034dc014 Mon Sep 17 00:00:00 2001
From: Mnikolenko ProductEngine <mnikolenko@productengine.com>
Date: Mon, 20 Oct 2014 11:11:45 +0300
Subject: MAINT-4533 FIXED viewer crash when uploading shiprigging88b.dae

---
 indra/llprimitive/llmodel.cpp | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp
index 0801dc96bf..d82075f9a4 100755
--- a/indra/llprimitive/llmodel.cpp
+++ b/indra/llprimitive/llmodel.cpp
@@ -128,10 +128,14 @@ void LLModel::sortVolumeFacesByMaterialName()
 {
 	std::vector<MaterialBinding> bindings;
 	bindings.resize(mVolumeFaces.size());
+
 	for (int i = 0; i < bindings.size(); i++)
 	{
 		bindings[i].index = i;
-		bindings[i].matName = mMaterialList[i];
+		if(i < mMaterialList.size())
+		{
+			bindings[i].matName = mMaterialList[i];
+		}
 	}
 	std::sort(bindings.begin(), bindings.end(), MaterialSort());
 	std::vector< LLVolumeFace > new_faces;
@@ -142,7 +146,10 @@ void LLModel::sortVolumeFacesByMaterialName()
 	for (int i = 0; i < bindings.size(); i++)
 	{
 		new_faces[i] = mVolumeFaces[bindings[i].index];
-		mMaterialList[i] = bindings[i].matName;
+		if(i < mMaterialList.size())
+		{
+			mMaterialList[i] = bindings[i].matName;
+		}
 	}
 
 	mVolumeFaces = new_faces;	
-- 
cgit v1.2.3


From 370cbd429d3763405795a95cb9355a302b56014a Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Fri, 17 Oct 2014 19:57:33 +0300
Subject: MAINT-4515 FIXED [loader mods] viewer crashed when uploading a large
 mesh

---
 indra/llprimitive/lldaeloader.cpp | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
index 80bb0cd265..945b691196 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -2287,7 +2287,10 @@ bool LLDAELoader::loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& mo
 			next->getVolumeFaces() = remainder;
 			next->mNormalizedScale = ret->mNormalizedScale;
 			next->mNormalizedTranslation = ret->mNormalizedTranslation;
-			next->mMaterialList.assign(ret->mMaterialList.begin() + LL_SCULPT_MESH_MAX_FACES, ret->mMaterialList.end());
+			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;
 		}
 
-- 
cgit v1.2.3


From 2c7019a4d32bc78a76440da43385604b70868401 Mon Sep 17 00:00:00 2001
From: Mnikolenko ProductEngine <mnikolenko@productengine.com>
Date: Wed, 22 Oct 2014 11:47:11 +0300
Subject: MAINT-4542 FIXED [loader mods] viewer does not display error when
 Material is not a subset of reference model.

---
 indra/newview/llfloatermodelpreview.cpp | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 3aa191cf51..9d372766a8 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -1550,7 +1550,21 @@ void LLModelPreview::rebuildUploadData()
 						setLoadState( LLModelLoader::ERROR_MATERIALS );
 						mFMP->childDisable( "calculate_btn" );
 					}
-				}				
+					else
+					{
+						if (mBaseModel.size() == mModel[i].size())
+						{
+							for (U32 idx = 0; idx < mBaseModel.size(); ++idx)
+							{
+								if (mModel[i][idx] && !mModel[i][idx]->matchMaterialOrder(mBaseModel[idx], refFaceCnt, modelFaceCnt ) )
+								{
+									setLoadState( LLModelLoader::ERROR_MATERIALS );
+									mFMP->childDisable( "calculate_btn" );
+								}
+							}
+						}
+					}
+				}
 			}
 			instance.mTransform = mat;
 			mUploadData.push_back(instance);
-- 
cgit v1.2.3


From 54968b2470f0ca80d8edc329236b1c6164a3e2f9 Mon Sep 17 00:00:00 2001
From: Mnikolenko ProductEngine <mnikolenko@productengine.com>
Date: Thu, 23 Oct 2014 17:31:14 +0300
Subject: MAINT-4542 FIXED Free calls shouldn't be used here , as it causes
 crash. (restored old fix)

---
 indra/llprimitive/lldaeloader.cpp | 31 +++++++++++++++++++++----------
 1 file changed, 21 insertions(+), 10 deletions(-)

diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
index 945b691196..b88913cf49 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -257,13 +257,13 @@ LLModel::EModelStatus load_face_from_dom_triangles(std::vector<LLVolumeFace>& fa
 				LLVolumeFace& new_face = *face_list.rbegin();
 				if (!norm_source)
 				{
-					ll_aligned_free_16(new_face.mNormals);
+					//ll_aligned_free_16(new_face.mNormals);
 					new_face.mNormals = NULL;
 				}
 
 				if (!tc_source)
 				{
-					ll_aligned_free_16(new_face.mTexCoords);
+					//ll_aligned_free_16(new_face.mTexCoords);
 					new_face.mTexCoords = NULL;
 				}
 
@@ -288,13 +288,13 @@ LLModel::EModelStatus load_face_from_dom_triangles(std::vector<LLVolumeFace>& fa
 		LLVolumeFace& new_face = *face_list.rbegin();
 		if (!norm_source)
 		{
-			ll_aligned_free_16(new_face.mNormals);
+			//ll_aligned_free_16(new_face.mNormals);
 			new_face.mNormals = NULL;
 		}
 
 		if (!tc_source)
 		{
-			ll_aligned_free_16(new_face.mTexCoords);
+			//ll_aligned_free_16(new_face.mTexCoords);
 			new_face.mTexCoords = NULL;
 		}
 	}
@@ -375,6 +375,11 @@ LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& fac
 				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)
@@ -388,6 +393,12 @@ LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& fac
 				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;
@@ -482,13 +493,13 @@ LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& fac
 				LLVolumeFace& new_face = *face_list.rbegin();
 				if (!norm_source)
 				{
-					ll_aligned_free_16(new_face.mNormals);
+					//ll_aligned_free_16(new_face.mNormals);
 					new_face.mNormals = NULL;
 				}
 
 				if (!tc_source)
 				{
-					ll_aligned_free_16(new_face.mTexCoords);
+					//ll_aligned_free_16(new_face.mTexCoords);
 					new_face.mTexCoords = NULL;
 				}
 
@@ -516,13 +527,13 @@ LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& fac
 		LLVolumeFace& new_face = *face_list.rbegin();
 		if (!norm_source)
 		{
-			ll_aligned_free_16(new_face.mNormals);
+			//ll_aligned_free_16(new_face.mNormals);
 			new_face.mNormals = NULL;
 		}
 
 		if (!tc_source)
 		{
-			ll_aligned_free_16(new_face.mTexCoords);
+			//ll_aligned_free_16(new_face.mTexCoords);
 			new_face.mTexCoords = NULL;
 		}
 	}
@@ -733,13 +744,13 @@ LLModel::EModelStatus load_face_from_dom_polygons(std::vector<LLVolumeFace>& fac
 		LLVolumeFace& new_face = *face_list.rbegin();
 		if (!n)
 		{
-			ll_aligned_free_16(new_face.mNormals);
+			//ll_aligned_free_16(new_face.mNormals);
 			new_face.mNormals = NULL;
 		}
 
 		if (!t)
 		{
-			ll_aligned_free_16(new_face.mTexCoords);
+			//ll_aligned_free_16(new_face.mTexCoords);
 			new_face.mTexCoords = NULL;
 		}
 	}
-- 
cgit v1.2.3


From 74cf2c804eb413237529666a992c85dd3d61a0ca Mon Sep 17 00:00:00 2001
From: maksymsproductengine <maksymsproductengine@lindenlab.com>
Date: Wed, 22 Oct 2014 12:11:50 +0300
Subject: MAINT-4487 FIXED [loader mods] triangle limits from .slm are ignored
 when uploading the model the second time.

---
 indra/llprimitive/llmodelloader.cpp     | 1 +
 indra/newview/llfloatermodelpreview.cpp | 7 ++++++-
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/indra/llprimitive/llmodelloader.cpp b/indra/llprimitive/llmodelloader.cpp
index cb3d482d14..677e4fcb37 100644
--- a/indra/llprimitive/llmodelloader.cpp
+++ b/indra/llprimitive/llmodelloader.cpp
@@ -125,6 +125,7 @@ LLModelLoader::LLModelLoader(
 , mOpaqueData(opaque_userdata)
 , mNoNormalize(false)
 , mNoOptimize(false)
+, mCacheOnlyHitIfRigged(false)
 {
 	mJointMap["mPelvis"] = "mPelvis";
 	mJointMap["mTorso"] = "mTorso";
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 9d372766a8..1e1e96acef 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -1733,7 +1733,12 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable
 	{
 		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"));
-- 
cgit v1.2.3


From f9d82b83a894418090fdf4268401c73fb632f2ef Mon Sep 17 00:00:00 2001
From: Mnikolenko ProductEngine <mnikolenko@productengine.com>
Date: Wed, 29 Oct 2014 17:50:19 +0200
Subject: MAINT-4632 FIXED [loader mods] teamcity linux build fails

---
 indra/newview/llfloatermodelpreview.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 1e1e96acef..c7dd20065c 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -1867,7 +1867,7 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)
 			mModel[lod].clear();
 			mVertexBuffer[lod].clear();
 			
-			if (mModelLoader->mScene.begin()->second[0].mLOD[loaded_lod].notNull())
+			if (mModelLoader->mScene.begin()->second[0].mLOD[lod].notNull())
 			{ //if this LoD exists in the loaded scene
 
 				//copy scene to current LoD
-- 
cgit v1.2.3


From bec06763d5e55dde5148ee8882c04ddfee9dd859 Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Thu, 20 Nov 2014 18:23:07 +0200
Subject: MAINT-4631 FIXED [loader mods] door model is missing triangles when
 uploaded.

---
 indra/llprimitive/lldaeloader.cpp | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
index b88913cf49..40b78ffa9d 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -53,6 +53,8 @@
 #pragma warning (default : 4264)
 #endif
 
+#include <boost/lexical_cast.hpp>
+
 #include "lldaeloader.h"
 #include "llsdserialize.h"
 #include "lljoint.h"
@@ -2128,19 +2130,24 @@ std::string LLDAELoader::getElementLabel(daeElement *element)
 
 	// if we have a parent, use it
 	daeElement* parent = element->getParent();
+	std::string index_string;
 	if (parent)
 	{
+		U32 ind = 0;
+		parent->getChildren().find(element, ind);
+		index_string = "_" + boost::lexical_cast<std::string>(ind);
+
 		// if parent has a name, use it
 		std::string name = parent->getAttribute("name");
 		if (name.length())
 		{
-			return name;
+			return name + index_string;
 		}
 
 		// if parent has an ID, use it
 		if (parent->getID())
 		{
-			return std::string(parent->getID());
+			return std::string(parent->getID()) + index_string;
 		}
 	}
 
@@ -2148,11 +2155,11 @@ std::string LLDAELoader::getElementLabel(daeElement *element)
 	daeString element_name = element->getElementName();
 	if (element_name)
 	{
-		return std::string(element_name);
+		return std::string(element_name) + index_string;
 	}
 
 	// if all else fails, use "object"
-	return std::string("object");
+	return std::string("object") + index_string;
 }
 
 LLColor4 LLDAELoader::getDaeColor(daeElement* element)
-- 
cgit v1.2.3


From 246390ab320623992c6f4fc2339bbe5ed23d2445 Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Tue, 2 Dec 2014 21:55:50 +0200
Subject: MAINT-4631 Buildfix for Mac

---
 indra/llprimitive/lldaeloader.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
index 40b78ffa9d..ff71d52138 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -2133,7 +2133,7 @@ std::string LLDAELoader::getElementLabel(daeElement *element)
 	std::string index_string;
 	if (parent)
 	{
-		U32 ind = 0;
+		size_t ind = 0;
 		parent->getChildren().find(element, ind);
 		index_string = "_" + boost::lexical_cast<std::string>(ind);
 
-- 
cgit v1.2.3


From 4193f9da922fd42ff6acb811c1bf302decbc6750 Mon Sep 17 00:00:00 2001
From: maksymsproductengine <maksymsproductengine@lindenlab.com>
Date: Sat, 20 Dec 2014 02:21:04 +0200
Subject: Fix of build issues after merge

---
 indra/llprimitive/lldaeloader.cpp       | 4 +++-
 indra/newview/llfloatermodelpreview.cpp | 3 ---
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
index ff71d52138..80c6ff4a96 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -1286,7 +1286,9 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do
 						LLJoint* pJoint = mJointLookupFunc(lookingForJoint,mOpaqueData);
 						if ( pJoint )
 						{   
-							pJoint->storeCurrentXform( jointTransform.getTranslation() );												
+							LLUUID fake_mesh_id;
+							fake_mesh_id.generate();
+							pJoint->addAttachmentPosOverride( jointTransform.getTranslation(), fake_mesh_id, "");
 						}
 						else
 						{
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index efe8b6c266..27c2a1ed42 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -1151,9 +1151,6 @@ void LLFloaterModelPreview::onMouseCaptureLostModelPreview(LLMouseHandler* handl
 }
 
 //-----------------------------------------------------------------------------
-											LLUUID fake_mesh_id;
-											fake_mesh_id.generate();
-											pJoint->addAttachmentPosOverride( jointTransform.getTranslation(), fake_mesh_id, gAgentAvatarp->avString());
 // LLModelPreview
 //-----------------------------------------------------------------------------
 
-- 
cgit v1.2.3


From 6262768772d6123e3262ad3e672940e623371f00 Mon Sep 17 00:00:00 2001
From: maksymsproductengine <maksymsproductengine@lindenlab.com>
Date: Thu, 22 Jan 2015 20:57:15 +0200
Subject: MAINT-4794 FIXED Present informative dialog if mesh upload fails due
 to specific reason

---
 indra/newview/llmeshrepository.cpp                 | 32 +++++++++++++++++++++-
 .../newview/skins/default/xui/en/notifications.xml |  5 ++--
 indra/newview/skins/default/xui/en/strings.xml     | 16 +++++++++--
 3 files changed, 47 insertions(+), 6 deletions(-)

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 35b5844914..55b94aa141 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -72,6 +72,7 @@
 #include "bufferstream.h"
 #include "llfasttimer.h"
 #include "llcorehttputil.h"
+#include "lltrans.h"
 
 #include "boost/lexical_cast.hpp"
 
@@ -699,12 +700,16 @@ void log_upload_error(LLCore::HttpStatus status, const LLSD& content,
 	args["MESSAGE"] = message;
 	args["IDENTIFIER"] = identifier;
 	args["LABEL"] = model_name;
-	gMeshRepo.uploadError(args);
 
 	// Log details.
 	LL_WARNS(LOG_MESH) << "Error in stage:  " << stage
 					   << ", Reason:  " << status.toString()
 					   << " (" << status.toTerseString() << ")" << LL_ENDL;
+
+	std::ostringstream details;
+	typedef std::set<std::string> mav_errors_set_t;
+	mav_errors_set_t mav_errors;
+
 	if (content.has("error"))
 	{
 		const LLSD& err = content["error"];
@@ -714,8 +719,11 @@ void log_upload_error(LLCore::HttpStatus status, const LLSD& content,
 						   << "', message '" << err["message"].asString()
 						   << "', id '" << err["identifier"].asString()
 						   << "'" << LL_ENDL;
+
 		if (err.has("errors"))
 		{
+			details << std::endl << std::endl;
+
 			S32 error_num = 0;
 			const LLSD& err_list = err["errors"];
 			for (LLSD::array_const_iterator it = err_list.beginArray();
@@ -723,6 +731,13 @@ void log_upload_error(LLCore::HttpStatus status, const LLSD& content,
 				 ++it)
 			{
 				const LLSD& err_entry = *it;
+				std::string message = err_entry["message"];
+
+				if (message.length() > 0)
+				{
+					mav_errors.insert(message);
+				}
+
 				LL_WARNS(LOG_MESH) << "  error[" << error_num << "]:" << LL_ENDL;
 				for (LLSD::map_const_iterator map_it = err_entry.beginMap();
 					 map_it != err_entry.endMap();
@@ -739,6 +754,21 @@ void log_upload_error(LLCore::HttpStatus status, const LLSD& content,
 	{
 		LL_WARNS(LOG_MESH) << "Bad response to mesh request, no additional error information available." << LL_ENDL;
 	}
+	
+	mav_errors_set_t::iterator mav_errors_it = mav_errors.begin();
+	for (; mav_errors_it != mav_errors.end(); ++mav_errors_it)
+	{
+		std::string mav_details = "Mav_Details_" + *mav_errors_it;
+		details << "Message: '" << *mav_errors_it << "': " << LLTrans::getString(mav_details) << std::endl << std::endl;
+	}
+
+	std::string details_str = details.str();
+	if (details_str.length() > 0)
+	{
+		args["DETAILS"] = details_str;
+	}
+
+	gMeshRepo.uploadError(args);
 }
 
 LLMeshRepoThread::LLMeshRepoThread()
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index ea1bc66236..a80340c5c7 100755
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -7754,9 +7754,8 @@ Select residents to share with.
     name="MeshUploadError"
     icon="alert.tga"
     type="alert">
-    [LABEL] failed to upload: [MESSAGE] [IDENTIFIER] 
-
-See the log file for details.
+      [LABEL] failed to upload: [MESSAGE] [IDENTIFIER]
+[DETAILS]See SecondLife.log for details
   </notification>
    
   <notification
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index 945a77c071..854fec47a9 100755
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -4045,5 +4045,17 @@ Try enclosing path to the editor with double quotes.
   <string name="loading_chat_logs">
     Loading...
   </string>
-  
-  </strings>
+
+  <string name="Mav_Details_MAV_FOUND_DEGENERATE_TRIANGLES">
+      The physics shape contains triangles which are too small. Try simplifying the physics model.
+  </string>
+
+  <string name="Mav_Details_MAV_CONFIRMATION_DATA_MISMATCH">
+      The physics shape contains bad confirmation data. Try to correct the physics model.
+  </string>
+
+  <string name="Mav_Details_MAV_UNKNOWN_VERSION">
+      The physics shape does not have correct version. Set the correct version for the physics model.
+  </string>
+
+</strings>
-- 
cgit v1.2.3


From 0939bf4063ffe494e0a5f108636b9469d387dfb4 Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Tue, 3 Feb 2015 15:50:08 +0200
Subject: MAINT-4806 naming fix

---
 indra/llprimitive/lldaeloader.cpp | 26 ++++++++++++++++++++------
 1 file changed, 20 insertions(+), 6 deletions(-)

diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
index 80c6ff4a96..72de651eea 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -2135,21 +2135,35 @@ std::string LLDAELoader::getElementLabel(daeElement *element)
 	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, use it
+		// if parent has a name or ID, use it
 		std::string name = parent->getAttribute("name");
-		if (name.length())
+		if (!name.length())
 		{
-			return name + index_string;
+			name = std::string(parent->getID());
 		}
 
-		// if parent has an ID, use it
-		if (parent->getID())
+		if (name.length())
 		{
-			return std::string(parent->getID()) + index_string;
+			// make sure that index won't mix up with pre-named lod extensions
+			size_t ext_pos = name.find("_LOD");
+			if (ext_pos == -1)
+			{
+				ext_pos = name.find("_PHYS");
+			}
+
+			if (ext_pos == -1)
+			{
+				return name + index_string;
+			}
+			else
+			{
+				return name.insert(ext_pos, index_string);
+			}
 		}
 	}
 
-- 
cgit v1.2.3


From cf5a5a0e9727cbcc8ea9dd8d0ea25d5ba03c93e7 Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Wed, 4 Feb 2015 19:25:55 +0200
Subject: MAINT-4806 sub-models fix

---
 indra/newview/llfloatermodelpreview.cpp | 88 +++++++++++++++++++++++++++------
 1 file changed, 74 insertions(+), 14 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 27c2a1ed42..a672d05760 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -1554,20 +1554,6 @@ void LLModelPreview::rebuildUploadData()
 						setLoadState( LLModelLoader::ERROR_MATERIALS );
 						mFMP->childDisable( "calculate_btn" );
 					}
-					else
-					{
-						if (mBaseModel.size() == mModel[i].size())
-						{
-							for (U32 idx = 0; idx < mBaseModel.size(); ++idx)
-							{
-								if (mModel[i][idx] && !mModel[i][idx]->matchMaterialOrder(mBaseModel[idx], refFaceCnt, modelFaceCnt ) )
-								{
-									setLoadState( LLModelLoader::ERROR_MATERIALS );
-									mFMP->childDisable( "calculate_btn" );
-								}
-							}
-						}
-					}
 				}
 			}
 			instance.mTransform = mat;
@@ -1575,6 +1561,79 @@ void LLModelPreview::rebuildUploadData()
 		}
 	}
 
+	// Since sub-models affect verification, we need to know number of original/primary models
+	U32 base_primary_count = 0;
+	U32 base_total_count = mBaseModel.size();
+	for (U32 base_ind = 0; base_ind < base_total_count; ++base_ind)
+	{
+		if (mBaseModel[base_ind] && !mBaseModel[base_ind]->mSubmodelID)
+		{
+			base_primary_count++;
+		}
+	}
+
+	//reorder materials to match mBaseModel
+	for (U32 i = 0; i < LLModel::NUM_LODS-1; i++)
+	{
+		int refFaceCnt = 0;
+		int modelFaceCnt = 0;
+
+		U32 model_primary_count = 0;
+		U32 model_total_count = mModel[i].size();
+		for (U32 model_ind = 0; model_ind < model_total_count; ++model_ind)
+		{
+			if (mModel[i][model_ind] && !mModel[i][model_ind]->mSubmodelID)
+			{
+				model_primary_count++;
+			}
+		}
+
+		// Since we don't have a reliable method to check sub-models, check only original/primary models
+		//
+		// Note: we can matchMaterialOrder() for sub-models if they have same id and same label,
+		// but since sub-models are leftovers from original models with random material order, we
+		// can't warranty that checking sub-models is valid.
+		// Original model retains full material list, so we should get ERROR_MATERIALS even
+		// if we don't check sub-models. See LLDAELoader::loadModelsFromDomMesh()
+		if (base_primary_count == model_primary_count)
+		{
+			U32 model_ind = 0;
+			U32 base_ind = 0;
+			U32 models_matched = 0;
+
+			while (models_matched < base_primary_count)
+			{
+				// filter out sub-models
+				while (mModel[i][model_ind]
+						&& mModel[i][model_ind]->mSubmodelID
+						&& model_ind < model_primary_count)
+				{
+					model_ind++;
+				}
+				while (mBaseModel[base_ind]
+						&& mBaseModel[base_ind]->mSubmodelID
+						&& base_ind < base_total_count)
+				{
+					base_ind++;
+				}
+				if (model_ind >= model_total_count || base_ind >= base_total_count)
+				{
+					// Safeguard, shouldn't happen unless something is wrong with models in the list
+					LL_WARNS() << "Materials of some models were not verified and reordered" << LL_ENDL;
+					break;
+				}
+				if (mModel[i][model_ind] && !mModel[i][model_ind]->matchMaterialOrder(mBaseModel[base_ind], refFaceCnt, modelFaceCnt ) )
+				{
+					setLoadState( LLModelLoader::ERROR_MATERIALS );
+					mFMP->childDisable( "calculate_btn" );
+				}
+				models_matched++;
+				model_ind++;
+				base_ind++;
+			}
+		}
+	}
+
 	F32 max_import_scale = (DEFAULT_MAX_PRIM_SCALE-0.1f)/max_scale;
 
 	F32 max_axis = llmax(mPreviewScale.mV[0], mPreviewScale.mV[1]);
@@ -1901,6 +1960,7 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)
 						}
 
 						mModel[lod][idx] = list_iter->mModel;
+						mModel[lod][idx]->mLabel = list_iter->mLabel;
 						if (!list_iter->mModel->mSkinWeights.empty())
 						{
 							skin_weights = true;
-- 
cgit v1.2.3


From 46101abd7348cdf386f58321c9e372b0c56983fd Mon Sep 17 00:00:00 2001
From: Mnikolenko ProductEngine <mnikolenko@productengine.com>
Date: Mon, 9 Feb 2015 12:17:44 +0200
Subject: MAINT-4863 FIXED Return LLModel::BAD_ELEMENT for model with empty or
 corrupted position array

---
 indra/llprimitive/lldaeloader.cpp | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
index 72de651eea..355826b428 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -169,6 +169,11 @@ LLModel::EModelStatus load_face_from_dom_triangles(std::vector<LLVolumeFace>& fa
 
 	if (pos_source)
 	{
+		if(!pos_source->getFloat_array() || (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]);
 	}
@@ -2202,7 +2207,7 @@ bool LLDAELoader::addVolumeFacesFromDomMesh(LLModel* pModel,domMesh* mesh)
 		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();
-- 
cgit v1.2.3


From 5a455a53e6454afa1127c89054658d8eb833b9e3 Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Tue, 10 Feb 2015 19:48:45 +0200
Subject: MAINT-4813 FIXED "failed to upload: MAV_BLOCK_MISSING" appears when
 try to upload a large mesh

---
 indra/llprimitive/llmodelloader.cpp     | 37 +++++++++++++++++++++++++--------
 indra/newview/llfloatermodelpreview.cpp |  1 -
 2 files changed, 28 insertions(+), 10 deletions(-)

diff --git a/indra/llprimitive/llmodelloader.cpp b/indra/llprimitive/llmodelloader.cpp
index 677e4fcb37..c99c24585a 100644
--- a/indra/llprimitive/llmodelloader.cpp
+++ b/indra/llprimitive/llmodelloader.cpp
@@ -357,14 +357,6 @@ bool LLModelLoader::loadFromSLM(const std::string& filename)
 		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;
 
@@ -377,6 +369,7 @@ bool LLModelLoader::loadFromSLM(const std::string& filename)
 
 		//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)
 		{
@@ -394,13 +387,39 @@ bool LLModelLoader::loadFromSLM(const std::string& filename)
 					}					
 					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.
+	std::string name = data["name"];
+	if (!name.empty() && model[LLModel::LOD_HIGH][0]->mLabel.empty())
+	{
+		// fall back value, should be reset later by names from instances
+		model[LLModel::LOD_HIGH][0]->mLabel = name;
+	}
 
 
 	//convert instance_list to mScene
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index a672d05760..823e0879d1 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -1960,7 +1960,6 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)
 						}
 
 						mModel[lod][idx] = list_iter->mModel;
-						mModel[lod][idx]->mLabel = list_iter->mLabel;
 						if (!list_iter->mModel->mSkinWeights.empty())
 						{
 							skin_weights = true;
-- 
cgit v1.2.3


From 9d0d5d7e6f479ee4c6b507e86d17679a1ae1251b Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Thu, 12 Feb 2015 18:46:37 +0200
Subject: MAINT-4806 FIXED Error appear when trying to upload models with more
 than 8 unique faces

---
 indra/newview/llfloatermodelpreview.cpp | 81 ++++++++-------------------------
 1 file changed, 18 insertions(+), 63 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 823e0879d1..230a740007 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -1356,6 +1356,8 @@ void LLModelPreview::rebuildUploadData()
 
 	F32 max_scale = 0.f;
 
+	BOOL importerDebug = gSavedSettings.getBOOL("ImporterDebug");
+
 	for (LLModelLoader::scene::iterator iter = mBaseScene.begin(); iter != mBaseScene.end(); ++iter)
 	{ //for each transform in scene
 		LLMatrix4 mat		= iter->first;
@@ -1418,8 +1420,6 @@ void LLModelPreview::rebuildUploadData()
 
                 FindModel(mScene[i], name_to_match, lod_model, transform);
 
-                BOOL importerDebug = gSavedSettings.getBOOL("ImporterDebug");                    
-
                 // Fall back to old method of index-based association if
                 // we could not find a match based on the mesh names
                 //
@@ -1561,75 +1561,30 @@ void LLModelPreview::rebuildUploadData()
 		}
 	}
 
-	// Since sub-models affect verification, we need to know number of original/primary models
-	U32 base_primary_count = 0;
-	U32 base_total_count = mBaseModel.size();
-	for (U32 base_ind = 0; base_ind < base_total_count; ++base_ind)
-	{
-		if (mBaseModel[base_ind] && !mBaseModel[base_ind]->mSubmodelID)
-		{
-			base_primary_count++;
-		}
-	}
-
-	//reorder materials to match mBaseModel
-	for (U32 i = 0; i < LLModel::NUM_LODS-1; i++)
+	for (U32 lod = 0; lod < LLModel::NUM_LODS-1; lod++)
 	{
-		int refFaceCnt = 0;
-		int modelFaceCnt = 0;
-
-		U32 model_primary_count = 0;
-		U32 model_total_count = mModel[i].size();
-		for (U32 model_ind = 0; model_ind < model_total_count; ++model_ind)
-		{
-			if (mModel[i][model_ind] && !mModel[i][model_ind]->mSubmodelID)
-			{
-				model_primary_count++;
-			}
-		}
-
-		// Since we don't have a reliable method to check sub-models, check only original/primary models
-		//
-		// Note: we can matchMaterialOrder() for sub-models if they have same id and same label,
-		// but since sub-models are leftovers from original models with random material order, we
-		// can't warranty that checking sub-models is valid.
-		// Original model retains full material list, so we should get ERROR_MATERIALS even
-		// if we don't check sub-models. See LLDAELoader::loadModelsFromDomMesh()
-		if (base_primary_count == model_primary_count)
+		// 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)
 		{
-			U32 model_ind = 0;
-			U32 base_ind = 0;
-			U32 models_matched = 0;
-
-			while (models_matched < base_primary_count)
+			bool found_model = false;
+			for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter)
 			{
-				// filter out sub-models
-				while (mModel[i][model_ind]
-						&& mModel[i][model_ind]->mSubmodelID
-						&& model_ind < model_primary_count)
-				{
-					model_ind++;
-				}
-				while (mBaseModel[base_ind]
-						&& mBaseModel[base_ind]->mSubmodelID
-						&& base_ind < base_total_count)
-				{
-					base_ind++;
-				}
-				if (model_ind >= model_total_count || base_ind >= base_total_count)
+				LLModelInstance& instance = *iter;
+				if (instance.mLOD[lod] == mModel[lod][model_ind])
 				{
-					// Safeguard, shouldn't happen unless something is wrong with models in the list
-					LL_WARNS() << "Materials of some models were not verified and reordered" << LL_ENDL;
+					found_model = true;
 					break;
 				}
-				if (mModel[i][model_ind] && !mModel[i][model_ind]->matchMaterialOrder(mBaseModel[base_ind], refFaceCnt, modelFaceCnt ) )
+			}
+			if (!found_model && mModel[lod][model_ind] && !mModel[lod][model_ind]->mSubmodelID)
+			{
+				if (importerDebug)
 				{
-					setLoadState( LLModelLoader::ERROR_MATERIALS );
-					mFMP->childDisable( "calculate_btn" );
+					LL_INFOS() << "Model " << mModel[lod][model_ind]->mLabel << " was not used - mismatching lod models." <<  LL_ENDL;
 				}
-				models_matched++;
-				model_ind++;
-				base_ind++;
+				setLoadState( LLModelLoader::ERROR_MATERIALS );
+				mFMP->childDisable( "calculate_btn" );
 			}
 		}
 	}
-- 
cgit v1.2.3


From 401eed50dea80a499df1f7080f1059b2f0b1e2d8 Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Fri, 13 Feb 2015 16:07:59 +0200
Subject: MAINT-4882 FIXED Mesh complex model upload with .slm uses different
 default model name

---
 indra/llprimitive/llmodelloader.cpp | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/indra/llprimitive/llmodelloader.cpp b/indra/llprimitive/llmodelloader.cpp
index c99c24585a..91198507c6 100644
--- a/indra/llprimitive/llmodelloader.cpp
+++ b/indra/llprimitive/llmodelloader.cpp
@@ -413,12 +413,11 @@ bool LLModelLoader::loadFromSLM(const std::string& filename)
 			instance_list[i].mModel = model[LLModel::LOD_HIGH][idx];
 	}
 
-	// Set name.
+	// Set name for UI to use
 	std::string name = data["name"];
-	if (!name.empty() && model[LLModel::LOD_HIGH][0]->mLabel.empty())
+	if (!name.empty())
 	{
-		// fall back value, should be reset later by names from instances
-		model[LLModel::LOD_HIGH][0]->mLabel = name;
+		model[LLModel::LOD_HIGH][0]->mRequestedLabel = name;
 	}
 
 
-- 
cgit v1.2.3


From b25dfc8cfd41c85c6752f6c63dded1e86ed10801 Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Tue, 17 Feb 2015 18:22:52 +0200
Subject: MAINT-4502 FIXED [loader mods] large scale model cannot be uploaded

---
 indra/llprimitive/lldaeloader.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
index 355826b428..242a669678 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -1845,7 +1845,8 @@ void LLDAELoader::processElement( daeElement* element, bool& badElement, DAE* da
 			}
 		}
 
-		mTransform *= matrix_transform;
+		matrix_transform *= mTransform;
+		mTransform = matrix_transform;
 		mTransform.condition();
 	}
 
-- 
cgit v1.2.3


From 6993f835e702fc41e01fe247427e795974edeb43 Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Mon, 23 Feb 2015 15:04:32 +0200
Subject: MAINT-4915 FIXED [loader mods] Incorrect error description in case of
 VERTEX_NUMBER_OVERFLOW

---
 indra/llprimitive/lldaeloader.cpp       | 2 +-
 indra/llprimitive/llmodelloader.h       | 3 ++-
 indra/newview/llfloatermodelpreview.cpp | 6 +++---
 3 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
index 242a669678..50bf03b32f 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -928,7 +928,7 @@ bool LLDAELoader::OpenFile(const std::string& filename)
 				LLModel* mdl = *i;
 				if(mdl->getStatus() != LLModel::NO_ERRORS)
 				{
-					setLoadState(ERROR_PARSING + mdl->getStatus()) ;
+					setLoadState(ERROR_MODEL + mdl->getStatus()) ;
 					return false; //abort
 				}
 
diff --git a/indra/llprimitive/llmodelloader.h b/indra/llprimitive/llmodelloader.h
index 90ffc0a83a..bb4d06dca3 100644
--- a/indra/llprimitive/llmodelloader.h
+++ b/indra/llprimitive/llmodelloader.h
@@ -89,7 +89,8 @@ public:
 		ERROR_LOADER_SETUP,
 		ERROR_INVALID_PARAMETERS,
 		ERROR_OUT_OF_RANGE,
-		ERROR_FILE_VERSION_INVALID
+		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;
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 230a740007..b2f237e71f 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -634,9 +634,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 )
-- 
cgit v1.2.3


From 84b178ccb140b44a48805ed08468c6dc4acbc395 Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Fri, 13 Mar 2015 18:11:48 +0200
Subject: MAINT-4982 FIXED Multi-Material Mesh Issues

---
 indra/llprimitive/lldaeloader.cpp | 26 +++++++++++++++++++++++---
 1 file changed, 23 insertions(+), 3 deletions(-)

diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
index 50bf03b32f..373c67f063 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -2271,11 +2271,30 @@ bool LLDAELoader::loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& mo
 
 	models_out.clear();
 
-	LLModel* ret = new LLModel(volume_params, 0.f); 
+	LLModel* ret = new LLModel(volume_params, 0.f);
+
+	std::string model_name = getElementLabel(mesh);
+	ret->mLabel = model_name;
+
+	llassert(!ret->mLabel.empty());
 
-    ret->mLabel = getElementLabel(mesh);    
+	// extract actual name and suffix for future use in submodels
+	std::string name_base, name_suffix;
+	size_t ext_pos = model_name.find("_LOD");
+	if (ext_pos == -1)
+	{
+		ext_pos = model_name.find("_PHYS");
+	}
 
-    llassert(!ret->mLabel.empty());
+	if (ext_pos == -1)
+	{
+		name_base = model_name;
+	}
+	else
+	{
+		name_base = model_name.substr(0, ext_pos);
+		name_suffix = model_name.substr(ext_pos, model_name.length() - ext_pos);
+	}
 
 	// Like a monkey, ready to be shot into space
 	//
@@ -2324,6 +2343,7 @@ bool LLDAELoader::loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& mo
 		{
 			LLModel* next = new LLModel(volume_params, 0.f);
 			next->mSubmodelID = ++submodelID;
+			next->mLabel = name_base + (char)((int)'a' + next->mSubmodelID) + name_suffix;
 			next->getVolumeFaces() = remainder;
 			next->mNormalizedScale = ret->mNormalizedScale;
 			next->mNormalizedTranslation = ret->mNormalizedTranslation;
-- 
cgit v1.2.3


From dfd19ed84322c7129509756e1c7fcc63a55f459e Mon Sep 17 00:00:00 2001
From: Mnikolenko ProductEngine <mnikolenko@productengine.com>
Date: Mon, 23 Mar 2015 11:23:28 +0200
Subject: MAINT-4998 FIXED Ignore loadedCallback if Modelpreview floater is
 already closed.

---
 indra/newview/llfloatermodelpreview.cpp | 11 ++++++++++-
 indra/newview/llfloatermodelpreview.h   |  5 ++++-
 2 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index b2f237e71f..e9f98a5c5f 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -93,6 +93,8 @@
 S32 LLFloaterModelPreview::sUploadAmount = 10;
 LLFloaterModelPreview* LLFloaterModelPreview::sInstance = NULL;
 
+bool LLModelPreview::sIgnoreLoadedCallback = false;
+
 const S32 PREVIEW_BORDER_WIDTH = 2;
 const S32 PREVIEW_RESIZE_HANDLE_SIZE = S32(RESIZE_HANDLE_WIDTH * OO_SQRT2) + PREVIEW_BORDER_WIDTH;
 const S32 PREVIEW_HPAD = PREVIEW_RESIZE_HANDLE_SIZE;
@@ -788,9 +790,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)
 {
@@ -3193,7 +3202,7 @@ void LLModelPreview::loadedCallback(
 	void* opaque)
 {
 	LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque);
-	if (pPreview)
+	if (pPreview && !LLModelPreview::sIgnoreLoadedCallback)
 	{
 		pPreview->loadModelCallback(lod);
 	}	
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index 0991980575..4a67c60943 100755
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -89,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; }
@@ -297,6 +298,8 @@ 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);
@@ -388,7 +391,7 @@ private:
 	bool		mLegacyRigValid;
 
 	bool		mLastJointUpdate;
-	
+
 	JointSet				mJointsFromNode;
 	JointTransformMap	mJointTransformMap;
 
-- 
cgit v1.2.3


From 27f83733e9967db82f7fcee153db48549038eeb5 Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Mon, 23 Mar 2015 16:11:36 +0200
Subject: MAINT-4983 Mesh upload of large model ryoma 3d loads indefinitely

---
 indra/llprimitive/lldaeloader.cpp | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
index 373c67f063..d04c7bc23c 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -69,6 +69,8 @@ std::string colladaVersion[VERSIONTYPE_COUNT+1] =
 	"Unsupported"
 };
 
+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)
 {
@@ -947,11 +949,16 @@ bool LLDAELoader::OpenFile(const std::string& filename)
 	model_list::iterator model_iter = mModelList.begin();
 	while (model_iter != mModelList.end())
 	{
-		LL_INFOS() << "Importing " << (*model_iter)->mLabel << LL_ENDL;
-		std::vector<std::string>::iterator mat_iter = (*model_iter)->mMaterialList.begin();
-		while (mat_iter != (*model_iter)->mMaterialList.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() << (*model_iter)->mLabel << " references " << (*mat_iter) << LL_ENDL;
+			LL_INFOS() << mdl->mLabel << " references " << (*mat_iter) << LL_ENDL;
 			mat_iter++;
 		}
 		model_iter++;
-- 
cgit v1.2.3


From 051052b141ef5c2c3a2f08a9d69b7b982386c8c4 Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Wed, 25 Mar 2015 20:07:21 +0200
Subject: MAINT-4983 Mesh upload of large model ryoma 3d loads indefinitely

---
 indra/llprimitive/lldaeloader.cpp       | 15 +++++++++++----
 indra/llprimitive/lldaeloader.h         |  6 ++++--
 indra/newview/app_settings/settings.xml | 11 +++++++++++
 indra/newview/llfloatermodelpreview.cpp |  3 ++-
 4 files changed, 28 insertions(+), 7 deletions(-)

diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
index d04c7bc23c..2ec627d722 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -779,7 +779,8 @@ LLDAELoader::LLDAELoader(
 	state_callback_t		state_cb,
 	void*						opaque_userdata,
 	JointTransformMap&	jointMap,
-	JointSet&				jointsFromNodes )
+	JointSet&				jointsFromNodes,
+	U32					modelLimit)
 : LLModelLoader(
 		filename,
 		lod,
@@ -789,7 +790,8 @@ LLDAELoader::LLDAELoader(
 		state_cb,
 		opaque_userdata,
 		jointMap,
-		jointsFromNodes)
+		jointsFromNodes),
+mGeneratedModelLimit(modelLimit)
 {
 }
 
@@ -911,6 +913,7 @@ bool LLDAELoader::OpenFile(const std::string& filename)
 
 	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;
@@ -921,7 +924,7 @@ bool LLDAELoader::OpenFile(const std::string& filename)
 
 			std::vector<LLModel*> models;
 
-			loadModelsFromDomMesh(mesh, models);
+			loadModelsFromDomMesh(mesh, models, submodel_limit);
 
 			std::vector<LLModel*>::iterator i;
 			i = models.begin();
@@ -2270,7 +2273,7 @@ LLModel* LLDAELoader::loadModelFromDomMesh(domMesh *mesh)
 //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)
+bool LLDAELoader::loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& models_out, U32 submodel_limit)
 {
 
 	LLVolumeParams volume_params;
@@ -2321,6 +2324,10 @@ bool LLDAELoader::loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& mo
 	bool normalized = false;
 
     int submodelID = 0;
+
+	// remove all faces that definitely won't fit into one model and submodel limit
+	ret->setNumVolumeFaces((submodel_limit + 1) * LL_SCULPT_MESH_MAX_FACES);
+
 	LLVolume::face_list_t remainder;
 	do 
 	{
diff --git a/indra/llprimitive/lldaeloader.h b/indra/llprimitive/lldaeloader.h
index e71c407909..cd07ade282 100644
--- a/indra/llprimitive/lldaeloader.h
+++ b/indra/llprimitive/lldaeloader.h
@@ -55,7 +55,8 @@ public:
 		LLModelLoader::state_callback_t		state_cb,
 		void*											opaque_userdata,
 		JointTransformMap&						jointMap,
-		JointSet&									jointsFromNodes);
+		JointSet&									jointsFromNodes,
+		U32									modelLimit);
 	virtual ~LLDAELoader() ;
 
 	virtual bool OpenFile(const std::string& filename);
@@ -93,11 +94,12 @@ protected:
 	// 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);
+	bool loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& models_out, U32 submodel_limit);
 
 	static std::string getElementLabel(daeElement *element);	
 
 private:
+	U32 mGeneratedModelLimit; // Attempt to limit amount of generated submodels
 
 };
 #endif  // LL_LLDAELLOADER_H
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 5f80a8095d..66d50a00a7 100755
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -13,6 +13,17 @@
     <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/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index e9f98a5c5f..9e2e8f4bc0 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -1754,7 +1754,8 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable
 		&LLModelPreview::stateChangedCallback,
 		this,
 		mJointTransformMap,
-		mJointsFromNode );	
+		mJointsFromNode,
+		gSavedSettings.getU32("ImporterModelLimit"));
 
 	if (force_disable_slm)
 	{
-- 
cgit v1.2.3


From 0818a0189c4ebadce44686a43d1bda176626d7c2 Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Fri, 27 Mar 2015 19:38:53 +0200
Subject: MAINT-4983 submodel limit fix

---
 indra/llprimitive/lldaeloader.cpp | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
index 2ec627d722..272ebb7552 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -2326,7 +2326,11 @@ bool LLDAELoader::loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& mo
     int submodelID = 0;
 
 	// remove all faces that definitely won't fit into one model and submodel limit
-	ret->setNumVolumeFaces((submodel_limit + 1) * LL_SCULPT_MESH_MAX_FACES);
+	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 
-- 
cgit v1.2.3


From e712a5230e4f6da7dd750aee2ca8aeb2e4371f72 Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Fri, 27 Mar 2015 21:22:18 +0200
Subject: MAINT-4982 Naming rules unification

---
 indra/llprimitive/lldaeloader.cpp | 65 ++++++++++++++++++---------------------
 indra/llprimitive/lldaeloader.h   |  1 +
 2 files changed, 31 insertions(+), 35 deletions(-)

diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
index 272ebb7552..ed38c97b06 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -1911,31 +1911,24 @@ void LLDAELoader::processElement( daeElement* element, bool& badElement, DAE* da
                     llassert(!label.empty());
 
                     if (model->mSubmodelID)
-                    {
-                        // CHECK FOR _LODX and _PHYS here to ensure we bolt the submodel 'salt' at the right loc
-                        //
-                        if ((label.find("_LOD") != -1) || (label.find("_PHYS") != -1))
-                        {
-                            std::string labelStart;
-                            std::string markup;
-                            size_t underscore_offset = label.rfind('_');
-                            if (underscore_offset != -1)
-                            {
-                                markup = label.substr(underscore_offset + 1, 4);
-                                label.erase(label.begin() + underscore_offset, label.end());
-                                label +=(char)((int)'a' + model->mSubmodelID);
-                                label += "_";
-                                label += markup;
-                            }
-                            else
-                            {
-                                label +=(char)((int)'a' + model->mSubmodelID);
-                            }                            
-                        }
-                        else
-                        {
-                            label += (char)((int)'a' + model->mSubmodelID);
-                        }
+					{
+						// CHECK FOR _LODX and _PHYS here to ensure we bolt the submodel 'salt' at the right loc
+						//
+						std::string labelStart;
+						std::string markup;
+						size_t label_offset = getSuffixPosition(label);
+						if (label_offset != -1)
+						{
+							markup = label.substr(label_offset + 1, 4);
+							label.erase(label.begin() + label_offset, label.end());
+							label +=(char)((int)'a' + model->mSubmodelID);
+							label += "_";
+							label += markup;
+						}
+						else
+						{
+							label +=(char)((int)'a' + model->mSubmodelID);
+						}
                     }
 
                     model->mLabel = label;
@@ -2166,11 +2159,7 @@ std::string LLDAELoader::getElementLabel(daeElement *element)
 		if (name.length())
 		{
 			// make sure that index won't mix up with pre-named lod extensions
-			size_t ext_pos = name.find("_LOD");
-			if (ext_pos == -1)
-			{
-				ext_pos = name.find("_PHYS");
-			}
+			size_t ext_pos = getSuffixPosition(name);
 
 			if (ext_pos == -1)
 			{
@@ -2194,6 +2183,16 @@ std::string LLDAELoader::getElementLabel(daeElement *element)
 	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;
+}
+
 LLColor4 LLDAELoader::getDaeColor(daeElement* element)
 {
 	LLColor4 value;
@@ -2290,11 +2289,7 @@ bool LLDAELoader::loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& mo
 
 	// extract actual name and suffix for future use in submodels
 	std::string name_base, name_suffix;
-	size_t ext_pos = model_name.find("_LOD");
-	if (ext_pos == -1)
-	{
-		ext_pos = model_name.find("_PHYS");
-	}
+	size_t ext_pos = getSuffixPosition(model_name);
 
 	if (ext_pos == -1)
 	{
diff --git a/indra/llprimitive/lldaeloader.h b/indra/llprimitive/lldaeloader.h
index cd07ade282..e31b023d22 100644
--- a/indra/llprimitive/lldaeloader.h
+++ b/indra/llprimitive/lldaeloader.h
@@ -97,6 +97,7 @@ protected:
 	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);
 
 private:
 	U32 mGeneratedModelLimit; // Attempt to limit amount of generated submodels
-- 
cgit v1.2.3


From 06f50c52e1bae3eb67c620c64879dcc6918280dc Mon Sep 17 00:00:00 2001
From: ruslantproductengine <ruslantproductengine@lindenlab.com>
Date: Wed, 1 Apr 2015 19:02:55 +0300
Subject: MAINT-3818 FIXED Certain dae files that used to display skin weights
 successfully now crash when ticking skin weights in model preview on ALL
 viewers

---
 indra/llprimitive/lldaeloader.cpp       | 46 ++++++++++++++++++++++-----------
 indra/newview/llfloatermodelpreview.cpp |  3 ++-
 2 files changed, 33 insertions(+), 16 deletions(-)

diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
index ed38c97b06..62801b8ed4 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -260,25 +260,33 @@ LLModel::EModelStatus load_face_from_dom_triangles(std::vector<LLVolumeFace>& fa
 		}
 
 		if (indices.size()%3 == 0 && verts.size() >= 65532)
+		{
+			std::string material;
+
+			if (tri->getMaterial())
 			{
-				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;
-				}
+				material = std::string(tri->getMaterial());
+			}
 
-				if (!tc_source)
-				{
-					//ll_aligned_free_16(new_face.mTexCoords);
-					new_face.mTexCoords = NULL;
-				}
+			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;
+			}
 
-				face = LLVolumeFace();
-				point_map.clear();
+			if (!tc_source)
+			{
+				//ll_aligned_free_16(new_face.mTexCoords);
+				new_face.mTexCoords = NULL;
 			}
+
+			face = LLVolumeFace();
+			point_map.clear();
+		}
 	}
 
 	if (!verts.empty())
@@ -497,6 +505,14 @@ LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& fac
 
 			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();
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 9e2e8f4bc0..672b2ee02b 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -3787,7 +3787,7 @@ BOOL LLModelPreview::render()
 
 					if (!model->mSkinWeights.empty())
 					{
-						for (U32 i = 0; i < mVertexBuffer[mPreviewLOD][model].size(); ++i)
+						for (U32 i = 0, e = mVertexBuffer[mPreviewLOD][model].size(); i < e; ++i)
 						{
 							LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i];
 
@@ -3856,6 +3856,7 @@ BOOL LLModelPreview::render()
 								position[j] = v;
 							}
 
+							llassert(model->mMaterialList.size() > i); 
 							const std::string& binding = instance.mModel->mMaterialList[i];
 							const LLImportMaterial& material = instance.mMaterial[binding];
 
-- 
cgit v1.2.3


From 3e0483df17181269c4cada7ad5585201f442598c Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Fri, 10 Apr 2015 15:15:08 +0300
Subject: MAINT-4483 FIXED Mesh uploader allows Low LODs to have more triangles
 than High LODs

---
 indra/newview/llfloatermodelpreview.cpp | 39 +++++++++++++++++++++------------
 indra/newview/llfloatermodelpreview.h   |  5 ++++-
 2 files changed, 29 insertions(+), 15 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 672b2ee02b..816b19330b 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -1169,6 +1169,7 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)
 , mLegacyRigValid( false )
 , mRigValidJointUpload( false )
 , mResetJoints( false )
+, mModelNoErrors( true )
 , mRigParityWithScene( false )
 , mLastJointUpdate( false )
 {
@@ -2575,7 +2576,7 @@ void LLModelPreview::updateStatusMessages()
 
 	S32 upload_status[LLModel::LOD_HIGH+1];
 
-	bool upload_ok = true;
+	mModelNoErrors = true;
 
 	for (S32 lod = 0; lod <= LLModel::LOD_HIGH; ++lod)
 	{
@@ -2640,7 +2641,7 @@ void LLModelPreview::updateStatusMessages()
 				{
 					//too many vertices in this lod
 					message = "mesh_status_too_many_vertices";
-					upload_status[lod] = 2;
+					upload_status[lod] = 1;
 				}
 			}
 		}
@@ -2652,7 +2653,7 @@ void LLModelPreview::updateStatusMessages()
 
 		if (upload_status[lod] >= 2)
 		{
-			upload_ok = false;
+			mModelNoErrors = false;
 		}
 
 		if (lod == mPreviewLOD)
@@ -2667,17 +2668,17 @@ 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)
+	for (U32 i = 0; mModelNoErrors && i < mModel[LLModel::LOD_PHYSICS].size(); ++i)
 	{
 		LLModel* mdl = mModel[LLModel::LOD_PHYSICS][i];
 
 		if (mdl)
 		{
-			for (U32 j = 0; upload_ok && j < mdl->mPhysics.mHull.size(); ++j)
+			for (U32 j = 0; mModelNoErrors && j < mdl->mPhysics.mHull.size(); ++j)
 			{
 				if (mdl->mPhysics.mHull[j].size() > 256)
 				{
-					upload_ok = false;
+					mModelNoErrors = false;
 				}
 			}
 		}		
@@ -2696,16 +2697,16 @@ void LLModelPreview::updateStatusMessages()
 			skinAndRigOk = false;
 		}	
 	}
-	
-	if(upload_ok && mModelLoader)
+
+	if(mModelNoErrors && mModelLoader)
 	{
 		if(!mModelLoader->areTexturesReady() && mFMP->childGetValue("upload_textures").asBoolean())
 		{
-			upload_ok = false ;
+			mModelNoErrors = false;
 		}
 	}
 
-	if (!upload_ok || errorStateFromLoader || !skinAndRigOk || has_degenerate)
+	if (!mModelNoErrors || errorStateFromLoader || !skinAndRigOk || has_degenerate)
 	{
 		mFMP->childDisable("ok_btn");
 	}
@@ -4085,7 +4086,7 @@ void LLFloaterModelPreview::toggleCalculateButton(bool visible)
 	}
 	
 	mUploadBtn->setVisible(!visible);
-	mUploadBtn->setEnabled(mHasUploadPerm && !mUploadModelUrl.empty());
+	mUploadBtn->setEnabled(isModelUploadAllowed());
 
 	if (visible)
 	{
@@ -4151,7 +4152,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)
@@ -4175,6 +4176,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)
@@ -4224,8 +4235,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 4a67c60943..86ce851ed1 100755
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -128,6 +128,8 @@ public:
 
 	/*virtual*/ void onModelUploadFailure();
 
+	bool isModelUploadAllowed();
+
 protected:
 	friend class LLModelPreview;
 	friend class LLMeshFilePicker;
@@ -342,7 +344,8 @@ private:
 	U32			mLoadState;
 	bool		mResetJoints;
 	bool		mRigParityWithScene;
-	
+	bool		mModelNoErrors;
+
 	std::map<std::string, bool> mViewOption;
 
 	//GLOD object parameters (must rebuild object if these change)
-- 
cgit v1.2.3


From 10e1bb6ea4db3fc84e3834786ab4614d48a7668d Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Thu, 16 Apr 2015 17:58:50 +0300
Subject: MAINT-5114 FIXED [Project-Importer] LOD files following new naming
 cannot be uploaded on their own.

---
 indra/llprimitive/lldaeloader.cpp | 73 +++++++++++++++++----------------------
 indra/llprimitive/lldaeloader.h   |  3 +-
 2 files changed, 34 insertions(+), 42 deletions(-)

diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
index 62801b8ed4..6c040470b7 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -69,6 +69,15 @@ std::string colladaVersion[VERSIONTYPE_COUNT+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,
@@ -1922,33 +1931,17 @@ void LLDAELoader::processElement( daeElement* element, bool& badElement, DAE* da
 						badElement = true;
 					}
 
-					std::string label = getElementLabel(instance_geo);
-                   
-                    llassert(!label.empty());
+					std::string label = getLodlessLabel(instance_geo);
+
+					llassert(!label.empty());
 
-                    if (model->mSubmodelID)
+					if (model->mSubmodelID)
 					{
-						// CHECK FOR _LODX and _PHYS here to ensure we bolt the submodel 'salt' at the right loc
-						//
-						std::string labelStart;
-						std::string markup;
-						size_t label_offset = getSuffixPosition(label);
-						if (label_offset != -1)
-						{
-							markup = label.substr(label_offset + 1, 4);
-							label.erase(label.begin() + label_offset, label.end());
-							label +=(char)((int)'a' + model->mSubmodelID);
-							label += "_";
-							label += markup;
-						}
-						else
-						{
-							label +=(char)((int)'a' + model->mSubmodelID);
-						}
-                    }
+						label +=(char)((int)'a' + model->mSubmodelID);
+					}
+
+					model->mLabel = label + lod_suffix[mLod];
 
-                    model->mLabel = label;
-                   
 					mScene[transformation].push_back(LLModelInstance(model, label, transformation, materials));
 					stretch_extents(model, transformation, mExtents[0], mExtents[1], mFirstTransform);
 					i++;
@@ -2209,6 +2202,18 @@ size_t LLDAELoader::getSuffixPosition(std::string label)
 	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;
@@ -2298,25 +2303,11 @@ bool LLDAELoader::loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& mo
 
 	LLModel* ret = new LLModel(volume_params, 0.f);
 
-	std::string model_name = getElementLabel(mesh);
-	ret->mLabel = model_name;
+	std::string model_name = getLodlessLabel(mesh);
+	ret->mLabel = model_name + lod_suffix[mLod];
 
 	llassert(!ret->mLabel.empty());
 
-	// extract actual name and suffix for future use in submodels
-	std::string name_base, name_suffix;
-	size_t ext_pos = getSuffixPosition(model_name);
-
-	if (ext_pos == -1)
-	{
-		name_base = model_name;
-	}
-	else
-	{
-		name_base = model_name.substr(0, ext_pos);
-		name_suffix = model_name.substr(ext_pos, model_name.length() - ext_pos);
-	}
-
 	// Like a monkey, ready to be shot into space
 	//
 	ret->ClearFacesAndMaterials();
@@ -2372,7 +2363,7 @@ bool LLDAELoader::loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& mo
 		{
 			LLModel* next = new LLModel(volume_params, 0.f);
 			next->mSubmodelID = ++submodelID;
-			next->mLabel = name_base + (char)((int)'a' + next->mSubmodelID) + name_suffix;
+			next->mLabel = model_name + (char)((int)'a' + next->mSubmodelID) + lod_suffix[mLod];
 			next->getVolumeFaces() = remainder;
 			next->mNormalizedScale = ret->mNormalizedScale;
 			next->mNormalizedTranslation = ret->mNormalizedTranslation;
diff --git a/indra/llprimitive/lldaeloader.h b/indra/llprimitive/lldaeloader.h
index e31b023d22..7d91a6063b 100644
--- a/indra/llprimitive/lldaeloader.h
+++ b/indra/llprimitive/lldaeloader.h
@@ -96,8 +96,9 @@ protected:
 	//
 	bool loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& models_out, U32 submodel_limit);
 
-	static std::string getElementLabel(daeElement *element);	
+	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
-- 
cgit v1.2.3


From 2c178d3fb00ccabc1d49289f820a0669fb62b03f Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Wed, 22 Apr 2015 16:54:58 +0300
Subject: MAINT-5115 [Project-Importer] Materials with > 21844 triangles cause
 chaos in models with > 8 materials.

---
 indra/newview/llfloatermodelpreview.cpp | 39 ++++++++++++++++++++++++++++-----
 indra/newview/llfloatermodelpreview.h   |  2 ++
 2 files changed, 36 insertions(+), 5 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index d4282c4a48..78794d3942 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -1822,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();
@@ -2381,6 +2384,7 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim
             }
 
             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);
@@ -2644,7 +2648,10 @@ void LLModelPreview::updateStatusMessages()
 
 	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;
 
@@ -2657,7 +2664,7 @@ void LLModelPreview::updateStatusMessages()
 		}
 		else
 		{
-			if (lod == LLModel::LOD_HIGH)
+			if (lod == lod_high)
 			{
 				upload_status[lod] = 2;
 				message = "mesh_status_missing_lod";
@@ -2678,8 +2685,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])
@@ -2687,6 +2692,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";
@@ -3263,6 +3275,23 @@ void LLModelPreview::createPreviewAvatar( void )
 	}
 }
 
+//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,
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index 2d0d42f26f..e101a6f21e 100755
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -318,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;
-- 
cgit v1.2.3


From 1382e9bae647d6b548cd9a1fc78339e5929ea202 Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Thu, 23 Apr 2015 16:28:15 +0300
Subject: MAINT-5112 Separating legacy matching method

---
 indra/newview/app_settings/settings.xml |  11 ++
 indra/newview/llfloatermodelpreview.cpp | 257 +++++++++++++++-----------------
 2 files changed, 129 insertions(+), 139 deletions(-)

diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index e2ccb89a29..f926720c51 100755
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -13,6 +13,17 @@
     <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>
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 78794d3942..eacc8d87e3 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -1372,6 +1372,7 @@ void LLModelPreview::rebuildUploadData()
 	F32 max_scale = 0.f;
 
 	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
@@ -1404,155 +1405,133 @@ void LLModelPreview::rebuildUploadData()
 				base_model->mMetric = metric;
 			}
 
-            for (int i = LLModel::NUM_LODS - 1; i >= LLModel::LOD_IMPOSTOR; i--)
-			{ // 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.
+			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.
 
-                LLModel* lod_model = NULL;
-                LLMatrix4 transform;
+					LLMatrix4 transform;
 
-                std::string name_to_match = instance.mLabel;
-                llassert(!name_to_match.empty());
+					std::string name_to_match = instance.mLabel;
+					llassert(!name_to_match.empty());
 
-                std::string toAdd;
-                switch (i)
-                {
-                    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;
-                }
+					std::string toAdd;
+					switch (i)
+					{
+					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;
-                }
+					if (name_to_match.find(toAdd) == -1)
+					{
+						name_to_match += toAdd;
+					}
 
-                FindModel(mScene[i], name_to_match, lod_model, transform);
+					FindModel(mScene[i], name_to_match, lod_model, transform);
 
-                // Fall back to old method of index-based association if
-                // we could not find a match based on the mesh names
-                //
-                if (lod_model)
-                {
-                    
-                    if (i == LLModel::LOD_PHYSICS)
-                    {
-                        if (importerDebug)
-                        {
-                            LL_INFOS() << "Assigning collision for " << instance.mLabel << " to match " << lod_model->mLabel << LL_ENDL;
-                        }
-                        instance.mLOD[i] = lod_model;
-                    }
-                    else
-                    {
-                        if (importerDebug)
-                        {
-                            LL_INFOS() << "Assigning LOD" << i << " for " << instance.mLabel << " to found match " << lod_model->mLabel << LL_ENDL;
-                        }
-                        instance.mLOD[i] = lod_model;
-                    }
-                }
-                else
-                {
+					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;
+						}
 
-                    if  (i != LLModel::LOD_PHYSICS)
-                    {
-                        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++;
-                        }
-                    }
+						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());
 
-                    // Fall back to old method of index-based association if
-                    // we could not find a match based on the mesh names at all.
-                    //
-                    if (lod_model)
-                    {
-                        if (importerDebug)
-                        {
-                            LL_INFOS() << "Falling back LOD" << i << " for " << instance.mLabel << " to found " << lod_model->mLabel << LL_ENDL;
-                        }
-                        instance.mLOD[i] = lod_model;
-                    }
-                    else
-                    {
-                        S32 idx = 0;
-			            for (idx = 0; idx < mBaseModel.size(); ++idx)
-			            {  //find reference instance for this model
-				            if (mBaseModel[idx] == base_model)
-				            {
-                                if (importerDebug)
-                                {
-                                    LL_INFOS() << "Falling back to 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];
-						    instance.mLOD[i] = lod_model;
-                            if (i == LLModel::LOD_PHYSICS)
-                            {
-                                if (importerDebug)
-                                {
-                                    LL_INFOS() << "Indexed fallback to model index " << idx << ": LOD " << i << " named " << lod_model->mLabel << " for collision for " << instance.mLabel <<  LL_ENDL;
-                                }
-                            }
-                            else
-                            {
-                                if (importerDebug)
-                                {
-                                    LL_INFOS() << "Indexed fallback to model index " << idx << " LOD " << i << " named " << lod_model->mLabel << " for LOD " << i << " for " << instance.mLabel << LL_ENDL;
-                                }
-                            }
-					    }
-                        else
-                        {
-                            if (importerDebug)
-                            {
-                                LL_INFOS() << "List of models for LOD " << i << " did not include index " << idx <<  LL_ENDL;
-                            }
-                        }
-                    }
-                }
+							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)
+			LLModel* high_lod_model = instance.mLOD[LLModel::LOD_HIGH];
+			if (!high_lod_model)
 			{
 				setLoadState( LLModelLoader::ERROR_MATERIALS );
 				mFMP->childDisable( "calculate_btn" );
-- 
cgit v1.2.3


From 0faa19e03d27c9acc8b4612d4ee14a343e9240c7 Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Tue, 5 May 2015 17:38:01 +0300
Subject: Linux build fix

---
 indra/llprimitive/llmodelloader.cpp     | 2 +-
 indra/newview/llfloatermodelpreview.cpp | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/indra/llprimitive/llmodelloader.cpp b/indra/llprimitive/llmodelloader.cpp
index 91198507c6..81d92d151b 100644
--- a/indra/llprimitive/llmodelloader.cpp
+++ b/indra/llprimitive/llmodelloader.cpp
@@ -307,7 +307,7 @@ bool LLModelLoader::loadFromSLM(const std::string& filename)
 
 	S32 file_size = (S32) stat.st_size;
 	
-	llifstream ifstream(filename, std::ifstream::in | std::ifstream::binary);
+	llifstream ifstream(filename.c_str(), std::ifstream::in | std::ifstream::binary);
 	LLSD data;
 	LLSDSerialize::fromBinary(data, ifstream, file_size);
 	ifstream.close();
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index eacc8d87e3..f27da87636 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -1670,7 +1670,7 @@ void LLModelPreview::saveUploadData(const std::string& filename, bool save_skinw
 		data["instance"][i] = instance.asLLSD();
 	}
 
-	llofstream out(filename, std::ios_base::out | std::ios_base::binary);
+	llofstream out(filename.c_str(), std::ios_base::out | std::ios_base::binary);
 	LLSDSerialize::toBinary(data, out);
 	out.flush();
 	out.close();
-- 
cgit v1.2.3


From 4f236dd7e8d28a6a473344b1aa527ba4e37c8881 Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Tue, 5 May 2015 19:30:28 +0300
Subject: Cleaning out unused variables.

---
 indra/newview/llfloatermodelpreview.cpp | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index f27da87636..d13db542d7 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -95,12 +95,6 @@ LLFloaterModelPreview* LLFloaterModelPreview::sInstance = NULL;
 
 bool LLModelPreview::sIgnoreLoadedCallback = false;
 
-const S32 PREVIEW_BORDER_WIDTH = 2;
-const S32 PREVIEW_RESIZE_HANDLE_SIZE = S32(RESIZE_HANDLE_WIDTH * OO_SQRT2) + PREVIEW_BORDER_WIDTH;
-const S32 PREVIEW_HPAD = PREVIEW_RESIZE_HANDLE_SIZE;
-const S32 PREF_BUTTON_HEIGHT = 16 + 7 + 16;
-const S32 PREVIEW_TEXTURE_HEIGHT = 300;
-
 // "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
 // should be represented by Retain spinner with values from 1 to 100 by 1.
-- 
cgit v1.2.3


From 9e2eca1a8c64bcc90f71870b034bccdd1fdd57ab Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Thu, 14 May 2015 19:11:53 +0300
Subject: MAINT-5216 FIXED Error appears when trying to upload the model
 "instance test.dae"

---
 indra/llprimitive/lldaeloader.cpp | 30 ++++++++++++++++++++++++------
 1 file changed, 24 insertions(+), 6 deletions(-)

diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
index 6c040470b7..1cb80a5769 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -1931,17 +1931,35 @@ void LLDAELoader::processElement( daeElement* element, bool& badElement, DAE* da
 						badElement = true;
 					}
 
-					std::string label = getLodlessLabel(instance_geo);
+					std::string label;
+					
+					if (model->mLabel.empty())
+					{
+						label = getLodlessLabel(instance_geo);
 
-					llassert(!label.empty());
+						llassert(!label.empty());
 
-					if (model->mSubmodelID)
+						if (model->mSubmodelID)
+						{
+							label += (char)((int)'a' + model->mSubmodelID);
+						}
+
+						model->mLabel = label + lod_suffix[mLod];
+					}
+					else
 					{
-						label +=(char)((int)'a' + model->mSubmodelID);
+						// 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;
+						}
 					}
 
-					model->mLabel = label + lod_suffix[mLod];
-
 					mScene[transformation].push_back(LLModelInstance(model, label, transformation, materials));
 					stretch_extents(model, transformation, mExtents[0], mExtents[1], mFirstTransform);
 					i++;
-- 
cgit v1.2.3


From 695ff76cb79598fd8e3402f797924f265ebbda1e Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Mon, 18 May 2015 17:55:34 +0300
Subject: MAINT-5214 FIXED physics layer isn't shown in the Preview Window
 while uploading model

---
 indra/newview/llfloatermodelpreview.cpp | 20 +++++++++++++++++++-
 indra/newview/llfloatermodelpreview.h   |  1 +
 2 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index d13db542d7..2e3e3aa239 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -457,6 +457,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();
 }
@@ -1167,6 +1172,7 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)
 , mPelvisZOffset( 0.0f )
 , mLegacyRigValid( false )
 , mRigValidJointUpload( false )
+, mPhysicsSearchLOD( LLModel::LOD_PHYSICS )
 , mResetJoints( false )
 , mModelNoErrors( true )
 , mRigParityWithScene( false )
@@ -1416,8 +1422,19 @@ void LLModelPreview::rebuildUploadData()
 					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 (i)
+					switch (extensionLOD)
 					{
 					case LLModel::LOD_IMPOSTOR: toAdd = "_LOD0"; break;
 					case LLModel::LOD_LOW:      toAdd = "_LOD1"; break;
@@ -1776,6 +1793,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();
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index e101a6f21e..7a518c798b 100755
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -341,6 +341,7 @@ private:
 	LLVector3	mPreviewTarget;
 	LLVector3	mPreviewScale;
 	S32			mPreviewLOD;
+	S32			mPhysicsSearchLOD;
 	U32			mResourceCost;
 	std::string mLODFile[LLModel::NUM_LODS];
 	bool		mLoading;
-- 
cgit v1.2.3


From 1a8016c03f73e1d1a1d440791afa8c486b3271f2 Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Tue, 19 May 2015 15:56:23 +0300
Subject: MAINT-5215 FIXED Mesh importer model name input should limit to 63
 characters

---
 indra/newview/skins/default/xui/en/floater_model_preview.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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 a4acd1df78..eb9244b537 100755
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -66,7 +66,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"
-- 
cgit v1.2.3


From b90b1ccca0fe5c88fdfed36647ea2a7e3f002fb0 Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Fri, 22 May 2015 18:15:24 +0300
Subject: MAINT-5209 FIXED Cannot upload some models with high level of detail

---
 indra/newview/llfloatermodelpreview.cpp            | 37 ++++++++++++++++------
 .../skins/default/xui/en/floater_model_preview.xml | 36 ++++++++++++++++++---
 2 files changed, 60 insertions(+), 13 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 2e3e3aa239..aa2c37055f 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -2736,26 +2736,41 @@ void LLModelPreview::updateStatusMessages()
 	}
 
 
-	//make sure no hulls have more than 256 points in them
+	//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];
 
 		if (mdl)
 		{
-			for (U32 j = 0; mModelNoErrors && j < mdl->mPhysics.mHull.size(); ++j)
+			for (U32 j = 0; j < mdl->mPhysics.mHull.size(); ++j)
 			{
 				if (mdl->mPhysics.mHull[j].size() > 256)
 				{
-					mModelNoErrors = false;
+					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();
 
@@ -2763,19 +2778,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(mModelNoErrors && mModelLoader)
 	{
 		if(!mModelLoader->areTexturesReady() && mFMP->childGetValue("upload_textures").asBoolean())
 		{
+			// Some textures are still loading, prevent upload until they are done
 			mModelNoErrors = false;
 		}
 	}
 
-	if (!mModelNoErrors || 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");
 	}
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 eb9244b537..7183b2f1f9 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>
@@ -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
-- 
cgit v1.2.3


From 8a6bfb0e2213151341a25dfc4b411c2718fc64a6 Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Fri, 7 Aug 2015 17:42:07 +0300
Subject: Merge fix - clearing duplicates

---
 indra/newview/CMakeLists.txt            |  3 ---
 indra/newview/app_settings/settings.xml | 11 -----------
 2 files changed, 14 deletions(-)

diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index e8e9e3e897..16877c345e 100755
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -1018,9 +1018,6 @@ set(viewer_HEADER_FILES
     llnameeditor.h
     llnamelistctrl.h
     llnavigationbar.h
-    llfloaterimnearbychat.h
-    llfloaterimnearbychathandler.h
-    llfloaterimnearbychatlistener.h
     llnetmap.h
     llnotificationhandler.h
     llnotificationmanager.h
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 477656c04e..4070aaa4e4 100755
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -34,17 +34,6 @@
     <string>U32</string>
     <key>Value</key>
     <integer>768</integer>
-  </map>
-  <key>IMShowTime</key>
-  <map>
-    <key>Comment</key>
-    <string>Enable(disable) timestamp showing in the chat.</string>
-    <key>Persist</key>
-    <integer>1</integer>
-    <key>Type</key>
-    <string>Boolean</string>
-    <key>Value</key>
-    <integer>1</integer>
   </map>
     <key>IMShowTime</key>
     <map>
-- 
cgit v1.2.3


From 003156a89d9edf9bdecdefccdb27d40f73bc979d Mon Sep 17 00:00:00 2001
From: "Brad Payne (Vir Linden)" <vir@lindenlab.com>
Date: Tue, 11 Aug 2015 12:09:38 -0400
Subject: SL-133 WIP - possible fix for unknown joints warping to origin

---
 indra/newview/lldrawpoolavatar.cpp | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp
index e1d3d1a905..0bbf7db46e 100755
--- a/indra/newview/lldrawpoolavatar.cpp
+++ b/indra/newview/lldrawpoolavatar.cpp
@@ -1600,6 +1600,14 @@ void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(LLVOAvatar* avatar, LLFace*
 		for (U32 j = 0; j < count; ++j)
 		{
 			LLJoint* joint = avatar->getJoint(skin->mJointNames[j]);
+			if (!joint)
+			{
+				joint = avatar->getJoint("mPelvis");
+			}
+			if (!joint)
+			{
+				LL_DEBUGS("Avatar") << "Failed to find " << skin->mJointNames[j] << LL_ENDL;
+			}
 			if (joint)
 			{
 				mat[j] = skin->mInvBindMatrix[j];
-- 
cgit v1.2.3


From d0eed685023fca913e643d3e90e97e5dc6914a99 Mon Sep 17 00:00:00 2001
From: "Brad Payne (Vir Linden)" <vir@lindenlab.com>
Date: Tue, 11 Aug 2015 15:53:00 -0400
Subject: SL-134 WIP - Possible fix for octree corruption/crash when mesh has
 unknown joints.

---
 indra/newview/llvovolume.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index 0432f6f27c..267061b83d 100755
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -4172,6 +4172,10 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons
 	for (U32 j = 0; j < maxJoints; ++j)
 	{
 		LLJoint* joint = avatar->getJoint(skin->mJointNames[j]);
+        if (!joint)
+        {
+            joint = avatar->getJoint("mPelvis");
+        }
 		if (joint)
 		{
 			mat[j] = skin->mInvBindMatrix[j];
-- 
cgit v1.2.3


From bb7ac16318cb51c7a0863ad349fbd0511f199d21 Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Thu, 20 Aug 2015 20:25:58 +0300
Subject: MAINT-5532 FIXED upload of duck.dae from MAINT-4483 crashes mac
 viewer

---
 indra/llmath/llvolume.cpp | 25 ++++++++++++++-----------
 1 file changed, 14 insertions(+), 11 deletions(-)

diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
index c2198b91a7..4703e6cf1a 100755
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -6072,7 +6072,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;
@@ -6088,18 +6088,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;
-- 
cgit v1.2.3


From 067790ffa59afdce183f597070c4b0535708b852 Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Fri, 21 Aug 2015 19:38:24 +0300
Subject: MAINT-5536 FIXED Mac viewer crash when started from a web browser
 handler

---
 indra/newview/llcommandhandler.cpp | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/indra/newview/llcommandhandler.cpp b/indra/newview/llcommandhandler.cpp
index 19dba3f917..5ea7efc045 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;
-- 
cgit v1.2.3


From 1d22db9ed806e534ce025ca7d9cd07ee16b88e08 Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Wed, 26 Aug 2015 21:00:57 +0300
Subject: MAINT-5532 related crash fix and some log improvements

---
 indra/llprimitive/lldaeloader.cpp       | 10 +++++++---
 indra/newview/llfloatermodelpreview.cpp |  2 +-
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
index 24aed7b01b..c1b74b1fd7 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -164,12 +164,12 @@ LLModel::EModelStatus load_face_from_dom_triangles(std::vector<LLVolumeFace>& fa
 
 	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 )
+	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)
+	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;
@@ -185,7 +185,7 @@ LLModel::EModelStatus load_face_from_dom_triangles(std::vector<LLVolumeFace>& fa
 
 	if (pos_source)
 	{
-		if(!pos_source->getFloat_array() || (v.getCount() == 0))
+		if(v.getCount() == 0)
 		{
 			return LLModel::BAD_ELEMENT;
 		}
@@ -898,6 +898,7 @@ bool LLDAELoader::OpenFile(const std::string& filename)
 		result = verifyController( pController );
 		if (!result)
 		{
+			LL_INFOS() << "Could not verify controller" << LL_ENDL;
 			setLoadState( ERROR_PARSING );
 			return true;
 		}
@@ -1024,6 +1025,8 @@ bool LLDAELoader::OpenFile(const std::string& filename)
 		}
 	}
 
+	LL_INFOS()<< "Collada skins processed: " << count <<LL_ENDL;
+
 	daeElement* scene = root->getDescendant("visual_scene");
 	
 	if (!scene)
@@ -1041,6 +1044,7 @@ bool LLDAELoader::OpenFile(const std::string& filename)
 	
 	if ( badElement )
 	{
+		LL_INFOS()<<"Scene could not be parsed"<<LL_ENDL;
 		setLoadState( ERROR_PARSING );
 	}
 	
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index c77a4228f9..20ba7548e5 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -3597,7 +3597,7 @@ BOOL LLModelPreview::render()
 			}
 			else
 			{
-				LL_INFOS(" ") << "Vertex Buffer[" << mPreviewLOD << "]" << " is EMPTY!!!" << LL_ENDL;
+				LL_INFOS() << "Vertex Buffer[" << mPreviewLOD << "]" << " is EMPTY!!!" << LL_ENDL;
 				regen = TRUE;
 			}
 		}
-- 
cgit v1.2.3


From 9ce8f7bfb425bc71942a654447fdddd430a3d250 Mon Sep 17 00:00:00 2001
From: AndreyL ProductEngine <andreylproductengine@lindenlab.com>
Date: Mon, 7 Sep 2015 02:59:36 +0300
Subject: MAINT-5602 FIXED Viewer crashes when loading physics from file

--HG--
branch : develop
---
 indra/newview/llfloatermodelpreview.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 20ba7548e5..a2a1dfbdb8 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -3768,7 +3768,7 @@ BOOL LLModelPreview::render()
 									genBuffers(LLModel::LOD_PHYSICS, false);
 								}
 
-								U32 num_models = mVertexBuffer[mPreviewLOD][model].size();
+								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];
-- 
cgit v1.2.3

-- 
cgit v1.2.3


From 0eba1287413f2e6362ca5db10aa14bef02f506ff Mon Sep 17 00:00:00 2001
From: AndreyL ProductEngine <andreylproductengine@lindenlab.com>
Date: Fri, 11 Sep 2015 00:15:53 +0300
Subject: MAINT-5601 FIXED Physics model not previewed, physics model not
 applied to mesh on upload

---
 indra/newview/llfloatermodelpreview.cpp | 40 +++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index a2a1dfbdb8..b74f10f5cb 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -1986,6 +1986,46 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)
 			mBaseScene = mScene[loaded_lod];
 			mVertexBuffer[5].clear();
 		}
+		else
+		{
+			BOOL importerDebug = gSavedSettings.getBOOL("ImporterDebug");
+			BOOL legacyMatching = gSavedSettings.getBOOL("ImporterLegacyMatching");
+			if (!legacyMatching)
+			{
+				if (!mBaseModel.empty())
+				{ // replace the name of the model loaded for any non-HIGH LOD to match the others (MAINT-5601)
+					for (U32 idx = 0; idx < mModel[loaded_lod].size() && idx < mBaseModel.size(); ++idx)
+					{
+						std::string name = mBaseModel[idx]->mLabel;
+						std::string loaded_name = mModel[loaded_lod][idx]->mLabel; 
+
+						if ((loaded_name.find("_LOD") != -1) || (loaded_name.find("_PHYS") != -1))
+						{ // base model is LOD_HIGH so its name has no suffix, stripping loaded LOD name to match it
+							loaded_name = loaded_name.substr(0, loaded_name.rfind('_'));
+						}
+
+						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_INFOS() << "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(loaded_lod);
 
-- 
cgit v1.2.3


From 182f799281f1f85453aed08c845c4650df79de66 Mon Sep 17 00:00:00 2001
From: AndreyL ProductEngine <andreylproductengine@lindenlab.com>
Date: Tue, 15 Sep 2015 23:43:43 +0300
Subject: MAINT-5601 Physics model not previewed, physics model not applied to
 mesh on upload Some improvements: renaming to act like "legacy matching"
 would apply only to the models simple enough for this

---
 indra/newview/llfloatermodelpreview.cpp | 88 +++++++++++++++++++++++++--------
 1 file changed, 67 insertions(+), 21 deletions(-)

diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index b74f10f5cb..918dc98a3a 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -202,6 +202,15 @@ LLViewerFetchedTexture* bindMaterialDiffuseTexture(const LLImportMaterial& mater
 	return NULL;
 }
 
+std::string stripSuffix(std::string name)
+{
+	if ((name.find("_LOD") != -1) || (name.find("_PHYS") != -1))
+	{
+		return name.substr(0, name.rfind('_'));
+	}
+	return name;
+}
+
 LLMeshFilePicker::LLMeshFilePicker(LLModelPreview* mp, S32 lod)
 : LLFilePickerThread(LLFilePicker::FFLOAD_COLLADA)
 	{
@@ -1993,34 +2002,71 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)
 			if (!legacyMatching)
 			{
 				if (!mBaseModel.empty())
-				{ // replace the name of the model loaded for any non-HIGH LOD to match the others (MAINT-5601)
-					for (U32 idx = 0; idx < mModel[loaded_lod].size() && idx < mBaseModel.size(); ++idx)
+				{ 
+					BOOL name_based = FALSE;
+					BOOL has_submodels = FALSE;
+					for (U32 idx = 0; idx < mBaseModel.size(); ++idx)
 					{
-						std::string name = mBaseModel[idx]->mLabel;
-						std::string loaded_name = mModel[loaded_lod][idx]->mLabel; 
+						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;
+						}
+					}
 
-						if ((loaded_name.find("_LOD") != -1) || (loaded_name.find("_PHYS") != -1))
-						{ // base model is LOD_HIGH so its name has no suffix, stripping loaded LOD name to match it
-							loaded_name = loaded_name.substr(0, loaded_name.rfind('_'));
+					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 (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 (mModel[loaded_lod][idx]->mSubmodelID)
+						{ // don't rename the models when loaded LOD model has submodels
+							has_submodels = TRUE;
+						}
+					}
 
-							if (importerDebug)
+					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)
 							{
-								LL_INFOS() << "Loded model name " << mModel[loaded_lod][idx]->mLabel << " for LOD " << loaded_lod << " doesn't match the base model. Renaming to " << name << LL_ENDL;
-							}
+								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;
+								}
 
-							mModel[loaded_lod][idx]->mLabel = name;
+								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;
+							}
 						}
 					}
 				}
-- 
cgit v1.2.3


From 5c01e7afa8835d8675807a7bee96f0d8d19a52f7 Mon Sep 17 00:00:00 2001
From: Oz Linden <oz@lindenlab.com>
Date: Tue, 29 Sep 2015 15:18:46 -0400
Subject: Added tag 3.8.4-release for changeset 27e3cf444c4c

---
 .hgtags | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.hgtags b/.hgtags
index f05b8bd17a..0cc10b0100 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
-- 
cgit v1.2.3


From 2d52b62e67cf46222396c724d00d2de874819197 Mon Sep 17 00:00:00 2001
From: Oz Linden <oz@lindenlab.com>
Date: Tue, 29 Sep 2015 15:18:47 -0400
Subject: increment viewer version to 3.8.5

---
 indra/newview/VIEWER_VERSION.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt
index ff313b8c21..0cbfaed0d9 100644
--- a/indra/newview/VIEWER_VERSION.txt
+++ b/indra/newview/VIEWER_VERSION.txt
@@ -1 +1 @@
-3.8.4
+3.8.5
-- 
cgit v1.2.3