diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp
index b2da5bb9f8fe86ec5eaa01f52eacbee5c5d72a23..473e55ce82383405638a6d5c9ad3ec8a655b44a8 100644
--- a/indra/newview/llface.cpp
+++ b/indra/newview/llface.cpp
@@ -1106,7 +1106,7 @@ void LLFace::getPlanarProjectedParams(LLQuaternion* face_rot, LLVector3* face_po
 
 // Returns the necessary texture transform to align this face's TE to align_to's TE
 bool LLFace::calcAlignedPlanarTE(const LLFace* align_to,  LLVector2* res_st_offset, 
-								 LLVector2* res_st_scale, F32* res_st_rot) const
+								 LLVector2* res_st_scale, F32* res_st_rot, LLRender::eTexIndex map) const
 {
 	if (!align_to)
 	{
@@ -1119,6 +1119,43 @@ bool LLFace::calcAlignedPlanarTE(const LLFace* align_to,  LLVector2* res_st_offs
 		return false;
 	}
 
+    F32 map_rot = 0.f, map_scaleS = 0.f, map_scaleT = 0.f, map_offsS = 0.f, map_offsT = 0.f;
+
+    switch (map)
+    {
+    case LLRender::DIFFUSE_MAP:
+        map_rot = orig_tep->getRotation();
+        map_scaleS = orig_tep->mScaleS;
+        map_scaleT = orig_tep->mScaleT;
+        map_offsS = orig_tep->mOffsetS;
+        map_offsT = orig_tep->mOffsetT;
+        break;
+    case LLRender::NORMAL_MAP:
+        if (orig_tep->getMaterialParams()->getNormalID().isNull())
+        {
+            return false;
+        }
+        map_rot = orig_tep->getMaterialParams()->getNormalRotation();
+        map_scaleS = orig_tep->getMaterialParams()->getNormalRepeatX();
+        map_scaleT = orig_tep->getMaterialParams()->getNormalRepeatY();
+        map_offsS = orig_tep->getMaterialParams()->getNormalOffsetX();
+        map_offsT = orig_tep->getMaterialParams()->getNormalOffsetY();
+        break;
+    case LLRender::SPECULAR_MAP:
+        if (orig_tep->getMaterialParams()->getSpecularID().isNull())
+        {
+            return false;
+        }
+        map_rot = orig_tep->getMaterialParams()->getSpecularRotation();
+        map_scaleS = orig_tep->getMaterialParams()->getSpecularRepeatX();
+        map_scaleT = orig_tep->getMaterialParams()->getSpecularRepeatY();
+        map_offsS = orig_tep->getMaterialParams()->getSpecularOffsetX();
+        map_offsT = orig_tep->getMaterialParams()->getSpecularOffsetY();
+        break;
+    default: /*make compiler happy*/
+        break;
+    }
+
 	LLVector3 orig_pos, this_pos;
 	LLQuaternion orig_face_rot, this_face_rot;
 	F32 orig_proj_scale, this_proj_scale;
@@ -1126,7 +1163,7 @@ bool LLFace::calcAlignedPlanarTE(const LLFace* align_to,  LLVector2* res_st_offs
 	getPlanarProjectedParams(&this_face_rot, &this_pos, &this_proj_scale);
 
 	// The rotation of "this face's" texture:
-	LLQuaternion orig_st_rot = LLQuaternion(orig_tep->getRotation(), LLVector3::z_axis) * orig_face_rot;
+	LLQuaternion orig_st_rot = LLQuaternion(map_rot, LLVector3::z_axis) * orig_face_rot;
 	LLQuaternion this_st_rot = orig_st_rot * ~this_face_rot;
 	F32 x_ang, y_ang, z_ang;
 	this_st_rot.getEulerAngles(&x_ang, &y_ang, &z_ang);
@@ -1134,10 +1171,10 @@ bool LLFace::calcAlignedPlanarTE(const LLFace* align_to,  LLVector2* res_st_offs
 
 	// Offset and scale of "this face's" texture:
 	LLVector3 centers_dist = (this_pos - orig_pos) * ~orig_st_rot;
-	LLVector3 st_scale(orig_tep->mScaleS, orig_tep->mScaleT, 1.f);
+	LLVector3 st_scale(map_scaleS, map_scaleT, 1.f);
 	st_scale *= orig_proj_scale;
 	centers_dist.scaleVec(st_scale);
-	LLVector2 orig_st_offset(orig_tep->mOffsetS, orig_tep->mOffsetT);
+	LLVector2 orig_st_offset(map_offsS, map_offsT);
 
 	*res_st_offset = orig_st_offset + (LLVector2)centers_dist;
 	res_st_offset->mV[VX] -= (S32)res_st_offset->mV[VX];
diff --git a/indra/newview/llface.h b/indra/newview/llface.h
index 736d45b7ad42888597692e66a30a4a5a925439b9..10c2b31b6f31948f016589a1ab8f9e9fddd0f6fd 100644
--- a/indra/newview/llface.h
+++ b/indra/newview/llface.h
@@ -114,7 +114,7 @@ class LLFace : public LLTrace::MemTrackableNonVirtual<LLFace, 16>
 	LLVector2       surfaceToTexture(LLVector2 surface_coord, const LLVector4a& position, const LLVector4a& normal);
 	void 			getPlanarProjectedParams(LLQuaternion* face_rot, LLVector3* face_pos, F32* scale) const;
 	bool			calcAlignedPlanarTE(const LLFace* align_to, LLVector2* st_offset,
-										LLVector2* st_scale, F32* st_rot) const;
+										LLVector2* st_scale, F32* st_rot, LLRender::eTexIndex map = LLRender::DIFFUSE_MAP) const;
 	
 	U32				getState()			const	{ return mState; }
 	void			setState(U32 state)			{ mState |= state; }
diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp
index 5e6a44c09da11b603ac3a2c890391d17475337b3..5cbd1e2f99abd3d6d922cec06955eae2d8ee60f2 100644
--- a/indra/newview/llpanelface.cpp
+++ b/indra/newview/llpanelface.cpp
@@ -82,6 +82,8 @@ const S32 ALPHAMODE_MASK = 2;		// Alpha masking mode
 const S32 BUMPY_TEXTURE = 18;		// use supplied normal map
 const S32 SHINY_TEXTURE = 4;		// use supplied specular map
 
+BOOST_STATIC_ASSERT(MATTYPE_DIFFUSE == LLRender::DIFFUSE_MAP && MATTYPE_NORMAL == LLRender::NORMAL_MAP && MATTYPE_SPECULAR == LLRender::SPECULAR_MAP);
+
 //
 // "Use texture" label for normal/specular type comboboxes
 // Filled in at initialization from translated strings
@@ -154,6 +156,7 @@ BOOL	LLPanelFace::postBuild()
 	childSetCommitCallback("maskcutoff",&LLPanelFace::onCommitMaterialMaskCutoff, this);
 
 	childSetAction("button align",&LLPanelFace::onClickAutoFix,this);
+	childSetAction("button align textures", &LLPanelFace::onAlignTexture, this);
 
 	LLTextureCtrl*	mTextureCtrl;
 	LLTextureCtrl*	mShinyTextureCtrl;
@@ -439,11 +442,28 @@ struct LLPanelFaceSetTEFunctor : public LLSelectedTEFunctor
 	{
 		BOOL valid;
 		F32 value;
-		LLSpinCtrl*	ctrlTexScaleS = mPanel->getChild<LLSpinCtrl>("TexScaleU");
-		LLSpinCtrl*	ctrlTexScaleT = mPanel->getChild<LLSpinCtrl>("TexScaleV");
-		LLSpinCtrl*	ctrlTexOffsetS = mPanel->getChild<LLSpinCtrl>("TexOffsetU");
-		LLSpinCtrl*	ctrlTexOffsetT = mPanel->getChild<LLSpinCtrl>("TexOffsetV");
-		LLSpinCtrl*	ctrlTexRotation = mPanel->getChild<LLSpinCtrl>("TexRot");
+
+        LLRadioGroup * radio_mat_type = mPanel->getChild<LLRadioGroup>("radio_material_type");
+        std::string prefix;
+        switch (radio_mat_type->getSelectedIndex())
+        {
+        case MATTYPE_DIFFUSE:
+            prefix = "Tex";
+            break;
+        case MATTYPE_NORMAL:
+            prefix = "bumpy";
+            break;
+        case MATTYPE_SPECULAR:
+            prefix = "shiny";
+            break;
+        }
+        
+        LLSpinCtrl * ctrlTexScaleS = mPanel->getChild<LLSpinCtrl>(prefix + "ScaleU");
+        LLSpinCtrl * ctrlTexScaleT = mPanel->getChild<LLSpinCtrl>(prefix + "ScaleV");
+        LLSpinCtrl * ctrlTexOffsetS = mPanel->getChild<LLSpinCtrl>(prefix + "OffsetU");
+        LLSpinCtrl * ctrlTexOffsetT = mPanel->getChild<LLSpinCtrl>(prefix + "OffsetV");
+        LLSpinCtrl * ctrlTexRotation = mPanel->getChild<LLSpinCtrl>(prefix + "Rot");
+
 		LLComboBox*	comboTexGen = mPanel->getChild<LLComboBox>("combobox texgen");
 		LLCheckBoxCtrl*	cb_planar_align = mPanel->getChild<LLCheckBoxCtrl>("checkbox planar align");
 		bool align_planar = (cb_planar_align && cb_planar_align->get());
@@ -466,8 +486,8 @@ struct LLPanelFaceSetTEFunctor : public LLSelectedTEFunctor
 
 				if (align_planar) 
 				{
-					LLPanelFace::LLSelectedTEMaterial::setNormalRepeatX(mPanel, value, te);
-					LLPanelFace::LLSelectedTEMaterial::setSpecularRepeatX(mPanel, value, te);
+					LLPanelFace::LLSelectedTEMaterial::setNormalRepeatX(mPanel, value, te, object->getID());
+					LLPanelFace::LLSelectedTEMaterial::setSpecularRepeatX(mPanel, value, te, object->getID());
 				}
 			}
 		}
@@ -491,8 +511,8 @@ struct LLPanelFaceSetTEFunctor : public LLSelectedTEFunctor
 
 				if (align_planar) 
 				{
-					LLPanelFace::LLSelectedTEMaterial::setNormalRepeatY(mPanel, value, te);
-					LLPanelFace::LLSelectedTEMaterial::setSpecularRepeatY(mPanel, value, te);
+					LLPanelFace::LLSelectedTEMaterial::setNormalRepeatY(mPanel, value, te, object->getID());
+					LLPanelFace::LLSelectedTEMaterial::setSpecularRepeatY(mPanel, value, te, object->getID());
 				}
 			}
 		}
