diff --git a/src/client.cpp b/src/client.cpp
index 09c940a7a1bf1fd0ae27228dc0788a5de0b7a43f..b830bcdf321c6813f3e04e3dad087e7799a6d378 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -893,30 +893,12 @@ bool Client::loadMedia(const std::string &data, const std::string &filename)
 	name = removeStringEnd(filename, model_ext);
 	if(name != "")
 	{
-		verbosestream<<"Client: Storing model into Irrlicht: "
+		verbosestream<<"Client: Storing model into memory: "
 				<<"\""<<filename<<"\""<<std::endl;
-		scene::ISceneManager *smgr = m_device->getSceneManager();
-
-		//check if mesh was already cached
-		scene::IAnimatedMesh *mesh =
-			smgr->getMeshCache()->getMeshByName(filename.c_str());
-
-		if (mesh != NULL) {
-			errorstream << "Multiple models with name: " << filename.c_str() <<
-					" found replacing previous model!" << std::endl;
-
-			smgr->getMeshCache()->removeMesh(mesh);
-			mesh = 0;
-		}
-
-		io::IFileSystem *irrfs = m_device->getFileSystem();
-		io::IReadFile *rfile = irrfs->createMemoryReadFile(
-				*data_rw, data_rw.getSize(), filename.c_str());
-		assert(rfile);
-		
-		mesh = smgr->getMesh(rfile);
-		smgr->getMeshCache()->addMesh(filename.c_str(), mesh);
-		rfile->drop();
+		if(m_mesh_data.count(filename))
+			errorstream<<"Multiple models with name \""<<filename.c_str()
+					<<"\" found; replacing previous model"<<std::endl;
+		m_mesh_data[filename] = data;
 		return true;
 	}
 
@@ -2836,3 +2818,31 @@ MtEventManager* Client::getEventManager()
 	return m_event;
 }
 
+scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
+{
+	std::map<std::string, std::string>::const_iterator i =
+			m_mesh_data.find(filename);
+	if(i == m_mesh_data.end()){
+		errorstream<<"Client::getMesh(): Mesh not found: \""<<filename<<"\""
+				<<std::endl;
+		return NULL;
+	}
+	const std::string &data = i->second;
+	scene::ISceneManager *smgr = m_device->getSceneManager();
+
+	// Create the mesh, remove it from cache and return it
+	// This allows unique vertex colors and other properties for each instance
+	Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht
+	io::IFileSystem *irrfs = m_device->getFileSystem();
+	io::IReadFile *rfile = irrfs->createMemoryReadFile(
+			*data_rw, data_rw.getSize(), filename.c_str());
+	assert(rfile);
+	scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
+	rfile->drop();
+	// NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
+	// of uniquely named instances and re-use them
+	mesh->grab();
+	smgr->getMeshCache()->removeMesh(mesh);
+	return mesh;
+}
+
diff --git a/src/client.h b/src/client.h
index a74668d5b8c1de2c561edd573baaee56f3a7530f..1ed80a2b0225dd58ef3c02f00c7b779b67a99cd0 100644
--- a/src/client.h
+++ b/src/client.h
@@ -420,6 +420,7 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
 	virtual MtEventManager* getEventManager();
 	virtual bool checkLocalPrivilege(const std::string &priv)
 	{ return checkPrivilege(priv); }
+	virtual scene::IAnimatedMesh* getMesh(const std::string &filename);
 
 	// The following set of functions is used by ClientMediaDownloader
 	// Insert a media file appropriately into the appropriate manager
@@ -509,6 +510,9 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
 	// Detached inventories
 	// key = name
 	std::map<std::string, Inventory*> m_detached_inventories;
+
+	// Storage for mesh data for creating multiple instances of the same mesh
+	std::map<std::string, std::string> m_mesh_data;
 };
 
 #endif // !CLIENT_HEADER
diff --git a/src/content_cao.cpp b/src/content_cao.cpp
index 640ab6c7302ae9162785299c5936a553297356d7..840103cc74644f7406ae98e569258b4c7bdad3e7 100644
--- a/src/content_cao.cpp
+++ b/src/content_cao.cpp
@@ -957,10 +957,11 @@ class GenericCAO : public ClientActiveObject
 		}
 		else if(m_prop.visual == "mesh"){
 			infostream<<"GenericCAO::addToScene(): mesh"<<std::endl;
-			scene::IAnimatedMesh *mesh = smgr->getMesh(m_prop.mesh.c_str());
+			scene::IAnimatedMesh *mesh = m_gamedef->getMesh(m_prop.mesh);
 			if(mesh)
 			{
 				m_animated_meshnode = smgr->addAnimatedMeshSceneNode(mesh, NULL);
+				mesh->drop(); // The scene node took hold of it
 				m_animated_meshnode->animateJoints(); // Needed for some animations
 				m_animated_meshnode->setScale(v3f(m_prop.visual_size.X,
 						m_prop.visual_size.Y,
diff --git a/src/gamedef.h b/src/gamedef.h
index 1d46b028e359f8103f75c66b2692ae8c6d9e4e71..6da288bad8879992475c30592ec6045f8a6440b0 100644
--- a/src/gamedef.h
+++ b/src/gamedef.h
@@ -31,6 +31,9 @@ class ISoundManager;
 class IShaderSource;
 class MtEventManager;
 class IRollbackReportSink;
+namespace irr { namespace scene {
+	class IAnimatedMesh;
+}}
 
 /*
 	An interface for fetching game-global definitions like tool and
@@ -58,6 +61,8 @@ class IGameDef
 	// Only usable on the client
 	virtual ISoundManager* getSoundManager()=0;
 	virtual MtEventManager* getEventManager()=0;
+	virtual scene::IAnimatedMesh* getMesh(const std::string &filename)
+	{ return NULL; }
 
 	// Only usable on the server, and NOT thread-safe. It is usable from the
 	// environment thread.