diff --git a/indra/llrender/llrender.h b/indra/llrender/llrender.h
index 78a310e525fbe8e8494d0d22f325e347ae367b3f..98222939e75d5dd1b3510b4dd9401af6501208b5 100644
--- a/indra/llrender/llrender.h
+++ b/indra/llrender/llrender.h
@@ -262,6 +262,14 @@ class LLRender
 	friend class LLTexUnit;
 public:
 
+	enum eTexIndex
+	{
+		DIFFUSE_MAP = 0,
+		NORMAL_MAP,
+		SPECULAR_MAP,
+		NUM_TEXTURE_CHANNELS,
+	};
+	
 	typedef enum {
 		TRIANGLES = 0,
 		TRIANGLE_STRIP,
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index e80505740f30578a0d2b949ae896c9a4e0c092be..3259ac8718acdd2a90fa3f6f937fd7316659a1c4 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -3182,17 +3182,6 @@
       <key>Value</key>
       <integer>1</integer>
     </map>
-    <key>EnableTextureAtlas</key>
-    <map>
-      <key>Comment</key>
-      <string>Whether to use texture atlas or not</string>
-      <key>Persist</key>
-      <integer>1</integer>
-      <key>Type</key>
-      <string>Boolean</string>
-      <key>Value</key>
-      <integer>0</integer>
-    </map>
     <key>EnableUIHints</key>
     <map>
       <key>Comment</key>
diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp
index db079a1f16c1a4f39a6340cb14a8394d4841b121..6f0b1ee5676690898207c5353d99c33642dee646 100644
--- a/indra/newview/llface.cpp
+++ b/indra/newview/llface.cpp
@@ -51,7 +51,7 @@
 #include "llviewerregion.h"
 #include "llviewerwindow.h"
 #include "llviewershadermgr.h"
-
+#include "llviewertexture.h"
 
 #define LL_MAX_INDICES_COUNT 1000000
 
@@ -167,8 +167,12 @@ void LLFace::init(LLDrawable* drawablep, LLViewerObject* objp)
 	//special value to indicate uninitialized position
 	mIndicesIndex	= 0xFFFFFFFF;
 	
-	mIndexInTex = 0;
-	mTexture		= NULL;
+	for (U32 i = 0; i < LLRender::NUM_TEXTURE_CHANNELS; ++i)
+	{
+		mIndexInTex[i] = 0;
+		mTexture[i] = NULL;
+	}
+
 	mTEOffset		= -1;
 	mTextureIndex = 255;
 
@@ -185,8 +189,6 @@ void LLFace::init(LLDrawable* drawablep, LLViewerObject* objp)
 	mImportanceToCamera = 0.f ;
 	mBoundingSphereRadius = 0.0f ;
 
-	mAtlasInfop = NULL ;
-	mUsingAtlas  = FALSE ;
 	mHasMedia = FALSE ;
 }
 
@@ -197,9 +199,12 @@ void LLFace::destroy()
 		gPipeline.checkReferences(this);
 	}
 
-	if(mTexture.notNull())
+	for (U32 i = 0; i < LLRender::NUM_TEXTURE_CHANNELS; ++i)
 	{
-		mTexture->removeFace(this) ;
+		if(mTexture[i].notNull())
+		{
+			mTexture[i]->removeFace(i, this) ;
+		}
 	}
 	
 	if (isState(LLFace::PARTICLE))
@@ -239,8 +244,7 @@ void LLFace::destroy()
 	}
 	
 	setDrawInfo(NULL);
-	removeAtlas();
-		
+			
 	mDrawablep = NULL;
 	mVObjp = NULL;
 }
@@ -293,90 +297,76 @@ void LLFace::setPool(LLFacePool* new_pool, LLViewerTexture *texturep)
 	setTexture(texturep) ;
 }
 
-void LLFace::setTexture(LLViewerTexture* tex) 
+void LLFace::setTexture(U32 ch, LLViewerTexture* tex) 
 {
-	if(mTexture == tex)
+	llassert(ch < LLRender::NUM_TEXTURE_CHANNELS);
+
+	if(mTexture[ch] == tex)
 	{
 		return ;
 	}
 
-	if(mTexture.notNull())
+	if(mTexture[ch].notNull())
 	{
-		mTexture->removeFace(this) ;
-		removeAtlas() ;
+		mTexture[ch]->removeFace(ch, this) ;
 	}	
 	
 	if(tex)
 	{
-		tex->addFace(this) ;
+		tex->addFace(ch, this) ;
 	}
 
-	mTexture = tex ;
+	mTexture[ch] = tex ;
+}
+
+void LLFace::setTexture(LLViewerTexture* tex) 
+{
+	setDiffuseMap(tex);
+}
+
+void LLFace::setDiffuseMap(LLViewerTexture* tex)
+{
+	setTexture(LLRender::DIFFUSE_MAP, tex);
 }
 
 void LLFace::setNormalMap(LLViewerTexture* tex)
 {
-	if(mNormalMap == tex)
-	{
-		return ;
-	}
-	
-	if(mNormalMap.notNull())
-	{
-		mNormalMap->removeFace(this) ;
-		removeAtlas() ;
-	}
-	
-	if(tex)
-	{
-		tex->addFace(this) ;
-	}
-	
-	mNormalMap = tex ;
+	setTexture(LLRender::NORMAL_MAP, tex);
 }
 
 void LLFace::setSpecularMap(LLViewerTexture* tex)
 {
-	if(mSpecMap == tex)
-	{
-		return ;
-	}
-	
-	if(mSpecMap.notNull())
-	{
-		mSpecMap->removeFace(this) ;
-		removeAtlas() ;
-	}
-	
-	if(tex)
-	{
-		tex->addFace(this) ;
-	}
-	
-	mSpecMap = tex ;
+	setTexture(LLRender::SPECULAR_MAP, tex);
 }
 
 void LLFace::dirtyTexture()
 {
 	LLDrawable* drawablep = getDrawable();
 
-	if (mVObjp.notNull() && mVObjp->getVolume() && 
-		mTexture.notNull() && mTexture->getComponents() == 4)
-	{ //dirty texture on an alpha object should be treated as an LoD update
-		LLVOVolume* vobj = drawablep->getVOVolume();
-		if (vobj)
+	if (mVObjp.notNull() && mVObjp->getVolume())
+	{
+		for (U32 ch = 0; ch < LLRender::NUM_TEXTURE_CHANNELS; ++ch)
 		{
-			vobj->mLODChanged = TRUE;
+			if (mTexture[ch].notNull() && mTexture[ch]->getComponents() == 4)
+			{ //dirty texture on an alpha object should be treated as an LoD update
+				LLVOVolume* vobj = drawablep->getVOVolume();
+				if (vobj)
+				{
+					vobj->mLODChanged = TRUE;
+				}
+				gPipeline.markRebuild(drawablep, LLDrawable::REBUILD_VOLUME, FALSE);
+			}
 		}
-		gPipeline.markRebuild(drawablep, LLDrawable::REBUILD_VOLUME, FALSE);
-	}		
+	}
 			
 	gPipeline.markTextured(drawablep);
 }
 
-void LLFace::switchTexture(LLViewerTexture* new_texture)
+void LLFace::switchTexture(U32 ch, LLViewerTexture* new_texture)
 {
-	if(mTexture == new_texture)
+	llassert(ch < LLRender::NUM_TEXTURE_CHANNELS);
+	
+	if(mTexture[ch] == new_texture)
 	{
 		return ;
 	}
@@ -386,10 +376,17 @@ void LLFace::switchTexture(LLViewerTexture* new_texture)
 		llerrs << "Can not switch to a null texture." << llendl;
 		return;
 	}
-	new_texture->addTextureStats(mTexture->getMaxVirtualSize()) ;
 
-	getViewerObject()->changeTEImage(mTEOffset, new_texture) ;
-	setTexture(new_texture) ;	
+	llassert(mTexture[ch].notNull());
+
+	new_texture->addTextureStats(mTexture[ch]->getMaxVirtualSize()) ;
+
+	if (ch == LLRender::DIFFUSE_MAP)
+	{
+		getViewerObject()->changeTEImage(mTEOffset, new_texture) ;
+	}
+
+	setTexture(ch, new_texture) ;	
 	dirtyTexture();
 }
 
