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/llmath/m4math.cpp b/indra/llmath/m4math.cpp
index 8741400e5293360c82f2d1418d48fc6dc50e2120..bad4deb4dee87498382345aa2b8da3578c80d667 100644
--- a/indra/llmath/m4math.cpp
+++ b/indra/llmath/m4math.cpp
@@ -829,4 +829,54 @@ std::ostream& operator<<(std::ostream& s, const LLMatrix4 &a)
 	return s;
 }
 
+LLSD LLMatrix4::getValue() const
+{
+	LLSD ret;
+	
+	ret[0] = mMatrix[0][0];
+	ret[1] = mMatrix[0][1];
+	ret[2] = mMatrix[0][2];
+	ret[3] = mMatrix[0][3];
+
+	ret[4] = mMatrix[1][0];
+	ret[5] = mMatrix[1][1];
+	ret[6] = mMatrix[1][2];
+	ret[7] = mMatrix[1][3];
+
+	ret[8] = mMatrix[2][0];
+	ret[9] = mMatrix[2][1];
+	ret[10] = mMatrix[2][2];
+	ret[11] = mMatrix[2][3];
+
+	ret[12] = mMatrix[3][0];
+	ret[13] = mMatrix[3][1];
+	ret[14] = mMatrix[3][2];
+	ret[15] = mMatrix[3][3];
+
+	return ret;
+}
+
+void LLMatrix4::setValue(const LLSD& data) 
+{
+	mMatrix[0][0] = data[0].asReal();
+	mMatrix[0][1] = data[1].asReal();
+	mMatrix[0][2] = data[2].asReal();
+	mMatrix[0][3] = data[3].asReal();
+
+	mMatrix[1][0] = data[4].asReal();
+	mMatrix[1][1] = data[5].asReal();
+	mMatrix[1][2] = data[6].asReal();
+	mMatrix[1][3] = data[7].asReal();
+
+	mMatrix[2][0] = data[8].asReal();
+	mMatrix[2][1] = data[9].asReal();
+	mMatrix[2][2] = data[10].asReal();
+	mMatrix[2][3] = data[11].asReal();
+
+	mMatrix[3][0] = data[12].asReal();
+	mMatrix[3][1] = data[13].asReal();
+	mMatrix[3][2] = data[14].asReal();
+	mMatrix[3][3] = data[15].asReal();
+}
+
 
diff --git a/indra/llmath/m4math.h b/indra/llmath/m4math.h
index 3588f36758c75f8858eeb8377baf0bed311804e7..a7dce10397839e4ea21c88d59f4689d29f9193ed 100644
--- a/indra/llmath/m4math.h
+++ b/indra/llmath/m4math.h
@@ -119,6 +119,8 @@ class LLMatrix4
 
 	~LLMatrix4(void);										// Destructor
 
+	LLSD getValue() const;
+	void setValue(const LLSD&);
 
 	//////////////////////////////
 	//
diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp
index eed82f924bf3b653274bbd1dde23be0945bda88a..3669fef0dc8a1af039f06e64afa19ee3d99cb819 100755
--- a/indra/llprimitive/llmodel.cpp
+++ b/indra/llprimitive/llmodel.cpp
@@ -60,6 +60,7 @@ LLModel::LLModel(LLVolumeParams& params, F32 detail)
 	, mPelvisOffset( 0.0f )
 {
 	mDecompID = -1;
+	mLocalID = -1;
 }
 
 LLModel::~LLModel()
diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h
index e9e33bdee51abd6abef57ff43a0b9b97a97892d8..addf527d8df5c965f03505c624f69f9d9e525868 100755
--- a/indra/llprimitive/llmodel.h
+++ b/indra/llprimitive/llmodel.h
@@ -205,6 +205,9 @@ class LLModel : public LLVolume
 	std::vector<LLVector3> mHullCenter;
 	U32 mHullPoints;
 
+	//ID for storing this model in a .slm file
+	S32 mLocalID;
+
 protected:
 	void addVolumeFacesFromDomMesh(domMesh* mesh);
 	virtual BOOL createVolumeFacesFromFile(const std::string& file_name);
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 33dcadfc50e6f39bc79422e109cd69b4bbe500aa..176211773fbd2ea5d45f46ce9d7e6d27b4dc9fb7 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/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp
index bbce847fdcbd74d2821f3fdbd86f82cbad6b3873..0a642f494bca3bd9bd479ef039d7e906f49e11f3 100644
--- a/indra/newview/lldrawpoolbump.cpp
+++ b/indra/newview/lldrawpoolbump.cpp
@@ -646,8 +646,6 @@ BOOL LLDrawPoolBump::bindBumpMap(U8 bump_code, LLViewerTexture* texture, F32 vsi
 		break;
 	}
 
-	llassert(bump);
-
 	if (bump)
 	{
 		if (channel == -2)
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index ce977b33bb458e2b67d028fb8b9d8c7ad728eacb..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"
@@ -90,6 +91,7 @@
 #include "llbutton.h"
 #include "llcheckboxctrl.h"
 #include "llradiogroup.h"
+#include "llsdserialize.h"
 #include "llsliderctrl.h"
 #include "llspinctrl.h"
 #include "lltoggleablemenu.h"
@@ -1087,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)
@@ -1166,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);
 	
@@ -1743,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)
@@ -2561,6 +2687,70 @@ void LLModelPreview::rebuildUploadData()
 
 }
 
