diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
index c4be1763535aa8fcccadb86fcac35e1ff4adcdd2..7a2f06da8facdb86770ccd82ac8178d325ed388d 100644
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -2162,7 +2162,7 @@ BOOL LLVolume::createVolumeFacesFromStream(std::istream& is)
 
 	LLSD header;
 	{
-		if (!LLSDSerialize::deserialize(header, is, 1024*1024*1024))
+		if (!LLSDSerialize::fromBinary(header, is, 1024*1024*1024))
 		{
 			llwarns << "Mesh header parse error.  Not a valid mesh asset!" << llendl;
 			return FALSE;
@@ -2174,34 +2174,18 @@ BOOL LLVolume::createVolumeFacesFromStream(std::istream& is)
 		"lowest_lod",
 		"low_lod",
 		"medium_lod",
-		"high_lod"
+		"high_lod",
+		"physics_shape",
 	};
 
-	S32 lod = llclamp((S32) mDetail, 0, 3);
+	const S32 MODEL_LODS = 5;
 
-	while (lod < 4 && 
-		(header[nm[lod]]["offset"].asInteger() == -1 || 
-		header[nm[lod]]["size"].asInteger() == 0 ))
-	{
-		++lod;
-	}
-
-	if (lod >= 4)
-	{
-		lod = llclamp((S32) mDetail, 0, 3);
+	S32 lod = llclamp((S32) mDetail, 0, MODEL_LODS);
 
-		while (lod >= 0 && 
-				(header[nm[lod]]["offset"].asInteger() == -1 ||
-				header[nm[lod]]["size"].asInteger() == 0) )
-		{
-			--lod;
-		}
-
-		if (lod < 0)
-		{
-			llwarns << "Mesh header missing LOD offsets.  Not a valid mesh asset!" << llendl;
-			return FALSE;
-		}
+	if (header[nm[lod]]["offset"].asInteger() == -1 || 
+		header[nm[lod]]["size"].asInteger() == 0 )
+	{ //cannot load requested LOD
+		return FALSE;
 	}
 
 	is.seekg(header[nm[lod]]["offset"].asInteger(), std::ios_base::cur);
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index e27397ab1afdb3e1ee6ac44ab56ca0e02e746caf..c11de57c42b473390a4a7249393baf8c1d3429c8 100755
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -5523,6 +5523,17 @@
     <key>Value</key>
     <real>1</real>
   </map>
+  <key>MeshImportUseSLM</key>
+  <map>
+    <key>Comment</key>
+    <string>Use cached copy of last upload for a dae if available instead of loading dae file from scratch.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Boolean</string>
+    <key>Value</key>
+    <real>0</real>
+  </map>
   <key>MigrateCacheDirectory</key>
     <map>
       <key>Comment</key>
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 04dfcac20b106039e570a3ddbf0496eeb9bfbca3..6145c6c16d85d637204b616c9e8cae68864d40d2 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -82,6 +82,7 @@
 #include "llvoavatarself.h"
 #include "pipeline.h"
 #include "lluictrlfactory.h"
+#include "llviewercontrol.h"
 #include "llviewermenu.h"
 #include "llviewermenufile.h"
 #include "llviewerregion.h"
@@ -1088,8 +1089,15 @@ LLModelLoader::LLModelLoader(std::string filename, S32 lod, LLModelPreview* prev
 
 	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->mBaseModel.empty();
 		mPreview->setLoadState(STARTING);
 	}
+	else
+	{
+		mTrySLM = false;
+	}
 }
 
 void stretch_extents(LLModel* model, LLMatrix4a& mat, LLVector4a& min, LLVector4a& max, BOOL& first_transform)
@@ -1167,6 +1175,38 @@ void LLModelLoader::run()
 
 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);
 	
@@ -1744,6 +1784,91 @@ void LLModelLoader::setLoadState(U32 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];
+
+	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->createVolumeFacesFromStream(str))
+			{
+				loaded_model->mLocalID = i;
+				model[lod].push_back(loaded_model);
+			}
+			else
+			{
+				llassert(model[lod].empty());
+			}
+		}
+	}	
+
+	if (model[LLModel::LOD_HIGH].empty())
+	{ //failed to load high lod
+		return false;
+	}
+
+	//load instance list
+	model_instance_list instance_list;
+
+	LLSD& instance = data["instance"];
+
+	for (U32 i = 0; i < instance.size(); ++i)
+	{
+		//deserialize instance list
+		instance_list.push_back(LLModelInstance(instance[i]));
+
+		//match up model instance pointers
+		S32 idx = instance_list[i].mLocalMeshID;
+
+		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);
+	}
+
+	return true;
+}
+
 void LLModelLoader::loadModelCallback()
 {
 	if (mPreview)
@@ -2614,6 +2739,7 @@ void LLModelPreview::saveUploadData(const std::string& filename, bool save_skinw
 				decomp, 
 				empty_hull, save_skinweights, save_joint_positions);
 
+			
 			data["mesh"][instance.mModel->mLocalID] = str.str();
 		}
 
@@ -2626,8 +2752,6 @@ void LLModelPreview::saveUploadData(const std::string& filename, bool save_skinw
 	out.close();
 }
 
-
-
 void LLModelPreview::clearModel(S32 lod)
 {
 	if (lod < 0 || lod > LLModel::LOD_PHYSICS)
@@ -2769,39 +2893,93 @@ void LLModelPreview::loadModelCallback(S32 lod)
 	}
 
 	mModelLoader->loadTextures() ;