@@ -1293,27 +1290,6 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 	const LLTextureEntry *tep = mVObjp->getTE(f);
 	const U8 bump_code = tep ? tep->getBumpmap() : 0;
 
-	F32 tcoord_xoffset = 0.f ;
-	F32 tcoord_yoffset = 0.f ;
-	F32 tcoord_xscale = 1.f ;
-	F32 tcoord_yscale = 1.f ;
-	BOOL in_atlas = FALSE ;
-
-	if (rebuild_tcoord)
-	{
-		in_atlas = isAtlasInUse() ;
-		if(in_atlas)
-		{
-			const LLVector2* tmp = getTexCoordOffset() ;
-			tcoord_xoffset = tmp->mV[0] ; 
-			tcoord_yoffset = tmp->mV[1] ;
-
-			tmp = getTexCoordScale() ;
-			tcoord_xscale = tmp->mV[0] ; 
-			tcoord_yscale = tmp->mV[1] ;	
-		}
-	}
-	
 	BOOL is_static = mDrawablep->isStatic();
 	BOOL is_global = is_static;
 
@@ -1598,11 +1574,11 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 					break;
 					case BE_BRIGHTNESS:
 					case BE_DARKNESS:
-					if( mTexture.notNull() && mTexture->hasGLTexture())
+					if( mTexture[LLRender::DIFFUSE_MAP].notNull() && mTexture[LLRender::DIFFUSE_MAP]->hasGLTexture())
 					{
 						// Offset by approximately one texel
-						S32 cur_discard = mTexture->getDiscardLevel();
-						S32 max_size = llmax( mTexture->getWidth(), mTexture->getHeight() );
+						S32 cur_discard = mTexture[LLRender::DIFFUSE_MAP]->getDiscardLevel();
+						S32 max_size = llmax( mTexture[LLRender::DIFFUSE_MAP]->getWidth(), mTexture[LLRender::DIFFUSE_MAP]->getHeight() );
 						max_size <<= cur_discard;
 						const F32 ARTIFICIAL_OFFSET = 2.f;
 						offset_multiple = ARTIFICIAL_OFFSET / (F32)max_size;
@@ -1675,8 +1651,8 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 			bool do_bump = bump_code && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_TEXCOORD1);
 			bool do_tex_mat = tex_mode && mTextureMatrix;
 
-			if (!in_atlas && !do_bump)
-			{ //not in atlas or not bump mapped, might be able to do a cheap update
+			if (!do_bump)
+			{ //not bump mapped, might be able to do a cheap update
 				mVertexBuffer->getTexCoord0Strider(tex_coords, mGeomIndex, mGeomCount);
 
 				if (texgen != LLTextureEntry::TEX_GEN_PLANAR)
@@ -1728,7 +1704,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 						}
 					}
 					else