@@ -507,8 +527,8 @@ struct LLPanelFaceSetTEFunctor : public LLSelectedTEFunctor
 
 				if (align_planar) 
 				{
-					LLPanelFace::LLSelectedTEMaterial::setNormalOffsetX(mPanel, value, te);
-					LLPanelFace::LLSelectedTEMaterial::setSpecularOffsetX(mPanel, value, te);
+					LLPanelFace::LLSelectedTEMaterial::setNormalOffsetX(mPanel, value, te, object->getID());
+					LLPanelFace::LLSelectedTEMaterial::setSpecularOffsetX(mPanel, value, te, object->getID());
 				}
 			}
 		}
@@ -523,8 +543,8 @@ struct LLPanelFaceSetTEFunctor : public LLSelectedTEFunctor
 
 				if (align_planar) 
 				{
-					LLPanelFace::LLSelectedTEMaterial::setNormalOffsetY(mPanel, value, te);
-					LLPanelFace::LLSelectedTEMaterial::setSpecularOffsetY(mPanel, value, te);
+					LLPanelFace::LLSelectedTEMaterial::setNormalOffsetY(mPanel, value, te, object->getID());
+					LLPanelFace::LLSelectedTEMaterial::setSpecularOffsetY(mPanel, value, te, object->getID());
 				}
 			}
 		}