+void LLModelPreview::saveUploadData(bool save_skinweights, bool save_joint_positions)
+{
+	if (!mLODFile[LLModel::LOD_HIGH].empty())
+	{
+		std::string filename = mLODFile[LLModel::LOD_HIGH];
+		
+		std::string::size_type i = filename.rfind(".");
+		if (i != std::string::npos)
+		{
+			filename.replace(i, filename.size()-1, ".slm");
+			saveUploadData(filename, save_skinweights, save_joint_positions);
+		}
+	}
+}
+
+void LLModelPreview::saveUploadData(const std::string& filename, bool save_skinweights, bool save_joint_positions)
+{
+	std::set<LLPointer<LLModel> > meshes;
+	std::map<LLModel*, std::string> mesh_binary;
+
+	LLModel::hull empty_hull;
+
+	LLSD data;
+
+	S32 mesh_id = 0;
+
+	//build list of unique models and initialize local id
+	for (U32 i = 0; i < mUploadData.size(); ++i)
+	{
+		LLModelInstance& instance = mUploadData[i];
+		
+		if (meshes.find(instance.mModel) == meshes.end())
+		{
+			instance.mModel->mLocalID = mesh_id++;
+			meshes.insert(instance.mModel);
+
+			std::stringstream str;
+
+			LLModel::convex_hull_decomposition& decomp =
+				instance.mLOD[LLModel::LOD_PHYSICS].notNull() ? 
+				instance.mLOD[LLModel::LOD_PHYSICS]->mConvexHullDecomp : 
+				instance.mModel->mConvexHullDecomp;
+
+			LLModel::writeModel(str, 
+				instance.mLOD[LLModel::LOD_PHYSICS], 
+				instance.mLOD[LLModel::LOD_HIGH], 
+				instance.mLOD[LLModel::LOD_MEDIUM], 
+				instance.mLOD[LLModel::LOD_LOW], 
+				instance.mLOD[LLModel::LOD_IMPOSTOR], 
+				decomp, 
+				empty_hull, save_skinweights, save_joint_positions);
+
+			
+			data["mesh"][instance.mModel->mLocalID] = str.str();
+		}
+
+		data["instance"][i] = instance.asLLSD();
+	}
+
+	llofstream out(filename, std::ios_base::out | std::ios_base::binary);
+	LLSDSerialize::toBinary(data, out);
+	out.flush();
+	out.close();
+}
 
 void LLModelPreview::clearModel(S32 lod)
 {
@@ -2703,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;
@@ -4317,8 +4561,13 @@ void LLFloaterModelPreview::onUpload(void* user_data)
 	
 	mp->mModelPreview->rebuildUploadData();
 
+	bool upload_skinweights = mp->childGetValue("upload_skin").asBoolean();
+	bool upload_joint_positions = mp->childGetValue("upload_joints").asBoolean();
+
+	mp->mModelPreview->saveUploadData(upload_skinweights, upload_joint_positions);
+
 	gMeshRepo.uploadModel(mp->mModelPreview->mUploadData, mp->mModelPreview->mPreviewScale,
-						  mp->childGetValue("upload_textures").asBoolean(), mp->childGetValue("upload_skin"), mp->childGetValue("upload_joints"));
+						  mp->childGetValue("upload_textures").asBoolean(), upload_skinweights, upload_joint_positions);
 
 	mp->closeFloater(false);
 }
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index 910a45f9fe5ac969a07a92afe89726229c568164..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.
@@ -288,6 +290,8 @@ class LLModelPreview : public LLViewerDynamicTexture, public LLMutex
 	void clearMaterials();
 	U32 calcResourceCost();
 	void rebuildUploadData();
+	void saveUploadData(bool save_skinweights, bool save_joint_poisitions);
+	void saveUploadData(const std::string& filename, bool save_skinweights, bool save_joint_poisitions);
 	void clearIncompatible(S32 lod);
 	void updateStatusMessages();
 	void clearGLODGroup();
@@ -332,7 +336,7 @@ class LLModelPreview : public LLViewerDynamicTexture, public LLMutex
 	std::string mLODFile[LLModel::NUM_LODS];
 	bool		mLoading;
 	U32			mLoadState;
-
+	
 	std::map<std::string, bool> mViewOption;
 
 	//GLOD object parameters (must rebuild object if these change)
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 93e773d33bdf4e2703fbd00772240a27cde4b5da..a227627bc100fea4507be3d898e343466d8ed9fa 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -3584,3 +3584,52 @@ 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;
+
+	ret["mesh_id"] = mModel->mLocalID;
+	ret["label"] = mLabel;
+	ret["transform"] = mTransform.getValue();
+	
+	for (U32 i = 0; i < mMaterial.size(); ++i)
+	{
+		ret["material"][i] = mMaterial[i].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;
+
+	ret["diffuse"]["filename"] = mDiffuseMapFilename;
+	ret["diffuse"]["label"] = mDiffuseMapLabel;
+	ret["diffuse"]["color"] = mDiffuseColor.getValue();
+	ret["fullbright"] = mFullbright;
+	
+	return ret;
+}
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index ccdcc03310fa38c3c1ffaa2f2f24a24d2cfd21bc..f0c0f308d5f931d3c305be4bcf88fdc3be969136 100644
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -101,6 +101,10 @@ class LLImportMaterial
 	{ 
 		mDiffuseColor.set(1,1,1,1);
 	}
+
+	LLImportMaterial(LLSD& data);
+
+	LLSD asLLSD();
 };
 
 class LLModelInstance 
@@ -112,6 +116,7 @@ class LLModelInstance
 	std::string mLabel;
 
 	LLUUID mMeshID;
+	S32 mLocalMeshID;
 
 	LLMatrix4 mTransform;
 	std::vector<LLImportMaterial> mMaterial;
@@ -119,7 +124,12 @@ 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();
 };
 
 class LLMeshSkinInfo