-					{ //do tex mat, no texgen, no atlas, no bump
+					{ //do tex mat, no texgen, no bump
 						for (S32 i = 0; i < num_vertices; i++)
 						{	
 							LLVector2 tc(vf.mTexCoords[i]);
@@ -1744,7 +1720,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 					}
 				}
 				else
-				{ //no bump, no atlas, tex gen planar
+				{ //no bump, tex gen planar
 					LLFastTimer t(FTM_FACE_TEX_QUICK_PLANAR);
 					if (do_tex_mat)
 					{
@@ -1789,7 +1765,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 				}
 			}
 			else
-			{ //either bump mapped or in atlas, just do the whole expensive loop
+			{ //bump mapped, just do the whole expensive loop
 				LLFastTimer t(FTM_FACE_TEX_DEFAULT);
 				mVertexBuffer->getTexCoord0Strider(tex_coords, mGeomIndex, mGeomCount, map_range);
 
@@ -1837,93 +1813,6 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 						xform(tc, cos_ang, sin_ang, os, ot, ms, mt);
 					}
 
-					if(in_atlas)
-					{
-						//
-						//manually calculate tex-coord per vertex for varying address modes.
-						//should be removed if shader can handle this.
-						//
-
-						S32 int_part = 0 ;
-						switch(mTexture->getAddressMode())
-						{
-						case LLTexUnit::TAM_CLAMP:
-							if(tc.mV[0] < 0.f)
-							{
-								tc.mV[0] = 0.f ;
-							}
-							else if(tc.mV[0] > 1.f)
-							{
-								tc.mV[0] = 1.f;
-							}
-
-							if(tc.mV[1] < 0.f)
-							{
-								tc.mV[1] = 0.f ;
-							}
-							else if(tc.mV[1] > 1.f)
-							{
-								tc.mV[1] = 1.f;
-							}
-							break;
-						case LLTexUnit::TAM_MIRROR:
-							if(tc.mV[0] < 0.f)
-							{
-								tc.mV[0] = -tc.mV[0] ;
-							}
-							int_part = (S32)tc.mV[0] ;
-							if(int_part & 1) //odd number
-							{
-								tc.mV[0] = int_part + 1 - tc.mV[0] ;
-							}
-							else //even number
-							{
-								tc.mV[0] -= int_part ;
-							}
-
-							if(tc.mV[1] < 0.f)
-							{
-								tc.mV[1] = -tc.mV[1] ;
-							}
-							int_part = (S32)tc.mV[1] ;
-							if(int_part & 1) //odd number
-							{
-								tc.mV[1] = int_part + 1 - tc.mV[1] ;
-							}
-							else //even number
-							{
-								tc.mV[1] -= int_part ;
-							}
-							break;
-						case LLTexUnit::TAM_WRAP:
-							if(tc.mV[0] > 1.f)
-								tc.mV[0] -= (S32)(tc.mV[0] - 0.00001f) ;
-							else if(tc.mV[0] < -1.f)
-								tc.mV[0] -= (S32)(tc.mV[0] + 0.00001f) ;
-
-							if(tc.mV[1] > 1.f)
-								tc.mV[1] -= (S32)(tc.mV[1] - 0.00001f) ;
-							else if(tc.mV[1] < -1.f)
-								tc.mV[1] -= (S32)(tc.mV[1] + 0.00001f) ;
-
-							if(tc.mV[0] < 0.f)
-							{
-								tc.mV[0] = 1.0f + tc.mV[0] ;
-							}
-							if(tc.mV[1] < 0.f)
-							{
-								tc.mV[1] = 1.0f + tc.mV[1] ;
-							}
-							break;
-						default:
-							break;
-						}
-				
-						tc.mV[0] = tcoord_xoffset + tcoord_xscale * tc.mV[0] ;
-						tc.mV[1] = tcoord_yoffset + tcoord_yscale * tc.mV[1] ;
-					}
-				
-
 					*tex_coords++ = tc;
 					if (do_bump)
 					{
@@ -2197,9 +2086,9 @@ BOOL LLFace::hasMedia() const
 	{
 		return TRUE ;
 	}
-	if(mTexture.notNull()) 
+	if(mTexture[LLRender::DIFFUSE_MAP].notNull()) 
 	{
-		return mTexture->hasParcelMedia() ;  //if has a parcel media
+		return mTexture[LLRender::DIFFUSE_MAP]->hasParcelMedia() ;  //if has a parcel media
 	}
 
 	return FALSE ; //no media.
@@ -2251,7 +2140,7 @@ F32 LLFace::getTextureVirtualSize()
 	face_area = LLFace::adjustPixelArea(mImportanceToCamera, face_area) ;
 	if(face_area > LLViewerTexture::sMinLargeImageSize) //if is large image, shrink face_area by considering the partial overlapping.
 	{
-		if(mImportanceToCamera > LEAST_IMPORTANCE_FOR_LARGE_IMAGE && mTexture.notNull() && mTexture->isLargeImage())
+		if(mImportanceToCamera > LEAST_IMPORTANCE_FOR_LARGE_IMAGE && mTexture[LLRender::DIFFUSE_MAP].notNull() && mTexture[LLRender::DIFFUSE_MAP]->isLargeImage())
 		{		
 			face_area *= adjustPartialOverlapPixelArea(cos_angle_to_view_dir, radius );
 		}	
@@ -2618,159 +2507,13 @@ LLVector3 LLFace::getPositionAgent() const
 	}
 }
 
-//
-//atlas
-//
-void LLFace::removeAtlas()
-{
-	setAtlasInUse(FALSE) ;
-	mAtlasInfop = NULL ;	
-}
-
-const LLTextureAtlas* LLFace::getAtlas()const 
-{
-	if(mAtlasInfop)
-	{
-		return mAtlasInfop->getAtlas() ;
-	}
-	return NULL ;
-}
-
-const LLVector2* LLFace::getTexCoordOffset()const 
-{
-	if(isAtlasInUse())
-	{
-		return mAtlasInfop->getTexCoordOffset() ;
-	}
-	return NULL ;
-}
-const LLVector2* LLFace::getTexCoordScale() const 
-{
-	if(isAtlasInUse())
-	{
-		return mAtlasInfop->getTexCoordScale() ;
-	}
-	return NULL ;
-}
-
-BOOL LLFace::isAtlasInUse()const
+LLViewerTexture* LLFace::getTexture(U32 ch) const
 {
-	return mUsingAtlas ;
-}
-
-BOOL LLFace::canUseAtlas()const
-{
-	//no drawable or no spatial group, do not use atlas
-	if(!mDrawablep || !mDrawablep->getSpatialGroup())
-	{
-		return FALSE ;
-	}
-
-	//if bump face, do not use atlas
-	if(getTextureEntry() && getTextureEntry()->getBumpmap())
-	{
-		return FALSE ;
-	}
-
-	//if animated texture, do not use atlas
-	if(isState(TEXTURE_ANIM))
-	{
-		return FALSE ;
-	}
+	llassert(ch < LLRender::NUM_TEXTURE_CHANNELS);
 
-	return TRUE ;
+	return mTexture[ch] ;
 }
 
-void LLFace::setAtlasInUse(BOOL flag)
-{
-	//no valid atlas to use.
-	if(flag && (!mAtlasInfop || !mAtlasInfop->isValid()))
-	{
-		flag = FALSE ;
-	}
-
-	if(!flag && !mUsingAtlas)
-	{
-		return ;
-	}
-
-	//
-	//at this stage (flag || mUsingAtlas) is always true.
-	//
-
-	//rebuild the tex coords
-	if(mDrawablep)
-	{
-		gPipeline.markRebuild(mDrawablep, LLDrawable::REBUILD_TCOORD);
-		mUsingAtlas = flag ;
-	}
-	else
-	{
-		mUsingAtlas = FALSE ;
-	}
-}
-
-LLTextureAtlasSlot* LLFace::getAtlasInfo()
-{
-	return mAtlasInfop ;
-}
-
-void LLFace::setAtlasInfo(LLTextureAtlasSlot* atlasp)
-{	
-	if(mAtlasInfop != atlasp)
-	{
-		if(mAtlasInfop)
-		{
-			//llerrs << "Atlas slot changed!" << llendl ;
-		}
-		mAtlasInfop = atlasp ;
-	}
-}
-
-LLViewerTexture* LLFace::getTexture() const
-{
-	if(isAtlasInUse())
-	{
-		return (LLViewerTexture*)mAtlasInfop->getAtlas() ;
-	}
-
-	return mTexture ;
-}
-
-//switch to atlas or switch back to gl texture 
-//return TRUE if using atlas.
-BOOL LLFace::switchTexture()
-{
-	//no valid atlas or texture
-	if(!mAtlasInfop || !mAtlasInfop->isValid() || !mTexture)
-	{
-		return FALSE ;
-	}
-	
-	if(mTexture->getTexelsInAtlas() >= (U32)mVSize || 
-		mTexture->getTexelsInAtlas() >= mTexture->getTexelsInGLTexture())
-	{
-		//switch to use atlas
-		//atlas resolution is qualified, use it.		
-		if(!mUsingAtlas)
-		{
-			setAtlasInUse(TRUE) ;
-		}
-	}
-	else //if atlas not qualified.
-	{
-		//switch back to GL texture
-		if(mUsingAtlas && mTexture->isGLTextureCreated() && 
-			mTexture->getDiscardLevel() < mTexture->getDiscardLevelInAtlas())
-		{
-			setAtlasInUse(FALSE) ;
-		}
-	}
-
-	return mUsingAtlas ;
-}
-
-
 void LLFace::setVertexBuffer(LLVertexBuffer* buffer)
 {
 	mVertexBuffer = buffer;
diff --git a/indra/newview/llface.h b/indra/newview/llface.h
index cb76c6e8a6356564b5a799ea2262c7072128383d..453d2c23d434d39cb693ecdbfdbcc0010f8ed251 100644
--- a/indra/newview/llface.h
+++ b/indra/newview/llface.h
@@ -41,7 +41,6 @@
 #include "llvertexbuffer.h"
 #include "llviewertexture.h"
 #include "lldrawable.h"
-#include "lltextureatlasmanager.h"
 
 class LLFacePool;
 class LLVolume;
@@ -50,7 +49,6 @@ class LLTextureEntry;
 class LLVertexProgram;
 class LLViewerTexture;
 class LLGeometryManager;
-class LLTextureAtlasSlot;
 
 const F32 MIN_ALPHA_SIZE = 1024.f;
 const F32 MIN_TEX_ANIM_SIZE = 512.f;
@@ -110,10 +108,12 @@ class LLFace
 	U16				getGeomStart()		const	{ return mGeomIndex; }		// index into draw pool
 	void			setTextureIndex(U8 index);
 	U8				getTextureIndex() const		{ return mTextureIndex; }
+	void			setTexture(U32 ch, LLViewerTexture* tex);
 	void			setTexture(LLViewerTexture* tex) ;
+	void			setDiffuseMap(LLViewerTexture* tex);
 	void			setNormalMap(LLViewerTexture* tex);
 	void			setSpecularMap(LLViewerTexture* tex);
-	void            switchTexture(LLViewerTexture* new_texture);
+	void            switchTexture(U32 ch, LLViewerTexture* new_texture);
 	void            dirtyTexture();
 	LLXformMatrix*	getXform()			const	{ return mXform; }
 	BOOL			hasGeometry()		const	{ return mGeomCount > 0; }
@@ -132,8 +132,8 @@ class LLFace
 	F32				getVirtualSize() const { return mVSize; }
 	F32				getPixelArea() const { return mPixelArea; }
 
-	S32             getIndexInTex() const {return mIndexInTex ;}
-	void            setIndexInTex(S32 index) { mIndexInTex = index ;}
+	S32             getIndexInTex(U32 ch) const {llassert(ch < LLRender::NUM_TEXTURE_CHANNELS); return mIndexInTex[ch];}
+	void            setIndexInTex(U32 ch, S32 index) { llassert(ch < LLRender::NUM_TEXTURE_CHANNELS);  mIndexInTex[ch] = index ;}
 
 	void			renderSetColor() const;
 	S32				renderElements(const U16 *index_array) const;
@@ -151,7 +151,7 @@ class LLFace
 	S32				getLOD()			const	{ return mVObjp.notNull() ? mVObjp->getLOD() : 0; }
 	void			setPoolType(U32 type)		{ mPoolType = type; }
 	S32				getTEOffset()				{ return mTEOffset; }
-	LLViewerTexture*	getTexture() const;
+	LLViewerTexture*	getTexture(U32 ch = LLRender::DIFFUSE_MAP) const;
 
 	void			setViewerObject(LLViewerObject* object);
 	void			setPool(LLFacePool *pool, LLViewerTexture *texturep);
@@ -225,16 +225,6 @@ class LLFace
 	void        setHasMedia(bool has_media)  { mHasMedia = has_media ;}
 	BOOL        hasMedia() const ;
 
-	//for atlas
-	LLTextureAtlasSlot*   getAtlasInfo() ;
-	void                  setAtlasInUse(BOOL flag);
-	void                  setAtlasInfo(LLTextureAtlasSlot* atlasp);
-	BOOL                  isAtlasInUse()const;
-	BOOL                  canUseAtlas() const;
-	const LLVector2*      getTexCoordScale() const ;
-	const LLVector2*      getTexCoordOffset()const;
-	const LLTextureAtlas* getAtlas()const ;
-	void                  removeAtlas() ;
 	BOOL                  switchTexture() ;
 
 	//vertex buffer tracking
@@ -285,12 +275,12 @@ class LLFace
 	U8			mTextureIndex;		// index of texture channel to use for pseudo-atlasing
 	U32			mIndicesCount;
 	U32			mIndicesIndex;		// index into draw pool for indices (yeah, I know!)
-	S32         mIndexInTex ;
+	S32         mIndexInTex[LLRender::NUM_TEXTURE_CHANNELS];
 
 	LLXformMatrix* mXform;
-	LLPointer<LLViewerTexture> mTexture;
-	LLPointer<LLViewerTexture> mSpecMap;
-	LLPointer<LLViewerTexture> mNormalMap;
+
+	LLPointer<LLViewerTexture> mTexture[LLRender::NUM_TEXTURE_CHANNELS];
+	
 	LLPointer<LLDrawable> mDrawablep;
 	LLPointer<LLViewerObject> mVObjp;
 	S32			mTEOffset;
@@ -308,9 +298,6 @@ class LLFace
 	F32         mBoundingSphereRadius ;
 	bool        mHasMedia ;
 
-	//atlas
-	LLPointer<LLTextureAtlasSlot> mAtlasInfop ;
-	BOOL                          mUsingAtlas ;
 	
 protected:
 	static BOOL	sSafeRenderSelect;
diff --git a/indra/newview/lllocalbitmaps.cpp b/indra/newview/lllocalbitmaps.cpp
index 97ba5b634a1f99c83541bc6f7ac9f1d5b1d396cf..6f213e018a881b701582acb95fa91aff73553cde 100644
--- a/indra/newview/lllocalbitmaps.cpp
+++ b/indra/newview/lllocalbitmaps.cpp
@@ -372,10 +372,12 @@ std::vector<LLViewerObject*> LLLocalBitmap::prepUpdateObjects(LLUUID old_id)
 	std::vector<LLViewerObject*> obj_list;
 	LLViewerFetchedTexture* old_texture = gTextureList.findImage(old_id);
 
-	for(U32 face_iterator = 0; face_iterator < old_texture->getNumFaces(); face_iterator++)
+	U32 ch = LLRender::DIFFUSE_MAP;
+
+	for(U32 face_iterator = 0; face_iterator < old_texture->getNumFaces(ch); face_iterator++)
 	{
 		// getting an object from a face
-		LLFace* face_to_object = (*old_texture->getFaceList())[face_iterator];
+		LLFace* face_to_object = (*old_texture->getFaceList(ch))[face_iterator];
 
 		if(face_to_object)
 		{
diff --git a/indra/newview/lltextureatlas.cpp b/indra/newview/lltextureatlas.cpp
index f8c1bca8aed1097696f9609a5ce6040487ad2683..dbbe331954e9d98378b27981d2cf8328ed2ebd22 100644
--- a/indra/newview/lltextureatlas.cpp
+++ b/indra/newview/lltextureatlas.cpp
@@ -71,7 +71,7 @@ LLTextureAtlas::~LLTextureAtlas()
 //virtual 
 S8 LLTextureAtlas::getType() const
 {
-	return LLViewerTexture::ATLAS_TEXTURE ;
+	return 0; //LLViewerTexture::ATLAS_TEXTURE ;
 }
 
 void LLTextureAtlas::getTexCoordOffset(S16 col, S16 row, F32& xoffset, F32& yoffset)
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index f4e66a928584f0b797281c33918dd1dc982ad31b..8bd0cf6d047236571e3840c3f71597be0d5c4bac 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -1193,28 +1193,6 @@ class LLAdvancedCheckWireframe : public view_listener_t
 	}
 };
 	
-//////////////////////
-// TEXTURE ATLAS //
-//////////////////////
-
-class LLAdvancedToggleTextureAtlas : public view_listener_t
-{
-	bool handleEvent(const LLSD& userdata)
-	{
-		LLViewerTexture::sUseTextureAtlas = !LLViewerTexture::sUseTextureAtlas;
-		gSavedSettings.setBOOL("EnableTextureAtlas", LLViewerTexture::sUseTextureAtlas) ;
-		return true;
-	}
-};
-
-class LLAdvancedCheckTextureAtlas : public view_listener_t
-{
-	bool handleEvent(const LLSD& userdata)
-	{
-		bool new_value = LLViewerTexture::sUseTextureAtlas; // <-- make this using LLCacheControl
-		return new_value;
-	}
-};
 
 //////////////////////////
 // DUMP SCRIPTED CAMERA //
@@ -8464,8 +8442,6 @@ void initialize_menus()
 	view_listener_t::addMenu(new LLAdvancedToggleWireframe(), "Advanced.ToggleWireframe");
 	view_listener_t::addMenu(new LLAdvancedCheckWireframe(), "Advanced.CheckWireframe");
 	// Develop > Render
-	view_listener_t::addMenu(new LLAdvancedToggleTextureAtlas(), "Advanced.ToggleTextureAtlas");
-	view_listener_t::addMenu(new LLAdvancedCheckTextureAtlas(), "Advanced.CheckTextureAtlas");
 	view_listener_t::addMenu(new LLAdvancedEnableObjectObjectOcclusion(), "Advanced.EnableObjectObjectOcclusion");
 	view_listener_t::addMenu(new LLAdvancedEnableRenderFBO(), "Advanced.EnableRenderFBO");
 	view_listener_t::addMenu(new LLAdvancedEnableRenderDeferred(), "Advanced.EnableRenderDeferred");
diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp
index 08fcb60d035e628b7705fc11a820583deb813e2d..a57270caebcb2af6552a049f41125fd2a38193ed 100644
--- a/indra/newview/llviewertexture.cpp
+++ b/indra/newview/llviewertexture.cpp
@@ -55,8 +55,6 @@
 #include "llappviewer.h"
 #include "llface.h"
 #include "llviewercamera.h"
-#include "lltextureatlas.h"
-#include "lltextureatlasmanager.h"
 #include "lltextureentry.h"
 #include "llmediaentry.h"
 #include "llvovolume.h"
@@ -97,7 +95,6 @@ S32 LLViewerTexture::sMinLargeImageSize = 65536 ; //256 * 256.
 S32 LLViewerTexture::sMaxSmallImageSize = MAX_CACHED_RAW_IMAGE_AREA ;
 BOOL LLViewerTexture::sFreezeImageScalingDown = FALSE ;
 F32 LLViewerTexture::sCurrentTime = 0.0f ;
-BOOL LLViewerTexture::sUseTextureAtlas        = FALSE ;
 F32  LLViewerTexture::sTexelPixelRatio = 1.0f;
 
 LLViewerTexture::EDebugTexels LLViewerTexture::sDebugTexelsMode = LLViewerTexture::DEBUG_TEXELS_OFF;
@@ -556,8 +553,7 @@ void LLViewerTexture::updateClass(const F32 velocity, const F32 angular_velocity
 		}
 	}
 	sDesiredDiscardBias = llclamp(sDesiredDiscardBias, desired_discard_bias_min, desired_discard_bias_max);
-	LLViewerTexture::sUseTextureAtlas = gSavedSettings.getBOOL("EnableTextureAtlas") ;
-	
+		
 	F32 camera_moving_speed = LLViewerCamera::getInstance()->getAverageSpeed() ;
 	F32 camera_angular_speed = LLViewerCamera::getInstance()->getAverageAngularSpeed();
 	sCameraMovingBias = llmax(0.2f * camera_moving_speed, 2.0f * camera_angular_speed - 1);
@@ -639,9 +635,12 @@ void LLViewerTexture::init(bool firstinit)
 	mMaxVirtualSizeResetCounter = mMaxVirtualSizeResetInterval ;
 	mAdditionalDecodePriority = 0.f ;	
 	mParcelMedia = NULL ;
-	mNumFaces = 0 ;
+	for (U32 i = 0; i < LLRender::NUM_TEXTURE_CHANNELS; ++i)
+	{	
+		mNumFaces[i] = 0;
+		mFaceList[i].clear();
+	}
 	mNumVolumes = 0;
-	mFaceList.clear() ;
 	mVolumeList.clear();
 }
 
@@ -653,7 +652,10 @@ S8 LLViewerTexture::getType() const
 
 void LLViewerTexture::cleanup()
 {
-	mFaceList.clear() ;
+	for (U32 i = 0; i < LLRender::NUM_TEXTURE_CHANNELS; ++i)
+	{
+		mFaceList[i].clear() ;
+	}
 	mVolumeList.clear();
 	if(mGLTexturep)
 	{
@@ -778,38 +780,57 @@ void LLViewerTexture::setKnownDrawSize(S32 width, S32 height)
 }
 
 //virtual
-void LLViewerTexture::addFace(LLFace* facep) 
+void LLViewerTexture::addFace(U32 ch, LLFace* facep) 
 {
-	if(mNumFaces >= mFaceList.size())
+	llassert(ch < LLRender::NUM_TEXTURE_CHANNELS);
+
+	if(mNumFaces[ch] >= mFaceList[ch].size())
 	{
-		mFaceList.resize(2 * mNumFaces + 1) ;		
+		mFaceList[ch].resize(2 * mNumFaces[ch] + 1) ;		
 	}
-	mFaceList[mNumFaces] = facep ;
-	facep->setIndexInTex(mNumFaces) ;
-	mNumFaces++ ;
+	mFaceList[ch][mNumFaces[ch]] = facep ;
+	facep->setIndexInTex(ch, mNumFaces[ch]) ;
+	mNumFaces[ch]++ ;
 	mLastFaceListUpdateTimer.reset() ;
 }
 
 //virtual
-void LLViewerTexture::removeFace(LLFace* facep) 
+void LLViewerTexture::removeFace(U32 ch, LLFace* facep) 
 {
-	if(mNumFaces > 1)
+	llassert(ch < LLRender::NUM_TEXTURE_CHANNELS);
+
+	if(mNumFaces[ch] > 1)
 	{
-		S32 index = facep->getIndexInTex() ; 
-		mFaceList[index] = mFaceList[--mNumFaces] ;
-		mFaceList[index]->setIndexInTex(index) ;
+		S32 index = facep->getIndexInTex(ch) ; 
+		llassert(index < mFaceList[ch].size());
+		llassert(index < mNumFaces[ch]);
+		mFaceList[ch][index] = mFaceList[ch][--mNumFaces[ch]] ;
+		mFaceList[ch][index]->setIndexInTex(ch, index) ;
 	}
 	else 
 	{
-		mFaceList.clear() ;
-		mNumFaces = 0 ;
+		mFaceList[ch].clear() ;
+		mNumFaces[ch] = 0 ;
 	}
 	mLastFaceListUpdateTimer.reset() ;
 }
 
-S32 LLViewerTexture::getNumFaces() const
+S32 LLViewerTexture::getTotalNumFaces() const
+{
+	S32 ret = 0;
+
+	for (U32 i = 0; i < LLRender::NUM_TEXTURE_CHANNELS; ++i)
+	{
+		ret += mNumFaces[i];
+	}
+
+	return ret;
+}
+
+S32 LLViewerTexture::getNumFaces(U32 ch) const
 {
-	return mNumFaces ;
+	llassert(ch < LLRender::NUM_TEXTURE_CHANNELS);
+	return mNumFaces[ch];
 }
 
 
@@ -832,6 +853,8 @@ void LLViewerTexture::removeVolume(LLVOVolume* volumep)
 	if(mNumVolumes > 1)
 	{
 		S32 index = volumep->getIndexInTex() ; 
+		llassert(index < mVolumeList.size());
+		llassert(index < mNumVolumes);
 		mVolumeList[index] = mVolumeList[--mNumVolumes] ;
 		mVolumeList[index]->setIndexInTex(index) ;
 	}
@@ -853,18 +876,22 @@ void LLViewerTexture::reorganizeFaceList()
 	static const F32 MAX_WAIT_TIME = 20.f; // seconds
 	static const U32 MAX_EXTRA_BUFFER_SIZE = 4 ;
 
-	if(mNumFaces + MAX_EXTRA_BUFFER_SIZE > mFaceList.size())
+	if(mLastFaceListUpdateTimer.getElapsedTimeF32() < MAX_WAIT_TIME)
 	{
-		return ;
+		return;
 	}
 
-	if(mLastFaceListUpdateTimer.getElapsedTimeF32() < MAX_WAIT_TIME)
+	for (U32 i = 0; i < LLRender::NUM_TEXTURE_CHANNELS; ++i)
 	{
-		return ;
+		if(mNumFaces[i] + MAX_EXTRA_BUFFER_SIZE > mFaceList[i].size())
+		{
+			return ;
+		}
+			
+		mFaceList[i].erase(mFaceList[i].begin() + mNumFaces[i], mFaceList[i].end());
 	}
-
+	
 	mLastFaceListUpdateTimer.reset() ;
-	mFaceList.erase(mFaceList.begin() + mNumFaces, mFaceList.end());
 }
 
 void LLViewerTexture::reorganizeVolumeList()
@@ -1130,13 +1157,6 @@ void LLViewerTexture::forceUpdateBindStats(void) const
 	return mGLTexturep->forceUpdateBindStats() ;
 }
 
-U32 LLViewerTexture::getTexelsInAtlas() const
-{
-	llassert(mGLTexturep.notNull()) ;
-
-	return mGLTexturep->getTexelsInAtlas() ;
-}
-
 U32 LLViewerTexture::getTexelsInGLTexture() const
 {
 	llassert(mGLTexturep.notNull()) ;
@@ -1151,13 +1171,6 @@ BOOL LLViewerTexture::isGLTextureCreated() const
 	return mGLTexturep->isGLTextureCreated() ;
 }
 
-S32  LLViewerTexture::getDiscardLevelInAtlas() const
-{
-	llassert(mGLTexturep.notNull()) ;
-
-	return mGLTexturep->getDiscardLevelInAtlas() ;
-}
-
 void LLViewerTexture::destroyGLTexture() 
 {
 	if(mGLTexturep.notNull() && mGLTexturep->getHasGLTexture())
@@ -1470,9 +1483,14 @@ void LLViewerFetchedTexture::addToCreateTexture()
 		mGLTexturep->setComponents(mComponents) ;
 		force_update = true ;
 
-		for(U32 i = 0 ; i < mNumFaces ; i++)
+		for (U32 j = 0; j < LLRender::NUM_TEXTURE_CHANNELS; ++j)
 		{
-			mFaceList[i]->dirtyTexture() ;
+			llassert(mNumFaces[j] <= mFaceList[j].size());
+
+			for(U32 i = 0 ; i < mNumFaces[j]; i++)
+			{
+				mFaceList[j][i]->dirtyTexture() ;
+			}
 		}
 
 		//discard the cached raw image and the saved raw image
@@ -1615,11 +1633,8 @@ BOOL LLViewerFetchedTexture::createTexture(S32 usename/*= 0*/)
 		return FALSE;
 	}
 	
-	if(!(res = insertToAtlas()))
-	{
-		res = mGLTexturep->createGLTexture(mRawDiscardLevel, mRawImage, usename, TRUE, mBoostLevel);
-		resetFaceAtlas() ;
-	}
+	res = mGLTexturep->createGLTexture(mRawDiscardLevel, mRawImage, usename, TRUE, mBoostLevel);
+	
 	setActive() ;
 
 	if (!needsToSaveRawImage())
@@ -1908,28 +1923,32 @@ void LLViewerFetchedTexture::updateVirtualSize()
 		addTextureStats(0.f, FALSE) ;//reset
 	}
 