@@ -539,8 +559,8 @@ struct LLPanelFaceSetTEFunctor : public LLSelectedTEFunctor
 
 				if (align_planar) 
 				{
-					LLPanelFace::LLSelectedTEMaterial::setNormalRotation(mPanel, value, te);
-					LLPanelFace::LLSelectedTEMaterial::setSpecularRotation(mPanel, value, te);
+					LLPanelFace::LLSelectedTEMaterial::setNormalRotation(mPanel, value, te, object->getID());
+					LLPanelFace::LLSelectedTEMaterial::setSpecularRotation(mPanel, value, te, object->getID());
 				}
 			}
 		}
@@ -612,6 +632,68 @@ struct LLPanelFaceSetAlignedTEFunctor : public LLSelectedTEFunctor
 	LLFace* mCenterFace;
 };
 
+struct LLPanelFaceSetAlignedConcreteTEFunctor : public LLSelectedTEFunctor
+{
+    LLPanelFaceSetAlignedConcreteTEFunctor(LLPanelFace* panel, LLFace* center_face, LLRender::eTexIndex map) :
+        mPanel(panel),
+        mChefFace(center_face),
+        mMap(map)
+    {}
+
+    virtual bool apply(LLViewerObject* object, S32 te)
+    {
+        LLFace* facep = object->mDrawable->getFace(te);
+        if (!facep)
+        {
+            return true;
+        }
+
+        if (facep->getViewerObject()->getVolume()->getNumVolumeFaces() <= te)
+        {
+            return true;
+        }
+
+        if (mChefFace != facep)
+        {
+            LLVector2 uv_offset, uv_scale;
+            F32 uv_rot;
+            if (facep->calcAlignedPlanarTE(mChefFace, &uv_offset, &uv_scale, &uv_rot, mMap))
+            {
+                switch (mMap)
+                {
+                case LLRender::DIFFUSE_MAP:
+                        object->setTEOffset(te, uv_offset.mV[VX], uv_offset.mV[VY]);
+                        object->setTEScale(te, uv_scale.mV[VX], uv_scale.mV[VY]);
+                        object->setTERotation(te, uv_rot);
+                    break;
+                case LLRender::NORMAL_MAP:
+                        LLPanelFace::LLSelectedTEMaterial::setNormalRotation(mPanel, uv_rot, te, object->getID());
+                        LLPanelFace::LLSelectedTEMaterial::setNormalOffsetX(mPanel, uv_offset.mV[VX], te, object->getID());
+                        LLPanelFace::LLSelectedTEMaterial::setNormalOffsetY(mPanel, uv_offset.mV[VY], te, object->getID());
+                        LLPanelFace::LLSelectedTEMaterial::setNormalRepeatX(mPanel, uv_scale.mV[VX], te, object->getID());
+                        LLPanelFace::LLSelectedTEMaterial::setNormalRepeatY(mPanel, uv_scale.mV[VY], te, object->getID());
+                    break;
+                case LLRender::SPECULAR_MAP:
+                        LLPanelFace::LLSelectedTEMaterial::setSpecularRotation(mPanel, uv_rot, te, object->getID());
+                        LLPanelFace::LLSelectedTEMaterial::setSpecularOffsetX(mPanel, uv_offset.mV[VX], te, object->getID());
+                        LLPanelFace::LLSelectedTEMaterial::setSpecularOffsetY(mPanel, uv_offset.mV[VY], te, object->getID());
+                        LLPanelFace::LLSelectedTEMaterial::setSpecularRepeatX(mPanel, uv_scale.mV[VX], te, object->getID());
+                        LLPanelFace::LLSelectedTEMaterial::setSpecularRepeatY(mPanel, uv_scale.mV[VY], te, object->getID());
+                    break;
+                default: /*make compiler happy*/
+                    break;
+                }
+            }
+        }
+        
+        return true;
+    }
+private:
+    LLPanelFace* mPanel;
+    LLFace* mChefFace;
+    LLRender::eTexIndex mMap;
+};
+
 // Functor that tests if a face is aligned to mCenterFace
 struct LLPanelFaceGetIsAlignedTEFunctor : public LLSelectedTEFunctor
 {
@@ -697,6 +779,17 @@ void LLPanelFace::sendTextureInfo()
 	LLSelectMgr::getInstance()->getSelection()->applyToObjects(&sendfunc);
 }
 