-	mModel[lod] = mModelLoader->mModelList;
-	mScene[lod] = mModelLoader->mScene;
-	mVertexBuffer[lod].clear();
 
-	if (lod == LLModel::LOD_PHYSICS)
-	{
-		mPhysicsMesh.clear();
-	}
+	if (lod == -1)
+	{ //populate all LoDs from model loader scene
+		mBaseModel.clear();
+		mBaseScene.clear();
 
-	setPreviewLOD(lod);
+		for (S32 lod = 0; lod < LLModel::NUM_LODS; ++lod)
+		{ //for each LoD
 
+			//clear scene and model info
+			mScene[lod].clear();
+			mModel[lod].clear();
+			mVertexBuffer[lod].clear();
+			
+			if (mModelLoader->mScene.begin()->second[0].mLOD[lod].notNull())
+			{ //if this LoD exists in the loaded scene
 
-	if (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;
+				//copy scene to current LoD
+				mScene[lod] = mModelLoader->mScene;
+			
+				//touch up copied scene to look like current LoD
+				for (LLModelLoader::scene::iterator iter = mScene[lod].begin(); iter != mScene[lod].end(); ++iter)
+				{
+					LLModelLoader::model_instance_list& list = iter->second;
+
+					for (LLModelLoader::model_instance_list::iterator list_iter = list.begin(); list_iter != list.end(); ++list_iter)
+					{	
+						//override displayed model with current LoD
+						list_iter->mModel = list_iter->mLOD[lod];
+
+						//add current model to current LoD's model list (LLModel::mLocalID makes a good vector index)
+						S32 idx = list_iter->mModel->mLocalID;
+
+						if (mModel[lod].size() <= idx)
+						{ //stretch model list to fit model at given index
+							mModel[lod].resize(idx+1);
+						}
+
+						mModel[lod][idx] = list_iter->mModel;	
+					}
+				}
+			}
 		}
 
-		mBaseModel = mModel[lod];
-		clearGLODGroup();
+		//copy high lod to base scene for LoD generation
+		mBaseScene = mScene[LLModel::LOD_HIGH];
+		mBaseModel = mModel[LLModel::LOD_HIGH];
 
-		mBaseScene = mScene[lod];
-		mVertexBuffer[5].clear();
+		mDirty = true;
+		resetPreviewTarget();
 	}
+	else
+	{ //only replace given LoD
+		mModel[lod] = mModelLoader->mModelList;
+		mScene[lod] = mModelLoader->mScene;
+		mVertexBuffer[lod].clear();
 
-	clearIncompatible(lod);
+		if (lod == LLModel::LOD_PHYSICS)
+		{
+			mPhysicsMesh.clear();
+		}
 
-	mDirty = true;
+		setPreviewLOD(lod);
 
-	if (lod == LLModel::LOD_HIGH)
-	{
-		resetPreviewTarget();
+
+		if (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];
+			clearGLODGroup();
+
+			mBaseScene = mScene[lod];
+			mVertexBuffer[5].clear();
+		}
+
+		clearIncompatible(lod);
+
+		mDirty = true;
+
+		if (lod == LLModel::LOD_HIGH)
+		{
+			resetPreviewTarget();
+		}
 	}
 
 	mLoading = false;
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index 5efac91ba335cc0ab19fccc162ad31a67f68799d..68fa0fa4c11dc172a0e677d494c54eee56766bf3 100644
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -76,6 +76,7 @@ class LLModelLoader : public LLThread
 	LLMatrix4 mTransform;
 	BOOL mFirstTransform;
 	LLVector3 mExtents[2];
+	bool mTrySLM;
 
 	std::map<daeElement*, LLPointer<LLModel> > mModel;
 	
@@ -97,6 +98,7 @@ class LLModelLoader : public LLThread
 
 	virtual void run();
 	bool doLoadModel();
+	bool loadFromSLM(const std::string& filename);
 	void loadModelCallback();
 
 	void loadTextures() ; //called in the main thread.
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 47dc343f5e4eeef1f0f3ddfc916b32a281afefd1..a227627bc100fea4507be3d898e343466d8ed9fa 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -3584,6 +3584,19 @@ 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)
+	{
+		mMaterial.push_back(LLImportMaterial(data["material"][i]));
+	}
+}
+
+
 LLSD LLModelInstance::asLLSD()
 {	
 	LLSD ret;
@@ -3600,6 +3613,15 @@ LLSD LLModelInstance::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();
+}
+
+
 LLSD LLImportMaterial::asLLSD()
 {
 	LLSD ret;
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index 4d727bf1f2da86613ba6974626fbdc8167dc9758..f0c0f308d5f931d3c305be4bcf88fdc3be969136 100644
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -102,6 +102,8 @@ class LLImportMaterial
 		mDiffuseColor.set(1,1,1,1);
 	}
 
+	LLImportMaterial(LLSD& data);
+
 	LLSD asLLSD();
 };
 
@@ -114,6 +116,7 @@ class LLModelInstance
 	std::string mLabel;
 
 	LLUUID mMeshID;
+	S32 mLocalMeshID;
 
 	LLMatrix4 mTransform;
 	std::vector<LLImportMaterial> mMaterial;
@@ -121,8 +124,11 @@ class LLModelInstance
 	LLModelInstance(LLModel* model, const std::string& label, LLMatrix4& transform, std::vector<LLImportMaterial>& materials)
 		: mModel(model), mLabel(label), mTransform(transform), mMaterial(materials)
 	{
+		mLocalMeshID = -1;
 	}
 
+	LLModelInstance(LLSD& data);
+
 	LLSD asLLSD();
 };