-	for(U32 i = 0 ; i < mNumFaces ; i++)
-	{				
-		LLFace* facep = mFaceList[i] ;
-		if( facep )
-		{
-			LLDrawable* drawable = facep->getDrawable();
-			if (drawable)
+	for (U32 ch = 0; ch < LLRender::NUM_TEXTURE_CHANNELS; ++ch)
+	{
+		llassert(mNumFaces[ch] <= mFaceList[ch].size());
+
+		for(U32 i = 0 ; i < mNumFaces[ch]; i++)
+		{				
+			LLFace* facep = mFaceList[ch][i] ;
+			if( facep )
 			{
-				if(drawable->isRecentlyVisible())
+				LLDrawable* drawable = facep->getDrawable();
+				if (drawable)
 				{
-					if (getBoostLevel() == LLViewerTexture::BOOST_NONE && 
-						drawable->getVObj() && drawable->getVObj()->isSelected())
+					if(drawable->isRecentlyVisible())
 					{
-						setBoostLevel(LLViewerTexture::BOOST_SELECTED);
+						if (getBoostLevel() == LLViewerTexture::BOOST_NONE && 
+							drawable->getVObj() && drawable->getVObj()->isSelected())
+						{
+							setBoostLevel(LLViewerTexture::BOOST_SELECTED);
+						}
+						addTextureStats(facep->getVirtualSize()) ;
+						setAdditionalDecodePriority(facep->getImportanceToCamera()) ;
 					}
-					addTextureStats(facep->getVirtualSize()) ;
-					setAdditionalDecodePriority(facep->getImportanceToCamera()) ;
 				}
 			}
 		}
 	}
-
 	//reset whether or not a face was selected after 10 seconds
 	const F32 SELECTION_RESET_TIME = 10.f;
 
@@ -3005,190 +3024,6 @@ F32 LLViewerFetchedTexture::getElapsedLastReferencedSavedRawImageTime() const
 { 
 	return sCurrentTime - mLastReferencedSavedRawImageTime ;
 }
-//----------------------------------------------------------------------------------------------
-//atlasing
-//----------------------------------------------------------------------------------------------
-void LLViewerFetchedTexture::resetFaceAtlas()
-{
-	//Nothing should be done here.
-}
-
-//invalidate all atlas slots for this image.
-void LLViewerFetchedTexture::invalidateAtlas(BOOL rebuild_geom)
-{
-	for(U32 i = 0 ; i < mNumFaces ; i++)
-	{
-		LLFace* facep = mFaceList[i] ;
-		facep->removeAtlas() ;
-		if(rebuild_geom && facep->getDrawable() && facep->getDrawable()->getSpatialGroup())
-		{
-			facep->getDrawable()->getSpatialGroup()->setState(LLSpatialGroup::GEOM_DIRTY);
-		}
-	}
-}
-
-BOOL LLViewerFetchedTexture::insertToAtlas()
-{
-	if(!LLViewerTexture::sUseTextureAtlas)
-	{
-		return FALSE ;
-	}
-	if(getNumFaces() < 1)
-	{
-		return FALSE ;
-	}						
-	if(mGLTexturep->getDiscardLevelInAtlas() > 0 && mRawDiscardLevel >= mGLTexturep->getDiscardLevelInAtlas())
-	{
-		return FALSE ;
-	}
-	if(!LLTextureAtlasManager::getInstance()->canAddToAtlas(mRawImage->getWidth(), mRawImage->getHeight(), mRawImage->getComponents(), mGLTexturep->getTexTarget()))
-	{
-		return FALSE ;
-	}
-
-	BOOL ret = TRUE ;//if ret is set to false, will generate a gl texture for this image.
-	S32 raw_w = mRawImage->getWidth() ;
-	S32 raw_h = mRawImage->getHeight() ;
-	F32 xscale = 1.0f, yscale = 1.0f ;
-	LLPointer<LLTextureAtlasSlot> slot_infop;
-	LLTextureAtlasSlot* cur_slotp ;//no need to be smart pointer.
-	LLSpatialGroup* groupp ;
-	LLFace* facep;
-
-	//if the atlas slot pointers for some faces are null, process them later.
-	ll_face_list_t waiting_list ;
-	for(U32 i = 0 ; i < mNumFaces ; i++)
-	{
-		{
-			facep = mFaceList[i] ;			
-			
-			//face can not use atlas.
-			if(!facep->canUseAtlas())
-			{
-				if(facep->getAtlasInfo())
-				{
-					facep->removeAtlas() ;	
-				}
-				ret = FALSE ;
-				continue ;
-			}
-
-			//the atlas slot is updated
-			slot_infop = facep->getAtlasInfo() ;
-			groupp = facep->getDrawable()->getSpatialGroup() ;	
-
-			if(slot_infop) 
-			{
-				if(slot_infop->getSpatialGroup() != groupp)
-				{
-					if((cur_slotp = groupp->getCurUpdatingSlot(this))) //switch slot
-					{
-						facep->setAtlasInfo(cur_slotp) ;
-						facep->setAtlasInUse(TRUE) ;
-						continue ;
-					}
-					else //do not forget to update slot_infop->getSpatialGroup().
-					{
-						LLSpatialGroup* gp = slot_infop->getSpatialGroup() ;
-						gp->setCurUpdatingTime(gFrameCount) ;
-						gp->setCurUpdatingTexture(this) ;
-						gp->setCurUpdatingSlot(slot_infop) ;
-					}
-				}
-				else //same group
-				{
-					if(gFrameCount && slot_infop->getUpdatedTime() == gFrameCount)//slot is just updated
-					{
-						facep->setAtlasInUse(TRUE) ;
-						continue ;
-					}
-				}
-			}				
-			else
-			{
-				//if the slot is null, wait to process them later.
-				waiting_list.push_back(facep) ;
-				continue ;
-			}
-						
-			//----------
-			//insert to atlas
-			if(!slot_infop->getAtlas()->insertSubTexture(mGLTexturep, mRawDiscardLevel, mRawImage, slot_infop->getSlotCol(), slot_infop->getSlotRow()))			
-			{
-				
-				//the texture does not qualify to add to atlas, do not bother to try for other faces.
-				//invalidateAtlas();
-				return FALSE ;
-			}
-			
-			//update texture scale		
-			slot_infop->getAtlas()->getTexCoordScale(raw_w, raw_h, xscale, yscale) ;
-			slot_infop->setTexCoordScale(xscale, yscale) ;
-			slot_infop->setValid() ;
-			slot_infop->setUpdatedTime(gFrameCount) ;
-			
-			//update spatial group atlas info
-			groupp->setCurUpdatingTime(gFrameCount) ;
-			groupp->setCurUpdatingTexture(this) ;
-			groupp->setCurUpdatingSlot(slot_infop) ;
-
-			//make the face to switch to the atlas.
-			facep->setAtlasInUse(TRUE) ;
-		}
-	}
-
-	//process the waiting_list
-	for(std::vector<LLFace*>::iterator iter = waiting_list.begin(); iter != waiting_list.end(); ++iter)
-	{
-		facep = (LLFace*)*iter ;	
-		groupp = facep->getDrawable()->getSpatialGroup() ;
-
-		//check if this texture already inserted to atlas for this group
-		if((cur_slotp = groupp->getCurUpdatingSlot(this)))
-		{
-			facep->setAtlasInfo(cur_slotp) ;
-			facep->setAtlasInUse(TRUE) ;		
-			continue ;
-		}
-
-		//need to reserve a slot from atlas
-		slot_infop = LLTextureAtlasManager::getInstance()->reserveAtlasSlot(llmax(mFullWidth, mFullHeight), getComponents(), groupp, this) ;	
-
-		facep->setAtlasInfo(slot_infop) ;
-		
-		groupp->setCurUpdatingTime(gFrameCount) ;
-		groupp->setCurUpdatingTexture(this) ;
-		groupp->setCurUpdatingSlot(slot_infop) ;
-
-		//slot allocation failed.
-		if(!slot_infop || !slot_infop->getAtlas())
-		{			
-			ret = FALSE ;
-			facep->setAtlasInUse(FALSE) ;
-			continue ;
-		}
-				
-		//insert to atlas
-		if(!slot_infop->getAtlas()->insertSubTexture(mGLTexturep, mRawDiscardLevel, mRawImage, slot_infop->getSlotCol(), slot_infop->getSlotRow()))
-		{
-			//the texture does not qualify to add to atlas, do not bother to try for other faces.
-			ret = FALSE ;
-			//invalidateAtlas();
-			break ; 
-		}
-		
-		//update texture scale		
-		slot_infop->getAtlas()->getTexCoordScale(raw_w, raw_h, xscale, yscale) ;
-		slot_infop->setTexCoordScale(xscale, yscale) ;
-		slot_infop->setValid() ;
-		slot_infop->setUpdatedTime(gFrameCount) ;
-
-		//make the face to switch to the atlas.
-		facep->setAtlasInUse(TRUE) ;
-	}
-	
-	return ret ;
-}
 
 //----------------------------------------------------------------------------------------------
 //end of LLViewerFetchedTexture
@@ -3535,11 +3370,14 @@ BOOL LLViewerMediaTexture::findFaces()
 	LLViewerTexture* tex = gTextureList.findImage(mID) ;
 	if(tex) //this media is a parcel media for tex.
 	{
-		const ll_face_list_t* face_list = tex->getFaceList() ;
-		U32 end = tex->getNumFaces() ;
-		for(U32 i = 0 ; i < end ; i++)
+		for (U32 ch = 0; ch < LLRender::NUM_TEXTURE_CHANNELS; ++ch)
 		{
-			mMediaFaceList.push_back((*face_list)[i]) ;
+			const ll_face_list_t* face_list = tex->getFaceList(ch) ;
+			U32 end = tex->getNumFaces(ch) ;
+			for(U32 i = 0 ; i < end ; i++)
+			{
+				mMediaFaceList.push_back((*face_list)[i]) ;
+			}
 		}
 	}
 	
@@ -3604,7 +3442,7 @@ void LLViewerMediaTexture::addMediaToFace(LLFace* facep)
 		return ; //no need to add the face because the media is not in playing.
 	}
 
-	switchTexture(facep) ;
+	switchTexture(LLRender::DIFFUSE_MAP, facep) ;
 }
 	
 void LLViewerMediaTexture::removeMediaFromFace(LLFace* facep) 