+void LLPanelFace::alignTestureLayer()
+{
+    LLFace* last_face = NULL;
+    bool identical_face = false;
+    LLSelectedTE::getFace(last_face, identical_face);
+
+    LLRadioGroup * radio_mat_type = getChild<LLRadioGroup>("radio_material_type");
+    LLPanelFaceSetAlignedConcreteTEFunctor setfunc(this, last_face, static_cast<LLRender::eTexIndex>(radio_mat_type->getSelectedIndex()));
+    LLSelectMgr::getInstance()->getSelection()->applyToTEs(&setfunc);
+}
+
 void LLPanelFace::getState()
 {
 	updateUI();
@@ -983,6 +1076,7 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)
 			bool enabled = (editable && isIdenticalPlanarTexgen());
 			childSetValue("checkbox planar align", align_planar && enabled);
 			childSetEnabled("checkbox planar align", enabled);
+			childSetEnabled("button align textures", enabled && LLSelectMgr::getInstance()->getSelection()->getObjectCount() > 1);
 
 			if (align_planar && enabled)
 			{
@@ -2129,7 +2223,18 @@ void LLPanelFace::onCommitMaterialBumpyRot(LLUICtrl* ctrl, void* userdata)
 	}
 	else
 	{
-		LLSelectedTEMaterial::setNormalRotation(self,self->getCurrentBumpyRot() * DEG_TO_RAD);
+        if ((bool)self->childGetValue("checkbox planar align").asBoolean())
+        {
+            LLFace* last_face = NULL;
+            bool identical_face = false;
+            LLSelectedTE::getFace(last_face, identical_face);
+            LLPanelFaceSetAlignedTEFunctor setfunc(self, last_face);
+            LLSelectMgr::getInstance()->getSelection()->applyToTEs(&setfunc);
+        }
+        else
+        {
+            LLSelectedTEMaterial::setNormalRotation(self, self->getCurrentBumpyRot() * DEG_TO_RAD);
+        }
 	}
 }
 