@@ -3621,19 +3459,19 @@ void LLViewerMediaTexture::removeMediaFromFace(LLFace* facep)
 	}	
 
 	mIsPlaying = FALSE ; //set to remove the media from the face.
-	switchTexture(facep) ;
+	switchTexture(LLRender::DIFFUSE_MAP, facep) ;
 	mIsPlaying = TRUE ; //set the flag back.
 
-	if(getNumFaces() < 1) //no face referencing to this media
+	if(getTotalNumFaces() < 1) //no face referencing to this media
 	{
 		stopPlaying() ;
 	}
 }
 
 //virtual 
-void LLViewerMediaTexture::addFace(LLFace* facep) 
+void LLViewerMediaTexture::addFace(U32 ch, LLFace* facep) 
 {
-	LLViewerTexture::addFace(facep) ;
+	LLViewerTexture::addFace(ch, facep) ;
 
 	const LLTextureEntry* te = facep->getTextureEntry() ;
 	if(te && te->getID().notNull())
@@ -3660,9 +3498,9 @@ void LLViewerMediaTexture::addFace(LLFace* facep)
 }
 
 //virtual 
-void LLViewerMediaTexture::removeFace(LLFace* facep) 
+void LLViewerMediaTexture::removeFace(U32 ch, LLFace* facep) 
 {
-	LLViewerTexture::removeFace(facep) ;
+	LLViewerTexture::removeFace(ch, facep) ;
 
 	const LLTextureEntry* te = facep->getTextureEntry() ;
 	if(te && te->getID().notNull())
@@ -3680,24 +3518,35 @@ void LLViewerMediaTexture::removeFace(LLFace* facep)
 				}
 			}
 
-			//
-			//we have some trouble here: the texture of the face is changed.
-			//we need to find the former texture, and remove it from the list to avoid memory leaking.
-			if(!mNumFaces)
+			std::vector<const LLTextureEntry*> te_list;
+			
+			for (U32 ch = 0; ch < 3; ++ch)
 			{
-				mTextureList.clear() ;
-				return ;
+				//
+				//we have some trouble here: the texture of the face is changed.
+				//we need to find the former texture, and remove it from the list to avoid memory leaking.
+				
+				llassert(mNumFaces[ch] <= mFaceList[ch].size());
+
+				for(U32 j = 0 ; j < mNumFaces[ch] ; j++)
+				{
+					te_list.push_back(mFaceList[ch][j]->getTextureEntry());//all textures are in use.
+				}
 			}
-			S32 end = getNumFaces() ;
-			std::vector<const LLTextureEntry*> te_list(end) ;
-			S32 i = 0 ;			
-			for(U32 j = 0 ; j < mNumFaces ; j++)
+
+			if (te_list.empty())
 			{
-				te_list[i++] = mFaceList[j]->getTextureEntry() ;//all textures are in use.
+				mTextureList.clear() ;
+				return ;
 			}
+
+			S32 end = te_list.size();
+
 			for(std::list< LLPointer<LLViewerTexture> >::iterator iter = mTextureList.begin();
 				iter != mTextureList.end(); ++iter)
 			{
+				S32 i = 0;
+
 				for(i = 0 ; i < end ; i++)
 				{
 					if(te_list[i] && te_list[i]->getID() == (*iter)->getID())//the texture is in use.
@@ -3742,7 +3591,7 @@ void LLViewerMediaTexture::stopPlaying()
 	mIsPlaying = FALSE ;			
 }
 
-void LLViewerMediaTexture::switchTexture(LLFace* facep)
+void LLViewerMediaTexture::switchTexture(U32 ch, LLFace* facep)
 {
 	if(facep)
 	{
@@ -3758,7 +3607,7 @@ void LLViewerMediaTexture::switchTexture(LLFace* facep)
 
 		if(mIsPlaying) //old textures switch to the media texture
 		{
-			facep->switchTexture(this) ;
+			facep->switchTexture(ch, this) ;
 		}
 		else //switch to old textures.
 		{
@@ -3774,7 +3623,7 @@ void LLViewerMediaTexture::switchTexture(LLFace* facep)
 				{
 					tex = LLViewerFetchedTexture::sDefaultImagep ;
 				}
-				facep->switchTexture(tex) ;
+				facep->switchTexture(ch, tex) ;
 			}
 		}
 	}
@@ -3813,14 +3662,17 @@ void LLViewerMediaTexture::setPlaying(BOOL playing)
 
 		for(std::list< LLFace* >::iterator iter = mMediaFaceList.begin(); iter!= mMediaFaceList.end(); ++iter)
 		{
-			switchTexture(*iter) ;
+			switchTexture(LLRender::DIFFUSE_MAP, *iter) ;
 		}
 	}
 	else //stop playing this media
 	{
-		for(U32 i = mNumFaces ; i ; i--)
+		U32 ch = LLRender::DIFFUSE_MAP;
+		
+		llassert(mNumFaces[ch] <= mFaceList[ch].size());
+		for(U32 i = mNumFaces[ch] ; i ; i--)
 		{
-			switchTexture(mFaceList[i - 1]) ; //current face could be removed in this function.
+			switchTexture(ch, mFaceList[ch][i - 1]) ; //current face could be removed in this function.
 		}
 	}
 	return ;