@@ -2146,7 +2251,18 @@ void LLPanelFace::onCommitMaterialShinyRot(LLUICtrl* ctrl, void* userdata)
 	}
 	else
 	{
-		LLSelectedTEMaterial::setSpecularRotation(self,self->getCurrentShinyRot() * DEG_TO_RAD);
+        if ((bool)self->childGetValue("checkbox planar align").asBoolean())
+        {
+            LLFace* last_face = NULL;
+            bool identical_face = false;
+            LLSelectedTE::getFace(last_face, identical_face);
+            LLPanelFaceSetAlignedTEFunctor setfunc(self, last_face);
+            LLSelectMgr::getInstance()->getSelection()->applyToTEs(&setfunc);
+        }
+        else
+        {
+            LLSelectedTEMaterial::setSpecularRotation(self, self->getCurrentShinyRot() * DEG_TO_RAD);
+        }
 	}
 }
 
@@ -2403,6 +2519,11 @@ void LLPanelFace::onClickAutoFix(void* userdata)
 	LLSelectMgr::getInstance()->getSelection()->applyToObjects(&sendfunc);
 }
 
+void LLPanelFace::onAlignTexture(void* userdata)
+{
+    LLPanelFace* self = (LLPanelFace*)userdata;
+    self->alignTestureLayer();
+}
 
 
 // TODO: I don't know who put these in or what these are for???
diff --git a/indra/newview/llpanelface.h b/indra/newview/llpanelface.h
index 0b40d7d41a124d27ce8ce96f2598e9f064c4be14..2d57d89a44f5622fff5ccab80fc47cba0ff3caff 100644
--- a/indra/newview/llpanelface.h
+++ b/indra/newview/llpanelface.h
@@ -126,6 +126,7 @@ class LLPanelFace : public LLPanel
 	void			sendFullbright();		// applies and sends full bright
 	void        sendGlow();
 	void			sendMedia();
+    void            alignTestureLayer();
 
 	// this function is to return TRUE if the drag should succeed.
 	static BOOL onDragTexture(LLUICtrl* ctrl, LLInventoryItem* item);
@@ -202,6 +203,7 @@ class LLPanelFace : public LLPanel
 	static void		onCommitPlanarAlign(		LLUICtrl* ctrl, void* userdata);
 	static void		onCommitRepeatsPerMeter(	LLUICtrl* ctrl, void* userinfo);
 	static void		onClickAutoFix(void*);
+    static void		onAlignTexture(void*);
 
 	static F32     valueGlow(LLViewerObject* object, S32 face);
 
diff --git a/indra/newview/skins/default/xui/en/panel_tools_texture.xml b/indra/newview/skins/default/xui/en/panel_tools_texture.xml
index 67b99ca5880dd032370a2c92c30c3bea68cbd4bd..90f32ae4526b85e238b745cb279abbb6cf8f2c64 100644
--- a/indra/newview/skins/default/xui/en/panel_tools_texture.xml
+++ b/indra/newview/skins/default/xui/en/panel_tools_texture.xml
@@ -770,6 +770,17 @@
              tool_tip="Align textures on all selected faces with the last selected face. Requires Planar texture mapping."
              top_delta="16"
              width="260" />
+			<button
+			 left="10"
+			 top="222"
+			 height="20"
+			 label="Align"
+			 label_selected="Align current texture layers"
+			 layout="topleft"
+			 name="button align textures"
+			 top_delta="0"
+			 tool_tip="Align current texture layers"
+			 width="66" />
             <web_browser
              visible="false"
              enabled="false"