@@ -3842,14 +3694,18 @@ F32 LLViewerMediaTexture::getMaxVirtualSize()
 
 	if(mIsPlaying) //media is playing
 	{
-		for(U32 i = 0 ; i < mNumFaces ; i++)
+		for (U32 ch = 0; ch < LLRender::NUM_TEXTURE_CHANNELS; ++ch)
 		{
-			LLFace* facep = mFaceList[i] ;
-			if(facep->getDrawable()->isRecentlyVisible())
+			llassert(mNumFaces[ch] <= mFaceList[ch].size());
+			for(U32 i = 0 ; i < mNumFaces[ch] ; i++)
 			{
-				addTextureStats(facep->getVirtualSize()) ;
-			}
-		}		
+				LLFace* facep = mFaceList[ch][i] ;
+				if(facep->getDrawable()->isRecentlyVisible())
+				{
+					addTextureStats(facep->getVirtualSize()) ;
+				}
+			}		
+		}
 	}
 	else //media is not in playing
 	{
diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h
index 2e7949e9a38f84d42ef8b42feba503d09baba45e..eb30139cb89a58760dda4b6a16104184d7750211 100644
--- a/indra/newview/llviewertexture.h
+++ b/indra/newview/llviewertexture.h
@@ -34,6 +34,7 @@
 #include "llgltypes.h"
 #include "llrender.h"
 #include "llmetricperformancetester.h"
+#include "llface.h"
 
 #include <map>
 #include <list>
@@ -41,7 +42,6 @@
 #define MIN_VIDEO_RAM_IN_MEGA_BYTES    32
 #define MAX_VIDEO_RAM_IN_MEGA_BYTES    512 // 512MB max for performance reasons.
 
-class LLFace;
 class LLImageGL ;
 class LLImageRaw;
 class LLViewerObject;
@@ -103,7 +103,6 @@ class LLViewerTexture : public LLTexture
 		DYNAMIC_TEXTURE,
 		FETCHED_TEXTURE,
 		LOD_TEXTURE,
-		ATLAS_TEXTURE,
 		INVALID_TEXTURE_TYPE
 	};
 
@@ -135,7 +134,6 @@ class LLViewerTexture : public LLTexture
 		AVATAR_SCRATCH_TEX,
 		DYNAMIC_TEX,
 		MEDIA,
-		ATLAS,
 		OTHER,
 		MAX_GL_IMAGE_CATEGORY
 	};
@@ -186,10 +184,11 @@ class LLViewerTexture : public LLTexture
 	S32 getFullHeight() const { return mFullHeight; }	
 	/*virtual*/ void setKnownDrawSize(S32 width, S32 height);
 
-	virtual void addFace(LLFace* facep) ;
-	virtual void removeFace(LLFace* facep) ; 
-	S32 getNumFaces() const;
-	const ll_face_list_t* getFaceList() const {return &mFaceList;}
+	virtual void addFace(U32 channel, LLFace* facep) ;
+	virtual void removeFace(U32 channel, LLFace* facep) ; 
+	S32 getTotalNumFaces() const;
+	S32 getNumFaces(U32 ch) const;
+	const ll_face_list_t* getFaceList(U32 channel) const {llassert(channel < LLRender::NUM_TEXTURE_CHANNELS); return &mFaceList[channel];}
 
 	virtual void addVolume(LLVOVolume* volumep);
 	virtual void removeVolume(LLVOVolume* volumep);
@@ -234,10 +233,9 @@ class LLViewerTexture : public LLTexture
 	BOOL       isJustBound()const ;
 	void       forceUpdateBindStats(void) const;
 
-	U32        getTexelsInAtlas() const ;
 	U32        getTexelsInGLTexture() const ;
 	BOOL       isGLTextureCreated() const ;
-	S32        getDiscardLevelInAtlas() const ;
+	
 	//---------------------------------------------------------------------------------------------
 	//end of functions to access LLImageGL
 	//---------------------------------------------------------------------------------------------
@@ -292,8 +290,8 @@ class LLViewerTexture : public LLTexture
 	LLPointer<LLImageGL> mGLTexturep ;
 	S8 mDontDiscard;			// Keep full res version of this image (for UI, etc)
 
-	ll_face_list_t    mFaceList ; //reverse pointer pointing to the faces using this image as texture
-	U32               mNumFaces ;
+	ll_face_list_t    mFaceList[LLRender::NUM_TEXTURE_CHANNELS]; //reverse pointer pointing to the faces using this image as texture
+	U32               mNumFaces[LLRender::NUM_TEXTURE_CHANNELS];
 	LLFrameTimer      mLastFaceListUpdateTimer ;
 
 	ll_volume_list_t  mVolumeList;
@@ -335,8 +333,7 @@ class LLViewerTexture : public LLTexture
 	static S32 sMaxSmallImageSize ;
 	static BOOL sFreezeImageScalingDown ;//do not scale down image res if set.
 	static F32  sCurrentTime ;
-	static BOOL sUseTextureAtlas ;
-
+	
 	enum EDebugTexels
 	{
 		DEBUG_TEXELS_OFF,
@@ -515,11 +512,6 @@ class LLViewerFetchedTexture : public LLViewerTexture
 	void saveRawImage() ;
 	void setCachedRawImage() ;
 
-	//for atlas
-	void resetFaceAtlas() ;
-	void invalidateAtlas(BOOL rebuild_geom) ;
-	BOOL insertToAtlas() ;
-
 private:
 	BOOL  mFullyLoaded;
 	BOOL  mInDebug;
@@ -661,12 +653,12 @@ class LLViewerMediaTexture : public LLViewerTexture
 	void addMediaToFace(LLFace* facep) ;
 	void removeMediaFromFace(LLFace* facep) ;
 
-	/*virtual*/ void addFace(LLFace* facep) ;
-	/*virtual*/ void removeFace(LLFace* facep) ; 
+	/*virtual*/ void addFace(U32 ch, LLFace* facep) ;
+	/*virtual*/ void removeFace(U32 ch, LLFace* facep) ; 
 
 	/*virtual*/ F32  getMaxVirtualSize() ;
 private:
-	void switchTexture(LLFace* facep) ;
+	void switchTexture(U32 ch, LLFace* facep) ;
 	BOOL findFaces() ;
 	void stopPlaying() ;
 
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index f2ede46cfcd7cbd456676d56c8ed0c1a2e447252..44eefd3c4abdf634df51d439f7398b8ec6b88938 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -556,37 +556,9 @@ void LLVOVolume::animateTextures()
 				tex_mat.setIdentity();
 				LLVector3 trans ;
 
-				if(facep->isAtlasInUse())
-				{
-					//
-					//if use atlas for animated texture
-					//apply the following transform to the animation matrix.
-					//
-
-					F32 tcoord_xoffset = 0.f ;
-					F32 tcoord_yoffset = 0.f ;
-					F32 tcoord_xscale = 1.f ;
-					F32 tcoord_yscale = 1.f ;			
-					if(facep->isAtlasInUse())
-					{
-						const LLVector2* tmp = facep->getTexCoordOffset() ;
-						tcoord_xoffset = tmp->mV[0] ; 
-						tcoord_yoffset = tmp->mV[1] ;
-
-						tmp = facep->getTexCoordScale() ;
-						tcoord_xscale = tmp->mV[0] ; 
-						tcoord_yscale = tmp->mV[1] ;	
-					}
-					trans.set(LLVector3(tcoord_xoffset + tcoord_xscale * (off_s+0.5f), tcoord_yoffset + tcoord_yscale * (off_t+0.5f), 0.f));
-
-					tex_mat.translate(LLVector3(-(tcoord_xoffset + tcoord_xscale * 0.5f), -(tcoord_yoffset + tcoord_yscale * 0.5f), 0.f));
-				}
-				else	//non atlas
-				{
-					trans.set(LLVector3(off_s+0.5f, off_t+0.5f, 0.f));			
-					tex_mat.translate(LLVector3(-0.5f, -0.5f, 0.f));
-				}
-
+				trans.set(LLVector3(off_s+0.5f, off_t+0.5f, 0.f));			
+				tex_mat.translate(LLVector3(-0.5f, -0.5f, 0.f));
+				
 				LLVector3 scale(scale_s, scale_t, 1.f);			
 				LLQuaternion quat;
 				quat.setQuat(rot, 0, 0, -1.f);
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index abf23935baef3610e5be88590a6cade33f2b216c..9f179c0df0b790febe9eafddcb053d1e5ce08607 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -2700,16 +2700,6 @@
                  parameter="TextureLoadFullRes" />
             </menu_item_check>
             <menu_item_check
-             label="Texture Atlas (experimental)"
-             name="Texture Atlas">
-              <menu_item_check.on_check
-               function="CheckControl"
-               parameter="EnableTextureAtlas" />
-              <menu_item_check.on_click
-               function="ToggleControl"
-               parameter="EnableTextureAtlas" />
-            </menu_item_check>
-              <menu_item_check
              label="Render Attached Lights"
              name="Render Attached Lights">
                 <menu_item_check.on_check