From 28f9fb06a9f4cb9edccb2ff8132c7f6a9b27c060 Mon Sep 17 00:00:00 2001
From: Dave Parks <davep@lindenlab.com>
Date: Sat, 20 Nov 2021 18:49:19 +0000
Subject: [PATCH] SL-16289 Rigged mesh rendering overhaul

---
 indra/llmath/llmatrix4a.h                     |    9 +-
 indra/llprimitive/llmodel.cpp                 |   35 +
 indra/llprimitive/llmodel.h                   |    3 +
 indra/llrender/llglslshader.cpp               |   13 +
 indra/llrender/llglslshader.h                 |    8 +-
 indra/llrender/llrender.cpp                   |    2 +-
 indra/llrender/llshadermgr.cpp                |    5 +-
 indra/llrender/llshadermgr.h                  |    2 +-
 indra/llwindow/llwindowwin32.cpp              |    2 +-
 indra/llxml/llcontrol.h                       |    1 +
 indra/newview/app_settings/settings.xml       |   24 +-
 .../shaders/class1/deferred/alphaV.glsl       |    4 +-
 .../shaders/class1/deferred/bumpSkinnedV.glsl |   64 -
 .../shaders/class1/deferred/bumpV.glsl        |   22 +-
 .../shaders/class1/deferred/diffuseV.glsl     |   20 +-
 .../class1/deferred/fullbrightShinyV.glsl     |   18 +-
 .../shaders/class1/deferred/fullbrightV.glsl  |   13 +-
 ...nedV.glsl => shadowAlphaMaskSkinnedV.glsl} |   45 +-
 .../shadowSkinnedV.glsl}                      |   41 +-
 .../treeShadowSkinnedV.glsl}                  |   31 +-
 .../class1/interface/debugSkinnedV.glsl       |   41 +
 .../occlusionSkinnedV.glsl}                   |   25 +-
 .../class1/lighting/lightFullbrightF.glsl     |    1 +
 .../shaders/class1/objects/bumpV.glsl         |   13 +
 .../shaders/class1/objects/emissiveV.glsl     |   20 +-
 .../objects/fullbrightShinySkinnedV.glsl      |   71 -
 .../class1/objects/fullbrightShinyV.glsl      |   17 +-
 .../shaders/class1/objects/fullbrightV.glsl   |   17 +-
 .../class1/objects/shinySimpleSkinnedV.glsl   |   66 -
 .../shaders/class1/objects/shinyV.glsl        |   20 +-
 .../shaders/class1/objects/simpleV.glsl       |   18 +-
 indra/newview/llappviewer.cpp                 |    5 +
 indra/newview/lldrawable.cpp                  |   15 -
 indra/newview/lldrawpool.cpp                  |  119 +-
 indra/newview/lldrawpool.h                    |   41 +-
 indra/newview/lldrawpoolalpha.cpp             |  391 ++--
 indra/newview/lldrawpoolalpha.h               |   22 +-
 indra/newview/lldrawpoolavatar.cpp            | 2023 ++---------------
 indra/newview/lldrawpoolavatar.h              |  202 +-
 indra/newview/lldrawpoolbump.cpp              |  309 ++-
 indra/newview/lldrawpoolbump.h                |    7 +-
 indra/newview/lldrawpoolmaterials.cpp         |   66 +-
 indra/newview/lldrawpoolmaterials.h           |   18 +-
 indra/newview/lldrawpoolsimple.cpp            |  511 +++--
 indra/newview/lldrawpoolsimple.h              |   33 +-
 indra/newview/llface.cpp                      |  133 +-
 indra/newview/llface.h                        |   10 +-
 indra/newview/llmeshrepository.cpp            |    2 +-
 indra/newview/llspatialpartition.cpp          |   25 +-
 indra/newview/llspatialpartition.h            |   21 +-
 indra/newview/llviewerdisplay.cpp             |    7 +-
 indra/newview/llviewershadermgr.cpp           | 1245 ++++------
 indra/newview/llviewershadermgr.h             |   25 -
 indra/newview/llvoavatar.cpp                  |   49 +
 indra/newview/llvoavatar.h                    |   23 +
 indra/newview/llvovolume.cpp                  |  470 ++--
 indra/newview/pipeline.cpp                    |  462 ++--
 indra/newview/pipeline.h                      |   35 +-
 58 files changed, 2535 insertions(+), 4405 deletions(-)
 delete mode 100644 indra/newview/app_settings/shaders/class1/deferred/bumpSkinnedV.glsl
 rename indra/newview/app_settings/shaders/class1/deferred/{diffuseSkinnedV.glsl => shadowAlphaMaskSkinnedV.glsl} (69%)
 rename indra/newview/app_settings/shaders/class1/{objects/simpleSkinnedV.glsl => deferred/shadowSkinnedV.glsl} (59%)
 rename indra/newview/app_settings/shaders/class1/{objects/emissiveSkinnedV.glsl => deferred/treeShadowSkinnedV.glsl} (76%)
 create mode 100644 indra/newview/app_settings/shaders/class1/interface/debugSkinnedV.glsl
 rename indra/newview/app_settings/shaders/class1/{objects/fullbrightSkinnedV.glsl => interface/occlusionSkinnedV.glsl} (73%)
 delete mode 100644 indra/newview/app_settings/shaders/class1/objects/fullbrightShinySkinnedV.glsl
 delete mode 100644 indra/newview/app_settings/shaders/class1/objects/shinySimpleSkinnedV.glsl

diff --git a/indra/llmath/llmatrix4a.h b/indra/llmath/llmatrix4a.h
index 5291a05607b..2cf50e9cd2b 100644
--- a/indra/llmath/llmatrix4a.h
+++ b/indra/llmath/llmatrix4a.h
@@ -78,8 +78,15 @@ class LLMatrix4a
 		mMatrix[1] = _mm_loadu_ps(src.mMatrix[1]);
 		mMatrix[2] = _mm_loadu_ps(src.mMatrix[2]);
 		mMatrix[3] = _mm_loadu_ps(src.mMatrix[3]);
-		
 	}
+    
+    inline void loadu(const F32* src)
+    {
+        mMatrix[0] = _mm_loadu_ps(src);
+        mMatrix[1] = _mm_loadu_ps(src+4);
+        mMatrix[2] = _mm_loadu_ps(src+8);
+        mMatrix[3] = _mm_loadu_ps(src+12);
+    }
 
 	inline void loadu(const LLMatrix3& src)
 	{
diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp
index a23b991f1d4..dd37b8ce0bf 100644
--- a/indra/llprimitive/llmodel.cpp
+++ b/indra/llprimitive/llmodel.cpp
@@ -31,6 +31,7 @@
 #include "llconvexdecomposition.h"
 #include "llsdserialize.h"
 #include "llvector4a.h"
+#include "llmd5.h"
 
 #ifdef LL_USESYSTEMLIBS
 # include <zlib.h>
@@ -1451,6 +1452,8 @@ void LLMeshSkinInfo::fromLLSD(LLSD& skin)
 	{
 		mLockScaleIfJointPosition = false;
 	}
+
+    updateHash();
 }
 
 LLSD LLMeshSkinInfo::asLLSD(bool include_joints, bool lock_scale_if_joint_position) const
@@ -1502,6 +1505,38 @@ LLSD LLMeshSkinInfo::asLLSD(bool include_joints, bool lock_scale_if_joint_positi
 	return ret;
 }
 
+void LLMeshSkinInfo::updateHash()
+{
+    //  get hash of data relevant to render batches
+    LLMD5 hash;
+
+    //mJointNames
+    for (auto& name : mJointNames)
+    {
+        hash.update(name);
+    }
+    
+    //mJointNums 
+    hash.update((U8*)&(mJointNums[0]), sizeof(S32) * mJointNums.size());
+    
+    //mInvBindMatrix
+    F32* src = mInvBindMatrix[0].getF32ptr();
+    
+    for (int i = 0; i < mInvBindMatrix.size() * 16; ++i)
+    {
+        S32 t = llround(src[i] * 10000.f);
+        hash.update((U8*)&t, sizeof(S32));
+    }
+    //hash.update((U8*)&(mInvBindMatrix[0]), sizeof(LLMatrix4a) * mInvBindMatrix.size());
+
+    hash.finalize();
+
+    U64 digest[2];
+    hash.raw_digest((U8*) digest);
+
+    mHash = digest[0];
+}
+
 LLModel::Decomposition::Decomposition(LLSD& data)
 {
 	fromLLSD(data);
diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h
index cd2b6c67281..2d27592bc82 100644
--- a/indra/llprimitive/llmodel.h
+++ b/indra/llprimitive/llmodel.h
@@ -49,6 +49,7 @@ class LLMeshSkinInfo
 	LLMeshSkinInfo(LLSD& data);
 	void fromLLSD(LLSD& data);
 	LLSD asLLSD(bool include_joints, bool lock_scale_if_joint_position) const;
+    void updateHash();
 
 	LLUUID mMeshID;
 	std::vector<std::string> mJointNames;
@@ -58,10 +59,12 @@ class LLMeshSkinInfo
 	matrix_list_t mAlternateBindMatrix;
 
 	LL_ALIGN_16(LLMatrix4a mBindShapeMatrix);
+
 	float mPelvisOffset;
     bool mLockScaleIfJointPosition;
     bool mInvalidJointsScrubbed;
     bool mJointNumsInitialized;
+    U64 mHash = 0;
 } LL_ALIGN_POSTFIX(16);
 
 LL_ALIGN_PREFIX(16)
diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp
index 08c9dd87695..2f1ce0eec92 100644
--- a/indra/llrender/llglslshader.cpp
+++ b/indra/llrender/llglslshader.cpp
@@ -970,6 +970,19 @@ void LLGLSLShader::bind()
     }
 }
 
+void LLGLSLShader::bind(bool rigged)
+{
+    if (rigged)
+    {
+        llassert(mRiggedVariant);
+        mRiggedVariant->bind();
+    }
+    else
+    {
+        bind();
+    }
+}
+
 void LLGLSLShader::unbind()
 {
     LL_PROFILE_ZONE_SCOPED;
diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h
index 3b23cf1b28c..6fdb7890874 100644
--- a/indra/llrender/llglslshader.h
+++ b/indra/llrender/llglslshader.h
@@ -230,6 +230,8 @@ class LLGLSLShader
 	
     BOOL link(BOOL suppress_errors = FALSE);
 	void bind();
+    //helper to conditionally bind mRiggedVariant instead of this
+    void bind(bool rigged);
 	void unbind();
 
 	// Unbinds any previously bound shader by explicitly binding no shader.
@@ -267,7 +269,8 @@ class LLGLSLShader
 	LLShaderFeatures mFeatures;
 	std::vector< std::pair< std::string, GLenum > > mShaderFiles;
 	std::string mName;
-	boost::unordered_map<std::string, std::string> mDefines;
+    typedef std::unordered_map<std::string, std::string> defines_map_t;
+	defines_map_t mDefines;
 
 	//statistcis for profiling shader performance
 	U32 mTimerQuery;
@@ -285,6 +288,9 @@ class LLGLSLShader
 	std::vector<U32> mTextureMagFilter;
 	std::vector<U32> mTextureMinFilter;
 
+    // this pointer should be set to whichever shader represents this shader's rigged variant
+    LLGLSLShader* mRiggedVariant = nullptr;
+
 private:
 	void unloadInternal();
 };
diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp
index aad04beea26..0c180ed50d7 100644
--- a/indra/llrender/llrender.cpp
+++ b/indra/llrender/llrender.cpp
@@ -1283,7 +1283,7 @@ void LLRender::syncLightState()
 
 void LLRender::syncMatrices()
 {
-	static const U32 name[] = 
+    static const U32 name[] = 
 	{
 		LLShaderMgr::MODELVIEW_MATRIX,
 		LLShaderMgr::PROJECTION_MATRIX,
diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp
index e8c62959303..a9a4314afa4 100644
--- a/indra/llrender/llshadermgr.cpp
+++ b/indra/llrender/llshadermgr.cpp
@@ -165,6 +165,7 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader)
 
 	if (features->hasObjectSkinning)
 	{
+        shader->mRiggedVariant = shader;
         if (!shader->attachVertexObject("avatar/objectSkinV.glsl"))
 		{
 			return FALSE;
@@ -599,7 +600,7 @@ void LLShaderMgr::dumpObjectLog(GLhandleARB ret, BOOL warns, const std::string&
 	}
  }
 
-GLhandleARB LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shader_level, GLenum type, boost::unordered_map<std::string, std::string>* defines, S32 texture_index_channels)
+GLhandleARB LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shader_level, GLenum type, std::unordered_map<std::string, std::string>* defines, S32 texture_index_channels)
 {
 
 // endsure work-around for missing GLSL funcs gets propogated to feature shader files (e.g. srgbF.glsl)
@@ -774,7 +775,7 @@ GLhandleARB LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shade
 	
 	if (defines)
 	{
-		for (boost::unordered_map<std::string,std::string>::iterator iter = defines->begin(); iter != defines->end(); ++iter)
+		for (std::unordered_map<std::string,std::string>::iterator iter = defines->begin(); iter != defines->end(); ++iter)
 		{
 			std::string define = "#define " + iter->first + " " + iter->second + "\n";
 			extra_code_text[extra_code_count++] = (GLcharARB *) strdup(define.c_str());
diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h
index 3908efd4ec7..67c0d6ab10b 100644
--- a/indra/llrender/llshadermgr.h
+++ b/indra/llrender/llshadermgr.h
@@ -264,7 +264,7 @@ class LLShaderMgr
     void dumpShaderSource(U32 shader_code_count, GLcharARB** shader_code_text);
 	BOOL	linkProgramObject(GLhandleARB obj, BOOL suppress_errors = FALSE);
 	BOOL	validateProgramObject(GLhandleARB obj);
-	GLhandleARB loadShaderFile(const std::string& filename, S32 & shader_level, GLenum type, boost::unordered_map<std::string, std::string>* defines = NULL, S32 texture_index_channels = -1);
+	GLhandleARB loadShaderFile(const std::string& filename, S32 & shader_level, GLenum type, std::unordered_map<std::string, std::string>* defines = NULL, S32 texture_index_channels = -1);
 
 	// Implemented in the application to actually point to the shader directory.
 	virtual std::string getShaderDirPrefix(void) = 0; // Pure Virtual
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
index e52624d66aa..485d3320683 100644
--- a/indra/llwindow/llwindowwin32.cpp
+++ b/indra/llwindow/llwindowwin32.cpp
@@ -1833,7 +1833,7 @@ void* LLWindowWin32::createSharedContext()
     S32 attribs[] =
     {
         WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
-        WGL_CONTEXT_MINOR_VERSION_ARB, 2,
+        WGL_CONTEXT_MINOR_VERSION_ARB, 6,
         WGL_CONTEXT_PROFILE_MASK_ARB,  LLRender::sGLCoreProfile ? WGL_CONTEXT_CORE_PROFILE_BIT_ARB : WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
         WGL_CONTEXT_FLAGS_ARB, gDebugGL ? WGL_CONTEXT_DEBUG_BIT_ARB : 0,
         0
diff --git a/indra/llxml/llcontrol.h b/indra/llxml/llcontrol.h
index 5da13f5010b..088502c0172 100644
--- a/indra/llxml/llcontrol.h
+++ b/indra/llxml/llcontrol.h
@@ -247,6 +247,7 @@ class LLControlGroup : public LLInstanceTracker<LLControlGroup, std::string>
 	// generic getter
 	template<typename T> T get(const std::string& name)
 	{
+        LL_PROFILE_ZONE_SCOPED;
 		LLControlVariable* control = getControl(name);
 		LLSD value;
 		eControlType type = TYPE_COUNT;
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 144323bb116..2d821b74512 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -8649,28 +8649,6 @@
     </array>
   </map>
 
-    <key>RenderAlphaBatchFullbrights</key>
-    <map>
-      <key>Comment</key>
-      <string>Render fullbright alpha content more efficiently, but with possible visual differences from prev viewers.</string>
-      <key>Persist</key>
-      <integer>1</integer>
-      <key>Type</key>
-      <string>Boolean</string>
-      <key>Value</key>
-      <integer>0</integer>
-    </map>
-    <key>RenderAlphaBatchEmissives</key>
-    <map>
-      <key>Comment</key>
-      <string>Render emissive alpha content more efficiently, but with possible visual differences from prev viewers.</string>
-      <key>Persist</key>
-      <integer>1</integer>
-      <key>Type</key>
-      <string>Boolean</string>
-      <key>Value</key>
-      <integer>1</integer>
-    </map>
     <key>RenderAnisotropic</key>
     <map>
       <key>Comment</key>
@@ -10145,7 +10123,7 @@
       <key>Type</key>
       <string>S32</string>
       <key>Value</key>
-      <integer>512</integer>
+      <integer>4096</integer>
     </map>
     <key>RenderNameFadeDuration</key>
     <map>
diff --git a/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl b/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl
index 506118d381d..6a93bc2fd25 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl
@@ -105,9 +105,9 @@ void main()
 	vec4 vert = vec4(position.xyz, 1.0);
 	pos = (modelview_matrix * vert);
 	gl_Position = modelview_projection_matrix*vec4(position.xyz, 1.0);
-#endif
+#endif //IS_AVATAR_SKIN
 	
-#endif
+#endif // HAS_SKIN
 
 #ifdef USE_INDEXED_TEX
 	passTextureIndex();
diff --git a/indra/newview/app_settings/shaders/class1/deferred/bumpSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/deferred/bumpSkinnedV.glsl
deleted file mode 100644
index 10144f3e161..00000000000
--- a/indra/newview/app_settings/shaders/class1/deferred/bumpSkinnedV.glsl
+++ /dev/null
@@ -1,64 +0,0 @@
-/** 
- * @file bumpV.glsl
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2007, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-uniform mat4 projection_matrix;
-uniform mat4 texture_matrix0;
-uniform mat4 modelview_matrix;
-
-ATTRIBUTE vec3 position;
-ATTRIBUTE vec4 diffuse_color;
-ATTRIBUTE vec3 normal;
-ATTRIBUTE vec2 texcoord0;
-ATTRIBUTE vec4 tangent;
-
-VARYING vec3 vary_mat0;
-VARYING vec3 vary_mat1;
-VARYING vec3 vary_mat2;
-VARYING vec4 vertex_color;
-VARYING vec2 vary_texcoord0;
-
-mat4 getObjectSkinnedTransform();
-
-void main()
-{
-	vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy;
-	
-	mat4 mat = getObjectSkinnedTransform();
-	
-	mat = modelview_matrix * mat;
-	
-	vec3 pos = (mat*vec4(position.xyz, 1.0)).xyz;
-	
-	
-	vec3 n = normalize((mat * vec4(normal.xyz+position.xyz, 1.0)).xyz-pos.xyz);
-	vec3 t = normalize((mat * vec4(tangent.xyz+position.xyz, 1.0)).xyz-pos.xyz);
-	vec3 b = cross(n, t) * tangent.w;
-	
-	vary_mat0 = vec3(t.x, b.x, n.x);
-	vary_mat1 = vec3(t.y, b.y, n.y);
-	vary_mat2 = vec3(t.z, b.z, n.z);
-	
-	gl_Position = projection_matrix*vec4(pos, 1.0);
-	vertex_color = diffuse_color;
-}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/bumpV.glsl b/indra/newview/app_settings/shaders/class1/deferred/bumpV.glsl
index 9f9749394e0..d90891aa208 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/bumpV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/bumpV.glsl
@@ -39,16 +39,32 @@ VARYING vec3 vary_mat2;
 VARYING vec4 vertex_color;
 VARYING vec2 vary_texcoord0;
 
+#ifdef HAS_SKIN
+mat4 getObjectSkinnedTransform();
+uniform mat4 projection_matrix;
+uniform mat4 modelview_matrix;
+#endif
+
 void main()
 {
 	//transform vertex
+#ifdef HAS_SKIN
+    mat4 mat = getObjectSkinnedTransform();
+	mat = modelview_matrix * mat;
+	vec3 pos = (mat*vec4(position.xyz, 1.0)).xyz;
+	gl_Position = projection_matrix*vec4(pos, 1.0);
+
+	vec3 n = normalize((mat * vec4(normal.xyz+position.xyz, 1.0)).xyz-pos.xyz);
+	vec3 t = normalize((mat * vec4(tangent.xyz+position.xyz, 1.0)).xyz-pos.xyz);
+#else
 	gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0); 
-	vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy;
-	
 	vec3 n = normalize(normal_matrix * normal);
 	vec3 t = normalize(normal_matrix * tangent.xyz);
+#endif
+
 	vec3 b = cross(n, t) * tangent.w;
-	
+	vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy;
+
 	vary_mat0 = vec3(t.x, b.x, n.x);
 	vary_mat1 = vec3(t.y, b.y, n.y);
 	vary_mat2 = vec3(t.z, b.z, n.z);
diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseV.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseV.glsl
index 3c026796c8b..d64bcefade7 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/diffuseV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseV.glsl
@@ -39,14 +39,28 @@ VARYING vec2 vary_texcoord0;
 
 void passTextureIndex();
 
+#ifdef HAS_SKIN
+mat4 getObjectSkinnedTransform();
+uniform mat4 projection_matrix;
+uniform mat4 modelview_matrix;
+#endif
+
 void main()
 {
-	//transform vertex
+#ifdef HAS_SKIN
+    mat4 mat = getObjectSkinnedTransform();
+    mat = modelview_matrix * mat;
+    vec4 pos = mat * vec4(position.xyz, 1.0);
+    gl_Position = projection_matrix * pos;
+    vary_normal = normalize((mat*vec4(normal.xyz+position.xyz,1.0)).xyz-pos.xyz);
+#else
 	gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0); 
+    vary_normal = normalize(normal_matrix * normal);
+#endif
+	
 	vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy;
 	
 	passTextureIndex();
-	vary_normal = normalize(normal_matrix * normal);
-	
+
 	vertex_color = diffuse_color;
 }
diff --git a/indra/newview/app_settings/shaders/class1/deferred/fullbrightShinyV.glsl b/indra/newview/app_settings/shaders/class1/deferred/fullbrightShinyV.glsl
index 8f6eb796682..2c139430e7e 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/fullbrightShinyV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/fullbrightShinyV.glsl
@@ -25,7 +25,6 @@
 
 uniform mat3 normal_matrix;
 uniform mat4 texture_matrix0;
-uniform mat4 texture_matrix1;
 uniform mat4 modelview_matrix;
 uniform mat4 modelview_projection_matrix;
 
@@ -47,19 +46,32 @@ VARYING vec2 vary_texcoord0;
 VARYING vec3 vary_texcoord1;
 VARYING vec4 vary_position;
 
+#ifdef HAS_SKIN
+mat4 getObjectSkinnedTransform();
+uniform mat4 projection_matrix;
+#endif
+
 void main()
 {
 	//transform vertex
 	vec4 vert = vec4(position.xyz,1.0);
 	passTextureIndex();
+
+#ifdef HAS_SKIN
+    mat4 mat = getObjectSkinnedTransform();
+    mat = modelview_matrix * mat;
+    vec4 pos = mat * vert;
+    vary_position = gl_Position = projection_matrix * pos;
+	vec3 norm = normalize((mat*vec4(normal.xyz+position.xyz,1.0)).xyz-pos.xyz);
+#else
 	vec4 pos = (modelview_matrix * vert);
 	vary_position = gl_Position = modelview_projection_matrix*vec4(position.xyz, 1.0);
-	
 	vec3 norm = normalize(normal_matrix * normal);
+#endif
 	vec3 ref = reflect(pos.xyz, -norm);
 
 	vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy;
-	vary_texcoord1 = (texture_matrix1*vec4(ref,1.0)).xyz;
+	vary_texcoord1 = transpose(normal_matrix) * ref.xyz;
 
 	calcAtmospherics(pos.xyz);
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/fullbrightV.glsl b/indra/newview/app_settings/shaders/class1/deferred/fullbrightV.glsl
index bdf3546aa5f..e71636f2c9e 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/fullbrightV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/fullbrightV.glsl
@@ -45,15 +45,26 @@ VARYING vec3 vary_position;
 VARYING vec4 vertex_color;
 VARYING vec2 vary_texcoord0;
 
+#ifdef HAS_SKIN
+mat4 getObjectSkinnedTransform();
+uniform mat4 projection_matrix;
+#endif
 
 void main()
 {
 	//transform vertex
 	vec4 vert = vec4(position.xyz, 1.0);
-	vec4 pos = (modelview_matrix * vert);
 	passTextureIndex();
 
+#ifdef HAS_SKIN
+    mat4 mat = getObjectSkinnedTransform();
+    mat = modelview_matrix * mat;
+    vec4 pos = mat * vert;
+    gl_Position = projection_matrix * pos;
+#else
+	vec4 pos = (modelview_matrix * vert);
 	gl_Position = modelview_projection_matrix*vec4(position.xyz, 1.0);
+#endif
 
 #ifdef WATER_FOG
 	vary_position = pos.xyz;
diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/deferred/shadowAlphaMaskSkinnedV.glsl
similarity index 69%
rename from indra/newview/app_settings/shaders/class1/deferred/diffuseSkinnedV.glsl
rename to indra/newview/app_settings/shaders/class1/deferred/shadowAlphaMaskSkinnedV.glsl
index 24871106242..2b17aea75a8 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/diffuseSkinnedV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/shadowAlphaMaskSkinnedV.glsl
@@ -1,8 +1,9 @@
 /** 
- * @file diffuseSkinnedV.glsl
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * @file shadowAlphaMaskSkinnedV.glsl
+ *
+ * $LicenseInfo:firstyear=2021&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2007, Linden Research, Inc.
+ * Copyright (C) 2011, Linden Research, Inc.
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -22,38 +23,48 @@
  * $/LicenseInfo$
  */
 
-uniform mat4 projection_matrix;
 uniform mat4 texture_matrix0;
 uniform mat4 modelview_matrix;
+uniform mat4 projection_matrix;
+uniform float shadow_target_width;
 
 ATTRIBUTE vec3 position;
 ATTRIBUTE vec4 diffuse_color;
-ATTRIBUTE vec3 normal;
 ATTRIBUTE vec2 texcoord0;
 
-VARYING vec3 vary_normal;
+VARYING vec4 post_pos;
+VARYING float target_pos_x;
 VARYING vec4 vertex_color;
 VARYING vec2 vary_texcoord0;
 
+void passTextureIndex();
+
 mat4 getObjectSkinnedTransform();
 
 void main()
 {
-	vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy;
-		
+	//transform vertex
+	vec4 pre_pos = vec4(position.xyz, 1.0);
+
 	mat4 mat = getObjectSkinnedTransform();
 	
 	mat = modelview_matrix * mat;
-	vec3 pos = (mat*vec4(position.xyz, 1.0)).xyz;
 	
-	vec4 norm = vec4(position.xyz, 1.0);
-	norm.xyz += normal.xyz;
-	norm.xyz = (mat*norm).xyz;
-	norm.xyz = normalize(norm.xyz-pos.xyz);
+	vec4 pos = mat * pre_pos;
+	pos = projection_matrix * pos;
 
-	vary_normal = norm.xyz;
-			
-	vertex_color = diffuse_color;
+	target_pos_x = 0.5 * (shadow_target_width - 1.0) * pos.x;
+
+	post_pos = pos;
+
+#if !defined(DEPTH_CLAMP)
+	gl_Position = vec4(pos.x, pos.y, pos.w*0.5, pos.w);
+#else
+	gl_Position = pos;
+#endif
 	
-	gl_Position = projection_matrix*vec4(pos, 1.0);
+	passTextureIndex();
+
+	vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy;
+	vertex_color = diffuse_color;
 }
diff --git a/indra/newview/app_settings/shaders/class1/objects/simpleSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/deferred/shadowSkinnedV.glsl
similarity index 59%
rename from indra/newview/app_settings/shaders/class1/objects/simpleSkinnedV.glsl
rename to indra/newview/app_settings/shaders/class1/deferred/shadowSkinnedV.glsl
index df31b5a79f1..bdf8e0854d0 100644
--- a/indra/newview/app_settings/shaders/class1/objects/simpleSkinnedV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/shadowSkinnedV.glsl
@@ -1,6 +1,7 @@
 /** 
- * @file simpleSkinnedV.glsl
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * @file shadowSkinnedV.glsl
+ *
+ * $LicenseInfo:firstyear=2021&license=viewerlgpl$
  * Second Life Viewer Source Code
  * Copyright (C) 2007, Linden Research, Inc.
  * 
@@ -22,44 +23,30 @@
  * $/LicenseInfo$
  */
 
-uniform mat4 texture_matrix0;
 uniform mat4 modelview_matrix;
 uniform mat4 projection_matrix;
 
 ATTRIBUTE vec3 position;
-ATTRIBUTE vec3 normal;
-ATTRIBUTE vec4 diffuse_color;
-ATTRIBUTE vec2 texcoord0;
 
-VARYING vec4 vertex_color;
-VARYING vec2 vary_texcoord0;
+VARYING vec4 post_pos;
 
-
-vec4 calcLighting(vec3 pos, vec3 norm, vec4 color);
-void calcAtmospherics(vec3 inPositionEye);
 mat4 getObjectSkinnedTransform();
 
 void main()
 {
 	//transform vertex
-	vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy;
-	
 	mat4 mat = getObjectSkinnedTransform();
 	
 	mat = modelview_matrix * mat;
-	vec3 pos = (mat*vec4(position.xyz, 1.0)).xyz;
-	
-	vec4 norm = vec4(position.xyz, 1.0);
-	norm.xyz += normal.xyz;
-	norm.xyz = (mat*norm).xyz;
-	norm.xyz = normalize(norm.xyz-pos.xyz);
-		
-	calcAtmospherics(pos.xyz);
+	vec4 pos = (mat*vec4(position.xyz, 1.0));
+	pos = projection_matrix*pos;
+
+	post_pos = pos;
+
+#if !defined(DEPTH_CLAMP)
+	gl_Position = vec4(pos.x, pos.y, pos.w*0.5, pos.w);
+#else
+	gl_Position = pos;
+#endif
 
-	vec4 color = calcLighting(pos.xyz, norm.xyz, diffuse_color);
-	vertex_color = color;
-	
-	gl_Position = projection_matrix*vec4(pos, 1.0);
-	
-	
 }
diff --git a/indra/newview/app_settings/shaders/class1/objects/emissiveSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/deferred/treeShadowSkinnedV.glsl
similarity index 76%
rename from indra/newview/app_settings/shaders/class1/objects/emissiveSkinnedV.glsl
rename to indra/newview/app_settings/shaders/class1/deferred/treeShadowSkinnedV.glsl
index 90649041917..d9ca6d3a463 100644
--- a/indra/newview/app_settings/shaders/class1/objects/emissiveSkinnedV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/treeShadowSkinnedV.glsl
@@ -1,7 +1,7 @@
 /** 
- * @file emissiveSkinnedV.glsl
+ * @file treeShadowV.glsl
  *
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * Second Life Viewer Source Code
  * Copyright (C) 2007, Linden Research, Inc.
  * 
@@ -23,34 +23,31 @@
  * $/LicenseInfo$
  */
 
-uniform mat4 projection_matrix;
 uniform mat4 texture_matrix0;
 uniform mat4 modelview_matrix;
-
+uniform mat4 projection_matrix;
+ 
 ATTRIBUTE vec3 position;
-ATTRIBUTE vec4 emissive;
 ATTRIBUTE vec2 texcoord0;
 
-VARYING vec4 vertex_color;
+VARYING vec4 post_pos;
 VARYING vec2 vary_texcoord0;
 
-
-void calcAtmospherics(vec3 inPositionEye);
 mat4 getObjectSkinnedTransform();
 
 void main()
 {
 	//transform vertex
-	vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy;
-	
-	mat4 mat = getObjectSkinnedTransform();
+    mat4 mat = getObjectSkinnedTransform();
 	
 	mat = modelview_matrix * mat;
-	vec3 pos = (mat*vec4(position.xyz, 1.0)).xyz;
 	
-	vertex_color = emissive;
-
-	calcAtmospherics(pos.xyz);
-
-	gl_Position = projection_matrix*vec4(pos, 1.0);
+	vec4 pos = mat * vec4(position.xyz, 1.0);
+    pos = projection_matrix * pos;
+	
+	post_pos = pos;
+	
+	gl_Position = vec4(pos.x, pos.y, pos.w*0.5, pos.w);
+	
+	vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy;
 }
diff --git a/indra/newview/app_settings/shaders/class1/interface/debugSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/interface/debugSkinnedV.glsl
new file mode 100644
index 00000000000..74f22aec4f5
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/interface/debugSkinnedV.glsl
@@ -0,0 +1,41 @@
+/** 
+ * @file debugSkinnedV.glsl
+ *
+ * $LicenseInfo:firstyear=2021&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+uniform mat4 projection_matrix;
+uniform mat4 modelview_matrix;
+
+mat4 getObjectSkinnedTransform();
+
+ATTRIBUTE vec3 position;
+
+void main()
+{
+    mat4 mat = getObjectSkinnedTransform();
+    mat = modelview_matrix * mat;
+    vec3 pos = (mat*vec4(position.xyz, 1.0)).xyz;
+
+    gl_Position = projection_matrix*vec4(pos, 1.0);
+}
+
diff --git a/indra/newview/app_settings/shaders/class1/objects/fullbrightSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/interface/occlusionSkinnedV.glsl
similarity index 73%
rename from indra/newview/app_settings/shaders/class1/objects/fullbrightSkinnedV.glsl
rename to indra/newview/app_settings/shaders/class1/interface/occlusionSkinnedV.glsl
index eff75435a94..7305065a057 100644
--- a/indra/newview/app_settings/shaders/class1/objects/fullbrightSkinnedV.glsl
+++ b/indra/newview/app_settings/shaders/class1/interface/occlusionSkinnedV.glsl
@@ -1,6 +1,7 @@
 /** 
- * @file fullbrightSkinnedV.glsl
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * @file occlusionSkinnedV.glsl
+ *
+ * $LicenseInfo:firstyear=2021&license=viewerlgpl$
  * Second Life Viewer Source Code
  * Copyright (C) 2007, Linden Research, Inc.
  * 
@@ -23,35 +24,17 @@
  */
 
 uniform mat4 projection_matrix;
-uniform mat4 texture_matrix0;
 uniform mat4 modelview_matrix;
 
 ATTRIBUTE vec3 position;
-ATTRIBUTE vec4 diffuse_color;
-ATTRIBUTE vec2 texcoord0;
 
-void calcAtmospherics(vec3 inPositionEye);
 mat4 getObjectSkinnedTransform();
 
-VARYING vec4 vertex_color;
-VARYING vec2 vary_texcoord0;
-
-
 void main()
 {
-	//transform vertex
-	vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy;
-	
 	mat4 mat = getObjectSkinnedTransform();
-	
 	mat = modelview_matrix * mat;
 	vec3 pos = (mat*vec4(position.xyz, 1.0)).xyz;
-	
-	calcAtmospherics(pos.xyz);
-
-	vertex_color = diffuse_color;
-	
 	gl_Position = projection_matrix*vec4(pos, 1.0);
-		
-	
 }
+
diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightF.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightF.glsl
index 5fcdf3107c6..89be8195f0f 100644
--- a/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightF.glsl
+++ b/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightF.glsl
@@ -56,5 +56,6 @@ void fullbright_lighting()
 	color.rgb = pow(color.rgb, vec3(1.0/texture_gamma));
 
 	frag_color = color;
+
 }
 
diff --git a/indra/newview/app_settings/shaders/class1/objects/bumpV.glsl b/indra/newview/app_settings/shaders/class1/objects/bumpV.glsl
index a7738087dcc..ee9970bc70a 100644
--- a/indra/newview/app_settings/shaders/class1/objects/bumpV.glsl
+++ b/indra/newview/app_settings/shaders/class1/objects/bumpV.glsl
@@ -33,10 +33,23 @@ ATTRIBUTE vec2 texcoord1;
 VARYING vec2 vary_texcoord0;
 VARYING vec2 vary_texcoord1;
 
+#ifdef HAS_SKIN
+mat4 getObjectSkinnedTransform();
+uniform mat4 projection_matrix;
+uniform mat4 modelview_matrix;
+#endif
+
 void main()
 {
 	//transform vertex
+#ifdef HAS_SKIN
+    mat4 mat = getObjectSkinnedTransform();
+    mat = modelview_matrix * mat;
+    vec4 pos = mat * vec4(position.xyz, 1.0);
+    gl_Position = projection_matrix * pos;
+#else
 	gl_Position = modelview_projection_matrix*vec4(position.xyz, 1.0);
+#endif
 	vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy;
 	vary_texcoord1 = (texture_matrix0 * vec4(texcoord1,0,1)).xy;
 }
diff --git a/indra/newview/app_settings/shaders/class1/objects/emissiveV.glsl b/indra/newview/app_settings/shaders/class1/objects/emissiveV.glsl
index e984deb0c89..d762239e511 100644
--- a/indra/newview/app_settings/shaders/class1/objects/emissiveV.glsl
+++ b/indra/newview/app_settings/shaders/class1/objects/emissiveV.glsl
@@ -37,20 +37,30 @@ VARYING vec2 vary_texcoord0;
 
 void calcAtmospherics(vec3 inPositionEye);
 
-
-
+#ifdef HAS_SKIN
+mat4 getObjectSkinnedTransform();
+uniform mat4 projection_matrix;
+#endif
 
 void main()
 {
 	//transform vertex
 	passTextureIndex();
+
+#ifdef HAS_SKIN
+    mat4 mat = getObjectSkinnedTransform();
+    mat = modelview_matrix * mat;
+
+    vec4 pos = mat * vec4(position.xyz, 1.0);
+    gl_Position = projection_matrix * pos;
+#else
 	gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0);
+    vec4 pos = (modelview_matrix * vec4(position.xyz, 1.0));
+#endif
 	vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy;
 	
-	vec4 pos = (modelview_matrix * vec4(position.xyz, 1.0));
+	
 	calcAtmospherics(pos.xyz);
 
 	vertex_color = emissive;
-
-	
 }
diff --git a/indra/newview/app_settings/shaders/class1/objects/fullbrightShinySkinnedV.glsl b/indra/newview/app_settings/shaders/class1/objects/fullbrightShinySkinnedV.glsl
deleted file mode 100644
index 1e244d9dfda..00000000000
--- a/indra/newview/app_settings/shaders/class1/objects/fullbrightShinySkinnedV.glsl
+++ /dev/null
@@ -1,71 +0,0 @@
-/** 
- * @file shinySimpleSkinnedV.glsl
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2007, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-uniform mat4 texture_matrix0;
-uniform mat4 texture_matrix1;
-uniform mat4 modelview_matrix;
-uniform mat4 projection_matrix;
-
-ATTRIBUTE vec3 position;
-ATTRIBUTE vec3 normal;
-ATTRIBUTE vec4 diffuse_color;
-ATTRIBUTE vec2 texcoord0;
-
-VARYING vec4 vertex_color;
-VARYING vec2 vary_texcoord0;
-VARYING vec3 vary_texcoord1;
-VARYING vec4 vary_position;
-
-
-void calcAtmospherics(vec3 inPositionEye);
-mat4 getObjectSkinnedTransform();
-
-void main()
-{
-	mat4 mat = getObjectSkinnedTransform();
-	
-	mat = modelview_matrix * mat;
-	vec3 pos = (mat*vec4(position.xyz, 1.0)).xyz;
-
-    mat4 mvp = modelview_matrix * projection_matrix;
-    vary_position = mvp * vec4(position, 1.0);
-	
-	vec4 norm = vec4(position.xyz, 1.0);
-	norm.xyz += normal.xyz;
-	norm.xyz = (mat*norm).xyz;
-	norm.xyz = normalize(norm.xyz-pos.xyz);
-		
-	vec3 ref = reflect(pos.xyz, -norm.xyz);
-
-	vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy;
-	vary_texcoord1 = (texture_matrix1*vec4(ref,1.0)).xyz;
-
-	calcAtmospherics(pos.xyz);
-
-	vertex_color = diffuse_color;
-	
-	gl_Position = projection_matrix*vec4(pos, 1.0);
-	
-	
-}
diff --git a/indra/newview/app_settings/shaders/class1/objects/fullbrightShinyV.glsl b/indra/newview/app_settings/shaders/class1/objects/fullbrightShinyV.glsl
index 34bd8d445a0..ace2574ac2a 100644
--- a/indra/newview/app_settings/shaders/class1/objects/fullbrightShinyV.glsl
+++ b/indra/newview/app_settings/shaders/class1/objects/fullbrightShinyV.glsl
@@ -25,7 +25,6 @@
 
 uniform mat3 normal_matrix;
 uniform mat4 texture_matrix0;
-uniform mat4 texture_matrix1;
 uniform mat4 modelview_matrix;
 uniform mat4 modelview_projection_matrix;
 
@@ -46,20 +45,32 @@ VARYING vec4 vertex_color;
 VARYING vec2 vary_texcoord0;
 VARYING vec3 vary_texcoord1;
 
+#ifdef HAS_SKIN
+mat4 getObjectSkinnedTransform();
+uniform mat4 projection_matrix;
+#endif
 
 void main()
 {
 	//transform vertex
 	vec4 vert = vec4(position.xyz,1.0);
 	passTextureIndex();
+
+#ifdef HAS_SKIN
+    mat4 mat = getObjectSkinnedTransform();
+    mat = modelview_matrix * mat;
+    vec4 pos = mat * vert;
+    gl_Position = projection_matrix * pos;
+	vec3 norm = normalize((mat*vec4(normal.xyz+position.xyz,1.0)).xyz-pos.xyz);
+#else
 	vec4 pos = (modelview_matrix * vert);
 	gl_Position = modelview_projection_matrix*vec4(position.xyz, 1.0);
-	
 	vec3 norm = normalize(normal_matrix * normal);
+#endif
 	vec3 ref = reflect(pos.xyz, -norm);
 
 	vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy;
-	vary_texcoord1 = (texture_matrix1*vec4(ref,1.0)).xyz;
+	vary_texcoord1 = transpose(normal_matrix) * ref;
 
 	calcAtmospherics(pos.xyz);
 
diff --git a/indra/newview/app_settings/shaders/class1/objects/fullbrightV.glsl b/indra/newview/app_settings/shaders/class1/objects/fullbrightV.glsl
index fc20d3270e2..5af42f1fcf9 100644
--- a/indra/newview/app_settings/shaders/class1/objects/fullbrightV.glsl
+++ b/indra/newview/app_settings/shaders/class1/objects/fullbrightV.glsl
@@ -33,26 +33,33 @@ ATTRIBUTE vec2 texcoord0;
 ATTRIBUTE vec3 normal;
 ATTRIBUTE vec4 diffuse_color;
 
-
 void calcAtmospherics(vec3 inPositionEye);
 
-
 VARYING vec4 vertex_color;
 VARYING vec2 vary_texcoord0;
 
+#ifdef HAS_SKIN
+mat4 getObjectSkinnedTransform();
+uniform mat4 projection_matrix;
+#endif
 
 void main()
 {
 	//transform vertex
 	vec4 vert = vec4(position.xyz,1.0);
 	passTextureIndex();
-	vec4 pos = (modelview_matrix * vert);
+#ifdef HAS_SKIN
+    mat4 mat = getObjectSkinnedTransform();
+    mat = modelview_matrix * mat;
+    vec4 pos = mat * vert;
+    gl_Position = projection_matrix * pos;
+#else
+    vec4 pos = (modelview_matrix * vert);
 	gl_Position = modelview_projection_matrix*vec4(position.xyz, 1.0);
+#endif
 	vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy;
 	
 	calcAtmospherics(pos.xyz);
 
 	vertex_color = diffuse_color;
-
-	
 }
diff --git a/indra/newview/app_settings/shaders/class1/objects/shinySimpleSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/objects/shinySimpleSkinnedV.glsl
deleted file mode 100644
index 727bae19c06..00000000000
--- a/indra/newview/app_settings/shaders/class1/objects/shinySimpleSkinnedV.glsl
+++ /dev/null
@@ -1,66 +0,0 @@
-/** 
- * @file shinySimpleSkinnedV.glsl
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2007, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-uniform mat4 projection_matrix;
-uniform mat4 texture_matrix0;
-uniform mat4 texture_matrix1;
-uniform mat4 modelview_matrix;
-
-ATTRIBUTE vec3 position;
-ATTRIBUTE vec3 normal;
-ATTRIBUTE vec4 diffuse_color;
-ATTRIBUTE vec2 texcoord0;
-
-VARYING vec4 vertex_color;
-VARYING vec2 vary_texcoord0;
-VARYING vec3 vary_texcoord1;
-
-vec4 calcLighting(vec3 pos, vec3 norm, vec4 color);
-void calcAtmospherics(vec3 inPositionEye);
-mat4 getObjectSkinnedTransform();
-
-void main()
-{
-	mat4 mat = getObjectSkinnedTransform();
-	
-	mat = modelview_matrix * mat;
-	vec3 pos = (mat*vec4(position.xyz, 1.0)).xyz;
-	
-	vec4 norm = vec4(position.xyz, 1.0);
-	norm.xyz += normal.xyz;
-	norm.xyz = (mat*norm).xyz;
-	norm.xyz = normalize(norm.xyz-pos.xyz);
-		
-	vec3 ref = reflect(pos.xyz, -norm.xyz);
-
-	vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy;
-	vary_texcoord1 = (texture_matrix1*vec4(ref,1.0)).xyz;
-
-	calcAtmospherics(pos.xyz);
-
-	vec4 color = calcLighting(pos.xyz, norm.xyz, diffuse_color);
-	vertex_color = color;
-	
-	gl_Position = projection_matrix*vec4(pos, 1.0);
-}
diff --git a/indra/newview/app_settings/shaders/class1/objects/shinyV.glsl b/indra/newview/app_settings/shaders/class1/objects/shinyV.glsl
index 4ba8194d038..097e42d2331 100644
--- a/indra/newview/app_settings/shaders/class1/objects/shinyV.glsl
+++ b/indra/newview/app_settings/shaders/class1/objects/shinyV.glsl
@@ -25,7 +25,6 @@
 
 uniform mat3 normal_matrix;
 uniform mat4 texture_matrix0;
-uniform mat4 texture_matrix1;
 uniform mat4 modelview_matrix;
 uniform mat4 modelview_projection_matrix;
 
@@ -45,19 +44,32 @@ void calcAtmospherics(vec3 inPositionEye);
 
 uniform vec4 origin;
 
+#ifdef HAS_SKIN
+mat4 getObjectSkinnedTransform();
+uniform mat4 projection_matrix;
+#endif
+
 void main()
 {
 	//transform vertex
 	vec4 vert = vec4(position.xyz,1.0);
 	passTextureIndex();
+
+#ifdef HAS_SKIN
+    mat4 mat = getObjectSkinnedTransform();
+    mat = modelview_matrix * mat;
+    vec4 pos = mat * vert;
+    gl_Position = projection_matrix * pos;
+	vec3 norm = normalize((mat*vec4(normal.xyz+position.xyz,1.0)).xyz-pos.xyz);
+#else
 	vec4 pos = (modelview_matrix * vert);
 	gl_Position = modelview_projection_matrix*vec4(position.xyz, 1.0);
-		
 	vec3 norm = normalize(normal_matrix * normal);
+#endif
 	vec3 ref = reflect(pos.xyz, -norm);
 
-	vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy;
-	vary_texcoord1 = (texture_matrix1*vec4(ref,1.0)).xyz;
+	vary_texcoord0 = (texture_matrix0*vec4(texcoord0,0,1)).xy;
+	vary_texcoord1 = transpose(normal_matrix) * ref;
 
 	calcAtmospherics(pos.xyz);
 
diff --git a/indra/newview/app_settings/shaders/class1/objects/simpleV.glsl b/indra/newview/app_settings/shaders/class1/objects/simpleV.glsl
index 9ef7704b703..2025174f7de 100644
--- a/indra/newview/app_settings/shaders/class1/objects/simpleV.glsl
+++ b/indra/newview/app_settings/shaders/class1/objects/simpleV.glsl
@@ -44,12 +44,16 @@ void calcAtmospherics(vec3 inPositionEye);
 VARYING vec4 vertex_color;
 VARYING vec2 vary_texcoord0;
 
+#ifdef HAS_SKIN
+mat4 getObjectSkinnedTransform();
+uniform mat4 projection_matrix;
+#endif
+
 
 void main()
 {
 	//transform vertex
 	vec4 vert = vec4(position.xyz,1.0);
-	gl_Position = modelview_projection_matrix*vec4(position.xyz, 1.0);
 
 	passTextureIndex();
 	vary_texcoord0 = (texture_matrix0 * vec4(texcoord0, 0, 1)).xy;
@@ -58,11 +62,23 @@ void main()
 	if (no_atmo == 1)
 	{
 		vertex_color = diffuse_color;
+        gl_Position = modelview_projection_matrix*vec4(position.xyz, 1.0);
 	}
 	else
 	{
+#ifdef HAS_SKIN
+        mat4 mat = getObjectSkinnedTransform();
+        mat = modelview_matrix * mat;
+
+        vec4 pos = mat * vert;
+        vec3 norm = normalize((mat*vec4(normal.xyz+vert.xyz,1.0)).xyz-pos.xyz);
+
+        gl_Position = projection_matrix * pos;
+#else
 		vec4 pos = (modelview_matrix * vert);
 		vec3 norm = normalize(normal_matrix * normal);
+        gl_Position = modelview_projection_matrix*vec4(position.xyz, 1.0);
+#endif
 
 		calcAtmospherics(pos.xyz);
 
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 52ef2966cef..177558d38fc 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -1430,6 +1430,11 @@ bool LLAppViewer::doFrame()
 {
 	LL_RECORD_BLOCK_TIME(FTM_FRAME);
 
+    if (!LLWorld::instanceExists())
+    {
+        LLWorld::createInstance();
+    }
+
 	LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop"));
 	LLSD newFrame;
 
diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp
index 502ebbd4b19..7e99b99284f 100644
--- a/indra/newview/lldrawable.cpp
+++ b/indra/newview/lldrawable.cpp
@@ -149,21 +149,6 @@ void LLDrawable::unload()
 {
 	LLVOVolume *pVVol = getVOVolume();
 	pVVol->setNoLOD();
-
-	for (S32 i = 0; i < getNumFaces(); i++)
-	{
-		LLFace* facep = getFace(i);
-		if (facep->isState(LLFace::RIGGED))
-		{
-			LLDrawPoolAvatar* pool = (LLDrawPoolAvatar*)facep->getPool();
-			if (pool) {
-				pool->removeRiggedFace(facep);
-			}
-			facep->setVertexBuffer(NULL);
-		}
-		facep->clearState(LLFace::RIGGED);
-	}
-
 	pVVol->markForUpdate(TRUE);
 }
 
diff --git a/indra/newview/lldrawpool.cpp b/indra/newview/lldrawpool.cpp
index 3e4f97e494a..92a9bed504f 100644
--- a/indra/newview/lldrawpool.cpp
+++ b/indra/newview/lldrawpool.cpp
@@ -50,6 +50,9 @@
 #include "lldrawpoolwlsky.h"
 #include "llglslshader.h"
 #include "llglcommonfunc.h"
+#include "llvoavatar.h"
+#include "llviewershadermgr.h"
+
 
 S32 LLDrawPool::sNumDrawPools = 0;
 
@@ -385,21 +388,43 @@ LLRenderPass::~LLRenderPass()
 }
 
 void LLRenderPass::renderGroup(LLSpatialGroup* group, U32 type, U32 mask, BOOL texture)
-{					
+{
+    LL_PROFILE_ZONE_SCOPED;
 	LLSpatialGroup::drawmap_elem_t& draw_info = group->mDrawMap[type];
 	
 	for (LLSpatialGroup::drawmap_elem_t::iterator k = draw_info.begin(); k != draw_info.end(); ++k)	
 	{
 		LLDrawInfo *pparams = *k;
-		if (pparams) {
+		if (pparams) 
+        {
 			pushBatch(*pparams, mask, texture);
 		}
 	}
 }
 
-void LLRenderPass::renderTexture(U32 type, U32 mask, BOOL batch_textures)
+void LLRenderPass::renderRiggedGroup(LLSpatialGroup* group, U32 type, U32 mask, BOOL texture)
 {
-	pushBatches(type, mask, true, batch_textures);
+    LL_PROFILE_ZONE_SCOPED;
+    LLSpatialGroup::drawmap_elem_t& draw_info = group->mDrawMap[type];
+    LLVOAvatar* lastAvatar = nullptr;
+    U64 lastMeshId = 0;
+    mask |= LLVertexBuffer::MAP_WEIGHT4;
+
+    for (LLSpatialGroup::drawmap_elem_t::iterator k = draw_info.begin(); k != draw_info.end(); ++k)
+    {
+        LLDrawInfo* pparams = *k;
+        if (pparams) 
+        {
+            if (lastAvatar != pparams->mAvatar || lastMeshId != pparams->mSkinInfo->mHash)
+            {
+                uploadMatrixPalette(*pparams);
+                lastAvatar = pparams->mAvatar;
+                lastMeshId = pparams->mSkinInfo->mHash;
+            }
+
+            pushBatch(*pparams, mask, texture);
+        }
+    }
 }
 
 void LLRenderPass::pushBatches(U32 type, U32 mask, BOOL texture, BOOL batch_textures)
@@ -415,27 +440,74 @@ void LLRenderPass::pushBatches(U32 type, U32 mask, BOOL texture, BOOL batch_text
 	}
 }
 
+void LLRenderPass::pushRiggedBatches(U32 type, U32 mask, BOOL texture, BOOL batch_textures)
+{
+    LL_PROFILE_ZONE_SCOPED;
+    LLVOAvatar* lastAvatar = nullptr;
+    U64 lastMeshId = 0;
+    mask |= LLVertexBuffer::MAP_WEIGHT4;
+    for (LLCullResult::drawinfo_iterator i = gPipeline.beginRenderMap(type); i != gPipeline.endRenderMap(type); ++i)
+    {
+        LLDrawInfo* pparams = *i;
+        if (pparams)
+        {
+            if (lastAvatar != pparams->mAvatar || lastMeshId != pparams->mSkinInfo->mHash)
+            {
+                uploadMatrixPalette(*pparams);
+                lastAvatar = pparams->mAvatar;
+                lastMeshId = pparams->mSkinInfo->mHash;
+            }
+
+            pushBatch(*pparams, mask, texture, batch_textures);
+        }
+    }
+}
+
 void LLRenderPass::pushMaskBatches(U32 type, U32 mask, BOOL texture, BOOL batch_textures)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	for (LLCullResult::drawinfo_iterator i = gPipeline.beginRenderMap(type); i != gPipeline.endRenderMap(type); ++i)	
 	{
 		LLDrawInfo* pparams = *i;
 		if (pparams) 
 		{
-			if (LLGLSLShader::sCurBoundShaderPtr)
-			{
-				LLGLSLShader::sCurBoundShaderPtr->setMinimumAlpha(pparams->mAlphaMaskCutoff);
-			}
-			else
-			{
-				gGL.setAlphaRejectSettings(LLRender::CF_GREATER, pparams->mAlphaMaskCutoff);
-			}
-			
+			LLGLSLShader::sCurBoundShaderPtr->setMinimumAlpha(pparams->mAlphaMaskCutoff);
 			pushBatch(*pparams, mask, texture, batch_textures);
 		}
 	}
 }
 
+void LLRenderPass::pushRiggedMaskBatches(U32 type, U32 mask, BOOL texture, BOOL batch_textures)
+{
+    LL_PROFILE_ZONE_SCOPED;
+    LLVOAvatar* lastAvatar = nullptr;
+    U64 lastMeshId = 0;
+    for (LLCullResult::drawinfo_iterator i = gPipeline.beginRenderMap(type); i != gPipeline.endRenderMap(type); ++i)
+    {
+        LLDrawInfo* pparams = *i;
+        if (pparams)
+        {
+            if (LLGLSLShader::sCurBoundShaderPtr)
+            {
+                LLGLSLShader::sCurBoundShaderPtr->setMinimumAlpha(pparams->mAlphaMaskCutoff);
+            }
+            else
+            {
+                gGL.setAlphaRejectSettings(LLRender::CF_GREATER, pparams->mAlphaMaskCutoff);
+            }
+
+            if (lastAvatar != pparams->mAvatar || lastMeshId != pparams->mSkinInfo->mHash)
+            {
+                uploadMatrixPalette(*pparams);
+                lastAvatar = pparams->mAvatar;
+                lastMeshId = pparams->mSkinInfo->mHash;
+            }
+
+            pushBatch(*pparams, mask | LLVertexBuffer::MAP_WEIGHT4, texture, batch_textures);
+        }
+    }
+}
+
 void LLRenderPass::applyModelMatrix(const LLDrawInfo& params)
 {
 	if (params.mModelMatrix != gGLLastMatrix)
@@ -514,7 +586,24 @@ void LLRenderPass::pushBatch(LLDrawInfo& params, U32 mask, BOOL texture, BOOL ba
 	}
 }
 
-void LLRenderPass::renderGroups(U32 type, U32 mask, BOOL texture)
+// static
+bool LLRenderPass::uploadMatrixPalette(LLDrawInfo& params)
 {
-	gPipeline.renderGroups(this, type, mask, texture);
+    // upload matrix palette to shader
+    const LLVOAvatar::MatrixPaletteCache& mpc = params.mAvatar->updateSkinInfoMatrixPalette(params.mSkinInfo);
+    U32 count = mpc.mMatrixPalette.size();
+
+    if (count == 0)
+    {
+        //skin info not loaded yet, don't render
+        return false;
+    }
+
+    LLGLSLShader::sCurBoundShaderPtr->uniformMatrix3x4fv(LLViewerShaderMgr::AVATAR_MATRIX,
+        count,
+        FALSE,
+        (GLfloat*)&(mpc.mGLMp[0]));
+
+    return true;
 }
+
diff --git a/indra/newview/lldrawpool.h b/indra/newview/lldrawpool.h
index ecd9bd034ff..6d49b0254bf 100644
--- a/indra/newview/lldrawpool.h
+++ b/indra/newview/lldrawpool.h
@@ -125,38 +125,68 @@ class LLDrawPool
 class LLRenderPass : public LLDrawPool
 {
 public:
+    // list of possible LLRenderPass types to assign a render batch to
+    // NOTE: "rigged" variant MUST be non-rigged variant + 1
 	enum
 	{
 		PASS_SIMPLE = NUM_POOL_TYPES,
+        PASS_SIMPLE_RIGGED,
 		PASS_GRASS,
 		PASS_FULLBRIGHT,
+        PASS_FULLBRIGHT_RIGGED,
 		PASS_INVISIBLE,
-		PASS_INVISI_SHINY,
+        PASS_INVISIBLE_RIGGED,
+		PASS_INVISI_SHINY, 
+        PASS_INVISI_SHINY_RIGGED,
 		PASS_FULLBRIGHT_SHINY,
+        PASS_FULLBRIGHT_SHINY_RIGGED,
 		PASS_SHINY,
+        PASS_SHINY_RIGGED,
 		PASS_BUMP,
+        PASS_BUMP_RIGGED,
 		PASS_POST_BUMP,
+        PASS_POST_BUMP_RIGGED,
 		PASS_MATERIAL,
+        PASS_MATERIAL_RIGGED,
 		PASS_MATERIAL_ALPHA,
+        PASS_MATERIAL_ALPHA_RIGGED,
 		PASS_MATERIAL_ALPHA_MASK,              // Diffuse texture used as alpha mask
+        PASS_MATERIAL_ALPHA_MASK_RIGGED,
 		PASS_MATERIAL_ALPHA_EMISSIVE,
+        PASS_MATERIAL_ALPHA_EMISSIVE_RIGGED,
 		PASS_SPECMAP,
+        PASS_SPECMAP_RIGGED,
 		PASS_SPECMAP_BLEND,
+        PASS_SPECMAP_BLEND_RIGGED,
 		PASS_SPECMAP_MASK,                     // Diffuse texture used as alpha mask and specular texture(map)
+        PASS_SPECMAP_MASK_RIGGED,
 		PASS_SPECMAP_EMISSIVE,
+        PASS_SPECMAP_EMISSIVE_RIGGED,
 		PASS_NORMMAP,
+        PASS_NORMMAP_RIGGED,
 		PASS_NORMMAP_BLEND,
+        PASS_NORMMAP_BLEND_RIGGED,
 		PASS_NORMMAP_MASK,                     // Diffuse texture used as alpha mask and normal map
+        PASS_NORMMAP_MASK_RIGGED,
 		PASS_NORMMAP_EMISSIVE,
+        PASS_NORMMAP_EMISSIVE_RIGGED,
 		PASS_NORMSPEC,
-		PASS_NORMSPEC_BLEND,
+        PASS_NORMSPEC_RIGGED,
+		PASS_NORMSPEC_BLEND, 
+        PASS_NORMSPEC_BLEND_RIGGED,
 		PASS_NORMSPEC_MASK,                    // Diffuse texture used as alpha mask with normal and specular map
+        PASS_NORMSPEC_MASK_RIGGED,
 		PASS_NORMSPEC_EMISSIVE,
+        PASS_NORMSPEC_EMISSIVE_RIGGED,
 		PASS_GLOW,
+        PASS_GLOW_RIGGED,
 		PASS_ALPHA,
 		PASS_ALPHA_MASK,
+        PASS_ALPHA_MASK_RIGGED,
 		PASS_FULLBRIGHT_ALPHA_MASK,            // Diffuse texture used as alpha mask and fullbright
+        PASS_FULLBRIGHT_ALPHA_MASK_RIGGED,
 		PASS_ALPHA_INVISIBLE,
+        PASS_ALPHA_INVISIBLE_RIGGED,
 		NUM_RENDER_TYPES,
 	};
 
@@ -169,12 +199,13 @@ class LLRenderPass : public LLDrawPool
 
 	static void applyModelMatrix(const LLDrawInfo& params);
 	virtual void pushBatches(U32 type, U32 mask, BOOL texture = TRUE, BOOL batch_textures = FALSE);
+    virtual void pushRiggedBatches(U32 type, U32 mask, BOOL texture = TRUE, BOOL batch_textures = FALSE);
 	virtual void pushMaskBatches(U32 type, U32 mask, BOOL texture = TRUE, BOOL batch_textures = FALSE);
+    virtual void pushRiggedMaskBatches(U32 type, U32 mask, BOOL texture = TRUE, BOOL batch_textures = FALSE);
 	virtual void pushBatch(LLDrawInfo& params, U32 mask, BOOL texture, BOOL batch_textures = FALSE);
+    static bool uploadMatrixPalette(LLDrawInfo& params);
 	virtual void renderGroup(LLSpatialGroup* group, U32 type, U32 mask, BOOL texture = TRUE);
-	virtual void renderGroups(U32 type, U32 mask, BOOL texture = TRUE);
-	virtual void renderTexture(U32 type, U32 mask, BOOL batch_textures = TRUE);
-
+    virtual void renderRiggedGroup(LLSpatialGroup* group, U32 type, U32 mask, BOOL texture = TRUE);
 };
 
 class LLFacePool : public LLDrawPool
diff --git a/indra/newview/lldrawpoolalpha.cpp b/indra/newview/lldrawpoolalpha.cpp
index 34f9bfe35da..9b298b120a8 100644
--- a/indra/newview/lldrawpoolalpha.cpp
+++ b/indra/newview/lldrawpoolalpha.cpp
@@ -48,18 +48,20 @@
 #include "lldrawpoolwater.h"
 #include "llspatialpartition.h"
 #include "llglcommonfunc.h"
+#include "llvoavatar.h"
 
 BOOL LLDrawPoolAlpha::sShowDebugAlpha = FALSE;
 
+#define current_shader (LLGLSLShader::sCurBoundShaderPtr)
+
 static BOOL deferred_render = FALSE;
 
 LLDrawPoolAlpha::LLDrawPoolAlpha(U32 type) :
-		LLRenderPass(type), current_shader(NULL), target_shader(NULL),
-		simple_shader(NULL), fullbright_shader(NULL), emissive_shader(NULL),
+		LLRenderPass(type), target_shader(NULL),
 		mColorSFactor(LLRender::BF_UNDEF), mColorDFactor(LLRender::BF_UNDEF),
 		mAlphaSFactor(LLRender::BF_UNDEF), mAlphaDFactor(LLRender::BF_UNDEF)
 {
-
+ 
 }
 
 LLDrawPoolAlpha::~LLDrawPoolAlpha()
@@ -98,33 +100,43 @@ void LLDrawPoolAlpha::beginPostDeferredPass(S32 pass)
 
     F32 gamma = gSavedSettings.getF32("RenderDeferredDisplayGamma");
 
-    emissive_shader = (LLPipeline::sRenderDeferred)   ? &gDeferredEmissiveProgram    :
-                      (LLPipeline::sUnderWaterRender) ? &gObjectEmissiveWaterProgram : &gObjectEmissiveProgram;
+    emissive_shader[0] = (LLPipeline::sUnderWaterRender) ? &gObjectEmissiveWaterProgram : &gObjectEmissiveProgram;
+    emissive_shader[1] = emissive_shader[0]->mRiggedVariant;
 
-    emissive_shader->bind();
-    emissive_shader->uniform1i(LLShaderMgr::NO_ATMO, (LLPipeline::sRenderingHUDs) ? 1 : 0);
-    emissive_shader->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 2.2f); 
-	emissive_shader->uniform1f(LLShaderMgr::DISPLAY_GAMMA, (gamma > 0.1f) ? 1.0f / gamma : (1.0f/2.2f));
+    for (int i = 0; i < 2; ++i)
+    {
+        emissive_shader[i]->bind();
+        emissive_shader[i]->uniform1i(LLShaderMgr::NO_ATMO, (LLPipeline::sRenderingHUDs) ? 1 : 0);
+        emissive_shader[i]->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 2.2f);
+        emissive_shader[i]->uniform1f(LLShaderMgr::DISPLAY_GAMMA, (gamma > 0.1f) ? 1.0f / gamma : (1.0f / 2.2f));
+    }
 
 	if (pass == 0)
 	{
-        fullbright_shader = (LLPipeline::sImpostorRender)   ? &gDeferredFullbrightProgram      :
-                            (LLPipeline::sUnderWaterRender) ? &gDeferredFullbrightWaterProgram : &gDeferredFullbrightProgram;
-
-		fullbright_shader->bind();
-		fullbright_shader->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 2.2f); 
-		fullbright_shader->uniform1f(LLShaderMgr::DISPLAY_GAMMA, (gamma > 0.1f) ? 1.0f / gamma : (1.0f/2.2f));
-        fullbright_shader->uniform1i(LLShaderMgr::NO_ATMO, LLPipeline::sRenderingHUDs ? 1 : 0);
-		fullbright_shader->unbind();
+        fullbright_shader[0] = (LLPipeline::sImpostorRender) ? &gDeferredFullbrightProgram :
+                (LLPipeline::sUnderWaterRender) ? &gDeferredFullbrightWaterProgram : &gDeferredFullbrightProgram;
+        fullbright_shader[1] = fullbright_shader[0]->mRiggedVariant;
+ 
+        for (int i = 0; i < 2; ++i)
+        {
+            fullbright_shader[i]->bind();
+            fullbright_shader[i]->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 2.2f);
+            fullbright_shader[i]->uniform1f(LLShaderMgr::DISPLAY_GAMMA, (gamma > 0.1f) ? 1.0f / gamma : (1.0f / 2.2f));
+            fullbright_shader[i]->uniform1i(LLShaderMgr::NO_ATMO, LLPipeline::sRenderingHUDs ? 1 : 0);
+            fullbright_shader[i]->unbind();
+        }
 
-        simple_shader = (LLPipeline::sImpostorRender)   ? &gDeferredAlphaImpostorProgram :
-                        (LLPipeline::sUnderWaterRender) ? &gDeferredAlphaWaterProgram    : &gDeferredAlphaProgram;
+        simple_shader[0] = (LLPipeline::sImpostorRender) ? &gDeferredAlphaImpostorProgram :
+                (LLPipeline::sUnderWaterRender) ? &gDeferredAlphaWaterProgram : &gDeferredAlphaProgram;
+        simple_shader[1] = simple_shader[0]->mRiggedVariant;
 
 		//prime simple shader (loads shadow relevant uniforms)
-		gPipeline.bindDeferredShader(*simple_shader);
-
-		simple_shader->uniform1f(LLShaderMgr::DISPLAY_GAMMA, (gamma > 0.1f) ? 1.0f / gamma : (1.0f/2.2f));
-        simple_shader->uniform1i(LLShaderMgr::NO_ATMO, LLPipeline::sRenderingHUDs ? 1 : 0);
+        for (int i = 0; i < 2; ++i)
+        {
+            gPipeline.bindDeferredShader(*simple_shader[i]);
+            simple_shader[i]->uniform1f(LLShaderMgr::DISPLAY_GAMMA, (gamma > 0.1f) ? 1.0f / gamma : (1.0f / 2.2f));
+            simple_shader[i]->uniform1i(LLShaderMgr::NO_ATMO, LLPipeline::sRenderingHUDs ? 1 : 0);
+        }
 	}
 	else if (!LLPipeline::sImpostorRender)
 	{
@@ -133,16 +145,21 @@ void LLDrawPoolAlpha::beginPostDeferredPass(S32 pass)
 		gPipeline.mDeferredDepth.copyContents(gPipeline.mDeferredScreen, 0, 0, gPipeline.mDeferredScreen.getWidth(), gPipeline.mDeferredScreen.getHeight(),
 							0, 0, gPipeline.mDeferredDepth.getWidth(), gPipeline.mDeferredDepth.getHeight(), GL_DEPTH_BUFFER_BIT, GL_NEAREST);	
 		gPipeline.mDeferredDepth.bindTarget();
-		simple_shader = fullbright_shader = &gObjectFullbrightAlphaMaskProgram;
-		gObjectFullbrightAlphaMaskProgram.bind();
-		gObjectFullbrightAlphaMaskProgram.setMinimumAlpha(0.33f);
+		simple_shader[0] = fullbright_shader[0] = &gObjectFullbrightAlphaMaskProgram;
+        simple_shader[1] = fullbright_shader[1] = simple_shader[0]->mRiggedVariant;
+        
+        for (int i = 0; i < 2; ++i)
+        {
+            simple_shader[i]->bind();
+            simple_shader[i]->setMinimumAlpha(0.33f);
+        }
 	}
 
 	deferred_render = TRUE;
 	if (mShaderLevel > 0)
 	{
 		// Start out with no shaders.
-		current_shader = target_shader = NULL;
+		target_shader = NULL;
 	}
 	gPipeline.enableLightsDynamic();
 }
@@ -155,7 +172,7 @@ void LLDrawPoolAlpha::endPostDeferredPass(S32 pass)
 	{
 		gPipeline.mDeferredDepth.flush();
 		gPipeline.mScreen.bindTarget();
-		gObjectFullbrightAlphaMaskProgram.unbind();
+		LLGLSLShader::sCurBoundShaderPtr->unbind();
 	}
 
 	deferred_render = FALSE;
@@ -172,51 +189,46 @@ void LLDrawPoolAlpha::beginRenderPass(S32 pass)
 {
     LL_PROFILE_ZONE_SCOPED;
 	
-    simple_shader     = (LLPipeline::sImpostorRender)   ? &gObjectSimpleImpostorProgram  :
+    simple_shader[0]     = (LLPipeline::sImpostorRender)   ? &gObjectSimpleImpostorProgram  :
                         (LLPipeline::sUnderWaterRender) ? &gObjectSimpleWaterProgram     : &gObjectSimpleProgram;
 
-    fullbright_shader = (LLPipeline::sImpostorRender)   ? &gObjectFullbrightProgram      :
+    fullbright_shader[0] = (LLPipeline::sImpostorRender)   ? &gObjectFullbrightProgram      :
                         (LLPipeline::sUnderWaterRender) ? &gObjectFullbrightWaterProgram : &gObjectFullbrightProgram;
 
-    emissive_shader   = (LLPipeline::sImpostorRender)   ? &gObjectEmissiveProgram        :
+    emissive_shader[0]   = (LLPipeline::sImpostorRender)   ? &gObjectEmissiveProgram        :
                         (LLPipeline::sUnderWaterRender) ? &gObjectEmissiveWaterProgram   : &gObjectEmissiveProgram;
 
+    simple_shader[1] = simple_shader[0]->mRiggedVariant;
+    fullbright_shader[1] = fullbright_shader[0]->mRiggedVariant;
+    emissive_shader[1] = emissive_shader[0]->mRiggedVariant;
+
     if (LLPipeline::sImpostorRender)
 	{
-        if (mShaderLevel > 0)
-		{
-            fullbright_shader->bind();
-			fullbright_shader->setMinimumAlpha(0.5f);
-            fullbright_shader->uniform1i(LLShaderMgr::NO_ATMO, LLPipeline::sRenderingHUDs ? 1 : 0);
-			simple_shader->bind();
-			simple_shader->setMinimumAlpha(0.5f);
-            simple_shader->uniform1i(LLShaderMgr::NO_ATMO, LLPipeline::sRenderingHUDs ? 1 : 0);
-        }
-        else
+        for (int i = 0; i < 2; ++i)
         {
-            gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.5f); //OK
+            fullbright_shader[i]->bind();
+            fullbright_shader[i]->setMinimumAlpha(0.5f);
+            fullbright_shader[i]->uniform1i(LLShaderMgr::NO_ATMO, LLPipeline::sRenderingHUDs ? 1 : 0);
+            simple_shader[i]->bind();
+            simple_shader[i]->setMinimumAlpha(0.5f);
+            simple_shader[i]->uniform1i(LLShaderMgr::NO_ATMO, LLPipeline::sRenderingHUDs ? 1 : 0);
         }
 	}
     else
 	{
-        if (mShaderLevel > 0)
-	    {
-			fullbright_shader->bind();
-			fullbright_shader->setMinimumAlpha(0.f);
-            fullbright_shader->uniform1i(LLShaderMgr::NO_ATMO, LLPipeline::sRenderingHUDs ? 1 : 0);
-			simple_shader->bind();
-			simple_shader->setMinimumAlpha(0.f);
-            simple_shader->uniform1i(LLShaderMgr::NO_ATMO, LLPipeline::sRenderingHUDs ? 1 : 0);
-		}
-        else
+        for (int i = 0; i < 2; ++i)
         {
-            gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); //OK
+            fullbright_shader[i]->bind();
+            fullbright_shader[i]->setMinimumAlpha(0.f);
+            fullbright_shader[i]->uniform1i(LLShaderMgr::NO_ATMO, LLPipeline::sRenderingHUDs ? 1 : 0);
+            simple_shader[i]->bind();
+            simple_shader[i]->setMinimumAlpha(0.f);
+            simple_shader[i]->uniform1i(LLShaderMgr::NO_ATMO, LLPipeline::sRenderingHUDs ? 1 : 0);
         }
     }
 	gPipeline.enableLightsDynamic();
 
     LLGLSLShader::bindNoShader();
-	current_shader = NULL;
 }
 
 void LLDrawPoolAlpha::endRenderPass( S32 pass )
@@ -266,14 +278,7 @@ void LLDrawPoolAlpha::render(S32 pass)
 		gGL.blendFunc(mColorSFactor, mColorDFactor, mAlphaSFactor, mAlphaDFactor);
 	}
 
-	if (mShaderLevel > 0)
-	{
-		renderAlpha(getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX | LLVertexBuffer::MAP_TANGENT | LLVertexBuffer::MAP_TEXCOORD1 | LLVertexBuffer::MAP_TEXCOORD2, pass);
-	}
-	else
-	{
-		renderAlpha(getVertexDataMask(), pass);
-	}
+	renderAlpha(getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX | LLVertexBuffer::MAP_TANGENT | LLVertexBuffer::MAP_TEXCOORD1 | LLVertexBuffer::MAP_TEXCOORD2, pass);
 
 	gGL.setColorMask(true, false);
 
@@ -284,16 +289,8 @@ void LLDrawPoolAlpha::render(S32 pass)
 
 	if (sShowDebugAlpha)
 	{
-		BOOL shaders = gPipeline.canUseVertexShaders();
-		if(shaders) 
-		{
-			gHighlightProgram.bind();
-		}
-		else
-		{
-			gPipeline.enableLightsFullbright();
-		}
-
+		gHighlightProgram.bind();
+		
 		gGL.diffuseColor4f(1,0,0,1);
 				
 		LLViewerFetchedTexture::sSmokeImagep->addTextureStats(1024.f*1024.f);
@@ -315,10 +312,23 @@ void LLDrawPoolAlpha::render(S32 pass)
 		gGL.diffuseColor4f(0, 1, 0, 1);
 		pushBatches(LLRenderPass::PASS_INVISIBLE, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE);
 
-		if(shaders) 
-		{
-			gHighlightProgram.unbind();
-		}
+        gHighlightProgram.mRiggedVariant->bind();
+        gGL.diffuseColor4f(1, 0, 0, 1);
+
+        pushRiggedBatches(LLRenderPass::PASS_ALPHA_MASK_RIGGED, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE);
+        pushRiggedBatches(LLRenderPass::PASS_ALPHA_INVISIBLE_RIGGED, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE);
+
+        // Material alpha mask
+        gGL.diffuseColor4f(0, 0, 1, 1);
+        pushRiggedBatches(LLRenderPass::PASS_MATERIAL_ALPHA_MASK_RIGGED, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE);
+        pushRiggedBatches(LLRenderPass::PASS_NORMMAP_MASK_RIGGED, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE);
+        pushRiggedBatches(LLRenderPass::PASS_SPECMAP_MASK_RIGGED, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE);
+        pushRiggedBatches(LLRenderPass::PASS_NORMSPEC_MASK_RIGGED, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE);
+        pushRiggedBatches(LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK_RIGGED, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE);
+
+        gGL.diffuseColor4f(0, 1, 0, 1);
+        pushRiggedBatches(LLRenderPass::PASS_INVISIBLE_RIGGED, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE);
+        LLGLSLShader::sCurBoundShaderPtr->unbind();
 	}
 }
 
@@ -375,7 +385,7 @@ inline void Draw(LLDrawInfo* draw, U32 mask)
 	draw->mVertexBuffer->drawRangeFast(draw->mDrawMode, draw->mStart, draw->mEnd, draw->mCount, draw->mOffset);                    
 }
 
-bool LLDrawPoolAlpha::TexSetup(LLDrawInfo* draw, bool use_material, LLGLSLShader* current_shader)
+bool LLDrawPoolAlpha::TexSetup(LLDrawInfo* draw, bool use_material)
 {
     bool tex_setup = false;
 
@@ -393,7 +403,7 @@ bool LLDrawPoolAlpha::TexSetup(LLDrawInfo* draw, bool use_material, LLGLSLShader
 			current_shader->bindTexture(LLShaderMgr::SPECULAR_MAP, draw->mSpecularMap);
 		} 
     }
-    else if (current_shader == simple_shader)
+    else if (current_shader == simple_shader[0] || current_shader == simple_shader[1])
     {
         current_shader->bindTexture(LLShaderMgr::BUMP_MAP, LLViewerFetchedTexture::sFlatNormalImagep);
 	    current_shader->bindTexture(LLShaderMgr::SPECULAR_MAP, LLViewerFetchedTexture::sWhiteImagep);
@@ -450,81 +460,86 @@ void LLDrawPoolAlpha::RestoreTexSetup(bool tex_setup)
 	}
 }
 
-void LLDrawPoolAlpha::renderFullbrights(U32 mask, std::vector<LLDrawInfo*>& fullbrights)
-{
-    gPipeline.enableLightsFullbright();
-    fullbright_shader->bind();
-    fullbright_shader->uniform1f(LLShaderMgr::EMISSIVE_BRIGHTNESS, 1.0f);
-    
-    for (LLDrawInfo* draw : fullbrights)
-    {
-        bool tex_setup = TexSetup(draw, false, fullbright_shader);
-
-        LLGLEnableFunc stencil_test(GL_STENCIL_TEST, draw->mSelected, &LLGLCommonFunc::selected_stencil_test);
-		gGL.blendFunc((LLRender::eBlendFactor) draw->mBlendFuncSrc, (LLRender::eBlendFactor) draw->mBlendFuncDst, mAlphaSFactor, mAlphaDFactor);
-
-        Draw(draw, mask & ~(LLVertexBuffer::MAP_TANGENT | LLVertexBuffer::MAP_TEXCOORD1 | LLVertexBuffer::MAP_TEXCOORD2));
-        RestoreTexSetup(tex_setup);
-    }
-    fullbright_shader->unbind();
-}
-
 void LLDrawPoolAlpha::drawEmissive(U32 mask, LLDrawInfo* draw)
 {
+    LLGLSLShader::sCurBoundShaderPtr->uniform1f(LLShaderMgr::EMISSIVE_BRIGHTNESS, 1.f);
     draw->mVertexBuffer->setBufferFast((mask & ~LLVertexBuffer::MAP_COLOR) | LLVertexBuffer::MAP_EMISSIVE);
 	draw->mVertexBuffer->drawRangeFast(draw->mDrawMode, draw->mStart, draw->mEnd, draw->mCount, draw->mOffset);
 }
 
-void LLDrawPoolAlpha::drawEmissiveInline(U32 mask, LLDrawInfo* draw)
+
+void LLDrawPoolAlpha::renderEmissives(U32 mask, std::vector<LLDrawInfo*>& emissives)
 {
+    emissive_shader[0]->bind();
+    emissive_shader[0]->uniform1f(LLShaderMgr::EMISSIVE_BRIGHTNESS, 1.f);
+
+    gPipeline.enableLightsDynamic();
+
     // install glow-accumulating blend mode
-    gGL.blendFunc(
-            LLRender::BF_ZERO, LLRender::BF_ONE, // don't touch color
-			LLRender::BF_ONE, LLRender::BF_ONE); // add to alpha (glow)
+    // don't touch color, add to alpha (glow)
+    gGL.blendFunc(LLRender::BF_ZERO, LLRender::BF_ONE, LLRender::BF_ONE, LLRender::BF_ONE);
 
-	emissive_shader->bind();
-					
-	drawEmissive(mask, draw);
+    for (LLDrawInfo* draw : emissives)
+    {
+        bool tex_setup = TexSetup(draw, false);
+        drawEmissive(mask, draw);
+        RestoreTexSetup(tex_setup);
+    }
 
-	// restore our alpha blend mode
-	gGL.blendFunc(mColorSFactor, mColorDFactor, mAlphaSFactor, mAlphaDFactor);
+    // restore our alpha blend mode
+    gGL.blendFunc(mColorSFactor, mColorDFactor, mAlphaSFactor, mAlphaDFactor);
 
-    current_shader->bind();
+    emissive_shader[0]->unbind();
 }
 
-void LLDrawPoolAlpha::renderEmissives(U32 mask, std::vector<LLDrawInfo*>& emissives)
+void LLDrawPoolAlpha::renderRiggedEmissives(U32 mask, std::vector<LLDrawInfo*>& emissives)
 {
-    emissive_shader->bind();
-    emissive_shader->uniform1f(LLShaderMgr::EMISSIVE_BRIGHTNESS, 1.f);
+    emissive_shader[1]->bind();
+    emissive_shader[1]->uniform1f(LLShaderMgr::EMISSIVE_BRIGHTNESS, 1.f);
 
     gPipeline.enableLightsDynamic();
 
+    mask |= LLVertexBuffer::MAP_WEIGHT4;
     // install glow-accumulating blend mode
     // don't touch color, add to alpha (glow)
-	gGL.blendFunc(LLRender::BF_ZERO, LLRender::BF_ONE, LLRender::BF_ONE, LLRender::BF_ONE); 
- 
+    gGL.blendFunc(LLRender::BF_ZERO, LLRender::BF_ONE, LLRender::BF_ONE, LLRender::BF_ONE);
+
+    LLVOAvatar* lastAvatar = nullptr;
+    U64 lastMeshId = 0;
+
     for (LLDrawInfo* draw : emissives)
     {
-        bool tex_setup = TexSetup(draw, false, emissive_shader);
+        bool tex_setup = TexSetup(draw, false);
+        if (lastAvatar != draw->mAvatar || lastMeshId != draw->mSkinInfo->mHash)
+        {
+            if (!uploadMatrixPalette(*draw))
+            { // failed to upload matrix palette, skip rendering
+                continue;
+            }
+            lastAvatar = draw->mAvatar;
+            lastMeshId = draw->mSkinInfo->mHash;
+        }
         drawEmissive(mask, draw);
         RestoreTexSetup(tex_setup);
     }
 
     // restore our alpha blend mode
-	gGL.blendFunc(mColorSFactor, mColorDFactor, mAlphaSFactor, mAlphaDFactor);
+    gGL.blendFunc(mColorSFactor, mColorDFactor, mAlphaSFactor, mAlphaDFactor);
 
-    emissive_shader->unbind();
+    emissive_shader[1]->unbind();
 }
 
 void LLDrawPoolAlpha::renderAlpha(U32 mask, S32 pass)
 {
     LL_PROFILE_ZONE_SCOPED;
-    BOOL batch_fullbrights = gSavedSettings.getBOOL("RenderAlphaBatchFullbrights");
-    BOOL batch_emissives   = gSavedSettings.getBOOL("RenderAlphaBatchEmissives");
-	BOOL initialized_lighting = FALSE;
+    BOOL initialized_lighting = FALSE;
 	BOOL light_enabled = TRUE;
-	
-	for (LLCullResult::sg_iterator i = gPipeline.beginAlphaGroups(); i != gPipeline.endAlphaGroups(); ++i)
+
+    LLVOAvatar* lastAvatar = nullptr;
+    U64 lastMeshId = 0;
+    LLGLSLShader* lastAvatarShader = nullptr;
+
+    for (LLCullResult::sg_iterator i = gPipeline.beginAlphaGroups(); i != gPipeline.endAlphaGroups(); ++i)
 	{
         LL_PROFILE_ZONE_NAMED("renderAlpha - group");
 		LLSpatialGroup* group = *i;
@@ -535,9 +550,9 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask, S32 pass)
 		    !group->isDead())
 		{
             static std::vector<LLDrawInfo*> emissives;
-            static std::vector<LLDrawInfo*> fullbrights;
+            static std::vector<LLDrawInfo*> rigged_emissives;
             emissives.resize(0);
-            fullbrights.resize(0);
+            rigged_emissives.resize(0);
 
 			bool is_particle_or_hud_particle = group->getSpatialPartition()->mPartitionType == LLViewerRegion::PARTITION_PARTICLE
 													  || group->getSpatialPartition()->mPartitionType == LLViewerRegion::PARTITION_HUD_PARTICLE;
@@ -579,12 +594,6 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask, S32 pass)
 					}
 				}
 
-                if (params.mFullbright && batch_fullbrights)
-				{
-                    fullbrights.push_back(&params);
-                    continue;
-				}
-
 				LLRenderPass::applyModelMatrix(params);
 
 				LLMaterial* mat = NULL;
@@ -600,7 +609,7 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask, S32 pass)
 					if (light_enabled || !initialized_lighting)
 					{
 						initialized_lighting = TRUE;
-						target_shader = fullbright_shader;
+						target_shader = fullbright_shader[0];
 
 						light_enabled = FALSE;
 					}
@@ -609,7 +618,7 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask, S32 pass)
 				else if (!light_enabled || !initialized_lighting)
 				{
 					initialized_lighting = TRUE;
-					target_shader = simple_shader;
+					target_shader = simple_shader[0];
 					light_enabled = TRUE;
 				}
 
@@ -625,27 +634,36 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask, S32 pass)
 						target_shader = &(gDeferredMaterialWaterProgram[mask]);
 					}
 
+                    if (params.mAvatar != nullptr)
+                    {
+                        llassert(target_shader->mRiggedVariant != nullptr);
+                        target_shader = target_shader->mRiggedVariant;
+                    }
+
 					if (current_shader != target_shader)
 					{
 						gPipeline.bindDeferredShader(*target_shader);
-                        current_shader = target_shader;
 					}
 				}
 				else if (!params.mFullbright)
 				{
-					target_shader = simple_shader;
+					target_shader = simple_shader[0];
 				}
 				else
 				{
-					target_shader = fullbright_shader;
+					target_shader = fullbright_shader[0];
 				}
 				
-				if(current_shader != target_shader)
-				{// If we need shaders, and we're not ALREADY using the proper shader, then bind it
-				// (this way we won't rebind shaders unnecessarily).
-					current_shader = target_shader;
-					current_shader->bind();
-				}
+                if (params.mAvatar != nullptr)
+                {
+                    target_shader = target_shader->mRiggedVariant;
+                }
+
+                if (current_shader != target_shader)
+                {// If we need shaders, and we're not ALREADY using the proper shader, then bind it
+                // (this way we won't rebind shaders unnecessarily).
+                    target_shader->bind();
+                }
 
                 LLVector4 spec_color(1, 1, 1, 1);
                 F32 env_intensity = 0.0f;
@@ -661,7 +679,7 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask, S32 pass)
 
                 if (current_shader)
                 {
-                    current_shader->uniform4f(LLShaderMgr::SPECULAR_COLOR, spec_color.mV[0], spec_color.mV[1], spec_color.mV[2], spec_color.mV[3]);						
+                    current_shader->uniform4f(LLShaderMgr::SPECULAR_COLOR, spec_color.mV[0], spec_color.mV[1], spec_color.mV[2], spec_color.mV[3]);
 				    current_shader->uniform1f(LLShaderMgr::ENVIRONMENT_INTENSITY, env_intensity);
 					current_shader->uniform1f(LLShaderMgr::EMISSIVE_BRIGHTNESS, brightness);
                 }
@@ -671,32 +689,54 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask, S32 pass)
 					params.mGroup->rebuildMesh();
 				}
 
-                bool tex_setup = TexSetup(&params, (mat != nullptr), current_shader);
+                if (params.mAvatar != nullptr)
+                {
+                    if (lastAvatar != params.mAvatar ||
+                        lastMeshId != params.mSkinInfo->mHash ||
+                        lastAvatarShader != LLGLSLShader::sCurBoundShaderPtr)
+                    {
+                        if (!uploadMatrixPalette(params))
+                        {
+                            continue;
+                        }
+                        lastAvatar = params.mAvatar;
+                        lastMeshId = params.mSkinInfo->mHash;
+                        lastAvatarShader = LLGLSLShader::sCurBoundShaderPtr;
+                    }
+                }
+
+                bool tex_setup = TexSetup(&params, (mat != nullptr));
 
 				{
 					LLGLEnableFunc stencil_test(GL_STENCIL_TEST, params.mSelected, &LLGLCommonFunc::selected_stencil_test);
 
 					gGL.blendFunc((LLRender::eBlendFactor) params.mBlendFuncSrc, (LLRender::eBlendFactor) params.mBlendFuncDst, mAlphaSFactor, mAlphaDFactor);
-					params.mVertexBuffer->setBufferFast(mask & ~(params.mFullbright ? (LLVertexBuffer::MAP_TANGENT | LLVertexBuffer::MAP_TEXCOORD1 | LLVertexBuffer::MAP_TEXCOORD2) : 0));
-
+                    U32 drawMask = mask;
+                    if (params.mFullbright)
                     {
-					    params.mVertexBuffer->drawRangeFast(params.mDrawMode, params.mStart, params.mEnd, params.mCount, params.mOffset);
+                        drawMask &= ~(LLVertexBuffer::MAP_TANGENT | LLVertexBuffer::MAP_TEXCOORD1 | LLVertexBuffer::MAP_TEXCOORD2);
                     }
+                    if (params.mAvatar != nullptr)
+                    {
+                        drawMask |= LLVertexBuffer::MAP_WEIGHT4;
+                    }
+
+                    params.mVertexBuffer->setBufferFast(drawMask);
+                    params.mVertexBuffer->drawRangeFast(params.mDrawMode, params.mStart, params.mEnd, params.mCount, params.mOffset);
 				}
 
 				// If this alpha mesh has glow, then draw it a second time to add the destination-alpha (=glow).  Interleaving these state-changing calls is expensive, but glow must be drawn Z-sorted with alpha.
-				if (current_shader && 
-					draw_glow_for_this_partition &&
+				if (draw_glow_for_this_partition &&
 					params.mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_EMISSIVE))
 				{
-                    if (batch_emissives)
+                    if (params.mAvatar != nullptr)
                     {
-                        emissives.push_back(&params);
+                        rigged_emissives.push_back(&params);
                     }
                     else
                     {
-                        drawEmissiveInline(mask, &params);
-                    } 
+                        emissives.push_back(&params);
+                    }
 				}
 			
 				if (tex_setup)
@@ -708,41 +748,56 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask, S32 pass)
 				}
 			}
 
-
-            bool rebind = false;
-            if (batch_fullbrights)
             {
-                if (!fullbrights.empty())
+                bool rebind = false;
+                LLGLSLShader* lastShader = current_shader;
+                if (!emissives.empty())
                 {
-                    light_enabled = false;
-                    renderFullbrights(mask, fullbrights);
+                    light_enabled = true;
+                    renderEmissives(mask, emissives);
                     rebind = true;
                 }
-            }
 
-            if (batch_emissives)
-            {
-                if (!emissives.empty())
+                if (!rigged_emissives.empty())
                 {
                     light_enabled = true;
-                    renderEmissives(mask, emissives);
+                    renderRiggedEmissives(mask, rigged_emissives);
                     rebind = true;
                 }
-            }
 
-            if (current_shader && rebind)
-            {
-                current_shader->bind();
+                if (lastShader && rebind)
+                {
+                    lastShader->bind();
+                }
             }
-		}        
+		}
 	}
 
 	gGL.setSceneBlendType(LLRender::BT_ALPHA);
 
-	LLVertexBuffer::unbind();	
-		
+	LLVertexBuffer::unbind();
+
 	if (!light_enabled)
 	{
 		gPipeline.enableLightsDynamic();
 	}
 }
+
+bool LLDrawPoolAlpha::uploadMatrixPalette(const LLDrawInfo& params)
+{
+    const LLVOAvatar::MatrixPaletteCache& mpc = params.mAvatar->updateSkinInfoMatrixPalette(params.mSkinInfo);
+    U32 count = mpc.mMatrixPalette.size();
+
+    if (count == 0)
+    {
+        //skin info not loaded yet, don't render
+        return false;
+    }
+
+    LLGLSLShader::sCurBoundShaderPtr->uniformMatrix3x4fv(LLViewerShaderMgr::AVATAR_MATRIX,
+        count,
+        FALSE,
+        (GLfloat*)&(mpc.mGLMp[0]));
+
+    return true;
+}
diff --git a/indra/newview/lldrawpoolalpha.h b/indra/newview/lldrawpoolalpha.h
index a50b1d929e4..64c17c3fefb 100644
--- a/indra/newview/lldrawpoolalpha.h
+++ b/indra/newview/lldrawpoolalpha.h
@@ -65,23 +65,22 @@ class LLDrawPoolAlpha: public LLRenderPass
 	void renderGroupAlpha(LLSpatialGroup* group, U32 type, U32 mask, BOOL texture = TRUE);
 	void renderAlpha(U32 mask, S32 pass);
 	void renderAlphaHighlight(U32 mask);
-		
+    bool uploadMatrixPalette(const LLDrawInfo& params);
+
 	static BOOL sShowDebugAlpha;
 
 private:
-	LLGLSLShader* current_shader;
 	LLGLSLShader* target_shader;
-	LLGLSLShader* simple_shader;
-	LLGLSLShader* fullbright_shader;	
-	LLGLSLShader* emissive_shader;
 
-    void renderFullbrights(U32 mask, std::vector<LLDrawInfo*>& fullbrights);
-    void renderEmissives(U32 mask, std::vector<LLDrawInfo*>& emissives);
+    // setup by beginFooPass, [0] is static variant, [1] is rigged variant
+    LLGLSLShader* simple_shader[2] = { nullptr };
+	LLGLSLShader* fullbright_shader[2] = { nullptr };
+	LLGLSLShader* emissive_shader[2] = { nullptr };
 
     void drawEmissive(U32 mask, LLDrawInfo* draw);
-    void drawEmissiveInline(U32 mask, LLDrawInfo* draw);
-
-    bool TexSetup(LLDrawInfo* draw, bool use_material, LLGLSLShader* current_shader);
+    void renderEmissives(U32 mask, std::vector<LLDrawInfo*>& emissives);
+    void renderRiggedEmissives(U32 mask, std::vector<LLDrawInfo*>& emissives);
+    bool TexSetup(LLDrawInfo* draw, bool use_material);
     void RestoreTexSetup(bool tex_setup);
 
 	// our 'normal' alpha blend function for this pass
@@ -89,6 +88,9 @@ class LLDrawPoolAlpha: public LLRenderPass
 	LLRender::eBlendFactor mColorDFactor;	
 	LLRender::eBlendFactor mAlphaSFactor;
 	LLRender::eBlendFactor mAlphaDFactor;
+
+    // if true, we're executing a rigged render pass
+    bool mRigged = false;
 };
 
 class LLDrawPoolAlphaPostWater : public LLDrawPoolAlpha
diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp
index 5b51e9db241..125cd3fd5b3 100644
--- a/indra/newview/lldrawpoolavatar.cpp
+++ b/indra/newview/lldrawpoolavatar.cpp
@@ -126,13 +126,6 @@ BOOL LLDrawPoolAvatar::isDead()
         return FALSE;
     }
     
-	for (U32 i = 0; i < NUM_RIGGED_PASSES; ++i)
-    {
-        if (mRiggedFace[i].size() > 0)
-        {
-            return FALSE;
-        }
-    }
     return TRUE;
 }
 
@@ -159,17 +152,6 @@ void LLDrawPoolAvatar::prerender()
 	{
 		sBufferUsage = GL_STREAM_DRAW_ARB;
 	}
-
-	if (!mDrawFace.empty())
-	{
-		const LLFace *facep = mDrawFace[0];
-		if (facep && facep->getDrawable())
-		{
-			LLVOAvatar* avatarp = (LLVOAvatar *)facep->getDrawable()->getVObj().get();
-			updateRiggedVertexBuffers(avatarp);
-            updateSkinInfoMatrixPalettes(avatarp);
-		}
-	}
 }
 
 LLMatrix4& LLDrawPoolAvatar::getModelView()
@@ -215,15 +197,6 @@ void LLDrawPoolAvatar::beginDeferredPass(S32 pass)
 	case 2:
 		beginDeferredSkinned();
 		break;
-	case 3:
-		beginDeferredRiggedSimple();
-		break;
-	case 4:
-		beginDeferredRiggedBump();
-		break;
-	default:
-		beginDeferredRiggedMaterial(pass-5);
-		break;
 	}
 }
 
@@ -250,15 +223,6 @@ void LLDrawPoolAvatar::endDeferredPass(S32 pass)
 	case 2:
 		endDeferredSkinned();
 		break;
-	case 3:
-		endDeferredRiggedSimple();
-		break;
-	case 4:
-		endDeferredRiggedBump();
-		break;
-	default:
-		endDeferredRiggedMaterial(pass-5);
-		break;
 	}
 }
 
@@ -271,176 +235,51 @@ void LLDrawPoolAvatar::renderDeferred(S32 pass)
 
 S32 LLDrawPoolAvatar::getNumPostDeferredPasses()
 {
-	return 10;
+	return 1;
 }
 
 void LLDrawPoolAvatar::beginPostDeferredPass(S32 pass)
 {
     LL_PROFILE_ZONE_SCOPED
 
-	switch (pass)
-	{
-	case 0:
-		beginPostDeferredAlpha();
-		break;
-	case 1:
-		beginRiggedFullbright();
-		break;
-	case 2:
-		beginRiggedFullbrightShiny();
-		break;
-	case 3:
-		beginDeferredRiggedAlpha();
-		break;
-	case 4:
-		beginRiggedFullbrightAlpha();
-		break;
-	case 9:
-		beginRiggedGlow();
-		break;
-	default:
-		beginDeferredRiggedMaterialAlpha(pass-5);
-		break;
-	}
-}
-
-void LLDrawPoolAvatar::beginPostDeferredAlpha()
-{
-    LL_PROFILE_ZONE_SCOPED
-
-	sSkipOpaque = TRUE;
-	sShaderLevel = mShaderLevel;
-	sVertexProgram = &gDeferredAvatarAlphaProgram;
-	sRenderingSkinned = TRUE;
-
-	gPipeline.bindDeferredShader(*sVertexProgram);
-
-	sVertexProgram->setMinimumAlpha(LLDrawPoolAvatar::sMinimumAlpha);
-
-	sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
-}
-
-void LLDrawPoolAvatar::beginDeferredRiggedAlpha()
-{
-    LL_PROFILE_ZONE_SCOPED
-
-	sVertexProgram = &gDeferredSkinnedAlphaProgram;
-	gPipeline.bindDeferredShader(*sVertexProgram);
-	sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
-	gPipeline.enableLightsDynamic();
-}
-
-void LLDrawPoolAvatar::beginDeferredRiggedMaterialAlpha(S32 pass)
-{
-    LL_PROFILE_ZONE_SCOPED
-
-	switch (pass)
-	{
-	case 0: pass = 1; break;
-	case 1: pass = 5; break;
-	case 2: pass = 9; break;
-	default: pass = 13; break;
-	}
-
-	pass += LLMaterial::SHADER_COUNT;
-
-	sVertexProgram = &gDeferredMaterialProgram[pass];
-
-	if (LLPipeline::sUnderWaterRender)
-	{
-		sVertexProgram = &(gDeferredMaterialWaterProgram[pass]);
-	}
+        sSkipOpaque = TRUE;
+    sShaderLevel = mShaderLevel;
+    sVertexProgram = &gDeferredAvatarAlphaProgram;
+    sRenderingSkinned = TRUE;
 
-	gPipeline.bindDeferredShader(*sVertexProgram);
-	sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
-	normal_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::BUMP_MAP);
-	specular_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::SPECULAR_MAP);
-	gPipeline.enableLightsDynamic();
-}
+    gPipeline.bindDeferredShader(*sVertexProgram);
 
-void LLDrawPoolAvatar::endDeferredRiggedAlpha()
-{
-    LL_PROFILE_ZONE_SCOPED
+    sVertexProgram->setMinimumAlpha(LLDrawPoolAvatar::sMinimumAlpha);
 
-	LLVertexBuffer::unbind();
-	gPipeline.unbindDeferredShader(*sVertexProgram);
-	sDiffuseChannel = 0;
-	normal_channel = -1;
-	specular_channel = -1;
-	sVertexProgram = NULL;
+    sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
 }
 
 void LLDrawPoolAvatar::endPostDeferredPass(S32 pass)
 {
     LL_PROFILE_ZONE_SCOPED
+    // if we're in software-blending, remember to set the fence _after_ we draw so we wait till this rendering is done
+    sRenderingSkinned = FALSE;
+    sSkipOpaque = FALSE;
 
-	switch (pass)
-	{
-	case 0:
-		endPostDeferredAlpha();
-		break;
-	case 1:
-		endRiggedFullbright();
-		break;
-	case 2:
-		endRiggedFullbrightShiny();
-		break;
-	case 3:
-		endDeferredRiggedAlpha();
-		break;
-	case 4:
-		endRiggedFullbrightAlpha();
-		break;
-	case 5:
-		endRiggedGlow();
-		break;
-	default:
-		endDeferredRiggedAlpha();
-		break;
-	}
-}
-
-void LLDrawPoolAvatar::endPostDeferredAlpha()
-{
-    LL_PROFILE_ZONE_SCOPED
-
-	// if we're in software-blending, remember to set the fence _after_ we draw so we wait till this rendering is done
-	sRenderingSkinned = FALSE;
-	sSkipOpaque = FALSE;
-		
-	gPipeline.unbindDeferredShader(*sVertexProgram);
-	sDiffuseChannel = 0;
-	sShaderLevel = mShaderLevel;
+    gPipeline.unbindDeferredShader(*sVertexProgram);
+    sDiffuseChannel = 0;
+    sShaderLevel = mShaderLevel;
 }
 
 void LLDrawPoolAvatar::renderPostDeferred(S32 pass)
 {
     LL_PROFILE_ZONE_SCOPED
 
-	static const S32 actual_pass[] =
-	{ //map post deferred pass numbers to what render() expects
-		2, //skinned
-		4, // rigged fullbright
-		6, //rigged fullbright shiny
-		7, //rigged alpha
-		8, //rigged fullbright alpha
-		9, //rigged material alpha 1
-		10,//rigged material alpha 2
-		11,//rigged material alpha 3
-		12,//rigged material alpha 4
-		13, //rigged glow
-	};
-
-	S32 p = actual_pass[pass];
-
+    is_post_deferred_render = true;
 	if (LLPipeline::sImpostorRender)
 	{ //HACK for impostors so actual pass ends up being proper pass
-		p -= 2;
+        render(0);
 	}
-
-	is_post_deferred_render = true;
-	render(p);
-	is_post_deferred_render = false;
+    else
+    {
+        render(2);
+    }
+    is_post_deferred_render = false;
 }
 
 
@@ -506,68 +345,12 @@ void LLDrawPoolAvatar::beginShadowPass(S32 pass)
 
         gGL.diffuseColor4f(1, 1, 1, 1);
     }
-    else if (pass == SHADOW_PASS_ATTACHMENT_ALPHA_BLEND)
-    {
-        sVertexProgram = &gDeferredAttachmentAlphaShadowProgram;
-
-        // bind diffuse tex so we can reference the alpha channel...
-        S32 loc = sVertexProgram->getUniformLocation(LLViewerShaderMgr::DIFFUSE_MAP);
-        sDiffuseChannel = 0;
-        if (loc != -1)
-        {
-            sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
-        }
-
-        if ((sShaderLevel > 0))  // for hardware blending
-        {
-            sRenderingSkinned = TRUE;
-            sVertexProgram->bind();
-        }
-
-        gGL.diffuseColor4f(1, 1, 1, 1);
-    }
-    else if (pass == SHADOW_PASS_ATTACHMENT_ALPHA_MASK)
-    {
-        sVertexProgram = &gDeferredAttachmentAlphaMaskShadowProgram;
-
-        // bind diffuse tex so we can reference the alpha channel...
-        S32 loc = sVertexProgram->getUniformLocation(LLViewerShaderMgr::DIFFUSE_MAP);
-        sDiffuseChannel = 0;
-        if (loc != -1)
-        {
-            sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
-        }
-
-        if ((sShaderLevel > 0))  // for hardware blending
-        {
-            sRenderingSkinned = TRUE;
-            sVertexProgram->bind();
-        }
-
-        gGL.diffuseColor4f(1, 1, 1, 1);
-    }
-    else // SHADOW_PASS_ATTACHMENT_OPAQUE
-    {
-        sVertexProgram = &gDeferredAttachmentShadowProgram;
-        S32 loc = sVertexProgram->getUniformLocation(LLViewerShaderMgr::DIFFUSE_MAP);
-        sDiffuseChannel = 0;
-        if (loc != -1)
-        {
-            sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
-        }
-        sVertexProgram->bind();
-    }
 }
 
 void LLDrawPoolAvatar::endShadowPass(S32 pass)
 {
 	LL_PROFILE_ZONE_SCOPED;
 
-    if (pass == SHADOW_PASS_ATTACHMENT_OPAQUE)
-    {
-        LLVertexBuffer::unbind();
-    }
-
     if (sShaderLevel > 0)
     {
         sVertexProgram->unbind();
@@ -625,77 +408,17 @@ void LLDrawPoolAvatar::renderShadow(S32 pass)
         avatarp->renderSkinned();
         LLDrawPoolAvatar::sSkipOpaque = false;
     }
-    else if (pass == SHADOW_PASS_ATTACHMENT_ALPHA_BLEND) // rigged alpha
-    {
-        LLDrawPoolAvatar::sSkipOpaque = true;
-        renderRigged(avatarp, RIGGED_MATERIAL_ALPHA);
-        renderRigged(avatarp, RIGGED_MATERIAL_ALPHA_EMISSIVE);
-        renderRigged(avatarp, RIGGED_ALPHA);
-        renderRigged(avatarp, RIGGED_FULLBRIGHT_ALPHA);
-        renderRigged(avatarp, RIGGED_GLOW);
-        renderRigged(avatarp, RIGGED_SPECMAP_BLEND);
-        renderRigged(avatarp, RIGGED_NORMMAP_BLEND);
-        renderRigged(avatarp, RIGGED_NORMSPEC_BLEND);
-        LLDrawPoolAvatar::sSkipOpaque = false;
-    }
-    else if (pass == SHADOW_PASS_ATTACHMENT_ALPHA_MASK) // rigged alpha mask
-    {
-        LLDrawPoolAvatar::sSkipOpaque = true;
-        renderRigged(avatarp, RIGGED_MATERIAL_ALPHA_MASK);
-        renderRigged(avatarp, RIGGED_NORMMAP_MASK);
-        renderRigged(avatarp, RIGGED_SPECMAP_MASK);
-        renderRigged(avatarp, RIGGED_NORMSPEC_MASK);
-        renderRigged(avatarp, RIGGED_GLOW);
-        LLDrawPoolAvatar::sSkipOpaque = false;
-    }
-    else // rigged opaque (SHADOW_PASS_ATTACHMENT_OPAQUE
-    {
-        LLDrawPoolAvatar::sSkipTransparent = true;
-        renderRigged(avatarp, RIGGED_MATERIAL);
-        renderRigged(avatarp, RIGGED_SPECMAP);
-        renderRigged(avatarp, RIGGED_SPECMAP_EMISSIVE);
-        renderRigged(avatarp, RIGGED_NORMMAP);
-        renderRigged(avatarp, RIGGED_NORMMAP_EMISSIVE);
-        renderRigged(avatarp, RIGGED_NORMSPEC);
-        renderRigged(avatarp, RIGGED_NORMSPEC_EMISSIVE);
-        renderRigged(avatarp, RIGGED_SIMPLE);
-        renderRigged(avatarp, RIGGED_FULLBRIGHT);
-        renderRigged(avatarp, RIGGED_SHINY);
-        renderRigged(avatarp, RIGGED_FULLBRIGHT_SHINY);
-        renderRigged(avatarp, RIGGED_GLOW);
-        renderRigged(avatarp, RIGGED_DEFERRED_BUMP);
-        renderRigged(avatarp, RIGGED_DEFERRED_SIMPLE);
-        LLDrawPoolAvatar::sSkipTransparent = false;
-    }
 }
 
 S32 LLDrawPoolAvatar::getNumPasses()
 {
-    LL_PROFILE_ZONE_SCOPED
-
-	if (LLPipeline::sImpostorRender)
-	{
-		return 8;
-	}
-	else 
-	{
-		return 10;
-	}
+    return 3;
 }
 
 
 S32 LLDrawPoolAvatar::getNumDeferredPasses()
 {
-    LL_PROFILE_ZONE_SCOPED
-
-	if (LLPipeline::sImpostorRender)
-	{
-		return 19;
-	}
-	else
-	{
-		return 21;
-	}
+    return 3;
 }
 
 
@@ -733,27 +456,6 @@ void LLDrawPoolAvatar::beginRenderPass(S32 pass)
 	case 2:
 		beginSkinned();
 		break;
-	case 3:
-		beginRiggedSimple();
-		break;
-	case 4:
-		beginRiggedFullbright();
-		break;
-	case 5:
-		beginRiggedShinySimple();
-		break;
-	case 6:
-		beginRiggedFullbrightShiny();
-		break;
-	case 7:
-		beginRiggedAlpha();
-		break;
-	case 8:
-		beginRiggedFullbrightAlpha();
-		break;
-	case 9:
-		beginRiggedGlow();
-		break;
 	}
 
 	if (pass == 0)
@@ -782,27 +484,6 @@ void LLDrawPoolAvatar::endRenderPass(S32 pass)
 	case 2:
 		endSkinned();
 		break;
-	case 3:
-		endRiggedSimple();
-		break;
-	case 4:
-		endRiggedFullbright();
-		break;
-	case 5:
-		endRiggedShinySimple();
-		break;
-	case 6:
-		endRiggedFullbrightShiny();
-		break;
-	case 7:
-		endRiggedAlpha();
-		break;
-	case 8:
-		endRiggedFullbrightAlpha();
-		break;
-	case 9:
-		endRiggedGlow();
-		break;
 	}
 }
 
@@ -1037,1559 +718,199 @@ void LLDrawPoolAvatar::endSkinned()
 	gGL.getTexUnit(0)->activate();
 }
 
-void LLDrawPoolAvatar::beginRiggedSimple()
+void LLDrawPoolAvatar::beginDeferredSkinned()
 {
     LL_PROFILE_ZONE_SCOPED
 
-	if (sShaderLevel > 0)
+	sShaderLevel = mShaderLevel;
+	sVertexProgram = &gDeferredAvatarProgram;
+	sRenderingSkinned = TRUE;
+
+	sVertexProgram->bind();
+	sVertexProgram->setMinimumAlpha(LLDrawPoolAvatar::sMinimumAlpha);
+	if (LLPipeline::sRenderingHUDs)
 	{
-		if (LLPipeline::sUnderWaterRender)
-		{
-			sVertexProgram = &gSkinnedObjectSimpleWaterProgram;
-		}
-		else
-		{
-			sVertexProgram = &gSkinnedObjectSimpleProgram;
-		}
+		sVertexProgram->uniform1i(LLShaderMgr::NO_ATMO, 1);
 	}
 	else
 	{
-		if (LLPipeline::sUnderWaterRender)
-		{
-			sVertexProgram = &gObjectSimpleNonIndexedWaterProgram;
-		}
-		else
-		{
-			sVertexProgram = &gObjectSimpleNonIndexedProgram;
-		}
+		sVertexProgram->uniform1i(LLShaderMgr::NO_ATMO, 0);
 	}
 
-	if (sShaderLevel > 0 || gPipeline.canUseVertexShaders())
-	{
-		sDiffuseChannel = 0;
-		sVertexProgram->bind();
-        if (LLPipeline::sRenderingHUDs)
-	    {
-		    sVertexProgram->uniform1i(LLShaderMgr::NO_ATMO, 1);
-	    }
-	    else
-	    {
-		    sVertexProgram->uniform1i(LLShaderMgr::NO_ATMO, 0);
-	    }
-	}
+	sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
+	gGL.getTexUnit(0)->activate();
 }
 
-void LLDrawPoolAvatar::endRiggedSimple()
+void LLDrawPoolAvatar::endDeferredSkinned()
 {
     LL_PROFILE_ZONE_SCOPED
 
-	LLVertexBuffer::unbind();
-	if (sShaderLevel > 0 || gPipeline.canUseVertexShaders())
-	{
-		sVertexProgram->unbind();
-		sVertexProgram = NULL;
-	}
-}
-
-void LLDrawPoolAvatar::beginRiggedAlpha()
-{
-    LL_PROFILE_ZONE_SCOPED
+	// if we're in software-blending, remember to set the fence _after_ we draw so we wait till this rendering is done
+	sRenderingSkinned = FALSE;
+	sVertexProgram->unbind();
 
-	beginRiggedSimple();
-}
+	sVertexProgram->disableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
 
-void LLDrawPoolAvatar::endRiggedAlpha()
-{
-    LL_PROFILE_ZONE_SCOPED
+	sShaderLevel = mShaderLevel;
 
-	endRiggedSimple();
+	gGL.getTexUnit(0)->activate();
 }
 
-
-void LLDrawPoolAvatar::beginRiggedFullbrightAlpha()
+void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass)
 {
-    LL_PROFILE_ZONE_SCOPED
-
-	beginRiggedFullbright();
-}
+	if (pass == -1)
+	{
+		for (S32 i = 1; i < getNumPasses(); i++)
+		{ //skip foot shadows
+			prerender();
+			beginRenderPass(i);
+			renderAvatars(single_avatar, i);
+			endRenderPass(i);
+		}
 
-void LLDrawPoolAvatar::endRiggedFullbrightAlpha()
-{
-    LL_PROFILE_ZONE_SCOPED
-
-	endRiggedFullbright();
-}
-
-void LLDrawPoolAvatar::beginRiggedGlow()
-{
-    LL_PROFILE_ZONE_SCOPED
-
-	if (sShaderLevel > 0)
-	{
-		if (LLPipeline::sUnderWaterRender)
-		{
-			sVertexProgram = &gSkinnedObjectEmissiveWaterProgram;
-		}
-		else
-		{
-			sVertexProgram = &gSkinnedObjectEmissiveProgram;
-		}
-	}
-	else
-	{
-		if (LLPipeline::sUnderWaterRender)
-		{
-			sVertexProgram = &gObjectEmissiveNonIndexedWaterProgram;
-		}
-		else
-		{
-			sVertexProgram = &gObjectEmissiveNonIndexedProgram;
-		}
-	}
-
-	if (sShaderLevel > 0 || gPipeline.canUseVertexShaders())
-	{
-		sDiffuseChannel = 0;
-		sVertexProgram->bind();
-
-		sVertexProgram->uniform1f(LLShaderMgr::TEXTURE_GAMMA, LLPipeline::sRenderDeferred ? 2.2f : 1.1f);
-
-        if (LLPipeline::sRenderingHUDs)
-	    {
-		    sVertexProgram->uniform1i(LLShaderMgr::NO_ATMO, 1);
-	    }
-	    else
-	    {
-		    sVertexProgram->uniform1i(LLShaderMgr::NO_ATMO, 0);
-	    }
-
-		F32 gamma = gSavedSettings.getF32("RenderDeferredDisplayGamma");
-		sVertexProgram->uniform1f(LLShaderMgr::DISPLAY_GAMMA, (gamma > 0.1f) ? 1.0f / gamma : (1.0f/2.2f));
-	}
-}
-
-void LLDrawPoolAvatar::endRiggedGlow()
-{
-    LL_PROFILE_ZONE_SCOPED
-
-	endRiggedFullbright();
-}
-
-void LLDrawPoolAvatar::beginRiggedFullbright()
-{
-    LL_PROFILE_ZONE_SCOPED
-
-	if (sShaderLevel > 0)
-	{
-		if (LLPipeline::sUnderWaterRender)
-		{
-			sVertexProgram = &gSkinnedObjectFullbrightWaterProgram;
-		}
-		else
-		{
-			if (LLPipeline::sRenderDeferred)
-			{
-				sVertexProgram = &gDeferredSkinnedFullbrightProgram;
-			}
-			else
-			{
-				sVertexProgram = &gSkinnedObjectFullbrightProgram;
-			}
-		}
-	}
-	else
-	{
-		if (LLPipeline::sUnderWaterRender)
-		{
-			sVertexProgram = &gObjectFullbrightNonIndexedWaterProgram;
-		}
-		else
-		{
-			sVertexProgram = &gObjectFullbrightNonIndexedProgram;
-		}
-	}
-
-	if (sShaderLevel > 0 || gPipeline.canUseVertexShaders())
-	{
-		sDiffuseChannel = 0;
-		sVertexProgram->bind();
-
-        if (LLPipeline::sRenderingHUDs)
-        {            
-            sVertexProgram->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 1.0f);
-            sVertexProgram->uniform1i(LLShaderMgr::NO_ATMO, 1);
-        }
-		else if (LLPipeline::sRenderDeferred)
-		{
-            sVertexProgram->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 2.2f);
-            sVertexProgram->uniform1i(LLShaderMgr::NO_ATMO, 0);
-			F32 gamma = gSavedSettings.getF32("RenderDeferredDisplayGamma");
-			sVertexProgram->uniform1f(LLShaderMgr::DISPLAY_GAMMA, (gamma > 0.1f) ? 1.0f / gamma : (1.0f/2.2f));
-		} 
-		else 
-		{
-            sVertexProgram->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 1.0f);
-            sVertexProgram->uniform1i(LLShaderMgr::NO_ATMO, 0);
-		}
-	}
-}
-
-void LLDrawPoolAvatar::endRiggedFullbright()
-{
-    LL_PROFILE_ZONE_SCOPED
-
-	LLVertexBuffer::unbind();
-	if (sShaderLevel > 0 || gPipeline.canUseVertexShaders())
-	{
-		sVertexProgram->unbind();
-		sVertexProgram = NULL;
-	}
-}
-
-void LLDrawPoolAvatar::beginRiggedShinySimple()
-{
-    LL_PROFILE_ZONE_SCOPED
-
-	if (sShaderLevel > 0)
-	{
-		if (LLPipeline::sUnderWaterRender)
-		{
-			sVertexProgram = &gSkinnedObjectShinySimpleWaterProgram;
-		}
-		else
-		{
-			sVertexProgram = &gSkinnedObjectShinySimpleProgram;
-		}
-	}
-	else
-	{
-		if (LLPipeline::sUnderWaterRender)
-		{
-			sVertexProgram = &gObjectShinyNonIndexedWaterProgram;
-		}
-		else
-		{
-			sVertexProgram = &gObjectShinyNonIndexedProgram;
-		}
-	}
-
-	if (sShaderLevel > 0 || gPipeline.canUseVertexShaders())
-	{
-		sVertexProgram->bind();
-        if (LLPipeline::sRenderingHUDs)
-	    {
-		    sVertexProgram->uniform1i(LLShaderMgr::NO_ATMO, 1);
-	    }
-	    else
-	    {
-		    sVertexProgram->uniform1i(LLShaderMgr::NO_ATMO, 0);
-	    }
-		LLDrawPoolBump::bindCubeMap(sVertexProgram, 2, sDiffuseChannel, cube_channel, false);
-	}
-}
-
-void LLDrawPoolAvatar::endRiggedShinySimple()
-{
-    LL_PROFILE_ZONE_SCOPED
-
-	LLVertexBuffer::unbind();
-	if (sShaderLevel > 0 || gPipeline.canUseVertexShaders())
-	{
-		LLDrawPoolBump::unbindCubeMap(sVertexProgram, 2, sDiffuseChannel, cube_channel, false);
-		sVertexProgram->unbind();
-		sVertexProgram = NULL;
-	}
-}
-
-void LLDrawPoolAvatar::beginRiggedFullbrightShiny()
-{
-    LL_PROFILE_ZONE_SCOPED
-
-	if (sShaderLevel > 0)
-	{
-		if (LLPipeline::sUnderWaterRender)
-		{
-			sVertexProgram = &gSkinnedObjectFullbrightShinyWaterProgram;
-		}
-		else
-		{
-			if (LLPipeline::sRenderDeferred)
-			{
-				sVertexProgram = &gDeferredSkinnedFullbrightShinyProgram;
-			}
-			else
-			{
-				sVertexProgram = &gSkinnedObjectFullbrightShinyProgram;
-			}
-		}
-	}
-	else
-	{
-		if (LLPipeline::sUnderWaterRender)
-		{
-			sVertexProgram = &gObjectFullbrightShinyNonIndexedWaterProgram;
-		}
-		else
-		{
-			sVertexProgram = &gObjectFullbrightShinyNonIndexedProgram;
-		}
-	}
-
-	if (sShaderLevel > 0 || gPipeline.canUseVertexShaders())
-	{
-		sVertexProgram->bind();
-        if (LLPipeline::sRenderingHUDs)
-	    {
-		    sVertexProgram->uniform1i(LLShaderMgr::NO_ATMO, 1);
-	    }
-	    else
-	    {
-		    sVertexProgram->uniform1i(LLShaderMgr::NO_ATMO, 0);
-	    }
-		LLDrawPoolBump::bindCubeMap(sVertexProgram, 2, sDiffuseChannel, cube_channel, false);
-
-        if (LLPipeline::sRenderingHUDs)
-		{
-			sVertexProgram->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 1.0f);
-            sVertexProgram->uniform1i(LLShaderMgr::NO_ATMO, 1);
-        }
-		else if (LLPipeline::sRenderDeferred)
-		{
-            sVertexProgram->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 2.2f);
-			F32 gamma = gSavedSettings.getF32("RenderDeferredDisplayGamma");
-			sVertexProgram->uniform1f(LLShaderMgr::DISPLAY_GAMMA, (gamma > 0.1f) ? 1.0f / gamma : (1.0f/2.2f));
-            sVertexProgram->uniform1i(LLShaderMgr::NO_ATMO, 0);
-        }
-        else
-        {
-			sVertexProgram->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 1.0f);
-            sVertexProgram->uniform1i(LLShaderMgr::NO_ATMO, 0);
-		}
-	}
-}
-
-void LLDrawPoolAvatar::endRiggedFullbrightShiny()
-{
-    LL_PROFILE_ZONE_SCOPED
-
-	LLVertexBuffer::unbind();
-	if (sShaderLevel > 0 || gPipeline.canUseVertexShaders())
-	{
-		LLDrawPoolBump::unbindCubeMap(sVertexProgram, 2, sDiffuseChannel, cube_channel, false);
-		sVertexProgram->unbind();
-		sVertexProgram = NULL;
-	}
-}
-
-
-void LLDrawPoolAvatar::beginDeferredRiggedSimple()
-{
-    LL_PROFILE_ZONE_SCOPED
-
-	sVertexProgram = &gDeferredSkinnedDiffuseProgram;
-	sDiffuseChannel = 0;
-	sVertexProgram->bind();
-    if (LLPipeline::sRenderingHUDs)
-	{
-		sVertexProgram->uniform1i(LLShaderMgr::NO_ATMO, 1);
-	}
-	else
-	{
-		sVertexProgram->uniform1i(LLShaderMgr::NO_ATMO, 0);
-	}
-}
-
-void LLDrawPoolAvatar::endDeferredRiggedSimple()
-{
-    LL_PROFILE_ZONE_SCOPED
-
-	LLVertexBuffer::unbind();
-	sVertexProgram->unbind();
-	sVertexProgram = NULL;
-}
-
-void LLDrawPoolAvatar::beginDeferredRiggedBump()
-{
-    LL_PROFILE_ZONE_SCOPED
-
-	sVertexProgram = &gDeferredSkinnedBumpProgram;
-	sVertexProgram->bind();
-    if (LLPipeline::sRenderingHUDs)
-	{
-		sVertexProgram->uniform1i(LLShaderMgr::NO_ATMO, 1);
-	}
-	else
-	{
-		sVertexProgram->uniform1i(LLShaderMgr::NO_ATMO, 0);
-	}
-	normal_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::BUMP_MAP);
-	sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
-}
-
-void LLDrawPoolAvatar::endDeferredRiggedBump()
-{
-    LL_PROFILE_ZONE_SCOPED
-
-	LLVertexBuffer::unbind();
-	sVertexProgram->disableTexture(LLViewerShaderMgr::BUMP_MAP);
-	sVertexProgram->disableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
-	sVertexProgram->unbind();
-	normal_channel = -1;
-	sDiffuseChannel = 0;
-	sVertexProgram = NULL;
-}
-
-void LLDrawPoolAvatar::beginDeferredRiggedMaterial(S32 pass)
-{
-    LL_PROFILE_ZONE_SCOPED
-
-	if (pass == 1 ||
-		pass == 5 ||
-		pass == 9 ||
-		pass == 13)
-	{ //skip alpha passes
-		return;
-	}
-	sVertexProgram = &gDeferredMaterialProgram[pass+LLMaterial::SHADER_COUNT];
-
-	if (LLPipeline::sUnderWaterRender)
-	{
-		sVertexProgram = &(gDeferredMaterialWaterProgram[pass+LLMaterial::SHADER_COUNT]);
-	}
-
-	sVertexProgram->bind();
-    if (LLPipeline::sRenderingHUDs)
-	{
-		sVertexProgram->uniform1i(LLShaderMgr::NO_ATMO, 1);
-	}
-	else
-	{
-		sVertexProgram->uniform1i(LLShaderMgr::NO_ATMO, 0);
-	}
-	normal_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::BUMP_MAP);
-	specular_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::SPECULAR_MAP);
-	sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
-}
-
-void LLDrawPoolAvatar::endDeferredRiggedMaterial(S32 pass)
-{
-    LL_PROFILE_ZONE_SCOPED
-
-	if (pass == 1 ||
-		pass == 5 ||
-		pass == 9 ||
-		pass == 13)
-	{
-		return;
-	}
-
-	LLVertexBuffer::unbind();
-	sVertexProgram->disableTexture(LLViewerShaderMgr::BUMP_MAP);
-	sVertexProgram->disableTexture(LLViewerShaderMgr::SPECULAR_MAP);
-	sVertexProgram->disableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
-	sVertexProgram->unbind();
-	normal_channel = -1;
-	sDiffuseChannel = 0;
-	sVertexProgram = NULL;
-}
-
-void LLDrawPoolAvatar::beginDeferredSkinned()
-{
-    LL_PROFILE_ZONE_SCOPED
-
-	sShaderLevel = mShaderLevel;
-	sVertexProgram = &gDeferredAvatarProgram;
-	sRenderingSkinned = TRUE;
-
-	sVertexProgram->bind();
-	sVertexProgram->setMinimumAlpha(LLDrawPoolAvatar::sMinimumAlpha);
-	if (LLPipeline::sRenderingHUDs)
-	{
-		sVertexProgram->uniform1i(LLShaderMgr::NO_ATMO, 1);
-	}
-	else
-	{
-		sVertexProgram->uniform1i(LLShaderMgr::NO_ATMO, 0);
-	}
-
-	sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
-	gGL.getTexUnit(0)->activate();
-}
-
-void LLDrawPoolAvatar::endDeferredSkinned()
-{
-    LL_PROFILE_ZONE_SCOPED
-
-	// if we're in software-blending, remember to set the fence _after_ we draw so we wait till this rendering is done
-	sRenderingSkinned = FALSE;
-	sVertexProgram->unbind();
-
-	sVertexProgram->disableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
-
-	sShaderLevel = mShaderLevel;
-
-	gGL.getTexUnit(0)->activate();
-}
-
-void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass)
-{
-	if (pass == -1)
-	{
-		for (S32 i = 1; i < getNumPasses(); i++)
-		{ //skip foot shadows
-			prerender();
-			beginRenderPass(i);
-			renderAvatars(single_avatar, i);
-			endRenderPass(i);
-		}
-
-		return;
-	}
-
-	if (mDrawFace.empty() && !single_avatar)
-	{
-		return;
-	}
-
-	LLVOAvatar *avatarp = NULL;
-
-	if (single_avatar)
-	{
-		avatarp = single_avatar;
-	}
-	else
-	{
-		const LLFace *facep = mDrawFace[0];
-		if (!facep->getDrawable())
-		{
-			return;
-		}
-		avatarp = (LLVOAvatar *)facep->getDrawable()->getVObj().get();
-	}
-
-    if (avatarp->isDead() || avatarp->mDrawable.isNull())
-	{
-		return;
-	}
-
-    LL_RECORD_BLOCK_TIME(FTM_RENDER_CHARACTERS);
-
-	if (!single_avatar && !avatarp->isFullyLoaded() )
-	{
-		if (pass==0 && (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_PARTICLES) || LLViewerPartSim::getMaxPartCount() <= 0))
-		{
-			// debug code to draw a sphere in place of avatar
-			gGL.getTexUnit(0)->bind(LLViewerFetchedTexture::sWhiteImagep);
-			gGL.setColorMask(true, true);
-			LLVector3 pos = avatarp->getPositionAgent();
-			gGL.color4f(1.0f, 1.0f, 1.0f, 0.7f);
-			
-			gGL.pushMatrix();	 
-			gGL.translatef((F32)(pos.mV[VX]),	 
-						   (F32)(pos.mV[VY]),	 
-							(F32)(pos.mV[VZ]));	 
-			 gGL.scalef(0.15f, 0.15f, 0.3f);
-
-			 gSphere.renderGGL();
-				 
-			 gGL.popMatrix();
-			 gGL.setColorMask(true, false);
-		}
-		// don't render please
-		return;
-	}
-
-	BOOL impostor = !LLPipeline::sImpostorRender && avatarp->isImpostor() && !single_avatar;
-
-	if (( avatarp->isInMuteList() 
-		  || impostor 
-		  || (LLVOAvatar::AOA_NORMAL != avatarp->getOverallAppearance() && !avatarp->needsImpostorUpdate()) ) && pass != 0)
-//		  || (LLVOAvatar::AV_DO_NOT_RENDER == avatarp->getVisualMuteSettings() && !avatarp->needsImpostorUpdate()) ) && pass != 0)
-	{ //don't draw anything but the impostor for impostored avatars
-		return;
-	}
-	
-	if (pass == 0 && !impostor && LLPipeline::sUnderWaterRender)
-	{ //don't draw foot shadows under water
-		return;
-	}
-
-	LLVOAvatar *attached_av = avatarp->getAttachedAvatar();
-	if (attached_av && LLVOAvatar::AOA_NORMAL != attached_av->getOverallAppearance())
-	{
-		// Animesh attachment of a jellydolled or invisible parent - don't show
-		return;
-	}
-
-	if (pass == 0)
-	{
-		if (!LLPipeline::sReflectionRender)
-		{
-			LLVOAvatar::sNumVisibleAvatars++;
-		}
-
-//		if (impostor || (LLVOAvatar::AV_DO_NOT_RENDER == avatarp->getVisualMuteSettings() && !avatarp->needsImpostorUpdate()))
-		if (impostor || (LLVOAvatar::AOA_NORMAL != avatarp->getOverallAppearance() && !avatarp->needsImpostorUpdate()))
-		{
-			if (LLPipeline::sRenderDeferred && !LLPipeline::sReflectionRender && avatarp->mImpostor.isComplete()) 
-			{
-				if (normal_channel > -1)
-				{
-					avatarp->mImpostor.bindTexture(2, normal_channel);
-				}
-				if (specular_channel > -1)
-				{
-					avatarp->mImpostor.bindTexture(1, specular_channel);
-				}
-			}
-			avatarp->renderImpostor(avatarp->getMutedAVColor(), sDiffuseChannel);
-		}
-		return;
-	}
-
-	if (pass == 1)
-	{
-		// render rigid meshes (eyeballs) first
-		avatarp->renderRigid();
-		return;
-	}
-
-	if (pass == 3)
-	{
-		if (is_deferred_render)
-		{
-			renderDeferredRiggedSimple(avatarp);
-		}
-		else
-		{
-			renderRiggedSimple(avatarp);
-
-			if (LLPipeline::sRenderDeferred)
-			{ //render "simple" materials
-				renderRigged(avatarp, RIGGED_MATERIAL);
-				renderRigged(avatarp, RIGGED_MATERIAL_ALPHA_MASK);
-				renderRigged(avatarp, RIGGED_MATERIAL_ALPHA_EMISSIVE);
-				renderRigged(avatarp, RIGGED_NORMMAP);
-				renderRigged(avatarp, RIGGED_NORMMAP_MASK);
-				renderRigged(avatarp, RIGGED_NORMMAP_EMISSIVE);
-				renderRigged(avatarp, RIGGED_SPECMAP);
-				renderRigged(avatarp, RIGGED_SPECMAP_MASK);
-				renderRigged(avatarp, RIGGED_SPECMAP_EMISSIVE);
-				renderRigged(avatarp, RIGGED_NORMSPEC);
-				renderRigged(avatarp, RIGGED_NORMSPEC_MASK);
-				renderRigged(avatarp, RIGGED_NORMSPEC_EMISSIVE);
-			}
-		}
-		return;
-	}
-
-	if (pass == 4)
-	{
-		if (is_deferred_render)
-		{
-			renderDeferredRiggedBump(avatarp);
-		}
-		else
-		{
-			renderRiggedFullbright(avatarp);
-		}
-
-		return;
-	}
-
-	if (is_deferred_render && pass >= 5 && pass <= 21)
-	{
-		S32 p = pass-5;
-
-		if (p != 1 &&
-			p != 5 &&
-			p != 9 &&
-			p != 13)
-		{
-			renderDeferredRiggedMaterial(avatarp, p);
-		}
-		return;
-	}
-
-
-
-
-	if (pass == 5)
-	{
-		renderRiggedShinySimple(avatarp);
-				
-		return;
-	}
-
-	if (pass == 6)
-	{
-		renderRiggedFullbrightShiny(avatarp);
 		return;
 	}
 
-	if (pass >= 7 && pass < 13)
-	{
-		if (pass == 7)
-		{
-			renderRiggedAlpha(avatarp);
-
-			if (LLPipeline::sRenderDeferred && !is_post_deferred_render)
-			{ //render transparent materials under water
-				LLGLEnable blend(GL_BLEND);
-
-				gGL.setColorMask(true, true);
-				gGL.blendFunc(LLRender::BF_SOURCE_ALPHA,
-								LLRender::BF_ONE_MINUS_SOURCE_ALPHA,
-								LLRender::BF_ZERO,
-								LLRender::BF_ONE_MINUS_SOURCE_ALPHA);
-
-				renderRigged(avatarp, RIGGED_MATERIAL_ALPHA);
-				renderRigged(avatarp, RIGGED_SPECMAP_BLEND);
-				renderRigged(avatarp, RIGGED_NORMMAP_BLEND);
-				renderRigged(avatarp, RIGGED_NORMSPEC_BLEND);
-
-				gGL.setColorMask(true, false);
-			}
-			return;
-		}
-
-		if (pass == 8)
-		{
-			renderRiggedFullbrightAlpha(avatarp);
-			return;
-		}
-
-		if (LLPipeline::sRenderDeferred && is_post_deferred_render)
-		{
-			S32 p = 0;
-			switch (pass)
-			{
-			case 9: p = 1; break;
-			case 10: p = 5; break;
-			case 11: p = 9; break;
-			case 12: p = 13; break;
-			}
-
-			{
-				LLGLEnable blend(GL_BLEND);
-				renderDeferredRiggedMaterial(avatarp, p);
-			}
-			return;
-		}
-		else if (pass == 9)
-		{
-			renderRiggedGlow(avatarp);
-			return;
-		}
-	}
-
-	if (pass == 13)
+	if (mDrawFace.empty() && !single_avatar)
 	{
-		renderRiggedGlow(avatarp);
-		
 		return;
 	}
-	
-	if ((sShaderLevel >= SHADER_LEVEL_CLOTH))
-	{
-		LLMatrix4 rot_mat;
-		LLViewerCamera::getInstance()->getMatrixToLocal(rot_mat);
-		LLMatrix4 cfr(OGL_TO_CFR_ROTATION);
-		rot_mat *= cfr;
-		
-		LLVector4 wind;
-		wind.setVec(avatarp->mWindVec);
-		wind.mV[VW] = 0;
-		wind = wind * rot_mat;
-		wind.mV[VW] = avatarp->mWindVec.mV[VW];
-
-		sVertexProgram->uniform4fv(LLViewerShaderMgr::AVATAR_WIND, 1, wind.mV);
-		F32 phase = -1.f * (avatarp->mRipplePhase);
-
-		F32 freq = 7.f + (noise1(avatarp->mRipplePhase) * 2.f);
-		LLVector4 sin_params(freq, freq, freq, phase);
-		sVertexProgram->uniform4fv(LLViewerShaderMgr::AVATAR_SINWAVE, 1, sin_params.mV);
-
-		LLVector4 gravity(0.f, 0.f, -CLOTHING_GRAVITY_EFFECT, 0.f);
-		gravity = gravity * rot_mat;
-		sVertexProgram->uniform4fv(LLViewerShaderMgr::AVATAR_GRAVITY, 1, gravity.mV);
-	}
-
-	if( !single_avatar || (avatarp == single_avatar) )
-	{
-		avatarp->renderSkinned();
-	}
-}
-
-void LLDrawPoolAvatar::getRiggedGeometry(
-    LLFace* face,
-    LLPointer<LLVertexBuffer>& buffer,
-    U32 data_mask,
-    const LLMeshSkinInfo* skin,
-    LLVolume* volume,
-    const LLVolumeFace& vol_face)
-{
-    LL_PROFILE_ZONE_SCOPED
-
-    face->setGeomIndex(0);
-    face->setIndicesIndex(0);
-
-    if (face->getTextureIndex() != FACE_DO_NOT_BATCH_TEXTURES)
-    {
-        face->setDrawInfo(NULL);
-    }
-
-    //rigged faces do not batch textures
-    face->setTextureIndex(FACE_DO_NOT_BATCH_TEXTURES);
-
-	if (buffer.isNull() || buffer->getTypeMask() != data_mask || !buffer->isWriteable())
-	{
-        // make a new buffer
-		if (sShaderLevel > 0)
-		{
-			buffer = new LLVertexBuffer(data_mask, GL_DYNAMIC_DRAW_ARB);
-		}
-		else
-		{
-			buffer = new LLVertexBuffer(data_mask, GL_STREAM_DRAW_ARB);
-		}
-
-		if (!buffer->allocateBuffer(vol_face.mNumVertices, vol_face.mNumIndices, true))
-		{
-			LL_WARNS("LLDrawPoolAvatar") << "Failed to allocate Vertex Buffer to "
-				<< vol_face.mNumVertices << " vertices and "
-				<< vol_face.mNumIndices << " indices" << LL_ENDL;
-			// allocate dummy triangle
-			buffer->allocateBuffer(1, 3, true);
-			memset((U8*)buffer->getMappedData(), 0, buffer->getSize());
-			memset((U8*)buffer->getMappedIndices(), 0, buffer->getIndicesSize());
-		}
-	}
-	else
-	{
-        //resize existing buffer
-		if(!buffer->resizeBuffer(vol_face.mNumVertices, vol_face.mNumIndices))
-		{
-			LL_WARNS("LLDrawPoolAvatar") << "Failed to resize Vertex Buffer to "
-				<< vol_face.mNumVertices << " vertices and "
-				<< vol_face.mNumIndices << " indices" << LL_ENDL;
-			// allocate dummy triangle
-			buffer->resizeBuffer(1, 3);
-			memset((U8*)buffer->getMappedData(), 0, buffer->getSize());
-			memset((U8*)buffer->getMappedIndices(), 0, buffer->getIndicesSize());
-		}
-	}
-
-	face->setSize(buffer->getNumVerts(), buffer->getNumIndices());
-	face->setVertexBuffer(buffer);
-
-	U16 offset = 0;
-		
-	LLMatrix4 mat_vert = LLMatrix4(skin->mBindShapeMatrix);
-	glh::matrix4f m((F32*) mat_vert.mMatrix);
-	m = m.inverse().transpose();
-		
-	F32 mat3[] = 
-        { m.m[0], m.m[1], m.m[2],
-          m.m[4], m.m[5], m.m[6],
-          m.m[8], m.m[9], m.m[10] };
-
-	LLMatrix3 mat_normal(mat3);				
-
-	//let getGeometryVolume know if alpha should override shiny
-	U32 type = gPipeline.getPoolTypeFromTE(face->getTextureEntry(), face->getTexture());
-
-	if (type == LLDrawPool::POOL_ALPHA)
-	{
-		face->setPoolType(LLDrawPool::POOL_ALPHA);
-	}
-	else
-	{
-		face->setPoolType(mType); // either POOL_AVATAR or POOL_CONTROL_AV
-	}
 
-	//LL_INFOS() << "Rebuilt face " << face->getTEOffset() << " of " << face->getDrawable() << " at " << gFrameTimeSeconds << LL_ENDL;
+	LLVOAvatar *avatarp = NULL;
 
-	// Let getGeometryVolume know if a texture matrix is in play
-	if (face->mTextureMatrix)
+	if (single_avatar)
 	{
-		face->setState(LLFace::TEXTURE_ANIM);
+		avatarp = single_avatar;
 	}
 	else
 	{
-		face->clearState(LLFace::TEXTURE_ANIM);
-	}
-	face->getGeometryVolume(*volume, face->getTEOffset(), mat_vert, mat_normal, offset, true);
-
-	buffer->flush();
-}
-
-void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(
-    LLVOAvatar* avatar,
-    LLFace* face,
-    const LLVOVolume* vobj,
-    LLVolume* volume,
-    LLVolumeFace& vol_face)
-{
-    LL_PROFILE_ZONE_SCOPED;
-
-	LLVector4a* weights = vol_face.mWeights;
-	if (!weights)
-	{
-		return;
-	}
-
-    if (!vobj || vobj->isNoLOD())
-    {
-        return;
-    }
-
-	LLPointer<LLVertexBuffer> buffer = face->getVertexBuffer();
-	LLDrawable* drawable = face->getDrawable();
-
-    const U32 max_joints = LLSkinningUtil::getMaxJointCount();
-
-#if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS
-    #define CONDITION_WEIGHT(f) ((U8)llclamp((S32)f, (S32)0, (S32)max_joints-1))
-    LLVector4a* just_weights = vol_face.mJustWeights;
-    // we need to calculate the separated indices and store just the matrix weights for this vol...
-    if (!vol_face.mJointIndices)
-    {
-        // not very consty after all...
-        vol_face.allocateJointIndices(vol_face.mNumVertices);
-        just_weights = vol_face.mJustWeights;
-
-        U8* joint_indices_cursor = vol_face.mJointIndices;
-        for (int i = 0; i < vol_face.mNumVertices; i++)
-        {
-            F32* w = weights[i].getF32ptr();
-            F32* w_ = just_weights[i].getF32ptr();
-
-            F32 w0 = floorf(w[0]);
-            F32 w1 = floorf(w[1]);
-            F32 w2 = floorf(w[2]);
-            F32 w3 = floorf(w[3]);
-
-            joint_indices_cursor[0] = CONDITION_WEIGHT(w0);
-            joint_indices_cursor[1] = CONDITION_WEIGHT(w1);
-            joint_indices_cursor[2] = CONDITION_WEIGHT(w2);
-            joint_indices_cursor[3] = CONDITION_WEIGHT(w3);
-
-            // remove joint portion of combined weight
-            w_[0] = w[0] - w0;
-            w_[1] = w[1] - w1;
-            w_[2] = w[2] - w2;
-            w_[3] = w[3] - w3;
-
-            joint_indices_cursor += 4;
-        }
-    }
-#endif
-
-    U32 data_mask = face->getRiggedVertexBufferDataMask();
-    const LLMeshSkinInfo* skin = nullptr;
-
-	if (buffer.isNull() || 
-		buffer->getTypeMask() != data_mask ||
-		buffer->getNumVerts() != vol_face.mNumVertices ||
-		buffer->getNumIndices() != vol_face.mNumIndices ||
-		(drawable && drawable->isState(LLDrawable::REBUILD_ALL)))
-	{
-        LL_PROFILE_ZONE_NAMED("Rigged VBO Rebuild");
-        skin = vobj->getSkinInfo();
-        // FIXME ugly const cast
-        LLSkinningUtil::scrubInvalidJoints(avatar, const_cast<LLMeshSkinInfo*>(skin));
-
-        if (!vol_face.mWeightsScrubbed)
-        {
-            LLSkinningUtil::scrubSkinWeights(weights, vol_face.mNumVertices, skin);
-            vol_face.mWeightsScrubbed = TRUE;
-        }
-
-		if (drawable && drawable->isState(LLDrawable::REBUILD_ALL))
-		{
-            //rebuild EVERY face in the drawable, not just this one, to avoid missing drawable wide rebuild issues
-			for (S32 i = 0; i < drawable->getNumFaces(); ++i)
-			{
-				LLFace* facep = drawable->getFace(i);
-				U32 face_data_mask = facep->getRiggedVertexBufferDataMask();
-				if (face_data_mask)
-				{
-					LLPointer<LLVertexBuffer> cur_buffer = facep->getVertexBuffer();
-					const LLVolumeFace& cur_vol_face = volume->getVolumeFace(i);
-					getRiggedGeometry(facep, cur_buffer, face_data_mask, skin, volume, cur_vol_face);
-				}
-			}
-			drawable->clearState(LLDrawable::REBUILD_ALL);
-
-			buffer = face->getVertexBuffer();
-		}
-		else
-		{
-			//just rebuild this face
-			getRiggedGeometry(face, buffer, data_mask, skin, volume, vol_face);
-		}
-	}
-
-	if (sShaderLevel <= 0 && 
-        face->mLastSkinTime < avatar->getLastSkinTime() &&
-        !buffer.isNull() &&
-        buffer->getNumVerts() == vol_face.mNumVertices &&
-        buffer->getNumIndices() == vol_face.mNumIndices)
-	{
-        LL_PROFILE_ZONE_NAMED("Software Skinning");
-		//perform software vertex skinning for this face
-		LLStrider<LLVector3> position;
-		LLStrider<LLVector3> normal;
-
-		bool has_normal = buffer->hasDataType(LLVertexBuffer::TYPE_NORMAL);
-		buffer->getVertexStrider(position);
-
-		if (has_normal)
+		const LLFace *facep = mDrawFace[0];
+		if (!facep->getDrawable())
 		{
-			buffer->getNormalStrider(normal);
-		}
-
-		LLVector4a* pos = (LLVector4a*) position.get();
-
-		LLVector4a* norm = has_normal ? (LLVector4a*) normal.get() : NULL;
-
-        const MatrixPaletteCache& mpc = updateSkinInfoMatrixPalette(avatar, vobj->getMeshID());
-        const LLMatrix4a* mat = &(mpc.mMatrixPalette[0]);
-        const LLMatrix4a& bind_shape_matrix = mpc.mBindShapeMatrix;
-
-        if (!mpc.mMatrixPalette.empty())
-        {
-            for (U32 j = 0; j < buffer->getNumVerts(); ++j)
-		    {
-			    LLMatrix4a final_mat;
-                LLSkinningUtil::getPerVertexSkinMatrix(weights[j].getF32ptr(), mat, false, final_mat, max_joints);
-
-			    LLVector4a& v = vol_face.mPositions[j];
-			    LLVector4a t;
-			    LLVector4a dst;
-			    bind_shape_matrix.affineTransform(v, t);
-			    final_mat.affineTransform(t, dst);
-			    pos[j] = dst;
-
-			    if (norm)
-			    {
-				    LLVector4a& n = vol_face.mNormals[j];
-				    bind_shape_matrix.rotate(n, t);
-				    final_mat.rotate(t, dst);
-				    //dst.normalize3fast();
-				    norm[j] = dst;
-			    }
-		    }
-        }
+			return;
+		}
+		avatarp = (LLVOAvatar *)facep->getDrawable()->getVObj().get();
 	}
-}
-
-void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, bool glow)
-{
-    LL_PROFILE_ZONE_SCOPED
 
-	if (!avatar->shouldRenderRigged())
+    if (avatarp->isDead() || avatarp->mDrawable.isNull())
 	{
 		return;
 	}
 
-    LLUUID lastMeshId;
+    LL_RECORD_BLOCK_TIME(FTM_RENDER_CHARACTERS);
 
-	for (U32 i = 0; i < mRiggedFace[type].size(); ++i)
+	if (!single_avatar && !avatarp->isFullyLoaded() )
 	{
-        LL_PROFILE_ZONE_NAMED("Render Rigged Face");
-		LLFace* face = mRiggedFace[type][i];
-
-        S32 offset = face->getIndicesStart();
-		U32 count = face->getIndicesCount();
-
-        U16 start = face->getGeomStart();
-		U16 end = start + face->getGeomCount()-1;
-
-		LLDrawable* drawable = face->getDrawable();
-		if (!drawable)
+		if (pass==0 && (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_PARTICLES) || LLViewerPartSim::getMaxPartCount() <= 0))
 		{
-			continue;
+			// debug code to draw a sphere in place of avatar
+			gGL.getTexUnit(0)->bind(LLViewerFetchedTexture::sWhiteImagep);
+			gGL.setColorMask(true, true);
+			LLVector3 pos = avatarp->getPositionAgent();
+			gGL.color4f(1.0f, 1.0f, 1.0f, 0.7f);
+			
+			gGL.pushMatrix();	 
+			gGL.translatef((F32)(pos.mV[VX]),	 
+						   (F32)(pos.mV[VY]),	 
+							(F32)(pos.mV[VZ]));	 
+			 gGL.scalef(0.15f, 0.15f, 0.3f);
+
+			 gSphere.renderGGL();
+				 
+			 gGL.popMatrix();
+			 gGL.setColorMask(true, false);
 		}
+		// don't render please
+		return;
+	}
 
-		LLVOVolume* vobj = drawable->getVOVolume();
+	BOOL impostor = !LLPipeline::sImpostorRender && avatarp->isImpostor() && !single_avatar;
 
-		if (!vobj)
-		{
-			continue;
-		}
+	if (( avatarp->isInMuteList() 
+		  || impostor 
+		  || (LLVOAvatar::AOA_NORMAL != avatarp->getOverallAppearance() && !avatarp->needsImpostorUpdate()) ) && pass != 0)
+//		  || (LLVOAvatar::AV_DO_NOT_RENDER == avatarp->getVisualMuteSettings() && !avatarp->needsImpostorUpdate()) ) && pass != 0)
+	{ //don't draw anything but the impostor for impostored avatars
+		return;
+	}
+	
+	if (pass == 0 && !impostor && LLPipeline::sUnderWaterRender)
+	{ //don't draw foot shadows under water
+		return;
+	}
 
-		LLVolume* volume = vobj->getVolume();
-		S32 te = face->getTEOffset();
+	LLVOAvatar *attached_av = avatarp->getAttachedAvatar();
+	if (attached_av && LLVOAvatar::AOA_NORMAL != attached_av->getOverallAppearance())
+	{
+		// Animesh attachment of a jellydolled or invisible parent - don't show
+		return;
+	}
 
-		if (!volume || volume->getNumVolumeFaces() <= te || !volume->isMeshAssetLoaded())
+	if (pass == 0)
+	{
+		if (!LLPipeline::sReflectionRender)
 		{
-			continue;
+			LLVOAvatar::sNumVisibleAvatars++;
 		}
 
-		U32 data_mask = LLFace::getRiggedDataMask(type);
-
-		LLVertexBuffer* buff = face->getVertexBuffer();
-
-        const LLTextureEntry* tex_entry = face->getTextureEntry();
-		LLMaterial* mat = tex_entry ? tex_entry->getMaterialParams().get() : nullptr;
-
-        if (LLDrawPoolAvatar::sShadowPass >= 0)
-        {
-            bool is_alpha_blend = false;
-            bool is_alpha_mask  = false;
-
-            LLViewerTexture* tex = face->getTexture(LLRender::DIFFUSE_MAP);
-            if (tex)
-            {
-                if (tex->getIsAlphaMask())
-                {
-                    is_alpha_mask = true;
-                }
-            }
-
-            if (tex)
-            {
-                LLGLenum image_format = tex->getPrimaryFormat();
-                if (!is_alpha_mask && (image_format == GL_RGBA || image_format == GL_ALPHA))
-                {
-                    is_alpha_blend = true;
-                }
-            }
-
-            if (tex_entry)
-            {
-                if (tex_entry->getAlpha() <= 0.99f)
-                {
-                    is_alpha_blend = true;
-                }
-            }
-
-            if (mat)
-            {                
-                switch (LLMaterial::eDiffuseAlphaMode(mat->getDiffuseAlphaMode()))
-                {
-                    case LLMaterial::DIFFUSE_ALPHA_MODE_MASK:
-                    {
-                        is_alpha_mask  = true;
-                        is_alpha_blend = false;
-                    }
-                    break;
-
-                    case LLMaterial::DIFFUSE_ALPHA_MODE_BLEND:
-                    {
-                        is_alpha_blend = true;
-                        is_alpha_mask  = false;
-                    }
-                    break;
-
-                    case LLMaterial::DIFFUSE_ALPHA_MODE_EMISSIVE:
-                    case LLMaterial::DIFFUSE_ALPHA_MODE_DEFAULT:
-                    case LLMaterial::DIFFUSE_ALPHA_MODE_NONE:
-                    default:
-                        is_alpha_blend = false;
-                        is_alpha_mask  = false;
-                        break;
-                }
-            }
-
-            // if this is alpha mask content and we're doing opaques or a non-alpha-mask shadow pass...
-            if (is_alpha_mask && (LLDrawPoolAvatar::sSkipTransparent || LLDrawPoolAvatar::sShadowPass != SHADOW_PASS_ATTACHMENT_ALPHA_MASK))
-            {
-                return;
-            }
-
-            // if this is alpha blend content and we're doing opaques or a non-alpha-blend shadow pass...
-            if (is_alpha_blend && (LLDrawPoolAvatar::sSkipTransparent || LLDrawPoolAvatar::sShadowPass != SHADOW_PASS_ATTACHMENT_ALPHA_BLEND))
-            {
-                return;
-            }
-
-            // if this is opaque content and we're skipping opaques...
-            if (!is_alpha_mask && !is_alpha_blend && LLDrawPoolAvatar::sSkipOpaque)
-            {
-                return;
-            }
-        }
-
-		if (buff)
+//		if (impostor || (LLVOAvatar::AV_DO_NOT_RENDER == avatarp->getVisualMuteSettings() && !avatarp->needsImpostorUpdate()))
+		if (impostor || (LLVOAvatar::AOA_NORMAL != avatarp->getOverallAppearance() && !avatarp->needsImpostorUpdate()))
 		{
-			if (sShaderLevel > 0)
-			{
-                auto& meshId = vobj->getMeshID();
-                
-                if (lastMeshId != meshId) // <== only upload matrix palette to GL if the skininfo changed
-                {
-                    // upload matrix palette to shader
-                    const MatrixPaletteCache& mpc = updateSkinInfoMatrixPalette(avatar, meshId);
-                    U32 count = mpc.mMatrixPalette.size();
-
-                    if (count == 0)
-                    {
-                        //skin info not loaded yet, don't render
-                        continue;
-                    }
-
-                    LLDrawPoolAvatar::sVertexProgram->uniformMatrix3x4fv(LLViewerShaderMgr::AVATAR_MATRIX,
-                        count,
-                        FALSE,
-                        (GLfloat*) &(mpc.mGLMp[0]));
-                }
-
-                lastMeshId = meshId;
-			}
-			else
-			{
-				data_mask &= ~LLVertexBuffer::MAP_WEIGHT4;
-			}
-
-			if (mat)
+			if (LLPipeline::sRenderDeferred && !LLPipeline::sReflectionRender && avatarp->mImpostor.isComplete()) 
 			{
-				//order is important here LLRender::DIFFUSE_MAP should be last, becouse it change 
-				//(gGL).mCurrTextureUnitIndex
-                LLViewerTexture* specular = NULL;
-                if (LLPipeline::sImpostorRender)
-                {
-                    specular = LLViewerTextureManager::findFetchedTexture(gBlackSquareID, TEX_LIST_STANDARD);
-                    llassert(NULL != specular);
-                }
-                else
-                {
-                    specular = face->getTexture(LLRender::SPECULAR_MAP);
-                }
-                if (specular && specular_channel >= 0)
-                {
-                    gGL.getTexUnit(specular_channel)->bindFast(specular);
-                }
-                
-                if (normal_channel >= 0)
-                {
-                    auto* texture = face->getTexture(LLRender::NORMAL_MAP);
-                    if (texture)
-                    {
-                        gGL.getTexUnit(normal_channel)->bindFast(texture);
-                    }
-                    //else
-                    //{
-                        // TODO handle missing normal map
-                    //}
-                }
-
-				gGL.getTexUnit(sDiffuseChannel)->bindFast(face->getTexture(LLRender::DIFFUSE_MAP));
-
-
-				LLColor4 col = mat->getSpecularLightColor();
-				F32 spec = mat->getSpecularLightExponent()/255.f;
-
-				F32 env = mat->getEnvironmentIntensity()/255.f;
-
-				if (mat->getSpecularID().isNull())
-				{
-					env = tex_entry->getShiny()*0.25f;
-					col.set(env,env,env,0);
-					spec = env;
-				}
-		
-				BOOL fullbright = tex_entry->getFullbright();
-
-				sVertexProgram->uniform1f(LLShaderMgr::EMISSIVE_BRIGHTNESS, fullbright ? 1.f : 0.f);
-				sVertexProgram->uniform4f(LLShaderMgr::SPECULAR_COLOR, col.mV[0], col.mV[1], col.mV[2], spec);
-				sVertexProgram->uniform1f(LLShaderMgr::ENVIRONMENT_INTENSITY, env);
-
-				if (mat->getDiffuseAlphaMode() == LLMaterial::DIFFUSE_ALPHA_MODE_MASK)
-				{
-                    F32 cutoff = mat->getAlphaMaskCutoff()/255.f;
-					sVertexProgram->setMinimumAlpha(cutoff);
-				}
-				else
+				if (normal_channel > -1)
 				{
-					sVertexProgram->setMinimumAlpha(0.f);
+					avatarp->mImpostor.bindTexture(2, normal_channel);
 				}
-
-                if (!LLPipeline::sShadowRender && !LLPipeline::sReflectionRender)
-                {
-                    for (U32 i = 0; i < LLRender::NUM_TEXTURE_CHANNELS; ++i)
-                    {
-                        LLViewerTexture* tex = face->getTexture(i);
-                        if (tex)
-                        {
-                            tex->addTextureStats(avatar->getPixelArea());
-                        }
-                    }
-                }
-			}
-			else
-			{
-				sVertexProgram->setMinimumAlpha(0.f);
-				if (normal_channel > -1)
+				if (specular_channel > -1)
 				{
-					LLDrawPoolBump::bindBumpMap(face, normal_channel);
+					avatarp->mImpostor.bindTexture(1, specular_channel);
 				}
-
-                gGL.getTexUnit(sDiffuseChannel)->bindFast(face->getTexture());
-
-			}
-
-			if (face->mTextureMatrix && vobj->mTexAnimMode)
-			{
-                U32 tex_index = gGL.getCurrentTexUnitIndex();
-
-                if (tex_index <= 1)
-                {
-                    gGL.matrixMode(LLRender::eMatrixMode(LLRender::MM_TEXTURE0 + tex_index));
-                    gGL.pushMatrix();
-				    gGL.loadMatrix((F32*) face->mTextureMatrix->mMatrix);
-                }
-
-				buff->setBufferFast(data_mask);
-				buff->drawRangeFast(LLRender::TRIANGLES, start, end, count, offset);
-
-                if (tex_index <= 1)
-                {
-                    gGL.matrixMode(LLRender::eMatrixMode(LLRender::MM_TEXTURE0 + tex_index));
-				    gGL.popMatrix();
-                    gGL.matrixMode(LLRender::MM_MODELVIEW);
-                }
-			}
-			else
-			{
-				buff->setBufferFast(data_mask);
-				buff->drawRangeFast(LLRender::TRIANGLES, start, end, count, offset);
 			}
+			avatarp->renderImpostor(avatarp->getMutedAVColor(), sDiffuseChannel);
 		}
+		return;
 	}
-}
 
-void LLDrawPoolAvatar::renderDeferredRiggedSimple(LLVOAvatar* avatar)
-{
-    LL_PROFILE_ZONE_SCOPED
-
-	renderRigged(avatar, RIGGED_DEFERRED_SIMPLE);
-}
-
-void LLDrawPoolAvatar::renderDeferredRiggedBump(LLVOAvatar* avatar)
-{
-    LL_PROFILE_ZONE_SCOPED
-
-	renderRigged(avatar, RIGGED_DEFERRED_BUMP);
-}
-
-void LLDrawPoolAvatar::renderDeferredRiggedMaterial(LLVOAvatar* avatar, S32 pass)
-{
-    LL_PROFILE_ZONE_SCOPED
-
-	renderRigged(avatar, pass);
-}
-
-static LLTrace::BlockTimerStatHandle FTM_RIGGED_VBO("Rigged VBO");
-
-void LLDrawPoolAvatar::updateRiggedVertexBuffers(LLVOAvatar* avatar)
-{
-	LL_RECORD_BLOCK_TIME(FTM_RIGGED_VBO);
-
-	//update rigged vertex buffers
-	for (U32 type = 0; type < NUM_RIGGED_PASSES; ++type)
+	if (pass == 1)
 	{
-        LL_PROFILE_ZONE_NAMED("Pass");
-		for (U32 i = 0; i < mRiggedFace[type].size(); ++i)
-		{
-            LL_PROFILE_ZONE_NAMED("Face");
-			LLFace* face = mRiggedFace[type][i];
-			LLDrawable* drawable = face->getDrawable();
-			if (!drawable)
-			{
-				continue;
-			}
-
-			LLVOVolume* vobj = drawable->getVOVolume();
-
-			if (!vobj || vobj->isNoLOD())
-			{
-				continue;
-			}
-
-			LLVolume* volume = vobj->getVolume();
-			S32 te = face->getTEOffset();
-
-			if (!volume || volume->getNumVolumeFaces() <= te)
-			{
-				continue;
-			}
-
-			LLVolumeFace& vol_face = volume->getVolumeFace(te);
-			updateRiggedFaceVertexBuffer(avatar, face, vobj, volume, vol_face);
-		}
+		// render rigid meshes (eyeballs) first
+		avatarp->renderRigid();
+		return;
 	}
-}
-
-void LLDrawPoolAvatar::updateSkinInfoMatrixPalettes(LLVOAvatar* avatarp)
-{
-    LL_PROFILE_ZONE_SCOPED;
-    //evict matrix palettes from the cache that haven't been updated in 10 frames
-    for (matrix_palette_cache_t::iterator iter = mMatrixPaletteCache.begin(); iter != mMatrixPaletteCache.end(); )
-    {
-        if (gFrameCount - iter->second.mFrame > 10)
-        {
-            iter = mMatrixPaletteCache.erase(iter);
-        }
-        else
-        {
-            ++iter;
-        }
-    }
-}
-
-const LLDrawPoolAvatar::MatrixPaletteCache& LLDrawPoolAvatar::updateSkinInfoMatrixPalette(LLVOAvatar * avatarp, const LLUUID& meshId)
-{
-    MatrixPaletteCache& entry = mMatrixPaletteCache[meshId];
-
-    if (entry.mFrame != gFrameCount)
-    {
-        LL_PROFILE_ZONE_SCOPED;
-
-        const LLMeshSkinInfo* skin = gMeshRepo.getSkinInfo(meshId);
-        entry.mFrame = gFrameCount;
-
-        if (skin != nullptr)
-        {
-            entry.mBindShapeMatrix = skin->mBindShapeMatrix;
-
-            //build matrix palette
-            U32 count = LLSkinningUtil::getMeshJointCount(skin);
-            entry.mMatrixPalette.resize(count);
-            LLSkinningUtil::initSkinningMatrixPalette(&(entry.mMatrixPalette[0]), count, skin, avatarp);
-
-            const LLMatrix4a* mat = &(entry.mMatrixPalette[0]);
-
-            entry.mGLMp.resize(count * 12);
-
-            F32* mp = &(entry.mGLMp[0]);
-
-            for (U32 i = 0; i < count; ++i)
-            {
-                F32* m = (F32*)mat[i].mMatrix[0].getF32ptr();
-
-                U32 idx = i * 12;
-
-                mp[idx + 0] = m[0];
-                mp[idx + 1] = m[1];
-                mp[idx + 2] = m[2];
-                mp[idx + 3] = m[12];
-
-                mp[idx + 4] = m[4];
-                mp[idx + 5] = m[5];
-                mp[idx + 6] = m[6];
-                mp[idx + 7] = m[13];
-
-                mp[idx + 8] = m[8];
-                mp[idx + 9] = m[9];
-                mp[idx + 10] = m[10];
-                mp[idx + 11] = m[14];
-            }
-        }
-        else
-        {
-            entry.mMatrixPalette.resize(0);
-            entry.mGLMp.resize(0);
-        }
-    }
-
-    return entry;
-}
-
-void LLDrawPoolAvatar::renderRiggedSimple(LLVOAvatar* avatar)
-{
-    LL_PROFILE_ZONE_SCOPED
-
-	renderRigged(avatar, RIGGED_SIMPLE);
-}
-
-void LLDrawPoolAvatar::renderRiggedFullbright(LLVOAvatar* avatar)
-{
-    LL_PROFILE_ZONE_SCOPED
-
-	renderRigged(avatar, RIGGED_FULLBRIGHT);
-}
-
-	
-void LLDrawPoolAvatar::renderRiggedShinySimple(LLVOAvatar* avatar)
-{
-    LL_PROFILE_ZONE_SCOPED
-
-	renderRigged(avatar, RIGGED_SHINY);
-}
-
-void LLDrawPoolAvatar::renderRiggedFullbrightShiny(LLVOAvatar* avatar)
-{
-    LL_PROFILE_ZONE_SCOPED
 
-	renderRigged(avatar, RIGGED_FULLBRIGHT_SHINY);
-}
-
-void LLDrawPoolAvatar::renderRiggedAlpha(LLVOAvatar* avatar)
-{
-    LL_PROFILE_ZONE_SCOPED
-
-	if (!mRiggedFace[RIGGED_ALPHA].empty())
+	if ((sShaderLevel >= SHADER_LEVEL_CLOTH))
 	{
-		LLGLEnable blend(GL_BLEND);
-
-		gGL.setColorMask(true, true);
-		gGL.blendFunc(LLRender::BF_SOURCE_ALPHA,
-						LLRender::BF_ONE_MINUS_SOURCE_ALPHA,
-						LLRender::BF_ZERO,
-						LLRender::BF_ONE_MINUS_SOURCE_ALPHA);
-
-		renderRigged(avatar, RIGGED_ALPHA);
-		gGL.setColorMask(true, false);
-	}
-}
-
-void LLDrawPoolAvatar::renderRiggedFullbrightAlpha(LLVOAvatar* avatar)
-{
-    LL_PROFILE_ZONE_SCOPED
+		LLMatrix4 rot_mat;
+		LLViewerCamera::getInstance()->getMatrixToLocal(rot_mat);
+		LLMatrix4 cfr(OGL_TO_CFR_ROTATION);
+		rot_mat *= cfr;
+		
+		LLVector4 wind;
+		wind.setVec(avatarp->mWindVec);
+		wind.mV[VW] = 0;
+		wind = wind * rot_mat;
+		wind.mV[VW] = avatarp->mWindVec.mV[VW];
 
-	if (!mRiggedFace[RIGGED_FULLBRIGHT_ALPHA].empty())
-	{
-		LLGLEnable blend(GL_BLEND);
+		sVertexProgram->uniform4fv(LLViewerShaderMgr::AVATAR_WIND, 1, wind.mV);
+		F32 phase = -1.f * (avatarp->mRipplePhase);
 
-		gGL.setColorMask(true, true);
-		gGL.blendFunc(LLRender::BF_SOURCE_ALPHA,
-						LLRender::BF_ONE_MINUS_SOURCE_ALPHA,
-						LLRender::BF_ZERO,
-						LLRender::BF_ONE_MINUS_SOURCE_ALPHA);
+		F32 freq = 7.f + (noise1(avatarp->mRipplePhase) * 2.f);
+		LLVector4 sin_params(freq, freq, freq, phase);
+		sVertexProgram->uniform4fv(LLViewerShaderMgr::AVATAR_SINWAVE, 1, sin_params.mV);
 
-		renderRigged(avatar, RIGGED_FULLBRIGHT_ALPHA);
-		gGL.setColorMask(true, false);
+		LLVector4 gravity(0.f, 0.f, -CLOTHING_GRAVITY_EFFECT, 0.f);
+		gravity = gravity * rot_mat;
+		sVertexProgram->uniform4fv(LLViewerShaderMgr::AVATAR_GRAVITY, 1, gravity.mV);
 	}
-}
-
-void LLDrawPoolAvatar::renderRiggedGlow(LLVOAvatar* avatar)
-{
-    LL_PROFILE_ZONE_SCOPED
 
-	if (!mRiggedFace[RIGGED_GLOW].empty())
+	if( !single_avatar || (avatarp == single_avatar) )
 	{
-		LLGLEnable blend(GL_BLEND);
-		LLGLDisable test(GL_ALPHA_TEST);
-		gGL.flush();
-
-		LLGLEnable polyOffset(GL_POLYGON_OFFSET_FILL);
-		glPolygonOffset(-1.0f, -1.0f);
-		gGL.setSceneBlendType(LLRender::BT_ADD);
-
-		LLGLDepthTest depth(GL_TRUE, GL_FALSE);
-		gGL.setColorMask(false, true);
-
-		renderRigged(avatar, RIGGED_GLOW, true);
-
-		gGL.setColorMask(true, false);
-		gGL.setSceneBlendType(LLRender::BT_ALPHA);
+		avatarp->renderSkinned();
 	}
 }
 
-
+static LLTrace::BlockTimerStatHandle FTM_RIGGED_VBO("Rigged VBO");
 
 //-----------------------------------------------------------------------------
 // getDebugTexture()
@@ -2619,66 +940,6 @@ LLColor3 LLDrawPoolAvatar::getDebugColor() const
 	return LLColor3(0.f, 1.f, 0.f);
 }
 
-void LLDrawPoolAvatar::addRiggedFace(LLFace* facep, U32 type)
-{
-    LL_PROFILE_ZONE_SCOPED
-
-    llassert (facep->isState(LLFace::RIGGED));
-    llassert(getType() == LLDrawPool::POOL_AVATAR || getType() == LLDrawPool::POOL_CONTROL_AV);
-    if (facep->getPool() && facep->getPool() != this)
-    {
-        LL_ERRS() << "adding rigged face that's already in another pool" << LL_ENDL;
-    }
-	if (type >= NUM_RIGGED_PASSES)
-	{
-		LL_ERRS() << "Invalid rigged face type." << LL_ENDL;
-	}
-	if (facep->getRiggedIndex(type) != -1)
-	{
-		LL_ERRS() << "Tried to add a rigged face that's referenced elsewhere." << LL_ENDL;
-	}	
-	
-	facep->setRiggedIndex(type, mRiggedFace[type].size());
-	facep->setPool(this);
-	mRiggedFace[type].push_back(facep);
-}
-
-void LLDrawPoolAvatar::removeRiggedFace(LLFace* facep)
-{
-    LL_PROFILE_ZONE_SCOPED
-
-    llassert (facep->isState(LLFace::RIGGED));
-    llassert(getType() == LLDrawPool::POOL_AVATAR || getType() == LLDrawPool::POOL_CONTROL_AV);
-    if (facep->getPool() != this)
-    {
-        LL_ERRS() << "Tried to remove a rigged face from the wrong pool" << LL_ENDL;
-    }
-	facep->setPool(NULL);
-
-	for (U32 i = 0; i < NUM_RIGGED_PASSES; ++i)
-	{
-		S32 index = facep->getRiggedIndex(i);
-		
-		if (index > -1)
-		{
-			if (mRiggedFace[i].size() > index && mRiggedFace[i][index] == facep)
-			{
-				facep->setRiggedIndex(i,-1);
-				mRiggedFace[i].erase(mRiggedFace[i].begin()+index);
-				for (U32 j = index; j < mRiggedFace[i].size(); ++j)
-				{ //bump indexes down for faces referenced after erased face
-					mRiggedFace[i][j]->setRiggedIndex(i, j);
-				}
-			}
-			else
-			{
-				LL_ERRS() << "Face reference data corrupt for rigged type " << i
-					<< ((mRiggedFace[i].size() <= index) ? "; wrong index (out of bounds)" : (mRiggedFace[i][index] != facep) ? "; wrong face pointer" : "")
-					<< LL_ENDL;
-			}
-		}
-	}
-}
 
 LLVertexBufferAvatar::LLVertexBufferAvatar()
 : LLVertexBuffer(sDataMask, 
diff --git a/indra/newview/lldrawpoolavatar.h b/indra/newview/lldrawpoolavatar.h
index 800bbc5f62a..21add39b21b 100644
--- a/indra/newview/lldrawpoolavatar.h
+++ b/indra/newview/lldrawpoolavatar.h
@@ -62,119 +62,11 @@ class LLDrawPoolAvatar : public LLFacePool
     ~LLDrawPoolAvatar();
     /*virtual*/ BOOL isDead();
 
-    typedef enum
-	{
-		RIGGED_MATERIAL=0,
-		RIGGED_MATERIAL_ALPHA,
-		RIGGED_MATERIAL_ALPHA_MASK,
-		RIGGED_MATERIAL_ALPHA_EMISSIVE,
-		RIGGED_SPECMAP,
-		RIGGED_SPECMAP_BLEND,
-		RIGGED_SPECMAP_MASK,
-		RIGGED_SPECMAP_EMISSIVE,
-		RIGGED_NORMMAP,
-		RIGGED_NORMMAP_BLEND,
-		RIGGED_NORMMAP_MASK,
-		RIGGED_NORMMAP_EMISSIVE,
-		RIGGED_NORMSPEC,
-		RIGGED_NORMSPEC_BLEND,
-		RIGGED_NORMSPEC_MASK,
-		RIGGED_NORMSPEC_EMISSIVE,
-		RIGGED_SIMPLE,
-		RIGGED_FULLBRIGHT,
-		RIGGED_SHINY,
-		RIGGED_FULLBRIGHT_SHINY,
-		RIGGED_GLOW,
-		RIGGED_ALPHA,
-		RIGGED_FULLBRIGHT_ALPHA,
-		RIGGED_DEFERRED_BUMP,
-		RIGGED_DEFERRED_SIMPLE,
-		NUM_RIGGED_PASSES,
-		RIGGED_UNKNOWN,
-	} eRiggedPass;
-
-	typedef enum
-	{
-		RIGGED_MATERIAL_MASK =
-						LLVertexBuffer::MAP_VERTEX | 
-						LLVertexBuffer::MAP_NORMAL | 
-						LLVertexBuffer::MAP_TEXCOORD0 |
-						LLVertexBuffer::MAP_COLOR |
-						LLVertexBuffer::MAP_WEIGHT4,
-		RIGGED_MATERIAL_ALPHA_VMASK = RIGGED_MATERIAL_MASK,
-		RIGGED_MATERIAL_ALPHA_MASK_MASK = RIGGED_MATERIAL_MASK,
-		RIGGED_MATERIAL_ALPHA_EMISSIVE_MASK = RIGGED_MATERIAL_MASK,
-		RIGGED_SPECMAP_VMASK =
-						LLVertexBuffer::MAP_VERTEX | 
-						LLVertexBuffer::MAP_NORMAL | 
-						LLVertexBuffer::MAP_TEXCOORD0 |
-						LLVertexBuffer::MAP_TEXCOORD2 |
-						LLVertexBuffer::MAP_COLOR |
-						LLVertexBuffer::MAP_WEIGHT4,
-		RIGGED_SPECMAP_BLEND_MASK = RIGGED_SPECMAP_VMASK,
-		RIGGED_SPECMAP_MASK_MASK = RIGGED_SPECMAP_VMASK,
-		RIGGED_SPECMAP_EMISSIVE_MASK = RIGGED_SPECMAP_VMASK,
-		RIGGED_NORMMAP_VMASK =
-						LLVertexBuffer::MAP_VERTEX | 
-						LLVertexBuffer::MAP_NORMAL | 
-						LLVertexBuffer::MAP_TANGENT | 
-						LLVertexBuffer::MAP_TEXCOORD0 |
-						LLVertexBuffer::MAP_TEXCOORD1 |
-						LLVertexBuffer::MAP_COLOR |
-						LLVertexBuffer::MAP_WEIGHT4,
-		RIGGED_NORMMAP_BLEND_MASK = RIGGED_NORMMAP_VMASK,
-		RIGGED_NORMMAP_MASK_MASK = RIGGED_NORMMAP_VMASK,
-		RIGGED_NORMMAP_EMISSIVE_MASK = RIGGED_NORMMAP_VMASK,
-		RIGGED_NORMSPEC_VMASK =
-						LLVertexBuffer::MAP_VERTEX | 
-						LLVertexBuffer::MAP_NORMAL | 
-						LLVertexBuffer::MAP_TANGENT | 
-						LLVertexBuffer::MAP_TEXCOORD0 |
-						LLVertexBuffer::MAP_TEXCOORD1 |
-						LLVertexBuffer::MAP_TEXCOORD2 |
-						LLVertexBuffer::MAP_COLOR |
-						LLVertexBuffer::MAP_WEIGHT4,
-		RIGGED_NORMSPEC_BLEND_MASK = RIGGED_NORMSPEC_VMASK,
-		RIGGED_NORMSPEC_MASK_MASK = RIGGED_NORMSPEC_VMASK,
-		RIGGED_NORMSPEC_EMISSIVE_MASK = RIGGED_NORMSPEC_VMASK,
-		RIGGED_SIMPLE_MASK = LLVertexBuffer::MAP_VERTEX | 
-							 LLVertexBuffer::MAP_NORMAL | 
-							 LLVertexBuffer::MAP_TEXCOORD0 |
-							 LLVertexBuffer::MAP_COLOR |
-							 LLVertexBuffer::MAP_WEIGHT4,
-		RIGGED_FULLBRIGHT_MASK = LLVertexBuffer::MAP_VERTEX | 
-							 LLVertexBuffer::MAP_TEXCOORD0 |
-							 LLVertexBuffer::MAP_COLOR |
-							 LLVertexBuffer::MAP_WEIGHT4,
-		RIGGED_SHINY_MASK = RIGGED_SIMPLE_MASK,
-		RIGGED_FULLBRIGHT_SHINY_MASK = RIGGED_SIMPLE_MASK,							 
-		RIGGED_GLOW_MASK = LLVertexBuffer::MAP_VERTEX | 
-							 LLVertexBuffer::MAP_TEXCOORD0 |
-							 LLVertexBuffer::MAP_EMISSIVE |
-							 LLVertexBuffer::MAP_WEIGHT4,
-		RIGGED_ALPHA_MASK = RIGGED_SIMPLE_MASK,
-		RIGGED_FULLBRIGHT_ALPHA_MASK = RIGGED_FULLBRIGHT_MASK,
-		RIGGED_DEFERRED_BUMP_MASK = LLVertexBuffer::MAP_VERTEX | 
-							 LLVertexBuffer::MAP_NORMAL | 
-							 LLVertexBuffer::MAP_TEXCOORD0 |
-							 LLVertexBuffer::MAP_TANGENT |
-							 LLVertexBuffer::MAP_COLOR |
-							 LLVertexBuffer::MAP_WEIGHT4,
-		RIGGED_DEFERRED_SIMPLE_MASK = LLVertexBuffer::MAP_VERTEX | 
-							 LLVertexBuffer::MAP_NORMAL | 
-							 LLVertexBuffer::MAP_TEXCOORD0 |
-							 LLVertexBuffer::MAP_COLOR |
-							 LLVertexBuffer::MAP_WEIGHT4,
-	} eRiggedDataMask;
-
 typedef enum
 	{
 		SHADOW_PASS_AVATAR_OPAQUE,
         SHADOW_PASS_AVATAR_ALPHA_BLEND,
         SHADOW_PASS_AVATAR_ALPHA_MASK,
-        SHADOW_PASS_ATTACHMENT_ALPHA_BLEND,
-        SHADOW_PASS_ATTACHMENT_ALPHA_MASK,
-        SHADOW_PASS_ATTACHMENT_OPAQUE,
         NUM_SHADOW_PASSES
 	} eShadowPass;
 
@@ -215,101 +107,19 @@ typedef enum
 	void endImpostor();
 	void endSkinned();
 
-	void beginDeferredImpostor();
-	void beginDeferredRigid();
-	void beginDeferredSkinned();
-	
-	void endDeferredImpostor();
-	void endDeferredRigid();
-	void endDeferredSkinned();
-	
-	void beginPostDeferredAlpha();
-	void endPostDeferredAlpha();
-
-	void beginRiggedSimple();
-	void beginRiggedFullbright();
-	void beginRiggedFullbrightShiny();
-	void beginRiggedShinySimple();
-	void beginRiggedAlpha();
-	void beginRiggedFullbrightAlpha();
-	void beginRiggedGlow();
-	void beginDeferredRiggedAlpha();
-	void beginDeferredRiggedMaterial(S32 pass);
-	void beginDeferredRiggedMaterialAlpha(S32 pass);
-
-	void endRiggedSimple();
-	void endRiggedFullbright();
-	void endRiggedFullbrightShiny();
-	void endRiggedShinySimple();
-	void endRiggedAlpha();
-	void endRiggedFullbrightAlpha();
-	void endRiggedGlow();
-	void endDeferredRiggedAlpha();
-	void endDeferredRiggedMaterial(S32 pass);
-	void endDeferredRiggedMaterialAlpha(S32 pass);
-
-	void beginDeferredRiggedSimple();
-	void beginDeferredRiggedBump();
-	
-	void endDeferredRiggedSimple();
-	void endDeferredRiggedBump();
-		
-	void getRiggedGeometry(LLFace* face, LLPointer<LLVertexBuffer>& buffer, U32 data_mask, const LLMeshSkinInfo* skin, LLVolume* volume, const LLVolumeFace& vol_face);
-	void updateRiggedFaceVertexBuffer(LLVOAvatar* avatar,
-									  LLFace* facep, 
-									  const LLVOVolume* vobj,
-									  LLVolume* volume,
-									  LLVolumeFace& vol_face);
-	void updateRiggedVertexBuffers(LLVOAvatar* avatar);
+    void beginDeferredRigid();
+    void beginDeferredImpostor();
+    void beginDeferredSkinned();
 
-    void updateSkinInfoMatrixPalettes(LLVOAvatar* avatarp);
-
-	void renderRigged(LLVOAvatar* avatar, U32 type, bool glow = false);
-	void renderRiggedSimple(LLVOAvatar* avatar);
-	void renderRiggedAlpha(LLVOAvatar* avatar);
-	void renderRiggedFullbrightAlpha(LLVOAvatar* avatar);
-	void renderRiggedFullbright(LLVOAvatar* avatar);
-	void renderRiggedShinySimple(LLVOAvatar* avatar);
-	void renderRiggedFullbrightShiny(LLVOAvatar* avatar);
-	void renderRiggedGlow(LLVOAvatar* avatar);
-	void renderDeferredRiggedSimple(LLVOAvatar* avatar);
-	void renderDeferredRiggedBump(LLVOAvatar* avatar);
-	void renderDeferredRiggedMaterial(LLVOAvatar* avatar, S32 pass);
-	
-	
-
-	void addRiggedFace(LLFace* facep, U32 type);
-	void removeRiggedFace(LLFace* facep); 
-
-	std::vector<LLFace*> mRiggedFace[NUM_RIGGED_PASSES];
-
-    LL_ALIGN_PREFIX(16)
-    class MatrixPaletteCache
-    {
-    public:
-        U32 mFrame;
-        LLMeshSkinInfo::matrix_list_t mMatrixPalette;
-        LL_ALIGN_16(LLMatrix4a mBindShapeMatrix);
-        // Float array ready to be sent to GL
-        std::vector<F32> mGLMp;
-
-        MatrixPaletteCache() :
-            mFrame(gFrameCount-1)
-        {
-        }
-    } LL_ALIGN_POSTFIX(16);
-    
-    const MatrixPaletteCache& updateSkinInfoMatrixPalette(LLVOAvatar* avatarp, const LLUUID& meshId);
-
-    typedef std::unordered_map<LLUUID, MatrixPaletteCache> matrix_palette_cache_t;
-    matrix_palette_cache_t mMatrixPaletteCache;
+    void endDeferredRigid();
+    void endDeferredImpostor();
+    void endDeferredSkinned();
 
 	/*virtual*/ LLViewerTexture *getDebugTexture();
 	/*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display
 
 	void renderAvatars(LLVOAvatar *single_avatar, S32 pass = -1); // renders only one avatar if single_avatar is not null.
 
-
 	static BOOL sSkipOpaque;
 	static BOOL sSkipTransparent;
     static S32  sShadowPass;
diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp
index 8f3b0c99b41..af8b194f38e 100644
--- a/indra/newview/lldrawpoolbump.cpp
+++ b/indra/newview/lldrawpoolbump.cpp
@@ -47,6 +47,7 @@
 #include "pipeline.h"
 #include "llspatialpartition.h"
 #include "llviewershadermgr.h"
+#include "llmodel.h"
 
 //#include "llimagebmp.h"
 //#include "../tools/imdebug/imdebug.h"
@@ -203,22 +204,11 @@ S32 LLDrawPoolBump::numBumpPasses()
 	{
 		if (mShaderLevel > 1)
 		{
-			if (LLPipeline::sImpostorRender)
-			{
-				return 2;
-			}
-			else
-			{
-				return 3;
-			}
-		}
-		else if (LLPipeline::sImpostorRender)
-		{
-			return 1;
+			return 6;
 		}
 		else
 		{
-			return 2;
+			return 4;
 		}
 	}
     else
@@ -235,6 +225,8 @@ S32 LLDrawPoolBump::getNumPasses()
 void LLDrawPoolBump::beginRenderPass(S32 pass)
 {
 	LL_RECORD_BLOCK_TIME(FTM_RENDER_BUMP);
+    mRigged = ((pass % 2) == 1);
+    pass /= 2;
 	switch( pass )
 	{
 		case 0:
@@ -267,7 +259,7 @@ void LLDrawPoolBump::render(S32 pass)
 	{
 		return;
 	}
-	
+    pass /= 2;
 	switch( pass )
 	{
 		case 0:
@@ -295,6 +287,7 @@ void LLDrawPoolBump::render(S32 pass)
 void LLDrawPoolBump::endRenderPass(S32 pass)
 {
 	LL_RECORD_BLOCK_TIME(FTM_RENDER_BUMP);
+    pass /= 2;
 	switch( pass )
 	{
 		case 0:
@@ -326,12 +319,7 @@ void LLDrawPoolBump::endRenderPass(S32 pass)
 void LLDrawPoolBump::beginShiny(bool invisible)
 {
 	LL_RECORD_BLOCK_TIME(FTM_RENDER_SHINY);
-	if ((!invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_SHINY))|| 
-		(invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_INVISI_SHINY)))
-	{
-		return;
-	}
-
+	
 	mShiny = TRUE;
 	sVertexMask = VERTEX_MASK_SHINY;
 	// Second pass: environment map
@@ -340,31 +328,31 @@ void LLDrawPoolBump::beginShiny(bool invisible)
 		sVertexMask = VERTEX_MASK_SHINY | LLVertexBuffer::MAP_TEXCOORD0;
 	}
 	
-	if (getShaderLevel() > 0)
+	if (LLPipeline::sUnderWaterRender)
 	{
-		if (LLPipeline::sUnderWaterRender)
-		{
-			shader = &gObjectShinyWaterProgram;
-		}
-		else
-		{
-			shader = &gObjectShinyProgram;
-		}
-		shader->bind();
-        if (LLPipeline::sRenderingHUDs)
-        {
-            shader->uniform1i(LLShaderMgr::NO_ATMO, 1);
-        }
-        else
-        {
-            shader->uniform1i(LLShaderMgr::NO_ATMO, 0);
-        }
+		shader = &gObjectShinyWaterProgram;
 	}
 	else
 	{
-		shader = NULL;
+		shader = &gObjectShinyProgram;
 	}
 
+    if (mRigged)
+    {
+        llassert(shader->mRiggedVariant);
+        shader = shader->mRiggedVariant;
+    }
+
+	shader->bind();
+    if (LLPipeline::sRenderingHUDs)
+    {
+        shader->uniform1i(LLShaderMgr::NO_ATMO, 1);
+    }
+    else
+    {
+        shader->uniform1i(LLShaderMgr::NO_ATMO, 0);
+    }
+
 	bindCubeMap(shader, mShaderLevel, diffuse_channel, cube_channel, invisible);
 
 	if (mShaderLevel > 1)
@@ -391,7 +379,6 @@ void LLDrawPoolBump::bindCubeMap(LLGLSLShader* shader, S32 shader_level, S32& di
 			shader->uniform4fv(LLViewerShaderMgr::SHINY_ORIGIN, 1, vec4.mV);			
 			if (shader_level > 1)
 			{
-				cube_map->setMatrix(1);
 				// Make sure that texture coord generation happens for tex unit 1, as that's the one we use for 
 				// the cube map in the one pass shiny shaders
 				cube_channel = shader->enableTexture(LLViewerShaderMgr::ENVIRONMENT_MAP, LLTexUnit::TT_CUBE_MAP);
@@ -403,7 +390,6 @@ void LLDrawPoolBump::bindCubeMap(LLGLSLShader* shader, S32 shader_level, S32& di
 			{
 				cube_channel = shader->enableTexture(LLViewerShaderMgr::ENVIRONMENT_MAP, LLTexUnit::TT_CUBE_MAP);
 				diffuse_channel = -1;
-				cube_map->setMatrix(0);
 				cube_map->enable(cube_channel);
 			}			
 			gGL.getTexUnit(cube_channel)->bind(cube_map);
@@ -415,7 +401,6 @@ void LLDrawPoolBump::bindCubeMap(LLGLSLShader* shader, S32 shader_level, S32& di
 			diffuse_channel = -1;
 			gGL.getTexUnit(0)->disable();
 			cube_map->enable(0);
-			cube_map->setMatrix(0);
 			gGL.getTexUnit(0)->bind(cube_map);
 
 			gGL.getTexUnit(0)->setTextureColorBlend(LLTexUnit::TBO_REPLACE, LLTexUnit::TBS_TEX_COLOR);
@@ -427,27 +412,32 @@ void LLDrawPoolBump::bindCubeMap(LLGLSLShader* shader, S32 shader_level, S32& di
 void LLDrawPoolBump::renderShiny(bool invisible)
 {
 	LL_RECORD_BLOCK_TIME(FTM_RENDER_SHINY);
-	if ((!invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_SHINY))|| 
-		(invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_INVISI_SHINY)))
-	{
-		return;
-	}
-
+	
 	if( gSky.mVOSkyp->getCubeMap() )
 	{
 		LLGLEnable blend_enable(GL_BLEND);
 		if (!invisible && mShaderLevel > 1)
 		{
-			LLRenderPass::pushBatches(LLRenderPass::PASS_SHINY, sVertexMask | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
+            if (mRigged)
+            {
+                LLRenderPass::pushRiggedBatches(LLRenderPass::PASS_SHINY_RIGGED, sVertexMask | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
+            }
+            else
+            {
+                LLRenderPass::pushBatches(LLRenderPass::PASS_SHINY, sVertexMask | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
+            }
 		}
 		else if (!invisible)
 		{
-			renderGroups(LLRenderPass::PASS_SHINY, sVertexMask);
+            if (mRigged)
+            {
+                gPipeline.renderRiggedGroups(this, LLRenderPass::PASS_SHINY_RIGGED, sVertexMask, TRUE);
+            }
+            else
+            {
+                gPipeline.renderGroups(this, LLRenderPass::PASS_SHINY, sVertexMask, TRUE);
+            }
 		}
-		//else // invisible (deprecated)
-		//{
-			//renderGroups(LLRenderPass::PASS_INVISI_SHINY, sVertexMask);
-		//}
 	}
 }
 
@@ -472,27 +462,12 @@ void LLDrawPoolBump::unbindCubeMap(LLGLSLShader* shader, S32 shader_level, S32&
         // Moved below shader->disableTexture call to avoid false alarms from auto-re-enable of textures on stage 0
         // MAINT-755
 		cube_map->disable();
-		cube_map->restoreMatrix();
-	}
-
-	if (!LLGLSLShader::sNoFixedFunction)
-	{
-		gGL.getTexUnit(diffuse_channel)->disable();
-		gGL.getTexUnit(cube_channel)->disable();
-
-		gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-		gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT);
 	}
 }
 
 void LLDrawPoolBump::endShiny(bool invisible)
 {
 	LL_RECORD_BLOCK_TIME(FTM_RENDER_SHINY);
-	if ((!invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_SHINY))|| 
-		(invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_INVISI_SHINY)))
-	{
-		return;
-	}
 
 	unbindCubeMap(shader, mShaderLevel, diffuse_channel, cube_channel, invisible);
 	if (shader)
@@ -508,11 +483,7 @@ void LLDrawPoolBump::endShiny(bool invisible)
 void LLDrawPoolBump::beginFullbrightShiny()
 {
 	LL_RECORD_BLOCK_TIME(FTM_RENDER_SHINY);
-	if (!gPipeline.hasRenderBatches(LLRenderPass::PASS_FULLBRIGHT_SHINY))
-	{
-		return;
-	}
-
+	
 	sVertexMask = VERTEX_MASK_SHINY | LLVertexBuffer::MAP_TEXCOORD0;
 
 	// Second pass: environment map
@@ -533,6 +504,12 @@ void LLDrawPoolBump::beginFullbrightShiny()
 		}
 	}
 
+    if (mRigged)
+    {
+        llassert(shader->mRiggedVariant);
+        shader = shader->mRiggedVariant;
+    }
+
 	LLCubeMap* cube_map = gSky.mVOSkyp ? gSky.mVOSkyp->getCubeMap() : NULL;
 	if( cube_map )
 	{
@@ -553,9 +530,8 @@ void LLDrawPoolBump::beginFullbrightShiny()
 
 		LLVector3 vec = LLVector3(gShinyOrigin) * mat;
 		LLVector4 vec4(vec, gShinyOrigin.mV[3]);
-		shader->uniform4fv(LLViewerShaderMgr::SHINY_ORIGIN, 1, vec4.mV);			
+		shader->uniform4fv(LLViewerShaderMgr::SHINY_ORIGIN, 1, vec4.mV);
 
-		cube_map->setMatrix(1);
 		// Make sure that texture coord generation happens for tex unit 1, as that's the one we use for 
 		// the cube map in the one pass shiny shaders
 		gGL.getTexUnit(1)->disable();
@@ -579,10 +555,6 @@ void LLDrawPoolBump::beginFullbrightShiny()
 void LLDrawPoolBump::renderFullbrightShiny()
 {
 	LL_RECORD_BLOCK_TIME(FTM_RENDER_SHINY);
-	if (!gPipeline.hasRenderBatches(LLRenderPass::PASS_FULLBRIGHT_SHINY))
-	{
-		return;
-	}
 
 	if( gSky.mVOSkyp->getCubeMap() )
 	{
@@ -590,11 +562,25 @@ void LLDrawPoolBump::renderFullbrightShiny()
 
 		if (mShaderLevel > 1)
 		{
-			LLRenderPass::pushBatches(LLRenderPass::PASS_FULLBRIGHT_SHINY, sVertexMask | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
+            if (mRigged)
+            {
+                LLRenderPass::pushRiggedBatches(LLRenderPass::PASS_FULLBRIGHT_SHINY_RIGGED, sVertexMask | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
+            }
+            else
+            {
+                LLRenderPass::pushBatches(LLRenderPass::PASS_FULLBRIGHT_SHINY, sVertexMask | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
+            }
 		}
 		else
 		{
-			LLRenderPass::renderTexture(LLRenderPass::PASS_FULLBRIGHT_SHINY, sVertexMask);
+            if (mRigged)
+            {
+                LLRenderPass::pushRiggedBatches(LLRenderPass::PASS_FULLBRIGHT_SHINY_RIGGED, sVertexMask);
+            }
+            else
+            {
+                LLRenderPass::pushBatches(LLRenderPass::PASS_FULLBRIGHT_SHINY, sVertexMask);
+            }
 		}
 	}
 }
@@ -602,18 +588,13 @@ void LLDrawPoolBump::renderFullbrightShiny()
 void LLDrawPoolBump::endFullbrightShiny()
 {
 	LL_RECORD_BLOCK_TIME(FTM_RENDER_SHINY);
-	if (!gPipeline.hasRenderBatches(LLRenderPass::PASS_FULLBRIGHT_SHINY))
-	{
-		return;
-	}
 
 	LLCubeMap* cube_map = gSky.mVOSkyp ? gSky.mVOSkyp->getCubeMap() : NULL;
 	if( cube_map )
 	{
 		cube_map->disable();
-		cube_map->restoreMatrix();
 
-		/*if (diffuse_channel != 0)
+        /*if (diffuse_channel != 0)
 		{
 			shader->disableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
 		}
@@ -726,53 +707,22 @@ BOOL LLDrawPoolBump::bindBumpMap(U8 bump_code, LLViewerTexture* texture, F32 vsi
 }
 
 //static
-void LLDrawPoolBump::beginBump(U32 pass)
+void LLDrawPoolBump::beginBump()
 {	
-	if (!gPipeline.hasRenderBatches(pass))
-	{
-		return;
-	}
-
 	sVertexMask = VERTEX_MASK_BUMP;
 	LL_RECORD_BLOCK_TIME(FTM_RENDER_BUMP);
 	// Optional second pass: emboss bump map
 	stop_glerror();
 
-	if (LLGLSLShader::sNoFixedFunction)
-	{
-		gObjectBumpProgram.bind();
-	}
-	else
-	{
-		// TEXTURE UNIT 0
-		// Output.rgb = texture at texture coord 0
-		gGL.getTexUnit(0)->activate();
-
-		gGL.getTexUnit(0)->setTextureColorBlend(LLTexUnit::TBO_REPLACE, LLTexUnit::TBS_TEX_ALPHA);
-		gGL.getTexUnit(0)->setTextureAlphaBlend(LLTexUnit::TBO_REPLACE, LLTexUnit::TBS_TEX_ALPHA);
+    shader = &gObjectBumpProgram;
 
-		// TEXTURE UNIT 1
-		gGL.getTexUnit(1)->activate();
- 
-		gGL.getTexUnit(1)->enable(LLTexUnit::TT_TEXTURE);
-
-		gGL.getTexUnit(1)->setTextureColorBlend(LLTexUnit::TBO_ADD_SIGNED, LLTexUnit::TBS_PREV_COLOR, LLTexUnit::TBS_ONE_MINUS_TEX_ALPHA);
-		gGL.getTexUnit(1)->setTextureAlphaBlend(LLTexUnit::TBO_REPLACE, LLTexUnit::TBS_TEX_ALPHA);
-
-		// src	= tex0 + (1 - tex1) - 0.5
-		//		= (bump0/2 + 0.5) + (1 - (bump1/2 + 0.5)) - 0.5
-		//		= (1 + bump0 - bump1) / 2
+    if (mRigged)
+    {
+        llassert(shader->mRiggedVariant);
+        shader = shader->mRiggedVariant;
+    }
 
-
-		// Blend: src * dst + dst * src
-		//		= 2 * src * dst
-		//		= 2 * ((1 + bump0 - bump1) / 2) * dst   [0 - 2 * dst]
-		//		= (1 + bump0 - bump1) * dst.rgb
-		//		= dst.rgb + dst.rgb * (bump0 - bump1)
-
-		gGL.getTexUnit(0)->activate();
-		gGL.getTexUnit(1)->unbind(LLTexUnit::TT_TEXTURE);
-	}
+    shader->bind();
 
 	gGL.setSceneBlendType(LLRender::BT_MULT_X2);
 	stop_glerror();
@@ -781,11 +731,6 @@ void LLDrawPoolBump::beginBump(U32 pass)
 //static
 void LLDrawPoolBump::renderBump(U32 pass)
 {
-	if (!gPipeline.hasRenderBatches(pass))
-	{
-		return;
-	}
-
 	LL_RECORD_BLOCK_TIME(FTM_RENDER_BUMP);
 	LLGLDisable fog(GL_FOG);
 	LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_LEQUAL);
@@ -800,11 +745,6 @@ void LLDrawPoolBump::renderBump(U32 pass)
 //static
 void LLDrawPoolBump::endBump(U32 pass)
 {
-	if (!gPipeline.hasRenderBatches(pass))
-	{
-		return;
-	}
-
 	if (LLGLSLShader::sNoFixedFunction)
 	{
 		gObjectBumpProgram.unbind();
@@ -828,7 +768,7 @@ S32 LLDrawPoolBump::getNumDeferredPasses()
 { 
 	if (gSavedSettings.getBOOL("RenderObjectBump"))
 	{
-		return 1;
+		return 2;
 	}
 	else
 	{
@@ -838,66 +778,86 @@ S32 LLDrawPoolBump::getNumDeferredPasses()
 
 void LLDrawPoolBump::beginDeferredPass(S32 pass)
 {
-	if (!gPipeline.hasRenderBatches(LLRenderPass::PASS_BUMP))
+	if (!gPipeline.hasRenderBatches( pass == 0 ? LLRenderPass::PASS_BUMP : LLRenderPass::PASS_BUMP_RIGGED))
 	{
 		return;
 	}
 	LL_RECORD_BLOCK_TIME(FTM_RENDER_BUMP);
 	mShiny = TRUE;
-	gDeferredBumpProgram.bind();
-	diffuse_channel = gDeferredBumpProgram.enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
-	bump_channel = gDeferredBumpProgram.enableTexture(LLViewerShaderMgr::BUMP_MAP);
+	gDeferredBumpProgram.bind(pass == 1);
+	diffuse_channel = LLGLSLShader::sCurBoundShaderPtr->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
+	bump_channel = LLGLSLShader::sCurBoundShaderPtr->enableTexture(LLViewerShaderMgr::BUMP_MAP);
 	gGL.getTexUnit(diffuse_channel)->unbind(LLTexUnit::TT_TEXTURE);
 	gGL.getTexUnit(bump_channel)->unbind(LLTexUnit::TT_TEXTURE);
 }
 
 void LLDrawPoolBump::endDeferredPass(S32 pass)
 {
-	if (!gPipeline.hasRenderBatches(LLRenderPass::PASS_BUMP))
+	if (!gPipeline.hasRenderBatches(pass == 0 ? LLRenderPass::PASS_BUMP : LLRenderPass::PASS_BUMP_RIGGED))
 	{
 		return;
 	}
 	LL_RECORD_BLOCK_TIME(FTM_RENDER_BUMP);
 	mShiny = FALSE;
-	gDeferredBumpProgram.disableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
-	gDeferredBumpProgram.disableTexture(LLViewerShaderMgr::BUMP_MAP);
-	gDeferredBumpProgram.unbind();
+    LLGLSLShader::sCurBoundShaderPtr->disableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
+    LLGLSLShader::sCurBoundShaderPtr->disableTexture(LLViewerShaderMgr::BUMP_MAP);
+    LLGLSLShader::sCurBoundShaderPtr->unbind();
 	gGL.getTexUnit(0)->activate();
 }
 
 void LLDrawPoolBump::renderDeferred(S32 pass)
 {
-	if (!gPipeline.hasRenderBatches(LLRenderPass::PASS_BUMP))
+    if (!gPipeline.hasRenderBatches(pass == 0 ? LLRenderPass::PASS_BUMP : LLRenderPass::PASS_BUMP_RIGGED))
 	{
 		return;
 	}
 	LL_RECORD_BLOCK_TIME(FTM_RENDER_BUMP);
 
-	U32 type = LLRenderPass::PASS_BUMP;
-	LLCullResult::drawinfo_iterator begin = gPipeline.beginRenderMap(type);
-	LLCullResult::drawinfo_iterator end = gPipeline.endRenderMap(type);
+    bool rigged = pass == 1;
+    U32 type = rigged ? LLRenderPass::PASS_BUMP_RIGGED : LLRenderPass::PASS_BUMP;
+    LLCullResult::drawinfo_iterator begin = gPipeline.beginRenderMap(type);
+    LLCullResult::drawinfo_iterator end = gPipeline.endRenderMap(type);
 
-	U32 mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_TANGENT | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_COLOR;
-	
-	for (LLCullResult::drawinfo_iterator i = begin; i != end; ++i)	
-	{
-		LLDrawInfo& params = **i;
+    U32 mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_TANGENT | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_COLOR;
 
-		gDeferredBumpProgram.setMinimumAlpha(params.mAlphaMaskCutoff);
-		LLDrawPoolBump::bindBumpMap(params, bump_channel);
-		pushBatch(params, mask, TRUE);
-	}
+    LLVOAvatar* avatar = nullptr;
+    U64 skin = 0;
+
+    for (LLCullResult::drawinfo_iterator i = begin; i != end; ++i)
+    {
+        LLDrawInfo& params = **i;
+
+        LLGLSLShader::sCurBoundShaderPtr->setMinimumAlpha(params.mAlphaMaskCutoff);
+        LLDrawPoolBump::bindBumpMap(params, bump_channel);
+
+        if (rigged)
+        {
+            if (avatar != params.mAvatar || skin != params.mSkinInfo->mHash)
+            {
+                uploadMatrixPalette(params);
+                avatar = params.mAvatar;
+                skin = params.mSkinInfo->mHash;
+            }
+            pushBatch(params, mask | LLVertexBuffer::MAP_WEIGHT4, TRUE, FALSE);
+        }
+        else
+        {
+            pushBatch(params, mask, TRUE, FALSE);
+        }
+    }
 }
 
 void LLDrawPoolBump::beginPostDeferredPass(S32 pass)
 {
+    mRigged = ((pass % 2) == 1);
+    pass /= 2;
 	switch (pass)
 	{
 	case 0:
 		beginFullbrightShiny();
 		break;
 	case 1:
-		beginBump(LLRenderPass::PASS_POST_BUMP);
+		beginBump();
 		break;
 	}
 }
@@ -920,6 +880,7 @@ void LLDrawPoolBump::endPostDeferredPass(S32 pass)
 
 void LLDrawPoolBump::renderPostDeferred(S32 pass)
 {
+    pass /= 2;
 	switch (pass)
 	{
 	case 0:
@@ -1462,8 +1423,17 @@ void LLBumpImageList::onSourceLoaded( BOOL success, LLViewerTexture *src_vi, LLI
 
 void LLDrawPoolBump::renderBump(U32 type, U32 mask)
 {	
-	LLCullResult::drawinfo_iterator begin = gPipeline.beginRenderMap(type);
-	LLCullResult::drawinfo_iterator end = gPipeline.endRenderMap(type);
+    LLVOAvatar* avatar = nullptr;
+    U64 skin = 0;
+
+    if (mRigged)
+    { // nudge type enum and include skinweights for rigged pass
+        type += 1;
+        mask |= LLVertexBuffer::MAP_WEIGHT4;
+    }
+
+    LLCullResult::drawinfo_iterator begin = gPipeline.beginRenderMap(type);
+    LLCullResult::drawinfo_iterator end = gPipeline.endRenderMap(type);
 
 	for (LLCullResult::drawinfo_iterator i = begin; i != end; ++i)	
 	{
@@ -1471,6 +1441,21 @@ void LLDrawPoolBump::renderBump(U32 type, U32 mask)
 
 		if (LLDrawPoolBump::bindBumpMap(params))
 		{
+            if (mRigged)
+            {
+                if (avatar != params.mAvatar || skin != params.mSkinInfo->mHash)
+                {
+                    if (uploadMatrixPalette(params))
+                    {
+                        avatar = params.mAvatar;
+                        skin = params.mSkinInfo->mHash;
+                    }
+                    else
+                    {
+                        continue;
+                    }
+                }
+            }
 			pushBatch(params, mask, FALSE);
 		}
 	}
diff --git a/indra/newview/lldrawpoolbump.h b/indra/newview/lldrawpoolbump.h
index bab160c34da..d76e925eb0f 100644
--- a/indra/newview/lldrawpoolbump.h
+++ b/indra/newview/lldrawpoolbump.h
@@ -57,7 +57,7 @@ protected :
 	virtual void endRenderPass( S32 pass );
 	virtual S32	 getNumPasses();
 	/*virtual*/ void prerender();
-	/*virtual*/ void pushBatch(LLDrawInfo& params, U32 mask, BOOL texture, BOOL batch_textures = FALSE);
+	void pushBatch(LLDrawInfo& params, U32 mask, BOOL texture, BOOL batch_textures = FALSE) override;
 
 	void renderBump(U32 type, U32 mask);
 	void renderGroup(LLSpatialGroup* group, U32 type, U32 mask, BOOL texture);
@@ -72,7 +72,7 @@ protected :
 	void renderFullbrightShiny();
 	void endFullbrightShiny();
 
-	void beginBump(U32 pass = LLRenderPass::PASS_BUMP);
+	void beginBump();
 	void renderBump(U32 pass = LLRenderPass::PASS_BUMP);
 	void endBump(U32 pass = LLRenderPass::PASS_BUMP);
 
@@ -84,7 +84,7 @@ protected :
 	/*virtual*/ void endDeferredPass(S32 pass);
 	/*virtual*/ void renderDeferred(S32 pass);
 
-	virtual S32 getNumPostDeferredPasses() { return 2; }
+	virtual S32 getNumPostDeferredPasses() { return 4; }
 	/*virtual*/ void beginPostDeferredPass(S32 pass);
 	/*virtual*/ void endPostDeferredPass(S32 pass);
 	/*virtual*/ void renderPostDeferred(S32 pass);
@@ -94,6 +94,7 @@ protected :
 
 private:
 	static BOOL bindBumpMap(U8 bump_code, LLViewerTexture* tex, F32 vsize, S32 channel);
+    bool mRigged = false; // if true, doing a rigged pass
 
 };
 
diff --git a/indra/newview/lldrawpoolmaterials.cpp b/indra/newview/lldrawpoolmaterials.cpp
index d2a87573799..fd5850084b0 100644
--- a/indra/newview/lldrawpoolmaterials.cpp
+++ b/indra/newview/lldrawpoolmaterials.cpp
@@ -31,6 +31,7 @@
 #include "llviewershadermgr.h"
 #include "pipeline.h"
 #include "llglcommonfunc.h"
+#include "llvoavatar.h"
 
 S32 diffuse_channel = -1;
 
@@ -47,11 +48,18 @@ void LLDrawPoolMaterials::prerender()
 
 S32 LLDrawPoolMaterials::getNumDeferredPasses()
 {
-	return 12;
+    // 12 render passes times 2 (one for each rigged and non rigged)
+	return 12*2;
 }
 
 void LLDrawPoolMaterials::beginDeferredPass(S32 pass)
 {
+    bool rigged = false;
+    if (pass >= 12)
+    { 
+        rigged = true;
+        pass -= 12;
+    }
 	U32 shader_idx[] = 
 	{
 		0, //LLRenderPass::PASS_MATERIAL,
@@ -72,13 +80,22 @@ void LLDrawPoolMaterials::beginDeferredPass(S32 pass)
 		15, //LLRenderPass::PASS_NORMSPEC_GLOW,
 	};
 	
-	mShader = &(gDeferredMaterialProgram[shader_idx[pass]]);
-
-	if (LLPipeline::sUnderWaterRender)
-	{
-		mShader = &(gDeferredMaterialWaterProgram[shader_idx[pass]]);
-	}
-
+    U32 idx = shader_idx[pass];
+    
+    if (LLPipeline::sUnderWaterRender)
+    {
+        mShader = &(gDeferredMaterialWaterProgram[idx]);
+    }
+    else
+    {
+        mShader = &(gDeferredMaterialProgram[idx]);
+    }
+    
+    if (rigged)
+    {
+        llassert(mShader->mRiggedVariant != nullptr);
+        mShader = mShader->mRiggedVariant;
+    }
 	mShader->bind();
 
     if (LLPipeline::sRenderingHUDs)
@@ -127,9 +144,20 @@ void LLDrawPoolMaterials::renderDeferred(S32 pass)
 		LLRenderPass::PASS_NORMSPEC_EMISSIVE,
 	};
 
+    bool rigged = false;
+    if (pass >= 12)
+    {
+        rigged = true;
+        pass -= 12;
+    }
+
 	llassert(pass < sizeof(type_list)/sizeof(U32));
 
 	U32 type = type_list[pass];
+    if (rigged)
+    {
+        type += 1;
+    }
 
 	U32 mask = mShader->mAttributeMask;
 
@@ -160,7 +188,7 @@ void LLDrawPoolMaterials::renderDeferred(S32 pass)
 
         {
             LL_PROFILE_ZONE_SCOPED;
-            pushMaterialsBatch(params, mask);
+            pushMaterialsBatch(params, mask, rigged);
         }
 	}
 }
@@ -175,7 +203,7 @@ void LLDrawPoolMaterials::bindNormalMap(LLViewerTexture* tex)
 	mShader->bindTexture(LLShaderMgr::BUMP_MAP, tex);
 }
 
-void LLDrawPoolMaterials::pushMaterialsBatch(LLDrawInfo& params, U32 mask)
+void LLDrawPoolMaterials::pushMaterialsBatch(LLDrawInfo& params, U32 mask, bool rigged)
 {
     LL_PROFILE_ZONE_SCOPED;
 	applyModelMatrix(params);
@@ -214,6 +242,24 @@ void LLDrawPoolMaterials::pushMaterialsBatch(LLDrawInfo& params, U32 mask)
 		params.mGroup->rebuildMesh();
 	}
 
+    // upload matrix palette to shader
+    if (rigged)
+    {
+        const LLVOAvatar::MatrixPaletteCache& mpc = params.mAvatar->updateSkinInfoMatrixPalette(params.mSkinInfo);
+        U32 count = mpc.mMatrixPalette.size();
+
+        if (count == 0)
+        {
+            //skin info not loaded yet, don't render
+            return;
+        }
+
+        mShader->uniformMatrix3x4fv(LLViewerShaderMgr::AVATAR_MATRIX,
+            count,
+            FALSE,
+            (GLfloat*)&(mpc.mGLMp[0]));
+    }
+
 	LLGLEnableFunc stencil_test(GL_STENCIL_TEST, params.mSelected, &LLGLCommonFunc::selected_stencil_test);
 
 	params.mVertexBuffer->setBufferFast(mask);
diff --git a/indra/newview/lldrawpoolmaterials.h b/indra/newview/lldrawpoolmaterials.h
index 6e39821b077..8a3ad923dfc 100644
--- a/indra/newview/lldrawpoolmaterials.h
+++ b/indra/newview/lldrawpoolmaterials.h
@@ -55,21 +55,21 @@ class LLDrawPoolMaterials : public LLRenderPass
 		LLVertexBuffer::MAP_TANGENT
 	};
 	
-	/*virtual*/ U32 getVertexDataMask() { return VERTEX_DATA_MASK; }
+	U32 getVertexDataMask() override { return VERTEX_DATA_MASK; }
 	
-	/*virtual*/ void render(S32 pass = 0) { }
-	/*virtual*/ S32	 getNumPasses() {return 0;}
-	/*virtual*/ void prerender();
+	void render(S32 pass = 0) override { }
+	S32	 getNumPasses() override {return 0;}
+	void prerender() override;
 	
-	/*virtual*/ S32 getNumDeferredPasses();
-	/*virtual*/ void beginDeferredPass(S32 pass);
-	/*virtual*/ void endDeferredPass(S32 pass);
-	/*virtual*/ void renderDeferred(S32 pass);
+	S32 getNumDeferredPasses() override;
+	void beginDeferredPass(S32 pass) override;
+	void endDeferredPass(S32 pass) override;
+	void renderDeferred(S32 pass) override;
 	
 	void bindSpecularMap(LLViewerTexture* tex);
 	void bindNormalMap(LLViewerTexture* tex);
 	
-	/*virtual*/ void pushMaterialsBatch(LLDrawInfo& params, U32 mask);
+	void pushMaterialsBatch(LLDrawInfo& params, U32 mask, bool rigged);
 };
 
 #endif //LL_LLDRAWPOOLMATERIALS_H
diff --git a/indra/newview/lldrawpoolsimple.cpp b/indra/newview/lldrawpoolsimple.cpp
index 320160d10dd..ca4e20ae9b2 100644
--- a/indra/newview/lldrawpoolsimple.cpp
+++ b/indra/newview/lldrawpoolsimple.cpp
@@ -45,15 +45,24 @@ static LLTrace::BlockTimerStatHandle FTM_RENDER_GRASS_DEFERRED("Deferred Grass")
 
 void LLDrawPoolGlow::beginPostDeferredPass(S32 pass)
 {
-	gDeferredEmissiveProgram.bind();
-	gDeferredEmissiveProgram.uniform1f(LLShaderMgr::TEXTURE_GAMMA, 2.2f);
+    if (pass == 0)
+    {
+        gDeferredEmissiveProgram.bind();
+    }
+    else
+    {
+        llassert(gDeferredEmissiveProgram.mRiggedVariant);
+        gDeferredEmissiveProgram.mRiggedVariant->bind();
+    }
+
+	LLGLSLShader::sCurBoundShaderPtr->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 2.2f);
     if (LLPipeline::sRenderingHUDs)
 	{
-		gDeferredEmissiveProgram.uniform1i(LLShaderMgr::NO_ATMO, 1);
+        LLGLSLShader::sCurBoundShaderPtr->uniform1i(LLShaderMgr::NO_ATMO, 1);
 	}
 	else
 	{
-		gDeferredEmissiveProgram.uniform1i(LLShaderMgr::NO_ATMO, 0);
+        LLGLSLShader::sCurBoundShaderPtr->uniform1i(LLShaderMgr::NO_ATMO, 0);
 	}
 }
 
@@ -71,7 +80,14 @@ void LLDrawPoolGlow::renderPostDeferred(S32 pass)
 	LLGLDepthTest depth(GL_TRUE, GL_FALSE);
 	gGL.setColorMask(false, true);
 
-	pushBatches(LLRenderPass::PASS_GLOW, getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
+    if (pass == 0)
+    {
+        pushBatches(LLRenderPass::PASS_GLOW, getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
+    }
+    else
+    {
+        pushRiggedBatches(LLRenderPass::PASS_GLOW_RIGGED, getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
+    }
 	
 	gGL.setColorMask(true, false);
 	gGL.setSceneBlendType(LLRender::BT_ALPHA);	
@@ -79,7 +95,8 @@ void LLDrawPoolGlow::renderPostDeferred(S32 pass)
 
 void LLDrawPoolGlow::endPostDeferredPass(S32 pass)
 {
-	gDeferredEmissiveProgram.unbind();
+    LLGLSLShader::sCurBoundShaderPtr->unbind();
+
 	LLRenderPass::endRenderPass(pass);
 }
 
@@ -87,7 +104,7 @@ S32 LLDrawPoolGlow::getNumPasses()
 {
 	if (LLViewerShaderMgr::instance()->getShaderLevel(LLViewerShaderMgr::SHADER_OBJECT) > 0)
 	{
-		return 1;
+		return 2;
 	}
 	else
 	{
@@ -112,6 +129,11 @@ void LLDrawPoolGlow::render(S32 pass)
 	llassert(shader_level > 0);
 	
 	LLGLSLShader* shader = LLPipeline::sUnderWaterRender ? &gObjectEmissiveWaterProgram : &gObjectEmissiveProgram;
+    if (pass == 1)
+    {
+        llassert(shader->mRiggedVariant);
+        shader = shader->mRiggedVariant;
+    }
 	shader->bind();
 	if (LLPipeline::sRenderDeferred)
 	{
@@ -120,7 +142,7 @@ void LLDrawPoolGlow::render(S32 pass)
 	else
 	{
 		shader->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 1.f);
-	}	
+	}
 
     if (LLPipeline::sRenderingHUDs)
 	{
@@ -134,7 +156,14 @@ void LLDrawPoolGlow::render(S32 pass)
 	LLGLDepthTest depth(GL_TRUE, GL_FALSE);
 	gGL.setColorMask(false, true);
 
-	pushBatches(LLRenderPass::PASS_GLOW, getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
+    if (pass == 0)
+    {
+        pushBatches(LLRenderPass::PASS_GLOW, getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
+    }
+    else
+    {
+        pushRiggedBatches(LLRenderPass::PASS_GLOW_RIGGED, getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
+    }
 	
 	gGL.setColorMask(true, false);
 	gGL.setSceneBlendType(LLRender::BT_ALPHA);
@@ -155,39 +184,43 @@ void LLDrawPoolSimple::prerender()
 	mShaderLevel = LLViewerShaderMgr::instance()->getShaderLevel(LLViewerShaderMgr::SHADER_OBJECT);
 }
 
+S32 LLDrawPoolSimple::getNumPasses()
+{
+    return 2;
+}
+
 void LLDrawPoolSimple::beginRenderPass(S32 pass)
 {
 	LL_RECORD_BLOCK_TIME(FTM_RENDER_SIMPLE);
 
-	if (LLPipeline::sImpostorRender)
-	{
-		simple_shader = &gObjectSimpleImpostorProgram;
-	}
-	else if (LLPipeline::sUnderWaterRender)
-	{
-		simple_shader = &gObjectSimpleWaterProgram;
-	}
-	else
-	{
-		simple_shader = &gObjectSimpleProgram;
-	}
+    if (LLPipeline::sImpostorRender)
+    {
+        simple_shader = &gObjectSimpleImpostorProgram;
+    }
+    else if (LLPipeline::sUnderWaterRender)
+    {
+        simple_shader = &gObjectSimpleWaterProgram;
+    }
+    else
+    {
+        simple_shader = &gObjectSimpleProgram;
+    }
+ 
+    if (pass == 1)
+    {
+        llassert(simple_shader->mRiggedVariant);
+        simple_shader = simple_shader->mRiggedVariant;
+    }
 
-	if (mShaderLevel > 0)
-	{
-		simple_shader->bind();
+	simple_shader->bind();
 
-        if (LLPipeline::sRenderingHUDs)
-	    {
-		    simple_shader->uniform1i(LLShaderMgr::NO_ATMO, 1);
-	    }
-	    else
-	    {
-		    simple_shader->uniform1i(LLShaderMgr::NO_ATMO, 0);
-	    }
+    if (LLPipeline::sRenderingHUDs)
+	{
+		simple_shader->uniform1i(LLShaderMgr::NO_ATMO, 1);
 	}
-	else 
+	else
 	{
-		LLGLSLShader::bindNoShader();
+		simple_shader->uniform1i(LLShaderMgr::NO_ATMO, 0);
 	}
 }
 
@@ -197,10 +230,7 @@ void LLDrawPoolSimple::endRenderPass(S32 pass)
 	stop_glerror();
 	LLRenderPass::endRenderPass(pass);
 	stop_glerror();
-	if (mShaderLevel > 0)
-	{
-		simple_shader->unbind();
-	}
+	simple_shader->unbind();
 }
 
 void LLDrawPoolSimple::render(S32 pass)
@@ -211,35 +241,38 @@ void LLDrawPoolSimple::render(S32 pass)
 		LL_RECORD_BLOCK_TIME(FTM_RENDER_SIMPLE);
 		gPipeline.enableLightsDynamic();
 
-		if (mShaderLevel > 0)
-		{
-			U32 mask = getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX;
-
-			pushBatches(LLRenderPass::PASS_SIMPLE, mask, TRUE, TRUE);
-
-			if (LLPipeline::sRenderDeferred)
-			{ //if deferred rendering is enabled, bump faces aren't registered as simple
-				//render bump faces here as simple so bump faces will appear under water
-				pushBatches(LLRenderPass::PASS_BUMP, mask, TRUE, TRUE);			
-				pushBatches(LLRenderPass::PASS_MATERIAL, mask, TRUE, TRUE);
-				pushBatches(LLRenderPass::PASS_SPECMAP, mask, TRUE, TRUE);
-				pushBatches(LLRenderPass::PASS_NORMMAP, mask, TRUE, TRUE);
-				pushBatches(LLRenderPass::PASS_NORMSPEC, mask, TRUE, TRUE);		
-			}
-		}
-		else
-		{
-			LLGLDisable alpha_test(GL_ALPHA_TEST);
-			renderTexture(LLRenderPass::PASS_SIMPLE, getVertexDataMask());
-		}
-		
-	}
-}
-
-
+		U32 mask = getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX;
 
+        if (pass == 0)
+        {
+            pushBatches(LLRenderPass::PASS_SIMPLE, mask, TRUE, TRUE);
 
+            if (LLPipeline::sRenderDeferred)
+            { //if deferred rendering is enabled, bump faces aren't registered as simple
+                //render bump faces here as simple so bump faces will appear under water
+                pushBatches(LLRenderPass::PASS_BUMP, mask, TRUE, TRUE);
+                pushBatches(LLRenderPass::PASS_MATERIAL, mask, TRUE, TRUE);
+                pushBatches(LLRenderPass::PASS_SPECMAP, mask, TRUE, TRUE);
+                pushBatches(LLRenderPass::PASS_NORMMAP, mask, TRUE, TRUE);
+                pushBatches(LLRenderPass::PASS_NORMSPEC, mask, TRUE, TRUE);
+            }
+        }
+        else
+        {
+            pushRiggedBatches(LLRenderPass::PASS_SIMPLE_RIGGED, mask, TRUE, TRUE);
 
+            if (LLPipeline::sRenderDeferred)
+            { //if deferred rendering is enabled, bump faces aren't registered as simple
+                //render bump faces here as simple so bump faces will appear under water
+                pushRiggedBatches(LLRenderPass::PASS_BUMP_RIGGED, mask, TRUE, TRUE);
+                pushRiggedBatches(LLRenderPass::PASS_MATERIAL_RIGGED, mask, TRUE, TRUE);
+                pushRiggedBatches(LLRenderPass::PASS_SPECMAP_RIGGED, mask, TRUE, TRUE);
+                pushRiggedBatches(LLRenderPass::PASS_NORMMAP_RIGGED, mask, TRUE, TRUE);
+                pushRiggedBatches(LLRenderPass::PASS_NORMSPEC_RIGGED, mask, TRUE, TRUE);
+            }
+        }
+	}
+}
 
 
 
@@ -261,32 +294,31 @@ void LLDrawPoolAlphaMask::beginRenderPass(S32 pass)
 {
 	LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_MASK);
 
-	if (LLPipeline::sUnderWaterRender)
-	{
-		simple_shader = &gObjectSimpleWaterAlphaMaskProgram;
-	}
-	else
-	{
-		simple_shader = &gObjectSimpleAlphaMaskProgram;
-	}
+    if (LLPipeline::sUnderWaterRender)
+    {
+        simple_shader = &gObjectSimpleWaterAlphaMaskProgram;
+    }
+    else
+    {
+        simple_shader = &gObjectSimpleAlphaMaskProgram;
+    }
 
-	if (mShaderLevel > 0)
-	{
-		simple_shader->bind();
+    if (pass == 1)
+    {
+        llassert(simple_shader->mRiggedVariant);
+        simple_shader = simple_shader->mRiggedVariant;
+    }
 
-        if (LLPipeline::sRenderingHUDs)
-	    {
-		    simple_shader->uniform1i(LLShaderMgr::NO_ATMO, 1);
-	    }
-	    else
-	    {
-		    simple_shader->uniform1i(LLShaderMgr::NO_ATMO, 0);
-	    }
-	}
-	else 
-	{
-		LLGLSLShader::bindNoShader();
-	}
+    simple_shader->bind();
+
+    if (LLPipeline::sRenderingHUDs)
+    {
+	    simple_shader->uniform1i(LLShaderMgr::NO_ATMO, 1);
+    }
+    else
+    {
+	    simple_shader->uniform1i(LLShaderMgr::NO_ATMO, 0);
+    }
 }
 
 void LLDrawPoolAlphaMask::endRenderPass(S32 pass)
@@ -306,20 +338,22 @@ void LLDrawPoolAlphaMask::render(S32 pass)
 	LLGLDisable blend(GL_BLEND);
     LL_PROFILE_ZONE_SCOPED;
 	
-	if (mShaderLevel > 0)
-	{
-		simple_shader->bind();
-		simple_shader->setMinimumAlpha(0.33f);
+	
 
-        if (LLPipeline::sRenderingHUDs)
-	    {
-		    simple_shader->uniform1i(LLShaderMgr::NO_ATMO, 1);
-	    }
-	    else
-	    {
-		    simple_shader->uniform1i(LLShaderMgr::NO_ATMO, 0);
-	    }
+	simple_shader->bind();
+	simple_shader->setMinimumAlpha(0.33f);
+
+    if (LLPipeline::sRenderingHUDs)
+	{
+		simple_shader->uniform1i(LLShaderMgr::NO_ATMO, 1);
+	}
+	else
+	{
+		simple_shader->uniform1i(LLShaderMgr::NO_ATMO, 0);
+	}
 
+    if (pass == 0)
+    {
 		pushMaskBatches(LLRenderPass::PASS_ALPHA_MASK, getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
 		pushMaskBatches(LLRenderPass::PASS_MATERIAL_ALPHA_MASK, getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
 		pushMaskBatches(LLRenderPass::PASS_SPECMAP_MASK, getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
@@ -328,9 +362,11 @@ void LLDrawPoolAlphaMask::render(S32 pass)
 	}
 	else
 	{
-		LLGLEnable test(GL_ALPHA_TEST);
-		pushMaskBatches(LLRenderPass::PASS_ALPHA_MASK, getVertexDataMask(), TRUE, FALSE);
-		gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); //OK
+        pushRiggedMaskBatches(LLRenderPass::PASS_ALPHA_MASK_RIGGED, getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
+        pushRiggedMaskBatches(LLRenderPass::PASS_MATERIAL_ALPHA_MASK_RIGGED, getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
+        pushRiggedMaskBatches(LLRenderPass::PASS_SPECMAP_MASK_RIGGED, getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
+        pushRiggedMaskBatches(LLRenderPass::PASS_NORMMAP_MASK_RIGGED, getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
+        pushRiggedMaskBatches(LLRenderPass::PASS_NORMSPEC_MASK_RIGGED, getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
 	}
 }
 
@@ -348,31 +384,32 @@ void LLDrawPoolFullbrightAlphaMask::beginRenderPass(S32 pass)
 {
 	LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_MASK);
 
+    bool rigged = (pass == 1);
 	if (LLPipeline::sUnderWaterRender)
 	{
-		simple_shader = &gObjectFullbrightWaterAlphaMaskProgram;
+        gObjectFullbrightWaterAlphaMaskProgram.bind(rigged);
 	}
 	else
 	{
-		simple_shader = &gObjectFullbrightAlphaMaskProgram;
+		gObjectFullbrightAlphaMaskProgram.bind(rigged);
 	}
 
-	if (mShaderLevel > 0)
+    if (LLPipeline::sRenderingHUDs)
 	{
-		simple_shader->bind();
-
-        if (LLPipeline::sRenderingHUDs)
-	    {
-		    simple_shader->uniform1i(LLShaderMgr::NO_ATMO, 1);
-	    }
-	    else
-	    {
-		    simple_shader->uniform1i(LLShaderMgr::NO_ATMO, 0);
-	    }
+		LLGLSLShader::sCurBoundShaderPtr->uniform1i(LLShaderMgr::NO_ATMO, 1);
+        LLGLSLShader::sCurBoundShaderPtr->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 1.f);
 	}
-	else 
+	else
 	{
-		LLGLSLShader::bindNoShader();
+        LLGLSLShader::sCurBoundShaderPtr->uniform1i(LLShaderMgr::NO_ATMO, 0);
+        if (LLPipeline::sRenderDeferred)
+        {
+            LLGLSLShader::sCurBoundShaderPtr->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 2.2f);
+        }
+        else
+        {
+            LLGLSLShader::sCurBoundShaderPtr->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 1.0f);
+        }
 	}
 }
 
@@ -382,70 +419,61 @@ void LLDrawPoolFullbrightAlphaMask::endRenderPass(S32 pass)
 	stop_glerror();
 	LLRenderPass::endRenderPass(pass);
 	stop_glerror();
-	if (mShaderLevel > 0)
-	{
-		simple_shader->unbind();
-	}
+    LLGLSLShader::sCurBoundShaderPtr->unbind();
 }
 
 void LLDrawPoolFullbrightAlphaMask::render(S32 pass)
 {
 	LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_MASK);
 
-	if (mShaderLevel > 0)
-	{
-		if (simple_shader)
-		{
-			simple_shader->bind();
-			simple_shader->setMinimumAlpha(0.33f);
-
-            if (LLPipeline::sRenderingHUDs)
-	        {
-		        simple_shader->uniform1i(LLShaderMgr::NO_ATMO, 1);
-                simple_shader->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 1.0f);
-	        }
-	        else
-	        {
-		        simple_shader->uniform1i(LLShaderMgr::NO_ATMO, 0);
-                if (LLPipeline::sRenderDeferred)
-			    {
-                    simple_shader->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 2.2f);				    
-			    }
-                else
-                {
-				    simple_shader->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 1.0f);
-			    }
-	        }
-		}
-		pushMaskBatches(LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK, getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
-		//LLGLSLShader::bindNoShader();
-	}
-	else
-	{
-		LLGLEnable test(GL_ALPHA_TEST);
-		gPipeline.enableLightsFullbright();
-		pushMaskBatches(LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK, getVertexDataMask(), TRUE, FALSE);
-		gPipeline.enableLightsDynamic();
-		gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); //OK
-	}
+    if (pass == 0)
+    {
+        pushMaskBatches(LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK, getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
+    }
+    else
+    {
+        pushRiggedMaskBatches(LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK_RIGGED, getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
+    }
+	
 }
 
 //===============================
 //DEFERRED IMPLEMENTATION
 //===============================
 
+S32 LLDrawPoolSimple::getNumDeferredPasses()
+{
+    if (LLPipeline::sRenderingHUDs)
+    {
+        return 1;
+    }
+    else
+    {
+        return 2;
+    }
+}
 void LLDrawPoolSimple::beginDeferredPass(S32 pass)
 {
 	LL_RECORD_BLOCK_TIME(FTM_RENDER_SIMPLE_DEFERRED);
-	gDeferredDiffuseProgram.bind();
+
+    mShader = &gDeferredDiffuseProgram;
+    
+    if (pass == 1)
+    {
+        llassert(mShader->mRiggedVariant != nullptr);
+        mShader = mShader->mRiggedVariant;
+    }
+    
+
+    mShader->bind();
 
     if (LLPipeline::sRenderingHUDs)
 	{
-		gDeferredDiffuseProgram.uniform1i(LLShaderMgr::NO_ATMO, 1);
+		mShader->uniform1i(LLShaderMgr::NO_ATMO, 1);
 	}
 	else
 	{
-		gDeferredDiffuseProgram.uniform1i(LLShaderMgr::NO_ATMO, 0);
+		mShader->uniform1i(LLShaderMgr::NO_ATMO, 0);
 	}
 }
 
@@ -454,7 +482,7 @@ void LLDrawPoolSimple::endDeferredPass(S32 pass)
 	LL_RECORD_BLOCK_TIME(FTM_RENDER_SIMPLE_DEFERRED);
 	LLRenderPass::endRenderPass(pass);
 
-	gDeferredDiffuseProgram.unbind();
+	mShader->unbind();
 }
 
 void LLDrawPoolSimple::renderDeferred(S32 pass)
@@ -463,41 +491,61 @@ void LLDrawPoolSimple::renderDeferred(S32 pass)
 	LLGLDisable blend(GL_BLEND);
 	LLGLDisable alpha_test(GL_ALPHA_TEST);
 
+    if (pass == 0)
 	{ //render simple
 		LL_RECORD_BLOCK_TIME(FTM_RENDER_SIMPLE_DEFERRED);
 		pushBatches(LLRenderPass::PASS_SIMPLE, getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
 	}
+    else
+    {
+        //render simple rigged
+        pushRiggedBatches(LLRenderPass::PASS_SIMPLE_RIGGED, getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
+    }
 }
 
 static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_MASK_DEFERRED("Deferred Alpha Mask");
 
 void LLDrawPoolAlphaMask::beginDeferredPass(S32 pass)
 {
-	
+    if (pass == 0)
+    {
+        gDeferredDiffuseAlphaMaskProgram.bind();
+    }
+    else
+    {
+        llassert(gDeferredDiffuseAlphaMaskProgram.mRiggedVariant);
+        gDeferredDiffuseAlphaMaskProgram.mRiggedVariant->bind();
+    }
+
 }
 
 void LLDrawPoolAlphaMask::endDeferredPass(S32 pass)
 {
-	
+    LLGLSLShader::sCurBoundShaderPtr->unbind();
 }
 
 void LLDrawPoolAlphaMask::renderDeferred(S32 pass)
 {
 	LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_MASK_DEFERRED);
-	gDeferredDiffuseAlphaMaskProgram.bind();
-	gDeferredDiffuseAlphaMaskProgram.setMinimumAlpha(0.33f);
+	LLGLSLShader::sCurBoundShaderPtr->setMinimumAlpha(0.33f);
 
     if (LLPipeline::sRenderingHUDs)
 	{
-		gDeferredDiffuseAlphaMaskProgram.uniform1i(LLShaderMgr::NO_ATMO, 1);
+        LLGLSLShader::sCurBoundShaderPtr->uniform1i(LLShaderMgr::NO_ATMO, 1);
 	}
 	else
 	{
-		gDeferredDiffuseAlphaMaskProgram.uniform1i(LLShaderMgr::NO_ATMO, 0);
+        LLGLSLShader::sCurBoundShaderPtr->uniform1i(LLShaderMgr::NO_ATMO, 0);
 	}
 
-	pushMaskBatches(LLRenderPass::PASS_ALPHA_MASK, getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
-	gDeferredDiffuseAlphaMaskProgram.unbind();			
+    if (pass == 0)
+    {
+        pushMaskBatches(LLRenderPass::PASS_ALPHA_MASK, getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
+    }
+    else
+    {
+        pushRiggedMaskBatches(LLRenderPass::PASS_ALPHA_MASK_RIGGED, getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
+    }
 }
 
 
@@ -572,7 +620,7 @@ void LLDrawPoolGrass::render(S32 pass)
 		LLGLEnable test(GL_ALPHA_TEST);
 		gGL.setSceneBlendType(LLRender::BT_ALPHA);
 		//render grass
-		LLRenderPass::renderTexture(LLRenderPass::PASS_GRASS, getVertexDataMask());
+		LLRenderPass::pushBatches(LLRenderPass::PASS_GRASS, getVertexDataMask());
 	}
 }
 
@@ -603,7 +651,7 @@ void LLDrawPoolGrass::renderDeferred(S32 pass)
 	    }
 
 		//render grass
-		LLRenderPass::renderTexture(LLRenderPass::PASS_GRASS, getVertexDataMask());
+		LLRenderPass::pushBatches(LLRenderPass::PASS_GRASS, getVertexDataMask());
 	}			
 }
 
@@ -621,24 +669,24 @@ void LLDrawPoolFullbright::prerender()
 
 void LLDrawPoolFullbright::beginPostDeferredPass(S32 pass)
 {
+    bool rigged = (pass == 1);
 	if (LLPipeline::sUnderWaterRender)
 	{
-		gDeferredFullbrightWaterProgram.bind();
+		gDeferredFullbrightWaterProgram.bind(rigged);
 	}
 	else
 	{
-		gDeferredFullbrightProgram.bind();
+		gDeferredFullbrightProgram.bind(rigged);
 
         if (LLPipeline::sRenderingHUDs)
 	    {
-		    gDeferredFullbrightProgram.uniform1i(LLShaderMgr::NO_ATMO, 1);
+            LLGLSLShader::sCurBoundShaderPtr->uniform1i(LLShaderMgr::NO_ATMO, 1);
 	    }
 	    else
 	    {
-		    gDeferredFullbrightProgram.uniform1i(LLShaderMgr::NO_ATMO, 0);
+            LLGLSLShader::sCurBoundShaderPtr->uniform1i(LLShaderMgr::NO_ATMO, 0);
 	    }
 	}
-	
 }
 
 void LLDrawPoolFullbright::renderPostDeferred(S32 pass)
@@ -647,19 +695,19 @@ void LLDrawPoolFullbright::renderPostDeferred(S32 pass)
 	
 	gGL.setSceneBlendType(LLRender::BT_ALPHA);
 	U32 fullbright_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_COLOR | LLVertexBuffer::MAP_TEXTURE_INDEX;
-	pushBatches(LLRenderPass::PASS_FULLBRIGHT, fullbright_mask, TRUE, TRUE);
+    if (pass == 0)
+    {
+        pushBatches(LLRenderPass::PASS_FULLBRIGHT, fullbright_mask, TRUE, TRUE);
+    }
+    else
+    {
+        pushRiggedBatches(LLRenderPass::PASS_FULLBRIGHT_RIGGED, fullbright_mask, TRUE, TRUE);
+    }
 }
 
 void LLDrawPoolFullbright::endPostDeferredPass(S32 pass)
 {
-	if (LLPipeline::sUnderWaterRender)
-	{
-		gDeferredFullbrightWaterProgram.unbind();
-	}
-	else
-	{
-		gDeferredFullbrightProgram.unbind();
-	}
+    LLGLSLShader::sCurBoundShaderPtr->unbind();
 	LLRenderPass::endRenderPass(pass);
 }
 
@@ -675,6 +723,12 @@ void LLDrawPoolFullbright::beginRenderPass(S32 pass)
 	{
 		fullbright_shader = &gObjectFullbrightProgram;
 	}
+
+    if (pass == 1)
+    {
+        llassert(fullbright_shader->mRiggedVariant);
+        fullbright_shader = fullbright_shader->mRiggedVariant;
+    }
 }
 
 void LLDrawPoolFullbright::endRenderPass(S32 pass)
@@ -715,21 +769,23 @@ void LLDrawPoolFullbright::render(S32 pass)
 	    }
 
 		U32 fullbright_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_COLOR | LLVertexBuffer::MAP_TEXTURE_INDEX;
-		pushBatches(LLRenderPass::PASS_FULLBRIGHT, fullbright_mask, TRUE, TRUE);
-		pushBatches(LLRenderPass::PASS_MATERIAL_ALPHA_EMISSIVE, fullbright_mask, TRUE, TRUE);
-		pushBatches(LLRenderPass::PASS_SPECMAP_EMISSIVE, fullbright_mask, TRUE, TRUE);
-		pushBatches(LLRenderPass::PASS_NORMMAP_EMISSIVE, fullbright_mask, TRUE, TRUE);
-		pushBatches(LLRenderPass::PASS_NORMSPEC_EMISSIVE, fullbright_mask, TRUE, TRUE);
-	}
-	else
-	{
-		gPipeline.enableLightsFullbright();
-		U32 fullbright_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_COLOR;
-		renderTexture(LLRenderPass::PASS_FULLBRIGHT, fullbright_mask);
-		pushBatches(LLRenderPass::PASS_MATERIAL_ALPHA_EMISSIVE, fullbright_mask);
-		pushBatches(LLRenderPass::PASS_SPECMAP_EMISSIVE, fullbright_mask);
-		pushBatches(LLRenderPass::PASS_NORMMAP_EMISSIVE, fullbright_mask);
-		pushBatches(LLRenderPass::PASS_NORMSPEC_EMISSIVE, fullbright_mask);
+
+        if (pass == 0)
+        {
+            pushBatches(LLRenderPass::PASS_FULLBRIGHT, fullbright_mask, TRUE, TRUE);
+            pushBatches(LLRenderPass::PASS_MATERIAL_ALPHA_EMISSIVE, fullbright_mask, TRUE, TRUE);
+            pushBatches(LLRenderPass::PASS_SPECMAP_EMISSIVE, fullbright_mask, TRUE, TRUE);
+            pushBatches(LLRenderPass::PASS_NORMMAP_EMISSIVE, fullbright_mask, TRUE, TRUE);
+            pushBatches(LLRenderPass::PASS_NORMSPEC_EMISSIVE, fullbright_mask, TRUE, TRUE);
+        }
+        else
+        {
+            pushRiggedBatches(LLRenderPass::PASS_FULLBRIGHT_RIGGED, fullbright_mask, TRUE, TRUE);
+            pushRiggedBatches(LLRenderPass::PASS_MATERIAL_ALPHA_EMISSIVE_RIGGED, fullbright_mask, TRUE, TRUE);
+            pushRiggedBatches(LLRenderPass::PASS_SPECMAP_EMISSIVE_RIGGED, fullbright_mask, TRUE, TRUE);
+            pushRiggedBatches(LLRenderPass::PASS_NORMMAP_EMISSIVE_RIGGED, fullbright_mask, TRUE, TRUE);
+            pushRiggedBatches(LLRenderPass::PASS_NORMSPEC_EMISSIVE_RIGGED, fullbright_mask, TRUE, TRUE);
+        }
 	}
 
 	stop_glerror();
@@ -737,39 +793,39 @@ void LLDrawPoolFullbright::render(S32 pass)
 
 S32 LLDrawPoolFullbright::getNumPasses()
 { 
-	return 1;
+	return 2;
 }
 
 
 void LLDrawPoolFullbrightAlphaMask::beginPostDeferredPass(S32 pass)
 {
-	
+    bool rigged = (pass == 1);
     if (LLPipeline::sRenderingHUDs)
     {
-        gObjectFullbrightAlphaMaskProgram.bind();
-		gObjectFullbrightAlphaMaskProgram.uniform1f(LLShaderMgr::TEXTURE_GAMMA, 1.0f);
-        gObjectFullbrightAlphaMaskProgram.uniform1i(LLShaderMgr::NO_ATMO, 1);
+        gObjectFullbrightAlphaMaskProgram.bind(rigged);
+		LLGLSLShader::sCurBoundShaderPtr->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 1.0f);
+        LLGLSLShader::sCurBoundShaderPtr->uniform1i(LLShaderMgr::NO_ATMO, 1);
     }
 	else if (LLPipeline::sRenderDeferred)
 	{
         if (LLPipeline::sUnderWaterRender)
 		{
-			gDeferredFullbrightAlphaMaskWaterProgram.bind();
-			gDeferredFullbrightAlphaMaskWaterProgram.uniform1f(LLShaderMgr::TEXTURE_GAMMA, 2.2f);
-            gDeferredFullbrightAlphaMaskProgram.uniform1i(LLShaderMgr::NO_ATMO, 1);
+            gDeferredFullbrightAlphaMaskWaterProgram.bind(rigged);
+            LLGLSLShader::sCurBoundShaderPtr->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 2.2f);
+            LLGLSLShader::sCurBoundShaderPtr->uniform1i(LLShaderMgr::NO_ATMO, 1);
 		}
 		else
 		{
-			gDeferredFullbrightAlphaMaskProgram.bind();
-			gDeferredFullbrightAlphaMaskProgram.uniform1f(LLShaderMgr::TEXTURE_GAMMA, 2.2f);
-            gDeferredFullbrightAlphaMaskProgram.uniform1i(LLShaderMgr::NO_ATMO, 0);
+			gDeferredFullbrightAlphaMaskProgram.bind(rigged);
+            LLGLSLShader::sCurBoundShaderPtr->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 2.2f);
+            LLGLSLShader::sCurBoundShaderPtr->uniform1i(LLShaderMgr::NO_ATMO, 0);
 		}
     }
     else
     {
-		gObjectFullbrightAlphaMaskProgram.bind();
-		gObjectFullbrightAlphaMaskProgram.uniform1f(LLShaderMgr::TEXTURE_GAMMA, 1.0f);
-        gObjectFullbrightAlphaMaskProgram.uniform1i(LLShaderMgr::NO_ATMO, 0);
+		gObjectFullbrightAlphaMaskProgram.bind(rigged);
+        LLGLSLShader::sCurBoundShaderPtr->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 1.0f);
+        LLGLSLShader::sCurBoundShaderPtr->uniform1i(LLShaderMgr::NO_ATMO, 0);
 	}
 }
 
@@ -778,26 +834,19 @@ void LLDrawPoolFullbrightAlphaMask::renderPostDeferred(S32 pass)
 	LL_RECORD_BLOCK_TIME(FTM_RENDER_FULLBRIGHT);
 	LLGLDisable blend(GL_BLEND);
 	U32 fullbright_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_COLOR | LLVertexBuffer::MAP_TEXTURE_INDEX;
-	pushMaskBatches(LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK, fullbright_mask, TRUE, TRUE);
+    if (pass == 0)
+    {
+        pushMaskBatches(LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK, fullbright_mask, TRUE, TRUE);
+    }
+    else
+    {
+        pushRiggedMaskBatches(LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK_RIGGED, fullbright_mask, TRUE, TRUE);
+    }
 }
 
 void LLDrawPoolFullbrightAlphaMask::endPostDeferredPass(S32 pass)
 {
-	if (LLPipeline::sRenderingHUDs || !LLPipeline::sRenderDeferred)
-	{
-		gObjectFullbrightAlphaMaskProgram.unbind();
-	}
-	else
-	{
-		if (LLPipeline::sUnderWaterRender)
-		{
-			gDeferredFullbrightAlphaMaskWaterProgram.unbind();
-		}
-		else
-		{
-			gDeferredFullbrightAlphaMaskProgram.unbind();
-		}
-	}
+    LLGLSLShader::sCurBoundShaderPtr->unbind();
 	LLRenderPass::endRenderPass(pass);
 }
 
diff --git a/indra/newview/lldrawpoolsimple.h b/indra/newview/lldrawpoolsimple.h
index b27cc4babc6..9ef9ea910d8 100644
--- a/indra/newview/lldrawpoolsimple.h
+++ b/indra/newview/lldrawpoolsimple.h
@@ -29,6 +29,8 @@
 
 #include "lldrawpool.h"
 
+class LLGLSLShader;
+
 class LLDrawPoolSimple : public LLRenderPass
 {
 public:
@@ -43,18 +45,19 @@ class LLDrawPoolSimple : public LLRenderPass
 
 	LLDrawPoolSimple();
 	
-	/*virtual*/ S32 getNumDeferredPasses() { return 1; }
-	/*virtual*/ void beginDeferredPass(S32 pass);
-	/*virtual*/ void endDeferredPass(S32 pass);
-	/*virtual*/ void renderDeferred(S32 pass);
+    S32 getNumDeferredPasses() override;
+	void beginDeferredPass(S32 pass) override;
+	void endDeferredPass(S32 pass) override;
+	void renderDeferred(S32 pass) override;
 
-	/*virtual*/ void beginRenderPass(S32 pass);
-	/*virtual*/ void endRenderPass(S32 pass);
+	void beginRenderPass(S32 pass) override;
+	void endRenderPass(S32 pass) override;
 	/// We need two passes so we can handle emissive materials separately.
-	/*virtual*/ S32	 getNumPasses() { return 1; }
-	/*virtual*/ void render(S32 pass = 0);
-	/*virtual*/ void prerender();
+    S32	 getNumPasses() override;
+	void render(S32 pass = 0) override;
+	void prerender() override;
 
+    LLGLSLShader* mShader = nullptr;
 };
 
 class LLDrawPoolGrass : public LLRenderPass
@@ -98,12 +101,12 @@ class LLDrawPoolAlphaMask : public LLRenderPass
 
 	LLDrawPoolAlphaMask();
 
-	/*virtual*/ S32 getNumDeferredPasses() { return 1; }
+	/*virtual*/ S32 getNumDeferredPasses() { return 2; }
 	/*virtual*/ void beginDeferredPass(S32 pass);
 	/*virtual*/ void endDeferredPass(S32 pass);
 	/*virtual*/ void renderDeferred(S32 pass);
 
-	/*virtual*/ S32	 getNumPasses() { return 1; }
+	/*virtual*/ S32	 getNumPasses() { return 2; }
 	/*virtual*/ void beginRenderPass(S32 pass);
 	/*virtual*/ void endRenderPass(S32 pass);
 	/*virtual*/ void render(S32 pass = 0);
@@ -124,12 +127,12 @@ class LLDrawPoolFullbrightAlphaMask : public LLRenderPass
 
 	LLDrawPoolFullbrightAlphaMask();
 	
-	/*virtual*/ S32 getNumPostDeferredPasses() { return 1; }
+	/*virtual*/ S32 getNumPostDeferredPasses() { return 2; }
 	/*virtual*/ void beginPostDeferredPass(S32 pass);
 	/*virtual*/ void endPostDeferredPass(S32 pass);
 	/*virtual*/ void renderPostDeferred(S32 pass);
 
-	/*virtual*/ S32	 getNumPasses() { return 1; }
+	/*virtual*/ S32	 getNumPasses() { return 2; }
 	/*virtual*/ void beginRenderPass(S32 pass);
 	/*virtual*/ void endRenderPass(S32 pass);
 	/*virtual*/ void render(S32 pass = 0);
@@ -150,7 +153,7 @@ class LLDrawPoolFullbright : public LLRenderPass
 
 	LLDrawPoolFullbright();
 	
-	/*virtual*/ S32 getNumPostDeferredPasses() { return 1; }
+	/*virtual*/ S32 getNumPostDeferredPasses() { return 2; }
 	/*virtual*/ void beginPostDeferredPass(S32 pass);
 	/*virtual*/ void endPostDeferredPass(S32 pass);
 	/*virtual*/ void renderPostDeferred(S32 pass);
@@ -179,7 +182,7 @@ class LLDrawPoolGlow : public LLRenderPass
 
 	virtual void prerender() { }
 
-	/*virtual*/ S32 getNumPostDeferredPasses() { return 1; }
+	/*virtual*/ S32 getNumPostDeferredPasses() { return 2; }
 	/*virtual*/ void beginPostDeferredPass(S32 pass); 
 	/*virtual*/ void endPostDeferredPass(S32 pass);
 	/*virtual*/ void renderPostDeferred(S32 pass);
diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp
index 88b958d24a7..39ca7961d85 100644
--- a/indra/newview/llface.cpp
+++ b/indra/newview/llface.cpp
@@ -56,6 +56,7 @@
 #include "llviewertexture.h"
 #include "llvoavatar.h"
 #include "llsculptidsize.h"
+#include "llmeshrepository.h"
 
 #if LL_LINUX
 // Work-around spurious used before init warning on Vector4a
@@ -71,6 +72,7 @@ static LLStaticHashedString sColorIn("color_in");
 
 BOOL LLFace::sSafeRenderSelect = TRUE; // FALSE
 
+
 #define DOTVEC(a,b) (a.mV[0]*b.mV[0] + a.mV[1]*b.mV[1] + a.mV[2]*b.mV[2])
 
 /*
@@ -197,14 +199,7 @@ void LLFace::destroy()
 
 	if (mDrawPoolp)
 	{
-		if (this->isState(LLFace::RIGGED) && (mDrawPoolp->getType() == LLDrawPool::POOL_CONTROL_AV || mDrawPoolp->getType() == LLDrawPool::POOL_AVATAR))
-		{
-			((LLDrawPoolAvatar*) mDrawPoolp)->removeRiggedFace(this);
-		}
-		else
-		{
-			mDrawPoolp->removeFace(this);
-		}
+		mDrawPoolp->removeFace(this);
 		mDrawPoolp = NULL;
 	}
 
@@ -1286,7 +1281,9 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 		return FALSE;
 	}
 
-	const LLVolumeFace &vf = volume.getVolumeFace(f);
+    bool rigged = isState(RIGGED);
+
+    const LLVolumeFace &vf = volume.getVolumeFace(f);
 	S32 num_vertices = (S32)vf.mNumVertices;
 	S32 num_indices = (S32) vf.mNumIndices;
 	
@@ -1450,9 +1447,6 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 		}
 	}
 	
-	LLMatrix4a mat_normal;
-	mat_normal.loadu(mat_norm_in);
-	
 	F32 r = 0, os = 0, ot = 0, ms = 0, mt = 0, cos_ang = 0, sin_ang = 0;
 	bool do_xform = false;
 	if (rebuild_tcoord)
@@ -1487,6 +1481,45 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 		}
 	}
 	
+    const LLMeshSkinInfo* skin = nullptr;
+    LLMatrix4a mat_vert;
+    LLMatrix4a mat_normal;
+
+    // prepare mat_vert
+    if (rebuild_pos)
+    {
+        if (rigged)
+        { //override with bind shape matrix if rigged
+            skin = mSkinInfo;
+            mat_vert = skin->mBindShapeMatrix;
+        }
+        else
+        {
+            mat_vert.loadu(mat_vert_in);
+        }
+    }
+
+    if (rebuild_normal || rebuild_tangent)
+    { //override mat_normal with inverse of skin->mBindShapeMatrix
+        LL_PROFILE_ZONE_NAMED("getGeometryVolume - norm mat override");
+        if (rigged)
+        {
+            if (skin == nullptr)
+            {
+                skin = mSkinInfo;
+            }
+
+            //TODO -- cache this (check profile marker above)?
+            glh::matrix4f m((F32*) skin->mBindShapeMatrix.getF32ptr());
+            m = m.inverse().transpose();
+            mat_normal.loadu(m.m);
+        }
+        else
+        {
+            mat_normal.loadu(mat_norm_in);
+        }
+    }
+
 	static LLCachedControl<bool> use_transform_feedback(gSavedSettings, "RenderUseTransformFeedback", false);
 
 #ifdef GL_TRANSFORM_FEEDBACK_BUFFER
@@ -1740,7 +1773,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 					do_xform = false;
 				}
 
-				if (getVirtualSize() >= MIN_TEX_ANIM_SIZE || isState(LLFace::RIGGED))
+				if (getVirtualSize() >= MIN_TEX_ANIM_SIZE) // || isState(LLFace::RIGGED))
 				{ //don't override texture transform during tc bake
 					tex_mode = 0;
 				}
@@ -2036,9 +2069,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 		
 			mVertexBuffer->getVertexStrider(vert, mGeomIndex, mGeomCount, map_range);
 			
-			LLMatrix4a mat_vert;
-			mat_vert.loadu(mat_vert_in);
-
+			
 			F32* dst = (F32*) vert.get();
 			F32* end_f32 = dst+mGeomCount*4;
 
@@ -2089,10 +2120,10 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 			}
 		}
 
-		
 		if (rebuild_normal)
 		{
-			//LL_RECORD_TIME_BLOCK(FTM_FACE_GEOM_NORMAL);
+            LL_PROFILE_ZONE_NAMED("getGeometryVolume - normal");
+
 			mVertexBuffer->getNormalStrider(norm, mGeomIndex, mGeomCount, map_range);
 			F32* normals = (F32*) norm.get();
 			LLVector4a* src = vf.mNormals;
@@ -2714,56 +2745,6 @@ void LLFace::clearVertexBuffer()
 	mVertexBuffer = NULL;
 }
 
-//static
-U32 LLFace::getRiggedDataMask(U32 type)
-{
-	static const U32 rigged_data_mask[] = {
-		LLDrawPoolAvatar::RIGGED_MATERIAL_MASK,
-		LLDrawPoolAvatar::RIGGED_MATERIAL_ALPHA_VMASK,
-		LLDrawPoolAvatar::RIGGED_MATERIAL_ALPHA_MASK_MASK,
-		LLDrawPoolAvatar::RIGGED_MATERIAL_ALPHA_EMISSIVE_MASK,
-		LLDrawPoolAvatar::RIGGED_SPECMAP_VMASK,
-		LLDrawPoolAvatar::RIGGED_SPECMAP_BLEND_MASK,
-		LLDrawPoolAvatar::RIGGED_SPECMAP_MASK_MASK,
-		LLDrawPoolAvatar::RIGGED_SPECMAP_EMISSIVE_MASK,
-		LLDrawPoolAvatar::RIGGED_NORMMAP_VMASK,
-		LLDrawPoolAvatar::RIGGED_NORMMAP_BLEND_MASK,
-		LLDrawPoolAvatar::RIGGED_NORMMAP_MASK_MASK,
-		LLDrawPoolAvatar::RIGGED_NORMMAP_EMISSIVE_MASK,
-		LLDrawPoolAvatar::RIGGED_NORMSPEC_VMASK,
-		LLDrawPoolAvatar::RIGGED_NORMSPEC_BLEND_MASK,
-		LLDrawPoolAvatar::RIGGED_NORMSPEC_MASK_MASK,
-		LLDrawPoolAvatar::RIGGED_NORMSPEC_EMISSIVE_MASK,
-		LLDrawPoolAvatar::RIGGED_SIMPLE_MASK,
-		LLDrawPoolAvatar::RIGGED_FULLBRIGHT_MASK,
-		LLDrawPoolAvatar::RIGGED_SHINY_MASK,
-		LLDrawPoolAvatar::RIGGED_FULLBRIGHT_SHINY_MASK,
-		LLDrawPoolAvatar::RIGGED_GLOW_MASK,
-		LLDrawPoolAvatar::RIGGED_ALPHA_MASK,
-		LLDrawPoolAvatar::RIGGED_FULLBRIGHT_ALPHA_MASK,
-		LLDrawPoolAvatar::RIGGED_DEFERRED_BUMP_MASK,						 
-		LLDrawPoolAvatar::RIGGED_DEFERRED_SIMPLE_MASK,
-	};
-
-	llassert(type < sizeof(rigged_data_mask)/sizeof(U32));
-
-	return rigged_data_mask[type];
-}
-
-U32 LLFace::getRiggedVertexBufferDataMask() const
-{
-	U32 data_mask = 0;
-	for (U32 i = 0; i < mRiggedIndex.size(); ++i)
-	{
-		if (mRiggedIndex[i] > -1)
-		{
-			data_mask |= LLFace::getRiggedDataMask(i);
-		}
-	}
-
-	return data_mask;
-}
-
 S32 LLFace::getRiggedIndex(U32 type) const
 {
 	if (mRiggedIndex.empty())
@@ -2776,19 +2757,7 @@ S32 LLFace::getRiggedIndex(U32 type) const
 	return mRiggedIndex[type];
 }
 
-void LLFace::setRiggedIndex(U32 type, S32 index)
+U64 LLFace::getSkinHash()
 {
-	if (mRiggedIndex.empty())
-	{
-		mRiggedIndex.resize(LLDrawPoolAvatar::NUM_RIGGED_PASSES);
-		for (U32 i = 0; i < mRiggedIndex.size(); ++i)
-		{
-			mRiggedIndex[i] = -1;
-		}
-	}
-
-	llassert(type < mRiggedIndex.size());
-
-	mRiggedIndex[type] = index;
+    return mSkinInfo ? mSkinInfo->mHash : 0;
 }
-
diff --git a/indra/newview/llface.h b/indra/newview/llface.h
index 2e76c974fac..c533edede42 100644
--- a/indra/newview/llface.h
+++ b/indra/newview/llface.h
@@ -49,6 +49,7 @@ class LLViewerTexture;
 class LLGeometryManager;
 class LLTextureAtlasSlot;
 class LLDrawInfo;
+class LLMeshSkinInfo;
 
 const F32 MIN_ALPHA_SIZE = 1024.f;
 const F32 MIN_TEX_ANIM_SIZE = 512.f;
@@ -228,11 +229,7 @@ class alignas(16) LLFace
 	void setVertexBuffer(LLVertexBuffer* buffer);
 	void clearVertexBuffer(); //sets mVertexBuffer to NULL
 	LLVertexBuffer* getVertexBuffer()	const	{ return mVertexBuffer; }
-	U32 getRiggedVertexBufferDataMask() const;
 	S32 getRiggedIndex(U32 type) const;
-	void setRiggedIndex(U32 type, S32 index);
-
-	static U32 getRiggedDataMask(U32 type);
 
 	void	notifyAboutCreatingTexture(LLViewerTexture *texture);
 	void	notifyAboutMissingAsset(LLViewerTexture *texture);
@@ -261,6 +258,11 @@ class alignas(16) LLFace
 	LLMatrix4*	mSpecMapMatrix;
 	LLMatrix4*	mNormalMapMatrix;
 	LLDrawInfo* mDrawInfo;
+    LLVOAvatar* mAvatar = nullptr;
+    LLMeshSkinInfo* mSkinInfo = nullptr;
+    
+    // return mSkinInfo->mHash or 0 if mSkinInfo is null
+    U64 getSkinHash();
 
 private:
 	LLPointer<LLVertexBuffer> mVertexBuffer;
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index a19d6d0b193..c5a3ff44b31 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -1956,7 +1956,7 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat
 		LLMeshSkinInfo info(skin);
 		info.mMeshID = mesh_id;
 
-		// LL_DEBUGS(LOG_MESH) << "info pelvis offset" << info.mPelvisOffset << LL_ENDL;
+        // LL_DEBUGS(LOG_MESH) << "info pelvis offset" << info.mPelvisOffset << LL_ENDL;
 		{
 			LLMutexLock lock(mMutex);
 			mSkinInfoQ.push_back(info);
diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp
index 30b7124550e..332fa73944d 100644
--- a/indra/newview/llspatialpartition.cpp
+++ b/indra/newview/llspatialpartition.cpp
@@ -2901,8 +2901,24 @@ void renderBatchSize(LLDrawInfo* params)
 {
 	LLGLEnable offset(GL_POLYGON_OFFSET_FILL);
 	glPolygonOffset(-1.f, 1.f);
-	gGL.diffuseColor4ubv((GLubyte*) &(params->mDebugColor));
-	pushVerts(params, LLVertexBuffer::MAP_VERTEX);
+    LLGLSLShader* old_shader = LLGLSLShader::sCurBoundShaderPtr;
+    U32 mask = LLVertexBuffer::MAP_VERTEX;
+    bool bind = false;
+    if (params->mAvatar)
+    { 
+        bind = true;
+        old_shader->mRiggedVariant->bind();
+        LLRenderPass::uploadMatrixPalette(*params);
+        mask |= LLVertexBuffer::MAP_WEIGHT4;
+    }
+	
+    gGL.diffuseColor4ubv((GLubyte*)&(params->mDebugColor));
+	pushVerts(params, mask);
+
+    if (bind)
+    {
+        old_shader->bind();
+    }
 }
 
 void renderShadowFrusta(LLDrawInfo* params)
@@ -4085,6 +4101,11 @@ void LLDrawInfo::validate()
 	mVertexBuffer->validateRange(mStart, mEnd, mCount, mOffset);
 }
 
+U64 LLDrawInfo::getSkinHash()
+{
+    return mSkinInfo ? mSkinInfo->mHash : 0;
+}
+
 LLVertexBuffer* LLGeometryManager::createVertexBuffer(U32 type_mask, U32 usage)
 {
 	return new LLVertexBuffer(type_mask, usage);
diff --git a/indra/newview/llspatialpartition.h b/indra/newview/llspatialpartition.h
index 8cc50e71b1a..5fca516f192 100644
--- a/indra/newview/llspatialpartition.h
+++ b/indra/newview/llspatialpartition.h
@@ -82,6 +82,9 @@ class LLDrawInfo : public LLRefCount
 
 	void validate();
 
+    // return mSkinHash->mHash, or 0 if mSkinHash is null
+    U64 getSkinHash();
+
 	LLVector4a mExtents[2];
 	
 	LLPointer<LLVertexBuffer> mVertexBuffer;
@@ -120,6 +123,8 @@ class LLDrawInfo : public LLRefCount
 	F32  mAlphaMaskCutoff;
 	U8   mDiffuseAlphaMode;
 	bool mSelected;
+    LLVOAvatar* mAvatar = nullptr;
+    LLMeshSkinInfo* mSkinInfo = nullptr;
 
 
 	struct CompareTexture
@@ -647,7 +652,7 @@ class LLVolumeGeometryManager: public LLGeometryManager
 	virtual void rebuildGeom(LLSpatialGroup* group);
 	virtual void rebuildMesh(LLSpatialGroup* group);
 	virtual void getGeometry(LLSpatialGroup* group);
-	U32 genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace** faces, U32 face_count, BOOL distance_sort = FALSE, BOOL batch_textures = FALSE, BOOL no_materials = FALSE);
+	U32 genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace** faces, U32 face_count, BOOL distance_sort = FALSE, BOOL batch_textures = FALSE, BOOL rigged = FALSE);
 	void registerFace(LLSpatialGroup* group, LLFace* facep, U32 type);
 
 private:
@@ -655,13 +660,13 @@ class LLVolumeGeometryManager: public LLGeometryManager
 	void freeFaces();
 
 	static int32_t sInstanceCount;
-	static LLFace** sFullbrightFaces;
-	static LLFace** sBumpFaces;
-	static LLFace** sSimpleFaces;
-	static LLFace** sNormFaces;
-	static LLFace** sSpecFaces;
-	static LLFace** sNormSpecFaces;
-	static LLFace** sAlphaFaces;
+	static LLFace** sFullbrightFaces[2];
+	static LLFace** sBumpFaces[2];
+	static LLFace** sSimpleFaces[2];
+	static LLFace** sNormFaces[2];
+	static LLFace** sSpecFaces[2];
+	static LLFace** sNormSpecFaces[2];
+	static LLFace** sAlphaFaces[2];
 };
 
 //spatial partition that uses volume geometry manager (implemented in LLVOVolume.cpp)
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
index 6368286f6e0..38ac4275cfe 100644
--- a/indra/newview/llviewerdisplay.cpp
+++ b/indra/newview/llviewerdisplay.cpp
@@ -578,8 +578,11 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot)
 	//
 
 	LLAppViewer::instance()->pingMainloopTimeout("Display:Camera");
-	LLViewerCamera::getInstance()->setZoomParameters(zoom_factor, subfield);
-	LLViewerCamera::getInstance()->setNear(MIN_NEAR_PLANE);
+    if (LLViewerCamera::instanceExists())
+    {
+        LLViewerCamera::getInstance()->setZoomParameters(zoom_factor, subfield);
+        LLViewerCamera::getInstance()->setNear(MIN_NEAR_PLANE);
+    }
 
 	//////////////////////////
 	//
diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp
index b34c5b1188f..a1f532dd351 100644
--- a/indra/newview/llviewershadermgr.cpp
+++ b/indra/newview/llviewershadermgr.cpp
@@ -77,6 +77,7 @@ LLGLSLShader			gTransformTangentProgram;
 
 //utility shaders
 LLGLSLShader	gOcclusionProgram;
+LLGLSLShader    gSkinnedOcclusionProgram;
 LLGLSLShader	gOcclusionCubeProgram;
 LLGLSLShader	gCustomAlphaProgram;
 LLGLSLShader	gGlowCombineProgram;
@@ -87,6 +88,7 @@ LLGLSLShader	gTwoTextureCompareProgram;
 LLGLSLShader	gOneTextureFilterProgram;
 LLGLSLShader	gOneTextureNoColorProgram;
 LLGLSLShader	gDebugProgram;
+LLGLSLShader    gSkinnedDebugProgram;
 LLGLSLShader	gClipProgram;
 LLGLSLShader	gDownsampleDepthProgram;
 LLGLSLShader	gDownsampleDepthRectProgram;
@@ -96,56 +98,49 @@ LLGLSLShader	gBenchmarkProgram;
 
 //object shaders
 LLGLSLShader		gObjectSimpleProgram;
+LLGLSLShader        gSkinnedObjectSimpleProgram;
 LLGLSLShader		gObjectSimpleImpostorProgram;
+LLGLSLShader        gSkinnedObjectSimpleImpostorProgram;
 LLGLSLShader		gObjectPreviewProgram;
 LLGLSLShader		gObjectSimpleWaterProgram;
+LLGLSLShader        gSkinnedObjectSimpleWaterProgram;
 LLGLSLShader		gObjectSimpleAlphaMaskProgram;
+LLGLSLShader        gSkinnedObjectSimpleAlphaMaskProgram;
 LLGLSLShader		gObjectSimpleWaterAlphaMaskProgram;
+LLGLSLShader        gSkinnedObjectSimpleWaterAlphaMaskProgram;
 LLGLSLShader		gObjectFullbrightProgram;
+LLGLSLShader        gSkinnedObjectFullbrightProgram;
 LLGLSLShader		gObjectFullbrightWaterProgram;
+LLGLSLShader        gSkinnedObjectFullbrightWaterProgram;
 LLGLSLShader		gObjectEmissiveProgram;
+LLGLSLShader        gSkinnedObjectEmissiveProgram;
 LLGLSLShader		gObjectEmissiveWaterProgram;
+LLGLSLShader        gSkinnedObjectEmissiveWaterProgram;
 LLGLSLShader		gObjectFullbrightAlphaMaskProgram;
+LLGLSLShader        gSkinnedObjectFullbrightAlphaMaskProgram;
 LLGLSLShader		gObjectFullbrightWaterAlphaMaskProgram;
+LLGLSLShader        gSkinnedObjectFullbrightWaterAlphaMaskProgram;
 LLGLSLShader		gObjectFullbrightShinyProgram;
+LLGLSLShader        gSkinnedObjectFullbrightShinyProgram;
 LLGLSLShader		gObjectFullbrightShinyWaterProgram;
+LLGLSLShader        gSkinnedObjectFullbrightShinyWaterProgram;
 LLGLSLShader		gObjectShinyProgram;
+LLGLSLShader        gSkinnedObjectShinyProgram;
 LLGLSLShader		gObjectShinyWaterProgram;
+LLGLSLShader        gSkinnedObjectShinyWaterProgram;
 LLGLSLShader		gObjectBumpProgram;
+LLGLSLShader        gSkinnedObjectBumpProgram;
 LLGLSLShader		gTreeProgram;
 LLGLSLShader		gTreeWaterProgram;
 LLGLSLShader		gObjectFullbrightNoColorProgram;
 LLGLSLShader		gObjectFullbrightNoColorWaterProgram;
 
-LLGLSLShader		gObjectSimpleNonIndexedProgram;
 LLGLSLShader		gObjectSimpleNonIndexedTexGenProgram;
 LLGLSLShader		gObjectSimpleNonIndexedTexGenWaterProgram;
-LLGLSLShader		gObjectSimpleNonIndexedWaterProgram;
 LLGLSLShader		gObjectAlphaMaskNonIndexedProgram;
 LLGLSLShader		gObjectAlphaMaskNonIndexedWaterProgram;
 LLGLSLShader		gObjectAlphaMaskNoColorProgram;
 LLGLSLShader		gObjectAlphaMaskNoColorWaterProgram;
-LLGLSLShader		gObjectFullbrightNonIndexedProgram;
-LLGLSLShader		gObjectFullbrightNonIndexedWaterProgram;
-LLGLSLShader		gObjectEmissiveNonIndexedProgram;
-LLGLSLShader		gObjectEmissiveNonIndexedWaterProgram;
-LLGLSLShader		gObjectFullbrightShinyNonIndexedProgram;
-LLGLSLShader		gObjectFullbrightShinyNonIndexedWaterProgram;
-LLGLSLShader		gObjectShinyNonIndexedProgram;
-LLGLSLShader		gObjectShinyNonIndexedWaterProgram;
-
-//object hardware skinning shaders
-LLGLSLShader		gSkinnedObjectSimpleProgram;
-LLGLSLShader		gSkinnedObjectFullbrightProgram;
-LLGLSLShader		gSkinnedObjectEmissiveProgram;
-LLGLSLShader		gSkinnedObjectFullbrightShinyProgram;
-LLGLSLShader		gSkinnedObjectShinySimpleProgram;
-
-LLGLSLShader		gSkinnedObjectSimpleWaterProgram;
-LLGLSLShader		gSkinnedObjectFullbrightWaterProgram;
-LLGLSLShader		gSkinnedObjectEmissiveWaterProgram;
-LLGLSLShader		gSkinnedObjectFullbrightShinyWaterProgram;
-LLGLSLShader		gSkinnedObjectShinySimpleWaterProgram;
 
 //environment shaders
 LLGLSLShader		gTerrainProgram;
@@ -191,17 +186,18 @@ LLGLSLShader			gDeferredWaterProgram;
 LLGLSLShader			gDeferredUnderWaterProgram;
 LLGLSLShader			gDeferredDiffuseProgram;
 LLGLSLShader			gDeferredDiffuseAlphaMaskProgram;
+LLGLSLShader            gDeferredSkinnedDiffuseAlphaMaskProgram;
 LLGLSLShader			gDeferredNonIndexedDiffuseProgram;
 LLGLSLShader			gDeferredNonIndexedDiffuseAlphaMaskProgram;
 LLGLSLShader			gDeferredNonIndexedDiffuseAlphaMaskNoColorProgram;
 LLGLSLShader			gDeferredSkinnedDiffuseProgram;
 LLGLSLShader			gDeferredSkinnedBumpProgram;
-LLGLSLShader			gDeferredSkinnedAlphaProgram;
 LLGLSLShader			gDeferredBumpProgram;
 LLGLSLShader			gDeferredTerrainProgram;
 LLGLSLShader            gDeferredTerrainWaterProgram;
 LLGLSLShader			gDeferredTreeProgram;
 LLGLSLShader			gDeferredTreeShadowProgram;
+LLGLSLShader            gDeferredSkinnedTreeShadowProgram;
 LLGLSLShader			gDeferredAvatarProgram;
 LLGLSLShader			gDeferredAvatarAlphaProgram;
 LLGLSLShader			gDeferredLightProgram;
@@ -213,9 +209,12 @@ LLGLSLShader			gDeferredBlurLightProgram;
 LLGLSLShader			gDeferredSoftenProgram;
 LLGLSLShader			gDeferredSoftenWaterProgram;
 LLGLSLShader			gDeferredShadowProgram;
+LLGLSLShader            gDeferredSkinnedShadowProgram;
 LLGLSLShader			gDeferredShadowCubeProgram;
 LLGLSLShader			gDeferredShadowAlphaMaskProgram;
+LLGLSLShader            gDeferredSkinnedShadowAlphaMaskProgram;
 LLGLSLShader			gDeferredShadowFullbrightAlphaMaskProgram;
+LLGLSLShader            gDeferredSkinnedShadowFullbrightAlphaMaskProgram;
 LLGLSLShader			gDeferredAvatarShadowProgram;
 LLGLSLShader			gDeferredAvatarAlphaShadowProgram;
 LLGLSLShader			gDeferredAvatarAlphaMaskShadowProgram;
@@ -223,14 +222,20 @@ LLGLSLShader			gDeferredAttachmentShadowProgram;
 LLGLSLShader			gDeferredAttachmentAlphaShadowProgram;
 LLGLSLShader			gDeferredAttachmentAlphaMaskShadowProgram;
 LLGLSLShader			gDeferredAlphaProgram;
+LLGLSLShader            gDeferredSkinnedAlphaProgram;
 LLGLSLShader			gDeferredAlphaImpostorProgram;
+LLGLSLShader            gDeferredSkinnedAlphaImpostorProgram;
 LLGLSLShader			gDeferredAlphaWaterProgram;
+LLGLSLShader            gDeferredSkinnedAlphaWaterProgram;
 LLGLSLShader			gDeferredAvatarEyesProgram;
 LLGLSLShader			gDeferredFullbrightProgram;
 LLGLSLShader			gDeferredFullbrightAlphaMaskProgram;
 LLGLSLShader			gDeferredFullbrightWaterProgram;
+LLGLSLShader            gDeferredSkinnedFullbrightWaterProgram;
 LLGLSLShader			gDeferredFullbrightAlphaMaskWaterProgram;
+LLGLSLShader            gDeferredSkinnedFullbrightAlphaMaskWaterProgram;
 LLGLSLShader			gDeferredEmissiveProgram;
+LLGLSLShader            gDeferredSkinnedEmissiveProgram;
 LLGLSLShader			gDeferredPostProgram;
 LLGLSLShader			gDeferredCoFProgram;
 LLGLSLShader			gDeferredDoFCombineProgram;
@@ -243,14 +248,29 @@ LLGLSLShader			gDeferredWLSunProgram;
 LLGLSLShader			gDeferredWLMoonProgram;
 LLGLSLShader			gDeferredStarProgram;
 LLGLSLShader			gDeferredFullbrightShinyProgram;
-LLGLSLShader			gDeferredSkinnedFullbrightShinyProgram;
+LLGLSLShader            gDeferredSkinnedFullbrightShinyProgram;
 LLGLSLShader			gDeferredSkinnedFullbrightProgram;
+LLGLSLShader            gDeferredSkinnedFullbrightAlphaMaskProgram;
 LLGLSLShader			gNormalMapGenProgram;
 
 // Deferred materials shaders
 LLGLSLShader			gDeferredMaterialProgram[LLMaterial::SHADER_COUNT*2];
 LLGLSLShader			gDeferredMaterialWaterProgram[LLMaterial::SHADER_COUNT*2];
 
+//helper for making a rigged variant of a given shader
+bool make_rigged_variant(LLGLSLShader& shader, LLGLSLShader& riggedShader)
+{
+    riggedShader.mName = llformat("Skinned %s", shader.mName.c_str());
+    riggedShader.mFeatures = shader.mFeatures;
+    riggedShader.mFeatures.hasObjectSkinning = true;
+    riggedShader.addPermutation("HAS_SKIN", "1");
+    riggedShader.mShaderFiles = shader.mShaderFiles;
+    riggedShader.mShaderLevel = shader.mShaderLevel;
+    riggedShader.mShaderGroup = shader.mShaderGroup;
+    shader.mRiggedVariant = &riggedShader;
+    return riggedShader.createShader(NULL, NULL);
+}
+
 LLViewerShaderMgr::LLViewerShaderMgr() :
 	mShaderLevel(SHADER_COUNT, 0),
 	mMaxAvatarShaderLevel(0)
@@ -263,75 +283,77 @@ LLViewerShaderMgr::LLViewerShaderMgr() :
 	mShaderList.push_back(&gWLMoonProgram);
 	mShaderList.push_back(&gAvatarProgram);
 	mShaderList.push_back(&gObjectShinyProgram);
-	mShaderList.push_back(&gObjectShinyNonIndexedProgram);
+    mShaderList.push_back(&gSkinnedObjectShinyProgram);
 	mShaderList.push_back(&gWaterProgram);
 	mShaderList.push_back(&gWaterEdgeProgram);
 	mShaderList.push_back(&gAvatarEyeballProgram); 
 	mShaderList.push_back(&gObjectSimpleProgram);
+    mShaderList.push_back(&gSkinnedObjectSimpleProgram);
 	mShaderList.push_back(&gObjectSimpleImpostorProgram);
+    mShaderList.push_back(&gSkinnedObjectSimpleImpostorProgram);
 	mShaderList.push_back(&gObjectPreviewProgram);
 	mShaderList.push_back(&gImpostorProgram);
 	mShaderList.push_back(&gObjectFullbrightNoColorProgram);
 	mShaderList.push_back(&gObjectFullbrightNoColorWaterProgram);
 	mShaderList.push_back(&gObjectSimpleAlphaMaskProgram);
+    mShaderList.push_back(&gSkinnedObjectSimpleAlphaMaskProgram);
 	mShaderList.push_back(&gObjectBumpProgram);
+    mShaderList.push_back(&gSkinnedObjectBumpProgram);
 	mShaderList.push_back(&gObjectEmissiveProgram);
+    mShaderList.push_back(&gSkinnedObjectEmissiveProgram);
 	mShaderList.push_back(&gObjectEmissiveWaterProgram);
+    mShaderList.push_back(&gSkinnedObjectEmissiveWaterProgram);
 	mShaderList.push_back(&gObjectFullbrightProgram);
+    mShaderList.push_back(&gSkinnedObjectFullbrightProgram);
 	mShaderList.push_back(&gObjectFullbrightAlphaMaskProgram);
+    mShaderList.push_back(&gSkinnedObjectFullbrightAlphaMaskProgram);
 	mShaderList.push_back(&gObjectFullbrightShinyProgram);
+    mShaderList.push_back(&gSkinnedObjectFullbrightShinyProgram);
 	mShaderList.push_back(&gObjectFullbrightShinyWaterProgram);
-	mShaderList.push_back(&gObjectSimpleNonIndexedProgram);
+    mShaderList.push_back(&gSkinnedObjectFullbrightShinyWaterProgram);
 	mShaderList.push_back(&gObjectSimpleNonIndexedTexGenProgram);
 	mShaderList.push_back(&gObjectSimpleNonIndexedTexGenWaterProgram);
-	mShaderList.push_back(&gObjectSimpleNonIndexedWaterProgram);
 	mShaderList.push_back(&gObjectAlphaMaskNonIndexedProgram);
 	mShaderList.push_back(&gObjectAlphaMaskNonIndexedWaterProgram);
 	mShaderList.push_back(&gObjectAlphaMaskNoColorProgram);
 	mShaderList.push_back(&gObjectAlphaMaskNoColorWaterProgram);
 	mShaderList.push_back(&gTreeProgram);
 	mShaderList.push_back(&gTreeWaterProgram);
-	mShaderList.push_back(&gObjectFullbrightNonIndexedProgram);
-	mShaderList.push_back(&gObjectFullbrightNonIndexedWaterProgram);
-	mShaderList.push_back(&gObjectEmissiveNonIndexedProgram);
-	mShaderList.push_back(&gObjectEmissiveNonIndexedWaterProgram);
-	mShaderList.push_back(&gObjectFullbrightShinyNonIndexedProgram);
-	mShaderList.push_back(&gObjectFullbrightShinyNonIndexedWaterProgram);
-	mShaderList.push_back(&gSkinnedObjectSimpleProgram);
-	mShaderList.push_back(&gSkinnedObjectFullbrightProgram);
-	mShaderList.push_back(&gSkinnedObjectEmissiveProgram);
-	mShaderList.push_back(&gSkinnedObjectFullbrightShinyProgram);
-	mShaderList.push_back(&gSkinnedObjectShinySimpleProgram);
-	mShaderList.push_back(&gSkinnedObjectSimpleWaterProgram);
-	mShaderList.push_back(&gSkinnedObjectFullbrightWaterProgram);
-	mShaderList.push_back(&gSkinnedObjectEmissiveWaterProgram);
-	mShaderList.push_back(&gSkinnedObjectFullbrightShinyWaterProgram);
-	mShaderList.push_back(&gSkinnedObjectShinySimpleWaterProgram);
 	mShaderList.push_back(&gTerrainProgram);
 	mShaderList.push_back(&gTerrainWaterProgram);
 	mShaderList.push_back(&gObjectSimpleWaterProgram);
+    mShaderList.push_back(&gSkinnedObjectSimpleWaterProgram);
 	mShaderList.push_back(&gObjectFullbrightWaterProgram);
+    mShaderList.push_back(&gSkinnedObjectFullbrightWaterProgram);
 	mShaderList.push_back(&gObjectSimpleWaterAlphaMaskProgram);
+    mShaderList.push_back(&gSkinnedObjectSimpleWaterAlphaMaskProgram);
 	mShaderList.push_back(&gObjectFullbrightWaterAlphaMaskProgram);
+    mShaderList.push_back(&gSkinnedObjectFullbrightWaterAlphaMaskProgram);
 	mShaderList.push_back(&gAvatarWaterProgram);
 	mShaderList.push_back(&gObjectShinyWaterProgram);
-	mShaderList.push_back(&gObjectShinyNonIndexedWaterProgram);
+    mShaderList.push_back(&gSkinnedObjectShinyWaterProgram);
 	mShaderList.push_back(&gUnderWaterProgram);
 	mShaderList.push_back(&gDeferredSunProgram);
 	mShaderList.push_back(&gDeferredSoftenProgram);
 	mShaderList.push_back(&gDeferredSoftenWaterProgram);
 	mShaderList.push_back(&gDeferredAlphaProgram);
+    mShaderList.push_back(&gDeferredSkinnedAlphaProgram);
 	mShaderList.push_back(&gDeferredAlphaImpostorProgram);
+    mShaderList.push_back(&gDeferredSkinnedAlphaImpostorProgram);
 	mShaderList.push_back(&gDeferredAlphaWaterProgram);
-	mShaderList.push_back(&gDeferredSkinnedAlphaProgram);
+    mShaderList.push_back(&gDeferredSkinnedAlphaWaterProgram);
 	mShaderList.push_back(&gDeferredFullbrightProgram);
 	mShaderList.push_back(&gDeferredFullbrightAlphaMaskProgram);
 	mShaderList.push_back(&gDeferredFullbrightWaterProgram);
-	mShaderList.push_back(&gDeferredFullbrightAlphaMaskWaterProgram);	
+    mShaderList.push_back(&gDeferredSkinnedFullbrightWaterProgram);
+	mShaderList.push_back(&gDeferredFullbrightAlphaMaskWaterProgram);
+    mShaderList.push_back(&gDeferredSkinnedFullbrightAlphaMaskWaterProgram);
 	mShaderList.push_back(&gDeferredFullbrightShinyProgram);
-	mShaderList.push_back(&gDeferredSkinnedFullbrightShinyProgram);
+    mShaderList.push_back(&gDeferredSkinnedFullbrightShinyProgram);
 	mShaderList.push_back(&gDeferredSkinnedFullbrightProgram);
+    mShaderList.push_back(&gDeferredSkinnedFullbrightAlphaMaskProgram);
 	mShaderList.push_back(&gDeferredEmissiveProgram);
+    mShaderList.push_back(&gDeferredSkinnedEmissiveProgram);
 	mShaderList.push_back(&gDeferredAvatarEyesProgram);
 	mShaderList.push_back(&gDeferredWaterProgram);
 	mShaderList.push_back(&gDeferredUnderWaterProgram);	
@@ -732,8 +754,10 @@ void LLViewerShaderMgr::setShaders()
 void LLViewerShaderMgr::unloadShaders()
 {
 	gOcclusionProgram.unload();
+    gSkinnedOcclusionProgram.unload();
 	gOcclusionCubeProgram.unload();
 	gDebugProgram.unload();
+    gSkinnedDebugProgram.unload();
 	gClipProgram.unload();
 	gDownsampleDepthProgram.unload();
 	gDownsampleDepthRectProgram.unload();
@@ -755,58 +779,50 @@ void LLViewerShaderMgr::unloadShaders()
 	gObjectFullbrightNoColorProgram.unload();
 	gObjectFullbrightNoColorWaterProgram.unload();
 	gObjectSimpleProgram.unload();
+    gSkinnedObjectSimpleProgram.unload();
 	gObjectSimpleImpostorProgram.unload();
+    gSkinnedObjectSimpleImpostorProgram.unload();
 	gObjectPreviewProgram.unload();
 	gImpostorProgram.unload();
 	gObjectSimpleAlphaMaskProgram.unload();
+    gSkinnedObjectSimpleAlphaMaskProgram.unload();
 	gObjectBumpProgram.unload();
+    gSkinnedObjectBumpProgram.unload();
 	gObjectSimpleWaterProgram.unload();
+    gSkinnedObjectSimpleWaterProgram.unload();
 	gObjectSimpleWaterAlphaMaskProgram.unload();
+    gSkinnedObjectSimpleWaterAlphaMaskProgram.unload();
 	gObjectFullbrightProgram.unload();
+    gSkinnedObjectFullbrightProgram.unload();
 	gObjectFullbrightWaterProgram.unload();
+    gSkinnedObjectFullbrightWaterProgram.unload();
 	gObjectEmissiveProgram.unload();
+    gSkinnedObjectEmissiveProgram.unload();
 	gObjectEmissiveWaterProgram.unload();
+    gSkinnedObjectEmissiveWaterProgram.unload();
 	gObjectFullbrightAlphaMaskProgram.unload();
+    gSkinnedObjectFullbrightAlphaMaskProgram.unload();
 	gObjectFullbrightWaterAlphaMaskProgram.unload();
+    gSkinnedObjectFullbrightWaterAlphaMaskProgram.unload();
 
 	gObjectShinyProgram.unload();
+    gSkinnedObjectShinyProgram.unload();
 	gObjectFullbrightShinyProgram.unload();
+    gSkinnedObjectFullbrightShinyProgram.unload();
 	gObjectFullbrightShinyWaterProgram.unload();
+    gSkinnedObjectFullbrightShinyWaterProgram.unload();
 	gObjectShinyWaterProgram.unload();
+    gSkinnedObjectShinyWaterProgram.unload();
 
-	gObjectSimpleNonIndexedProgram.unload();
 	gObjectSimpleNonIndexedTexGenProgram.unload();
 	gObjectSimpleNonIndexedTexGenWaterProgram.unload();
-	gObjectSimpleNonIndexedWaterProgram.unload();
 	gObjectAlphaMaskNonIndexedProgram.unload();
 	gObjectAlphaMaskNonIndexedWaterProgram.unload();
 	gObjectAlphaMaskNoColorProgram.unload();
 	gObjectAlphaMaskNoColorWaterProgram.unload();
-	gObjectFullbrightNonIndexedProgram.unload();
-	gObjectFullbrightNonIndexedWaterProgram.unload();
-	gObjectEmissiveNonIndexedProgram.unload();
-	gObjectEmissiveNonIndexedWaterProgram.unload();
 	gTreeProgram.unload();
 	gTreeWaterProgram.unload();
 
-	gObjectShinyNonIndexedProgram.unload();
-	gObjectFullbrightShinyNonIndexedProgram.unload();
-	gObjectFullbrightShinyNonIndexedWaterProgram.unload();
-	gObjectShinyNonIndexedWaterProgram.unload();
-
-	gSkinnedObjectSimpleProgram.unload();
-	gSkinnedObjectFullbrightProgram.unload();
-	gSkinnedObjectEmissiveProgram.unload();
-	gSkinnedObjectFullbrightShinyProgram.unload();
-	gSkinnedObjectShinySimpleProgram.unload();
-	
-	gSkinnedObjectSimpleWaterProgram.unload();
-	gSkinnedObjectFullbrightWaterProgram.unload();
-	gSkinnedObjectEmissiveWaterProgram.unload();
-	gSkinnedObjectFullbrightShinyWaterProgram.unload();
-	gSkinnedObjectShinySimpleWaterProgram.unload();
-	
-
 	gWaterProgram.unload();
     gWaterEdgeProgram.unload();
 	gUnderWaterProgram.unload();
@@ -832,13 +848,13 @@ void LLViewerShaderMgr::unloadShaders()
 
 	gDeferredDiffuseProgram.unload();
 	gDeferredDiffuseAlphaMaskProgram.unload();
+    gDeferredSkinnedDiffuseAlphaMaskProgram.unload();
 	gDeferredNonIndexedDiffuseAlphaMaskProgram.unload();
 	gDeferredNonIndexedDiffuseAlphaMaskNoColorProgram.unload();
 	gDeferredNonIndexedDiffuseProgram.unload();
 	gDeferredSkinnedDiffuseProgram.unload();
 	gDeferredSkinnedBumpProgram.unload();
-	gDeferredSkinnedAlphaProgram.unload();
-
+	
 	gTransformPositionProgram.unload();
 	gTransformTexCoordProgram.unload();
 	gTransformNormalProgram.unload();
@@ -915,7 +931,7 @@ BOOL LLViewerShaderMgr::loadBasicShaders()
 	}
 	shaders.push_back( make_pair( "objects/nonindexedTextureV.glsl",        1 ) );
 
-	boost::unordered_map<std::string, std::string> attribs;
+	std::unordered_map<std::string, std::string> attribs;
 	attribs["MAX_JOINTS_PER_MESH_OBJECT"] = 
 		boost::lexical_cast<std::string>(LLSkinningUtil::getMaxJointCount());
 
@@ -1227,14 +1243,15 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 	{
 		gDeferredTreeProgram.unload();
 		gDeferredTreeShadowProgram.unload();
+        gDeferredSkinnedTreeShadowProgram.unload();
 		gDeferredDiffuseProgram.unload();
 		gDeferredDiffuseAlphaMaskProgram.unload();
+        gDeferredSkinnedDiffuseAlphaMaskProgram.unload();
 		gDeferredNonIndexedDiffuseAlphaMaskProgram.unload();
 		gDeferredNonIndexedDiffuseAlphaMaskNoColorProgram.unload();
 		gDeferredNonIndexedDiffuseProgram.unload();
 		gDeferredSkinnedDiffuseProgram.unload();
 		gDeferredSkinnedBumpProgram.unload();
-		gDeferredSkinnedAlphaProgram.unload();
 		gDeferredBumpProgram.unload();
 		gDeferredImpostorProgram.unload();
 		gDeferredTerrainProgram.unload();
@@ -1251,9 +1268,12 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 		gDeferredSoftenProgram.unload();
 		gDeferredSoftenWaterProgram.unload();
 		gDeferredShadowProgram.unload();
+        gDeferredSkinnedShadowProgram.unload();
 		gDeferredShadowCubeProgram.unload();
         gDeferredShadowAlphaMaskProgram.unload();
+        gDeferredSkinnedShadowAlphaMaskProgram.unload();
         gDeferredShadowFullbrightAlphaMaskProgram.unload();
+        gDeferredSkinnedShadowFullbrightAlphaMaskProgram.unload();
 		gDeferredAvatarShadowProgram.unload();
         gDeferredAvatarAlphaShadowProgram.unload();
         gDeferredAvatarAlphaMaskShadowProgram.unload();
@@ -1263,12 +1283,17 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 		gDeferredAvatarProgram.unload();
 		gDeferredAvatarAlphaProgram.unload();
 		gDeferredAlphaProgram.unload();
+        gDeferredSkinnedAlphaProgram.unload();
 		gDeferredAlphaWaterProgram.unload();
+        gDeferredSkinnedAlphaWaterProgram.unload();
 		gDeferredFullbrightProgram.unload();
 		gDeferredFullbrightAlphaMaskProgram.unload();
 		gDeferredFullbrightWaterProgram.unload();
+        gDeferredSkinnedFullbrightWaterProgram.unload();
 		gDeferredFullbrightAlphaMaskWaterProgram.unload();
+        gDeferredSkinnedFullbrightAlphaMaskWaterProgram.unload();
 		gDeferredEmissiveProgram.unload();
+        gDeferredSkinnedEmissiveProgram.unload();
 		gDeferredAvatarEyesProgram.unload();
 		gDeferredPostProgram.unload();		
 		gDeferredCoFProgram.unload();		
@@ -1283,8 +1308,9 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
         gDeferredWLMoonProgram.unload();
 		gDeferredStarProgram.unload();
 		gDeferredFullbrightShinyProgram.unload();
-		gDeferredSkinnedFullbrightShinyProgram.unload();
+        gDeferredSkinnedFullbrightShinyProgram.unload();
 		gDeferredSkinnedFullbrightProgram.unload();
+        gDeferredSkinnedFullbrightAlphaMaskProgram.unload();
 
         gDeferredHighlightProgram.unload();
         gDeferredHighlightNormalProgram.unload();
@@ -1341,7 +1367,8 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 		gDeferredDiffuseProgram.mShaderFiles.push_back(make_pair("deferred/diffuseIndexedF.glsl", GL_FRAGMENT_SHADER_ARB));
 		gDeferredDiffuseProgram.mFeatures.mIndexedTextureChannels = LLGLSLShader::sIndexedTextureChannels;
 		gDeferredDiffuseProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
-		success = gDeferredDiffuseProgram.createShader(NULL, NULL);
+        success = make_rigged_variant(gDeferredDiffuseProgram, gDeferredSkinnedDiffuseProgram);
+		success = success && gDeferredDiffuseProgram.createShader(NULL, NULL);
 	}
 
 	if (success)
@@ -1353,7 +1380,8 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 		gDeferredDiffuseAlphaMaskProgram.mShaderFiles.push_back(make_pair("deferred/diffuseAlphaMaskIndexedF.glsl", GL_FRAGMENT_SHADER_ARB));
 		gDeferredDiffuseAlphaMaskProgram.mFeatures.mIndexedTextureChannels = LLGLSLShader::sIndexedTextureChannels;
 		gDeferredDiffuseAlphaMaskProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
-		success = gDeferredDiffuseAlphaMaskProgram.createShader(NULL, NULL);
+        success = make_rigged_variant(gDeferredDiffuseAlphaMaskProgram, gDeferredSkinnedDiffuseAlphaMaskProgram);
+		success = success && gDeferredDiffuseAlphaMaskProgram.createShader(NULL, NULL);
 	}
 
 	if (success)
@@ -1393,87 +1421,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
         llassert(success);
 	}
 
-	if (success)
-	{
-		gDeferredSkinnedDiffuseProgram.mName = "Deferred Skinned Diffuse Shader";
-		gDeferredSkinnedDiffuseProgram.mFeatures.hasObjectSkinning = true;
-		gDeferredSkinnedDiffuseProgram.mFeatures.encodesNormal = true;
-        gDeferredSkinnedDiffuseProgram.mFeatures.hasSrgb = true;
-		gDeferredSkinnedDiffuseProgram.mShaderFiles.clear();
-		gDeferredSkinnedDiffuseProgram.mShaderFiles.push_back(make_pair("deferred/diffuseSkinnedV.glsl", GL_VERTEX_SHADER_ARB));
-		gDeferredSkinnedDiffuseProgram.mShaderFiles.push_back(make_pair("deferred/diffuseF.glsl", GL_FRAGMENT_SHADER_ARB));
-		gDeferredSkinnedDiffuseProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
-		success = gDeferredSkinnedDiffuseProgram.createShader(NULL, NULL);
-		llassert(success);
-	}
-
-	if (success)
-	{
-		gDeferredSkinnedBumpProgram.mName = "Deferred Skinned Bump Shader";
-		gDeferredSkinnedBumpProgram.mFeatures.hasObjectSkinning = true;
-        gDeferredSkinnedBumpProgram.mFeatures.encodesNormal = true;
-		gDeferredSkinnedBumpProgram.mShaderFiles.clear();
-		gDeferredSkinnedBumpProgram.mShaderFiles.push_back(make_pair("deferred/bumpSkinnedV.glsl", GL_VERTEX_SHADER_ARB));
-		gDeferredSkinnedBumpProgram.mShaderFiles.push_back(make_pair("deferred/bumpF.glsl", GL_FRAGMENT_SHADER_ARB));
-		gDeferredSkinnedBumpProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
-		success = gDeferredSkinnedBumpProgram.createShader(NULL, NULL);
-        llassert(success);
-	}
-
-	if (success)
-	{
-		gDeferredSkinnedAlphaProgram.mName = "Deferred Skinned Alpha Shader";
-		gDeferredSkinnedAlphaProgram.mFeatures.hasObjectSkinning = true;
-		gDeferredSkinnedAlphaProgram.mFeatures.calculatesLighting = false;
-		gDeferredSkinnedAlphaProgram.mFeatures.hasLighting = false;
-		gDeferredSkinnedAlphaProgram.mFeatures.isAlphaLighting = true;
-		gDeferredSkinnedAlphaProgram.mFeatures.disableTextureIndex = true;
-		gDeferredSkinnedAlphaProgram.mFeatures.hasSrgb = true;
-		gDeferredSkinnedAlphaProgram.mFeatures.encodesNormal = true;
-		gDeferredSkinnedAlphaProgram.mFeatures.calculatesAtmospherics = true;
-		gDeferredSkinnedAlphaProgram.mFeatures.hasAtmospherics = true;
-		gDeferredSkinnedAlphaProgram.mFeatures.hasTransport = true;
-		gDeferredSkinnedAlphaProgram.mFeatures.hasGamma = true;
-		gDeferredSkinnedAlphaProgram.mFeatures.hasShadows = true;
-        
-		gDeferredSkinnedAlphaProgram.mShaderFiles.clear();
-		gDeferredSkinnedAlphaProgram.mShaderFiles.push_back(make_pair("deferred/alphaV.glsl", GL_VERTEX_SHADER_ARB));
-		gDeferredSkinnedAlphaProgram.mShaderFiles.push_back(make_pair("deferred/alphaF.glsl", GL_FRAGMENT_SHADER_ARB));
-		gDeferredSkinnedAlphaProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
-
-		gDeferredSkinnedAlphaProgram.clearPermutations();
-		gDeferredSkinnedAlphaProgram.addPermutation("USE_DIFFUSE_TEX", "1");
-		gDeferredSkinnedAlphaProgram.addPermutation("HAS_SKIN", "1");
-        gDeferredSkinnedAlphaProgram.addPermutation("USE_VERTEX_COLOR", "1");
-
-		if (use_sun_shadow)
-		{
-			gDeferredSkinnedAlphaProgram.addPermutation("HAS_SHADOW", "1");
-		}
-
-        if (ambient_kill)
-        {
-            gDeferredSkinnedAlphaProgram.addPermutation("AMBIENT_KILL", "1");
-        }
-
-        if (sunlight_kill)
-        {
-            gDeferredSkinnedAlphaProgram.addPermutation("SUNLIGHT_KILL", "1");
-        }
-
-        if (local_light_kill)
-        {
-            gDeferredSkinnedAlphaProgram.addPermutation("LOCAL_LIGHT_KILL", "1");
-        }
-
-		success = gDeferredSkinnedAlphaProgram.createShader(NULL, NULL);
-		llassert(success);
-
-		// Hack to include uniforms for lighting without linking in lighting file
-		gDeferredSkinnedAlphaProgram.mFeatures.calculatesLighting = true;
-		gDeferredSkinnedAlphaProgram.mFeatures.hasLighting = true;
-	}
-
 	if (success)
 	{
 		gDeferredBumpProgram.mName = "Deferred Bump Shader";
@@ -1482,7 +1429,8 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 		gDeferredBumpProgram.mShaderFiles.push_back(make_pair("deferred/bumpV.glsl", GL_VERTEX_SHADER_ARB));
 		gDeferredBumpProgram.mShaderFiles.push_back(make_pair("deferred/bumpF.glsl", GL_FRAGMENT_SHADER_ARB));
 		gDeferredBumpProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
-		success = gDeferredBumpProgram.createShader(NULL, NULL);
+        success = make_rigged_variant(gDeferredBumpProgram, gDeferredSkinnedBumpProgram);
+		success = success && gDeferredBumpProgram.createShader(NULL, NULL);
 		llassert(success);
 	}
 
@@ -1570,6 +1518,10 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
                 gDeferredMaterialProgram[i].addPermutation("HAS_SKIN", "1");
                 gDeferredMaterialProgram[i].mFeatures.hasObjectSkinning = true;
             }
+            else
+            {
+                gDeferredMaterialProgram[i].mRiggedVariant = &gDeferredMaterialProgram[i + 0x10];
+            }
 
             success = gDeferredMaterialProgram[i].createShader(NULL, NULL);
             llassert(success);
@@ -1615,6 +1567,10 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
             {
                 gDeferredMaterialWaterProgram[i].addPermutation("HAS_SKIN", "1");
             }
+            else
+            {
+                gDeferredMaterialWaterProgram[i].mRiggedVariant = &(gDeferredMaterialWaterProgram[i + 0x10]);
+            }
             gDeferredMaterialWaterProgram[i].addPermutation("WATER_FOG","1");
 
             if (ambient_kill)
@@ -1691,10 +1647,25 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 		gDeferredTreeShadowProgram.mShaderFiles.push_back(make_pair("deferred/treeShadowV.glsl", GL_VERTEX_SHADER_ARB));
 		gDeferredTreeShadowProgram.mShaderFiles.push_back(make_pair("deferred/treeShadowF.glsl", GL_FRAGMENT_SHADER_ARB));
 		gDeferredTreeShadowProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
+        gDeferredTreeShadowProgram.mRiggedVariant = &gDeferredSkinnedTreeShadowProgram;
 		success = gDeferredTreeShadowProgram.createShader(NULL, NULL);
         llassert(success);
 	}
 
+    if (success)
+    {
+        gDeferredSkinnedTreeShadowProgram.mName = "Deferred Skinned Tree Shadow Shader";
+        gDeferredSkinnedTreeShadowProgram.mShaderFiles.clear();
+        gDeferredSkinnedTreeShadowProgram.mFeatures.isDeferred = true;
+        gDeferredSkinnedTreeShadowProgram.mFeatures.hasShadows = true;
+        gDeferredSkinnedTreeShadowProgram.mFeatures.hasObjectSkinning = true;
+        gDeferredSkinnedTreeShadowProgram.mShaderFiles.push_back(make_pair("deferred/treeShadowSkinnedV.glsl", GL_VERTEX_SHADER_ARB));
+        gDeferredSkinnedTreeShadowProgram.mShaderFiles.push_back(make_pair("deferred/treeShadowF.glsl", GL_FRAGMENT_SHADER_ARB));
+        gDeferredSkinnedTreeShadowProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
+        success = gDeferredSkinnedTreeShadowProgram.createShader(NULL, NULL);
+        llassert(success);
+    }
+
 	if (success)
 	{
 		gDeferredImpostorProgram.mName = "Deferred Impostor Shader";
@@ -1883,172 +1854,235 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 
 	if (success)
 	{
-		gDeferredAlphaProgram.mName = "Deferred Alpha Shader";
-
-		gDeferredAlphaProgram.mFeatures.calculatesLighting = false;
-		gDeferredAlphaProgram.mFeatures.hasLighting = false;
-		gDeferredAlphaProgram.mFeatures.isAlphaLighting = true;
-		gDeferredAlphaProgram.mFeatures.disableTextureIndex = true; //hack to disable auto-setup of texture channels
-		gDeferredAlphaProgram.mFeatures.hasSrgb = true;
-		gDeferredAlphaProgram.mFeatures.encodesNormal = true;
-		gDeferredAlphaProgram.mFeatures.calculatesAtmospherics = true;
-        gDeferredAlphaProgram.mFeatures.hasAtmospherics = true;
-        gDeferredAlphaProgram.mFeatures.hasGamma = true;
-        gDeferredAlphaProgram.mFeatures.hasTransport = true;
-        gDeferredAlphaProgram.mFeatures.hasShadows = use_sun_shadow;
-
-        if (mShaderLevel[SHADER_DEFERRED] < 1)
+        for (int i = 0; i < 2 && success; ++i)
         {
-            gDeferredAlphaProgram.mFeatures.mIndexedTextureChannels = LLGLSLShader::sIndexedTextureChannels;
-        }
-        else
-        { //shave off some texture units for shadow maps
-            gDeferredAlphaProgram.mFeatures.mIndexedTextureChannels = llmax(LLGLSLShader::sIndexedTextureChannels - 6, 1);
-        }
-            
-        gDeferredAlphaProgram.mShaderFiles.clear();
-        gDeferredAlphaProgram.mShaderFiles.push_back(make_pair("deferred/alphaV.glsl", GL_VERTEX_SHADER_ARB));
-        gDeferredAlphaProgram.mShaderFiles.push_back(make_pair("deferred/alphaF.glsl", GL_FRAGMENT_SHADER_ARB));
-
-        gDeferredAlphaProgram.clearPermutations();
-        gDeferredAlphaProgram.addPermutation("USE_VERTEX_COLOR", "1");
-        gDeferredAlphaProgram.addPermutation("USE_INDEXED_TEX", "1");
-        if (use_sun_shadow)
-        {
-            gDeferredAlphaProgram.addPermutation("HAS_SHADOW", "1");
-        }
+            LLGLSLShader* shader = nullptr;
+            bool rigged = i == 1;
+            if (!rigged)
+            {
+                shader = &gDeferredAlphaProgram;
+                shader->mName = "Deferred Alpha Shader";
+                shader->mRiggedVariant = &gDeferredSkinnedAlphaProgram;
+            }
+            else
+            {
+                shader = &gDeferredSkinnedAlphaProgram;
+                shader->mName = "Skinned Deferred Alpha Shader";
+                shader->mFeatures.hasObjectSkinning = true;
+            }
 
-        if (ambient_kill)
-        {
-            gDeferredAlphaProgram.addPermutation("AMBIENT_KILL", "1");
-        }
+            shader->mFeatures.calculatesLighting = false;
+            shader->mFeatures.hasLighting = false;
+            shader->mFeatures.isAlphaLighting = true;
+            shader->mFeatures.disableTextureIndex = true; //hack to disable auto-setup of texture channels
+            shader->mFeatures.hasSrgb = true;
+            shader->mFeatures.encodesNormal = true;
+            shader->mFeatures.calculatesAtmospherics = true;
+            shader->mFeatures.hasAtmospherics = true;
+            shader->mFeatures.hasGamma = true;
+            shader->mFeatures.hasTransport = true;
+            shader->mFeatures.hasShadows = use_sun_shadow;
+
+            if (mShaderLevel[SHADER_DEFERRED] < 1)
+            {
+                shader->mFeatures.mIndexedTextureChannels = LLGLSLShader::sIndexedTextureChannels;
+            }
+            else
+            { //shave off some texture units for shadow maps
+                shader->mFeatures.mIndexedTextureChannels = llmax(LLGLSLShader::sIndexedTextureChannels - 6, 1);
+            }
 
-        if (sunlight_kill)
-        {
-            gDeferredAlphaProgram.addPermutation("SUNLIGHT_KILL", "1");
-        }
+            shader->mShaderFiles.clear();
+            shader->mShaderFiles.push_back(make_pair("deferred/alphaV.glsl", GL_VERTEX_SHADER_ARB));
+            shader->mShaderFiles.push_back(make_pair("deferred/alphaF.glsl", GL_FRAGMENT_SHADER_ARB));
 
-        if (local_light_kill)
-        {
-            gDeferredAlphaProgram.addPermutation("LOCAL_LIGHT_KILL", "1");
-        }
+            shader->clearPermutations();
+            shader->addPermutation("USE_VERTEX_COLOR", "1");
+            shader->addPermutation("USE_INDEXED_TEX", "1");
+            if (use_sun_shadow)
+            {
+                shader->addPermutation("HAS_SHADOW", "1");
+            }
 
-        gDeferredAlphaProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
+            if (ambient_kill)
+            {
+                shader->addPermutation("AMBIENT_KILL", "1");
+            }
 
-        success = gDeferredAlphaProgram.createShader(NULL, NULL);
-        llassert(success);
+            if (sunlight_kill)
+            {
+                shader->addPermutation("SUNLIGHT_KILL", "1");
+            }
 
-        // Hack
-        gDeferredAlphaProgram.mFeatures.calculatesLighting = true;
-        gDeferredAlphaProgram.mFeatures.hasLighting = true;
+            if (local_light_kill)
+            {
+                shader->addPermutation("LOCAL_LIGHT_KILL", "1");
+            }
+
+            if (rigged)
+            {
+                shader->addPermutation("HAS_SKIN", "1");
+            }
+
+            shader->mShaderLevel = mShaderLevel[SHADER_DEFERRED];
+
+            success = shader->createShader(NULL, NULL);
+            llassert(success);
+
+            // Hack
+            shader->mFeatures.calculatesLighting = true;
+            shader->mFeatures.hasLighting = true;
+        }
     }
 
     if (success)
     {
-        gDeferredAlphaImpostorProgram.mName = "Deferred Alpha Impostor Shader";
+        LLGLSLShader* shaders[] = { 
+            &gDeferredAlphaImpostorProgram, 
+            &gDeferredSkinnedAlphaImpostorProgram 
+        };
 
-// Begin Hack
-		gDeferredAlphaImpostorProgram.mFeatures.calculatesLighting = false;
-		gDeferredAlphaImpostorProgram.mFeatures.hasLighting = false;
+        for (int i = 0; i < 2 && success; ++i)
+        {
+            bool rigged = i == 1;
+            LLGLSLShader* shader = shaders[i];
 
-        gDeferredAlphaImpostorProgram.mFeatures.hasSrgb = true;
-		gDeferredAlphaImpostorProgram.mFeatures.isAlphaLighting = true;
-        gDeferredAlphaImpostorProgram.mFeatures.encodesNormal = true;
-        gDeferredAlphaImpostorProgram.mFeatures.hasShadows = use_sun_shadow;
+            shader->mName = rigged ? "Skinned Deferred Alpha Impostor Shader" : "Deferred Alpha Impostor Shader";
 
-        if (mShaderLevel[SHADER_DEFERRED] < 1)
-        {
-            gDeferredAlphaImpostorProgram.mFeatures.mIndexedTextureChannels = LLGLSLShader::sIndexedTextureChannels;
-        }
-        else
-        { //shave off some texture units for shadow maps
-            gDeferredAlphaImpostorProgram.mFeatures.mIndexedTextureChannels = llmax(LLGLSLShader::sIndexedTextureChannels - 6, 1);
-        }
+            // Begin Hack
+            shader->mFeatures.calculatesLighting = false;
+            shader->mFeatures.hasLighting = false;
 
-        gDeferredAlphaImpostorProgram.mShaderFiles.clear();
-        gDeferredAlphaImpostorProgram.mShaderFiles.push_back(make_pair("deferred/alphaV.glsl", GL_VERTEX_SHADER_ARB));
-        gDeferredAlphaImpostorProgram.mShaderFiles.push_back(make_pair("deferred/alphaF.glsl", GL_FRAGMENT_SHADER_ARB));
+            shader->mFeatures.hasSrgb = true;
+            shader->mFeatures.isAlphaLighting = true;
+            shader->mFeatures.encodesNormal = true;
+            shader->mFeatures.hasShadows = use_sun_shadow;
 
-        gDeferredAlphaImpostorProgram.clearPermutations();
-        gDeferredAlphaImpostorProgram.addPermutation("USE_INDEXED_TEX", "1");
-        gDeferredAlphaImpostorProgram.addPermutation("FOR_IMPOSTOR", "1");
-        gDeferredAlphaImpostorProgram.addPermutation("USE_VERTEX_COLOR", "1");
+            if (mShaderLevel[SHADER_DEFERRED] < 1)
+            {
+                shader->mFeatures.mIndexedTextureChannels = LLGLSLShader::sIndexedTextureChannels;
+            }
+            else
+            { //shave off some texture units for shadow maps
+                shader->mFeatures.mIndexedTextureChannels = llmax(LLGLSLShader::sIndexedTextureChannels - 6, 1);
+            }
 
-        if (use_sun_shadow)
-        {
-            gDeferredAlphaImpostorProgram.addPermutation("HAS_SHADOW", "1");
-        }
+            shader->mShaderFiles.clear();
+            shader->mShaderFiles.push_back(make_pair("deferred/alphaV.glsl", GL_VERTEX_SHADER_ARB));
+            shader->mShaderFiles.push_back(make_pair("deferred/alphaF.glsl", GL_FRAGMENT_SHADER_ARB));
+
+            shader->clearPermutations();
+            shader->addPermutation("USE_INDEXED_TEX", "1");
+            shader->addPermutation("FOR_IMPOSTOR", "1");
+            shader->addPermutation("USE_VERTEX_COLOR", "1");
+            if (rigged)
+            {
+                shader->mFeatures.hasObjectSkinning = true;
+                shader->addPermutation("HAS_SKIN", "1");
+            }
 
-        gDeferredAlphaImpostorProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
+            if (use_sun_shadow)
+            {
+                shader->addPermutation("HAS_SHADOW", "1");
+            }
 
-        success = gDeferredAlphaImpostorProgram.createShader(NULL, NULL);
-        llassert(success);
+            shader->mRiggedVariant = &gDeferredSkinnedAlphaImpostorProgram;
+            shader->mShaderLevel = mShaderLevel[SHADER_DEFERRED];
+            if (!rigged)
+            {
+                shader->mRiggedVariant = shaders[1];
+            }
+            success = shader->createShader(NULL, NULL);
+            llassert(success);
 
-// End Hack
-        gDeferredAlphaImpostorProgram.mFeatures.calculatesLighting = true;
-        gDeferredAlphaImpostorProgram.mFeatures.hasLighting = true;
+            // End Hack
+            shader->mFeatures.calculatesLighting = true;
+            shader->mFeatures.hasLighting = true;
+        }
     }
 
-	if (success)
-	{
-		gDeferredAlphaWaterProgram.mName = "Deferred Alpha Underwater Shader";
-		gDeferredAlphaWaterProgram.mFeatures.calculatesLighting = false;
-		gDeferredAlphaWaterProgram.mFeatures.hasLighting = false;
-		gDeferredAlphaWaterProgram.mFeatures.isAlphaLighting = true;
-		gDeferredAlphaWaterProgram.mFeatures.disableTextureIndex = true; //hack to disable auto-setup of texture channels
-		gDeferredAlphaWaterProgram.mFeatures.hasWaterFog = true;
-		gDeferredAlphaWaterProgram.mFeatures.hasSrgb = true;
-		gDeferredAlphaWaterProgram.mFeatures.encodesNormal = true;
-		gDeferredAlphaWaterProgram.mFeatures.calculatesAtmospherics = true;
-		gDeferredAlphaWaterProgram.mFeatures.hasAtmospherics = true;
-		gDeferredAlphaWaterProgram.mFeatures.hasGamma = true;
-		gDeferredAlphaWaterProgram.mFeatures.hasTransport = true;
-		gDeferredAlphaWaterProgram.mFeatures.hasShadows = use_sun_shadow;
-
-		if (mShaderLevel[SHADER_DEFERRED] < 1)
-		{
-			gDeferredAlphaWaterProgram.mFeatures.mIndexedTextureChannels = LLGLSLShader::sIndexedTextureChannels;
-		}
-		else
-		{ //shave off some texture units for shadow maps
-			gDeferredAlphaWaterProgram.mFeatures.mIndexedTextureChannels = llmax(LLGLSLShader::sIndexedTextureChannels - 6, 1);
-		}
-		gDeferredAlphaWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER;
-		gDeferredAlphaWaterProgram.mShaderFiles.clear();
-		gDeferredAlphaWaterProgram.mShaderFiles.push_back(make_pair("deferred/alphaV.glsl", GL_VERTEX_SHADER_ARB));
-		gDeferredAlphaWaterProgram.mShaderFiles.push_back(make_pair("deferred/alphaF.glsl", GL_FRAGMENT_SHADER_ARB));
-
-		gDeferredAlphaWaterProgram.clearPermutations();
-		gDeferredAlphaWaterProgram.addPermutation("USE_INDEXED_TEX", "1");
-		gDeferredAlphaWaterProgram.addPermutation("WATER_FOG", "1");
-        gDeferredAlphaWaterProgram.addPermutation("USE_VERTEX_COLOR", "1");
-		if (use_sun_shadow)
-		{
-			gDeferredAlphaWaterProgram.addPermutation("HAS_SHADOW", "1");
-		}
+    if (success)
+    {
+        LLGLSLShader* shader[] = {
+            &gDeferredAlphaWaterProgram,
+            &gDeferredSkinnedAlphaWaterProgram
+        };
+        
+        gDeferredAlphaWaterProgram.mRiggedVariant = &gDeferredSkinnedAlphaWaterProgram;
+		
+        gDeferredAlphaWaterProgram.mName = "Deferred Alpha Underwater Shader";
+        gDeferredSkinnedAlphaWaterProgram.mName = "Deferred Skinned Alpha Underwater Shader";
 
-        if (ambient_kill)
+        for (int i = 0; i < 2 && success; ++i)
         {
-            gDeferredAlphaWaterProgram.addPermutation("AMBIENT_KILL", "1");
-        }
+            shader[i]->mFeatures.calculatesLighting = false;
+            shader[i]->mFeatures.hasLighting = false;
+            shader[i]->mFeatures.isAlphaLighting = true;
+            shader[i]->mFeatures.disableTextureIndex = true; //hack to disable auto-setup of texture channels
+            shader[i]->mFeatures.hasWaterFog = true;
+            shader[i]->mFeatures.hasSrgb = true;
+            shader[i]->mFeatures.encodesNormal = true;
+            shader[i]->mFeatures.calculatesAtmospherics = true;
+            shader[i]->mFeatures.hasAtmospherics = true;
+            shader[i]->mFeatures.hasGamma = true;
+            shader[i]->mFeatures.hasTransport = true;
+            shader[i]->mFeatures.hasShadows = use_sun_shadow;
+
+            if (mShaderLevel[SHADER_DEFERRED] < 1)
+            {
+                shader[i]->mFeatures.mIndexedTextureChannels = LLGLSLShader::sIndexedTextureChannels;
+            }
+            else
+            { //shave off some texture units for shadow maps
+                shader[i]->mFeatures.mIndexedTextureChannels = llmax(LLGLSLShader::sIndexedTextureChannels - 6, 1);
+            }
+            shader[i]->mShaderGroup = LLGLSLShader::SG_WATER;
+            shader[i]->mShaderFiles.clear();
+            shader[i]->mShaderFiles.push_back(make_pair("deferred/alphaV.glsl", GL_VERTEX_SHADER_ARB));
+            shader[i]->mShaderFiles.push_back(make_pair("deferred/alphaF.glsl", GL_FRAGMENT_SHADER_ARB));
+
+            shader[i]->clearPermutations();
+            shader[i]->addPermutation("USE_INDEXED_TEX", "1");
+            shader[i]->addPermutation("WATER_FOG", "1");
+            shader[i]->addPermutation("USE_VERTEX_COLOR", "1");
+            if (use_sun_shadow)
+            {
+                shader[i]->addPermutation("HAS_SHADOW", "1");
+            }
 
-        if (sunlight_kill)
-        {
-            gDeferredAlphaWaterProgram.addPermutation("SUNLIGHT_KILL", "1");
-        }
+            if (ambient_kill)
+            {
+                shader[i]->addPermutation("AMBIENT_KILL", "1");
+            }
 
-        if (local_light_kill)
-        {
-            gDeferredAlphaWaterProgram.addPermutation("LOCAL_LIGHT_KILL", "1");
-        }
-        gDeferredAlphaWaterProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
+            if (sunlight_kill)
+            {
+                shader[i]->addPermutation("SUNLIGHT_KILL", "1");
+            }
 
-		success = gDeferredAlphaWaterProgram.createShader(NULL, NULL);
-		llassert(success);
+            if (local_light_kill)
+            {
+                shader[i]->addPermutation("LOCAL_LIGHT_KILL", "1");
+            }
 
-		// Hack
-		gDeferredAlphaWaterProgram.mFeatures.calculatesLighting = true;
-		gDeferredAlphaWaterProgram.mFeatures.hasLighting = true;
+            if (i == 1)
+            { // rigged variant
+                shader[i]->mFeatures.hasObjectSkinning = true;
+                shader[i]->addPermutation("HAS_SKIN", "1");
+            }
+            else
+            {
+                shader[i]->mRiggedVariant = shader[1];
+            }
+            shader[i]->mShaderLevel = mShaderLevel[SHADER_DEFERRED];
+
+            success = shader[i]->createShader(NULL, NULL);
+            llassert(success);
+
+            // Hack
+            shader[i]->mFeatures.calculatesLighting = true;
+            shader[i]->mFeatures.hasLighting = true;
+        }
 	}
 
 	if (success)
@@ -2082,6 +2116,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 		gDeferredFullbrightProgram.mShaderFiles.push_back(make_pair("deferred/fullbrightV.glsl", GL_VERTEX_SHADER_ARB));
 		gDeferredFullbrightProgram.mShaderFiles.push_back(make_pair("deferred/fullbrightF.glsl", GL_FRAGMENT_SHADER_ARB));
 		gDeferredFullbrightProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
+        success = make_rigged_variant(gDeferredFullbrightProgram, gDeferredSkinnedFullbrightProgram);
 		success = gDeferredFullbrightProgram.createShader(NULL, NULL);
 		llassert(success);
 	}
@@ -2099,7 +2134,8 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 		gDeferredFullbrightAlphaMaskProgram.mShaderFiles.push_back(make_pair("deferred/fullbrightF.glsl", GL_FRAGMENT_SHADER_ARB));
 		gDeferredFullbrightAlphaMaskProgram.addPermutation("HAS_ALPHA_MASK","1");
 		gDeferredFullbrightAlphaMaskProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
-		success = gDeferredFullbrightAlphaMaskProgram.createShader(NULL, NULL);
+        success = make_rigged_variant(gDeferredFullbrightAlphaMaskProgram, gDeferredSkinnedFullbrightAlphaMaskProgram);
+		success = success && gDeferredFullbrightAlphaMaskProgram.createShader(NULL, NULL);
 		llassert(success);
 	}
 
@@ -2118,10 +2154,11 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 		gDeferredFullbrightWaterProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
 		gDeferredFullbrightWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER;
 		gDeferredFullbrightWaterProgram.addPermutation("WATER_FOG","1");
-		success = gDeferredFullbrightWaterProgram.createShader(NULL, NULL);
+        success = make_rigged_variant(gDeferredFullbrightWaterProgram, gDeferredSkinnedFullbrightWaterProgram);
+		success = success && gDeferredFullbrightWaterProgram.createShader(NULL, NULL);
 		llassert(success);
 	}
-
+    
 	if (success)
 	{
 		gDeferredFullbrightAlphaMaskWaterProgram.mName = "Deferred Fullbright Underwater Alpha Masking Shader";
@@ -2138,7 +2175,8 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 		gDeferredFullbrightAlphaMaskWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER;
 		gDeferredFullbrightAlphaMaskWaterProgram.addPermutation("HAS_ALPHA_MASK","1");
 		gDeferredFullbrightAlphaMaskWaterProgram.addPermutation("WATER_FOG","1");
-		success = gDeferredFullbrightAlphaMaskWaterProgram.createShader(NULL, NULL);
+        success = make_rigged_variant(gDeferredFullbrightAlphaMaskWaterProgram, gDeferredSkinnedFullbrightAlphaMaskWaterProgram);
+		success = success && gDeferredFullbrightAlphaMaskWaterProgram.createShader(NULL, NULL);
 		llassert(success);
 	}
 
@@ -2155,43 +2193,8 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 		gDeferredFullbrightShinyProgram.mShaderFiles.push_back(make_pair("deferred/fullbrightShinyV.glsl", GL_VERTEX_SHADER_ARB));
 		gDeferredFullbrightShinyProgram.mShaderFiles.push_back(make_pair("deferred/fullbrightShinyF.glsl", GL_FRAGMENT_SHADER_ARB));
 		gDeferredFullbrightShinyProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
-		success = gDeferredFullbrightShinyProgram.createShader(NULL, NULL);
-		llassert(success);
-	}
-
-	if (success)
-	{
-		gDeferredSkinnedFullbrightProgram.mName = "Skinned Fullbright Shader";
-		gDeferredSkinnedFullbrightProgram.mFeatures.calculatesAtmospherics = true;
-		gDeferredSkinnedFullbrightProgram.mFeatures.hasAtmospherics = true;
-		gDeferredSkinnedFullbrightProgram.mFeatures.hasGamma = true;
-		gDeferredSkinnedFullbrightProgram.mFeatures.hasTransport = true;
-		gDeferredSkinnedFullbrightProgram.mFeatures.hasObjectSkinning = true;
-		gDeferredSkinnedFullbrightProgram.mFeatures.disableTextureIndex = true;
-		gDeferredSkinnedFullbrightProgram.mFeatures.hasSrgb = true;
-		gDeferredSkinnedFullbrightProgram.mShaderFiles.clear();
-		gDeferredSkinnedFullbrightProgram.mShaderFiles.push_back(make_pair("objects/fullbrightSkinnedV.glsl", GL_VERTEX_SHADER_ARB));
-		gDeferredSkinnedFullbrightProgram.mShaderFiles.push_back(make_pair("deferred/fullbrightF.glsl", GL_FRAGMENT_SHADER_ARB));
-		gDeferredSkinnedFullbrightProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-		success = gDeferredSkinnedFullbrightProgram.createShader(NULL, NULL);
-		llassert(success);
-	}
-
-	if (success)
-	{
-		gDeferredSkinnedFullbrightShinyProgram.mName = "Skinned Fullbright Shiny Shader";
-		gDeferredSkinnedFullbrightShinyProgram.mFeatures.calculatesAtmospherics = true;
-        gDeferredSkinnedFullbrightShinyProgram.mFeatures.hasAtmospherics = true;
-		gDeferredSkinnedFullbrightShinyProgram.mFeatures.hasGamma = true;
-		gDeferredSkinnedFullbrightShinyProgram.mFeatures.hasTransport = true;
-		gDeferredSkinnedFullbrightShinyProgram.mFeatures.hasObjectSkinning = true;
-		gDeferredSkinnedFullbrightShinyProgram.mFeatures.disableTextureIndex = true;
-        gDeferredSkinnedFullbrightShinyProgram.mFeatures.hasSrgb = true;
-		gDeferredSkinnedFullbrightShinyProgram.mShaderFiles.clear();
-		gDeferredSkinnedFullbrightShinyProgram.mShaderFiles.push_back(make_pair("objects/fullbrightShinySkinnedV.glsl", GL_VERTEX_SHADER_ARB));
-		gDeferredSkinnedFullbrightShinyProgram.mShaderFiles.push_back(make_pair("deferred/fullbrightShinyF.glsl", GL_FRAGMENT_SHADER_ARB));
-		gDeferredSkinnedFullbrightShinyProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-		success = gDeferredSkinnedFullbrightShinyProgram.createShader(NULL, NULL);
+        success = make_rigged_variant(gDeferredFullbrightShinyProgram, gDeferredSkinnedFullbrightShinyProgram);
+		success = success && gDeferredFullbrightShinyProgram.createShader(NULL, NULL);
 		llassert(success);
 	}
 
@@ -2206,7 +2209,8 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 		gDeferredEmissiveProgram.mShaderFiles.push_back(make_pair("deferred/emissiveV.glsl", GL_VERTEX_SHADER_ARB));
 		gDeferredEmissiveProgram.mShaderFiles.push_back(make_pair("deferred/emissiveF.glsl", GL_FRAGMENT_SHADER_ARB));
 		gDeferredEmissiveProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
-		success = gDeferredEmissiveProgram.createShader(NULL, NULL);
+        success = make_rigged_variant(gDeferredEmissiveProgram, gDeferredSkinnedEmissiveProgram);
+		success = success && gDeferredEmissiveProgram.createShader(NULL, NULL);
 		llassert(success);
 	}
 
@@ -2349,10 +2353,29 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 		{
 			gDeferredShadowProgram.addPermutation("DEPTH_CLAMP", "1");
 		}
+        gDeferredShadowProgram.mRiggedVariant = &gDeferredSkinnedShadowProgram;
 		success = gDeferredShadowProgram.createShader(NULL, NULL);
 		llassert(success);
 	}
 
+    if (success)
+    {
+        gDeferredSkinnedShadowProgram.mName = "Deferred Skinned Shadow Shader";
+        gDeferredSkinnedShadowProgram.mFeatures.isDeferred = true;
+        gDeferredSkinnedShadowProgram.mFeatures.hasShadows = true;
+        gDeferredSkinnedShadowProgram.mFeatures.hasObjectSkinning = true;
+        gDeferredSkinnedShadowProgram.mShaderFiles.clear();
+        gDeferredSkinnedShadowProgram.mShaderFiles.push_back(make_pair("deferred/shadowSkinnedV.glsl", GL_VERTEX_SHADER_ARB));
+        gDeferredSkinnedShadowProgram.mShaderFiles.push_back(make_pair("deferred/shadowF.glsl", GL_FRAGMENT_SHADER_ARB));
+        gDeferredSkinnedShadowProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
+        if (gGLManager.mHasDepthClamp)
+        {
+            gDeferredSkinnedShadowProgram.addPermutation("DEPTH_CLAMP", "1");
+        }
+        success = gDeferredSkinnedShadowProgram.createShader(NULL, NULL);
+        llassert(success);
+    }
+
 	if (success)
 	{
 		gDeferredShadowCubeProgram.mName = "Deferred Shadow Cube Shader";
@@ -2386,10 +2409,31 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 		}
         gDeferredShadowFullbrightAlphaMaskProgram.addPermutation("IS_FULLBRIGHT", "1");
 		gDeferredShadowFullbrightAlphaMaskProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
+        gDeferredShadowFullbrightAlphaMaskProgram.mRiggedVariant = &gDeferredSkinnedShadowFullbrightAlphaMaskProgram;
 		success = gDeferredShadowFullbrightAlphaMaskProgram.createShader(NULL, NULL);
 		llassert(success);
 	}
     
+    if (success)
+    {
+        gDeferredSkinnedShadowFullbrightAlphaMaskProgram.mName = "Deferred Skinned Shadow Fullbright Alpha Mask Shader";
+        gDeferredSkinnedShadowFullbrightAlphaMaskProgram.mFeatures.mIndexedTextureChannels = LLGLSLShader::sIndexedTextureChannels;
+        gDeferredSkinnedShadowFullbrightAlphaMaskProgram.mFeatures.hasObjectSkinning = true;
+        gDeferredSkinnedShadowFullbrightAlphaMaskProgram.mShaderFiles.clear();
+        gDeferredSkinnedShadowFullbrightAlphaMaskProgram.mShaderFiles.push_back(make_pair("deferred/shadowAlphaMaskSkinnedV.glsl", GL_VERTEX_SHADER_ARB));
+        gDeferredSkinnedShadowFullbrightAlphaMaskProgram.mShaderFiles.push_back(make_pair("deferred/shadowAlphaMaskF.glsl", GL_FRAGMENT_SHADER_ARB));
+
+        gDeferredSkinnedShadowFullbrightAlphaMaskProgram.clearPermutations();
+        if (gGLManager.mHasDepthClamp)
+        {
+            gDeferredSkinnedShadowFullbrightAlphaMaskProgram.addPermutation("DEPTH_CLAMP", "1");
+        }
+        gDeferredSkinnedShadowFullbrightAlphaMaskProgram.addPermutation("IS_FULLBRIGHT", "1");
+        gDeferredSkinnedShadowFullbrightAlphaMaskProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
+        success = gDeferredSkinnedShadowFullbrightAlphaMaskProgram.createShader(NULL, NULL);
+        llassert(success);
+    }
+
     if (success)
 	{
 		gDeferredShadowAlphaMaskProgram.mName = "Deferred Shadow Alpha Mask Shader";
@@ -2403,10 +2447,28 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 			gDeferredShadowAlphaMaskProgram.addPermutation("DEPTH_CLAMP", "1");
 		}
 		gDeferredShadowAlphaMaskProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
+        gDeferredShadowAlphaMaskProgram.mRiggedVariant = &gDeferredSkinnedShadowAlphaMaskProgram;
 		success = gDeferredShadowAlphaMaskProgram.createShader(NULL, NULL);
 		llassert(success);
 	}
 
+    if (success)
+    {
+        gDeferredSkinnedShadowAlphaMaskProgram.mName = "Deferred Skinned Shadow Alpha Mask Shader";
+        gDeferredSkinnedShadowAlphaMaskProgram.mFeatures.mIndexedTextureChannels = LLGLSLShader::sIndexedTextureChannels;
+        gDeferredSkinnedShadowAlphaMaskProgram.mFeatures.hasObjectSkinning = true;
+        gDeferredSkinnedShadowAlphaMaskProgram.mShaderFiles.clear();
+        gDeferredSkinnedShadowAlphaMaskProgram.mShaderFiles.push_back(make_pair("deferred/shadowAlphaMaskSkinnedV.glsl", GL_VERTEX_SHADER_ARB));
+        gDeferredSkinnedShadowAlphaMaskProgram.mShaderFiles.push_back(make_pair("deferred/shadowAlphaMaskF.glsl", GL_FRAGMENT_SHADER_ARB));
+        if (gGLManager.mHasDepthClamp)
+        {
+            gDeferredSkinnedShadowAlphaMaskProgram.addPermutation("DEPTH_CLAMP", "1");
+        }
+        gDeferredSkinnedShadowAlphaMaskProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
+        success = gDeferredSkinnedShadowAlphaMaskProgram.createShader(NULL, NULL);
+        llassert(success);
+    }
+
 	if (success)
 	{
 		gDeferredAvatarShadowProgram.mName = "Deferred Avatar Shadow Shader";
@@ -2782,77 +2844,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 BOOL LLViewerShaderMgr::loadShadersObject()
 {
 	BOOL success = TRUE;
-	
-	if (mShaderLevel[SHADER_OBJECT] == 0)
-	{
-		gObjectShinyProgram.unload();
-		gObjectFullbrightShinyProgram.unload();
-		gObjectFullbrightShinyWaterProgram.unload();
-		gObjectShinyWaterProgram.unload();
-		gObjectFullbrightNoColorProgram.unload();
-		gObjectFullbrightNoColorWaterProgram.unload();
-		gObjectSimpleProgram.unload();
-		gObjectSimpleImpostorProgram.unload();
-		gObjectPreviewProgram.unload();
-		gImpostorProgram.unload();
-		gObjectSimpleAlphaMaskProgram.unload();
-		gObjectBumpProgram.unload();
-		gObjectSimpleWaterProgram.unload();
-		gObjectSimpleWaterAlphaMaskProgram.unload();
-		gObjectEmissiveProgram.unload();
-		gObjectEmissiveWaterProgram.unload();
-		gObjectFullbrightProgram.unload();
-		gObjectFullbrightAlphaMaskProgram.unload();
-		gObjectFullbrightWaterProgram.unload();
-		gObjectFullbrightWaterAlphaMaskProgram.unload();
-		gObjectShinyNonIndexedProgram.unload();
-		gObjectFullbrightShinyNonIndexedProgram.unload();
-		gObjectFullbrightShinyNonIndexedWaterProgram.unload();
-		gObjectShinyNonIndexedWaterProgram.unload();
-		gObjectSimpleNonIndexedTexGenProgram.unload();
-		gObjectSimpleNonIndexedTexGenWaterProgram.unload();
-		gObjectSimpleNonIndexedWaterProgram.unload();
-		gObjectAlphaMaskNonIndexedProgram.unload();
-		gObjectAlphaMaskNonIndexedWaterProgram.unload();
-		gObjectAlphaMaskNoColorProgram.unload();
-		gObjectAlphaMaskNoColorWaterProgram.unload();
-		gObjectFullbrightNonIndexedProgram.unload();
-		gObjectFullbrightNonIndexedWaterProgram.unload();
-		gObjectEmissiveNonIndexedProgram.unload();
-		gObjectEmissiveNonIndexedWaterProgram.unload();
-		gSkinnedObjectSimpleProgram.unload();
-		gSkinnedObjectFullbrightProgram.unload();
-		gSkinnedObjectEmissiveProgram.unload();
-		gSkinnedObjectFullbrightShinyProgram.unload();
-		gSkinnedObjectShinySimpleProgram.unload();
-		gSkinnedObjectSimpleWaterProgram.unload();
-		gSkinnedObjectFullbrightWaterProgram.unload();
-		gSkinnedObjectEmissiveWaterProgram.unload();
-		gSkinnedObjectFullbrightShinyWaterProgram.unload();
-		gSkinnedObjectShinySimpleWaterProgram.unload();
-		gTreeProgram.unload();
-		gTreeWaterProgram.unload();
-	
-		return TRUE;
-	}
 
-	if (success)
-	{
-		gObjectSimpleNonIndexedProgram.mName = "Non indexed Shader";
-		gObjectSimpleNonIndexedProgram.mFeatures.calculatesLighting = true;
-		gObjectSimpleNonIndexedProgram.mFeatures.calculatesAtmospherics = true;
-		gObjectSimpleNonIndexedProgram.mFeatures.hasGamma = true;
-		gObjectSimpleNonIndexedProgram.mFeatures.hasAtmospherics = true;
-		gObjectSimpleNonIndexedProgram.mFeatures.hasLighting = true;
-        gObjectSimpleNonIndexedProgram.mFeatures.hasAlphaMask = true; // Fix for MAINT-8836
-		gObjectSimpleNonIndexedProgram.mFeatures.disableTextureIndex = true;
-		gObjectSimpleNonIndexedProgram.mShaderFiles.clear();
-		gObjectSimpleNonIndexedProgram.mShaderFiles.push_back(make_pair("objects/simpleV.glsl", GL_VERTEX_SHADER_ARB));
-		gObjectSimpleNonIndexedProgram.mShaderFiles.push_back(make_pair("objects/simpleF.glsl", GL_FRAGMENT_SHADER_ARB));
-		gObjectSimpleNonIndexedProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-		success = gObjectSimpleNonIndexedProgram.createShader(NULL, NULL);
-	}
-	
 	if (success)
 	{
 		gObjectSimpleNonIndexedTexGenProgram.mName = "Non indexed tex-gen Shader";
@@ -2869,24 +2861,6 @@ BOOL LLViewerShaderMgr::loadShadersObject()
 		success = gObjectSimpleNonIndexedTexGenProgram.createShader(NULL, NULL);
 	}
 	
-
-	if (success)
-	{
-		gObjectSimpleNonIndexedWaterProgram.mName = "Non indexed Water Shader";
-		gObjectSimpleNonIndexedWaterProgram.mFeatures.calculatesLighting = true;
-		gObjectSimpleNonIndexedWaterProgram.mFeatures.calculatesAtmospherics = true;
-		gObjectSimpleNonIndexedWaterProgram.mFeatures.hasWaterFog = true;
-		gObjectSimpleNonIndexedWaterProgram.mFeatures.hasAtmospherics = true;
-		gObjectSimpleNonIndexedWaterProgram.mFeatures.hasLighting = true;
-		gObjectSimpleNonIndexedWaterProgram.mFeatures.disableTextureIndex = true;
-		gObjectSimpleNonIndexedWaterProgram.mShaderFiles.clear();
-		gObjectSimpleNonIndexedWaterProgram.mShaderFiles.push_back(make_pair("objects/simpleV.glsl", GL_VERTEX_SHADER_ARB));
-		gObjectSimpleNonIndexedWaterProgram.mShaderFiles.push_back(make_pair("objects/simpleWaterF.glsl", GL_FRAGMENT_SHADER_ARB));
-		gObjectSimpleNonIndexedWaterProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-		gObjectSimpleNonIndexedWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER;
-		success = gObjectSimpleNonIndexedWaterProgram.createShader(NULL, NULL);
-	}
-
 	if (success)
 	{
 		gObjectSimpleNonIndexedTexGenWaterProgram.mName = "Non indexed tex-gen Water Shader";
@@ -3009,70 +2983,6 @@ BOOL LLViewerShaderMgr::loadShadersObject()
 		success = gTreeWaterProgram.createShader(NULL, NULL);
 	}
 
-	if (success)
-	{
-		gObjectFullbrightNonIndexedProgram.mName = "Non Indexed Fullbright Shader";
-		gObjectFullbrightNonIndexedProgram.mFeatures.calculatesAtmospherics = true;
-		gObjectFullbrightNonIndexedProgram.mFeatures.hasGamma = true;
-		gObjectFullbrightNonIndexedProgram.mFeatures.hasTransport = true;
-		gObjectFullbrightNonIndexedProgram.mFeatures.isFullbright = true;
-		gObjectFullbrightNonIndexedProgram.mFeatures.disableTextureIndex = true;
-		gObjectFullbrightNonIndexedProgram.mShaderFiles.clear();
-		gObjectFullbrightNonIndexedProgram.mShaderFiles.push_back(make_pair("objects/fullbrightV.glsl", GL_VERTEX_SHADER_ARB));
-		gObjectFullbrightNonIndexedProgram.mShaderFiles.push_back(make_pair("objects/fullbrightF.glsl", GL_FRAGMENT_SHADER_ARB));
-		gObjectFullbrightNonIndexedProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-		success = gObjectFullbrightNonIndexedProgram.createShader(NULL, NULL);
-	}
-
-	if (success)
-	{
-		gObjectFullbrightNonIndexedWaterProgram.mName = "Non Indexed Fullbright Water Shader";
-		gObjectFullbrightNonIndexedWaterProgram.mFeatures.calculatesAtmospherics = true;
-		gObjectFullbrightNonIndexedWaterProgram.mFeatures.isFullbright = true;
-		gObjectFullbrightNonIndexedWaterProgram.mFeatures.hasWaterFog = true;		
-		gObjectFullbrightNonIndexedWaterProgram.mFeatures.hasTransport = true;
-		gObjectFullbrightNonIndexedWaterProgram.mFeatures.disableTextureIndex = true;
-		gObjectFullbrightNonIndexedWaterProgram.mFeatures.hasSrgb = true;
-		gObjectFullbrightNonIndexedWaterProgram.mShaderFiles.clear();
-		gObjectFullbrightNonIndexedWaterProgram.mShaderFiles.push_back(make_pair("objects/fullbrightV.glsl", GL_VERTEX_SHADER_ARB));
-		gObjectFullbrightNonIndexedWaterProgram.mShaderFiles.push_back(make_pair("objects/fullbrightWaterF.glsl", GL_FRAGMENT_SHADER_ARB));
-		gObjectFullbrightNonIndexedWaterProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-		gObjectFullbrightNonIndexedWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER;
-		success = gObjectFullbrightNonIndexedWaterProgram.createShader(NULL, NULL);
-	}
-
-	if (success)
-	{
-		gObjectEmissiveNonIndexedProgram.mName = "Non Indexed Emissive Shader";
-		gObjectEmissiveNonIndexedProgram.mFeatures.calculatesAtmospherics = true;
-		gObjectEmissiveNonIndexedProgram.mFeatures.hasGamma = true;
-		gObjectEmissiveNonIndexedProgram.mFeatures.hasTransport = true;
-		gObjectEmissiveNonIndexedProgram.mFeatures.isFullbright = true;
-		gObjectEmissiveNonIndexedProgram.mFeatures.disableTextureIndex = true;
-		gObjectEmissiveNonIndexedProgram.mFeatures.hasSrgb = true;
-		gObjectEmissiveNonIndexedProgram.mShaderFiles.clear();
-		gObjectEmissiveNonIndexedProgram.mShaderFiles.push_back(make_pair("objects/emissiveV.glsl", GL_VERTEX_SHADER_ARB));
-		gObjectEmissiveNonIndexedProgram.mShaderFiles.push_back(make_pair("objects/fullbrightF.glsl", GL_FRAGMENT_SHADER_ARB));
-		gObjectEmissiveNonIndexedProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-		success = gObjectEmissiveNonIndexedProgram.createShader(NULL, NULL);
-	}
-
-	if (success)
-	{
-		gObjectEmissiveNonIndexedWaterProgram.mName = "Non Indexed Emissive Water Shader";
-		gObjectEmissiveNonIndexedWaterProgram.mFeatures.calculatesAtmospherics = true;
-		gObjectEmissiveNonIndexedWaterProgram.mFeatures.isFullbright = true;
-		gObjectEmissiveNonIndexedWaterProgram.mFeatures.hasWaterFog = true;		
-		gObjectEmissiveNonIndexedWaterProgram.mFeatures.hasTransport = true;
-		gObjectEmissiveNonIndexedWaterProgram.mFeatures.disableTextureIndex = true;
-		gObjectEmissiveNonIndexedWaterProgram.mShaderFiles.clear();
-		gObjectEmissiveNonIndexedWaterProgram.mShaderFiles.push_back(make_pair("objects/emissiveV.glsl", GL_VERTEX_SHADER_ARB));
-		gObjectEmissiveNonIndexedWaterProgram.mShaderFiles.push_back(make_pair("objects/fullbrightWaterF.glsl", GL_FRAGMENT_SHADER_ARB));
-		gObjectEmissiveNonIndexedWaterProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-		gObjectEmissiveNonIndexedWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER;
-		success = gObjectEmissiveNonIndexedWaterProgram.createShader(NULL, NULL);
-	}
-
 	if (success)
 	{
 		gObjectFullbrightNoColorProgram.mName = "Non Indexed no color Fullbright Shader";
@@ -3105,73 +3015,6 @@ BOOL LLViewerShaderMgr::loadShadersObject()
 		success = gObjectFullbrightNoColorWaterProgram.createShader(NULL, NULL);
 	}
 
-	if (success)
-	{
-		gObjectShinyNonIndexedProgram.mName = "Non Indexed Shiny Shader";
-		gObjectShinyNonIndexedProgram.mFeatures.calculatesAtmospherics = true;
-		gObjectShinyNonIndexedProgram.mFeatures.calculatesLighting = true;
-		gObjectShinyNonIndexedProgram.mFeatures.hasGamma = true;
-		gObjectShinyNonIndexedProgram.mFeatures.hasAtmospherics = true;
-		gObjectShinyNonIndexedProgram.mFeatures.isShiny = true;
-		gObjectShinyNonIndexedProgram.mFeatures.disableTextureIndex = true;
-		gObjectShinyNonIndexedProgram.mShaderFiles.clear();
-		gObjectShinyNonIndexedProgram.mShaderFiles.push_back(make_pair("objects/shinyV.glsl", GL_VERTEX_SHADER_ARB));
-		gObjectShinyNonIndexedProgram.mShaderFiles.push_back(make_pair("objects/shinyF.glsl", GL_FRAGMENT_SHADER_ARB));		
-		gObjectShinyNonIndexedProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-		success = gObjectShinyNonIndexedProgram.createShader(NULL, NULL);
-	}
-
-	if (success)
-	{
-		gObjectShinyNonIndexedWaterProgram.mName = "Non Indexed Shiny Water Shader";
-		gObjectShinyNonIndexedWaterProgram.mFeatures.calculatesAtmospherics = true;
-		gObjectShinyNonIndexedWaterProgram.mFeatures.calculatesLighting = true;
-		gObjectShinyNonIndexedWaterProgram.mFeatures.isShiny = true;
-		gObjectShinyNonIndexedWaterProgram.mFeatures.hasWaterFog = true;
-		gObjectShinyNonIndexedWaterProgram.mFeatures.hasAtmospherics = true;
-		gObjectShinyNonIndexedWaterProgram.mFeatures.disableTextureIndex = true;
-		gObjectShinyNonIndexedWaterProgram.mShaderFiles.clear();
-		gObjectShinyNonIndexedWaterProgram.mShaderFiles.push_back(make_pair("objects/shinyWaterF.glsl", GL_FRAGMENT_SHADER_ARB));
-		gObjectShinyNonIndexedWaterProgram.mShaderFiles.push_back(make_pair("objects/shinyV.glsl", GL_VERTEX_SHADER_ARB));
-		gObjectShinyNonIndexedWaterProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-		gObjectShinyNonIndexedWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER;
-		success = gObjectShinyNonIndexedWaterProgram.createShader(NULL, NULL);
-	}
-	
-    if (success)
-	{
-		gObjectFullbrightShinyNonIndexedProgram.mName = "Non Indexed Fullbright Shiny Shader";
-		gObjectFullbrightShinyNonIndexedProgram.mFeatures.calculatesAtmospherics = true;
-		gObjectFullbrightShinyNonIndexedProgram.mFeatures.isFullbright = true;
-		gObjectFullbrightShinyNonIndexedProgram.mFeatures.isShiny = true;
-		gObjectFullbrightShinyNonIndexedProgram.mFeatures.hasGamma = true;
-		gObjectFullbrightShinyNonIndexedProgram.mFeatures.hasTransport = true;
-		gObjectFullbrightShinyNonIndexedProgram.mFeatures.disableTextureIndex = true;
-		gObjectFullbrightShinyNonIndexedProgram.mShaderFiles.clear();
-		gObjectFullbrightShinyNonIndexedProgram.mShaderFiles.push_back(make_pair("objects/fullbrightShinyV.glsl", GL_VERTEX_SHADER_ARB));
-		gObjectFullbrightShinyNonIndexedProgram.mShaderFiles.push_back(make_pair("objects/fullbrightShinyF.glsl", GL_FRAGMENT_SHADER_ARB));
-		gObjectFullbrightShinyNonIndexedProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-		success = gObjectFullbrightShinyNonIndexedProgram.createShader(NULL, NULL);
-	}
-
-	if (success)
-	{
-		gObjectFullbrightShinyNonIndexedWaterProgram.mName = "Non Indexed Fullbright Shiny Water Shader";
-		gObjectFullbrightShinyNonIndexedWaterProgram.mFeatures.calculatesAtmospherics = true;
-		gObjectFullbrightShinyNonIndexedWaterProgram.mFeatures.isFullbright = true;
-		gObjectFullbrightShinyNonIndexedWaterProgram.mFeatures.isShiny = true;
-		gObjectFullbrightShinyNonIndexedWaterProgram.mFeatures.hasGamma = true;
-		gObjectFullbrightShinyNonIndexedWaterProgram.mFeatures.hasTransport = true;
-		gObjectFullbrightShinyNonIndexedWaterProgram.mFeatures.hasWaterFog = true;
-		gObjectFullbrightShinyNonIndexedWaterProgram.mFeatures.disableTextureIndex = true;
-		gObjectFullbrightShinyNonIndexedWaterProgram.mShaderFiles.clear();
-		gObjectFullbrightShinyNonIndexedWaterProgram.mShaderFiles.push_back(make_pair("objects/fullbrightShinyV.glsl", GL_VERTEX_SHADER_ARB));
-		gObjectFullbrightShinyNonIndexedWaterProgram.mShaderFiles.push_back(make_pair("objects/fullbrightShinyWaterF.glsl", GL_FRAGMENT_SHADER_ARB));
-		gObjectFullbrightShinyNonIndexedWaterProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-		gObjectFullbrightShinyNonIndexedWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER;
-		success = gObjectFullbrightShinyNonIndexedWaterProgram.createShader(NULL, NULL);
-	}
-
 	if (success)
 	{
 		gImpostorProgram.mName = "Impostor Shader";
@@ -3215,7 +3058,8 @@ BOOL LLViewerShaderMgr::loadShadersObject()
 		gObjectSimpleProgram.mShaderFiles.push_back(make_pair("objects/simpleV.glsl", GL_VERTEX_SHADER_ARB));
 		gObjectSimpleProgram.mShaderFiles.push_back(make_pair("objects/simpleF.glsl", GL_FRAGMENT_SHADER_ARB));
 		gObjectSimpleProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-		success = gObjectSimpleProgram.createShader(NULL, NULL);
+        success = make_rigged_variant(gObjectSimpleProgram, gSkinnedObjectSimpleProgram);
+		success = success && gObjectSimpleProgram.createShader(NULL, NULL);
 	}
 
 	if (success)
@@ -3235,8 +3079,8 @@ BOOL LLViewerShaderMgr::loadShadersObject()
 		gObjectSimpleImpostorProgram.mShaderFiles.push_back(make_pair("objects/simpleV.glsl", GL_VERTEX_SHADER_ARB));
 		gObjectSimpleImpostorProgram.mShaderFiles.push_back(make_pair("objects/simpleF.glsl", GL_FRAGMENT_SHADER_ARB));
 		gObjectSimpleImpostorProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-		
-		success = gObjectSimpleImpostorProgram.createShader(NULL, NULL);
+        success = make_rigged_variant(gObjectSimpleImpostorProgram, gSkinnedObjectSimpleImpostorProgram);
+		success = success && gObjectSimpleImpostorProgram.createShader(NULL, NULL);
 	}
 
 	if (success)
@@ -3253,30 +3097,30 @@ BOOL LLViewerShaderMgr::loadShadersObject()
 		gObjectSimpleWaterProgram.mShaderFiles.push_back(make_pair("objects/simpleWaterF.glsl", GL_FRAGMENT_SHADER_ARB));
 		gObjectSimpleWaterProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
 		gObjectSimpleWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER;
+        make_rigged_variant(gObjectSimpleWaterProgram, gSkinnedObjectSimpleWaterProgram);
 		success = gObjectSimpleWaterProgram.createShader(NULL, NULL);
 	}
 	
 	if (success)
 	{
 		gObjectBumpProgram.mName = "Bump Shader";
-		/*gObjectBumpProgram.mFeatures.calculatesLighting = true;
-		gObjectBumpProgram.mFeatures.calculatesAtmospherics = true;
-		gObjectBumpProgram.mFeatures.hasGamma = true;
-		gObjectBumpProgram.mFeatures.hasAtmospherics = true;
-		gObjectBumpProgram.mFeatures.hasLighting = true;
-		gObjectBumpProgram.mFeatures.mIndexedTextureChannels = 0;*/
 		gObjectBumpProgram.mFeatures.encodesNormal = true;
 		gObjectBumpProgram.mShaderFiles.clear();
 		gObjectBumpProgram.mShaderFiles.push_back(make_pair("objects/bumpV.glsl", GL_VERTEX_SHADER_ARB));
 		gObjectBumpProgram.mShaderFiles.push_back(make_pair("objects/bumpF.glsl", GL_FRAGMENT_SHADER_ARB));
 		gObjectBumpProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-		success = gObjectBumpProgram.createShader(NULL, NULL);
+        success = make_rigged_variant(gObjectBumpProgram, gSkinnedObjectBumpProgram);
+		success = success && gObjectBumpProgram.createShader(NULL, NULL);
 		if (success)
 		{ //lldrawpoolbump assumes "texture0" has channel 0 and "texture1" has channel 1
-			gObjectBumpProgram.bind();
-			gObjectBumpProgram.uniform1i(sTexture0, 0);
-			gObjectBumpProgram.uniform1i(sTexture1, 1);
-			gObjectBumpProgram.unbind();
+            LLGLSLShader* shader[] = { &gObjectBumpProgram, &gSkinnedObjectBumpProgram };
+            for (int i = 0; i < 2; ++i)
+            {
+                shader[i]->bind();
+                shader[i]->uniform1i(sTexture0, 0);
+                shader[i]->uniform1i(sTexture1, 1);
+                shader[i]->unbind();
+            }
 		}
 	}
 	
@@ -3295,7 +3139,8 @@ BOOL LLViewerShaderMgr::loadShadersObject()
 		gObjectSimpleAlphaMaskProgram.mShaderFiles.push_back(make_pair("objects/simpleV.glsl", GL_VERTEX_SHADER_ARB));
 		gObjectSimpleAlphaMaskProgram.mShaderFiles.push_back(make_pair("objects/simpleF.glsl", GL_FRAGMENT_SHADER_ARB));
 		gObjectSimpleAlphaMaskProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-		success = gObjectSimpleAlphaMaskProgram.createShader(NULL, NULL);
+        success = make_rigged_variant(gObjectSimpleAlphaMaskProgram, gSkinnedObjectSimpleAlphaMaskProgram);
+		success = success && gObjectSimpleAlphaMaskProgram.createShader(NULL, NULL);
 	}
 
 	if (success)
@@ -3313,7 +3158,8 @@ BOOL LLViewerShaderMgr::loadShadersObject()
 		gObjectSimpleWaterAlphaMaskProgram.mShaderFiles.push_back(make_pair("objects/simpleWaterF.glsl", GL_FRAGMENT_SHADER_ARB));
 		gObjectSimpleWaterAlphaMaskProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
 		gObjectSimpleWaterAlphaMaskProgram.mShaderGroup = LLGLSLShader::SG_WATER;
-		success = gObjectSimpleWaterAlphaMaskProgram.createShader(NULL, NULL);
+        success = make_rigged_variant(gObjectSimpleWaterAlphaMaskProgram, gSkinnedObjectSimpleWaterAlphaMaskProgram);
+		success = success && gObjectSimpleWaterAlphaMaskProgram.createShader(NULL, NULL);
 	}
 
 	if (success)
@@ -3329,7 +3175,8 @@ BOOL LLViewerShaderMgr::loadShadersObject()
 		gObjectFullbrightProgram.mShaderFiles.push_back(make_pair("objects/fullbrightV.glsl", GL_VERTEX_SHADER_ARB));
 		gObjectFullbrightProgram.mShaderFiles.push_back(make_pair("objects/fullbrightF.glsl", GL_FRAGMENT_SHADER_ARB));
 		gObjectFullbrightProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-		success = gObjectFullbrightProgram.createShader(NULL, NULL);
+        success = make_rigged_variant(gObjectFullbrightProgram, gSkinnedObjectFullbrightProgram);
+        success = success && gObjectFullbrightProgram.createShader(NULL, NULL);
 	}
 
 	if (success)
@@ -3337,7 +3184,7 @@ BOOL LLViewerShaderMgr::loadShadersObject()
 		gObjectFullbrightWaterProgram.mName = "Fullbright Water Shader";
 		gObjectFullbrightWaterProgram.mFeatures.calculatesAtmospherics = true;
 		gObjectFullbrightWaterProgram.mFeatures.isFullbright = true;
-		gObjectFullbrightWaterProgram.mFeatures.hasWaterFog = true;		
+		gObjectFullbrightWaterProgram.mFeatures.hasWaterFog = true;
 		gObjectFullbrightWaterProgram.mFeatures.hasTransport = true;
 		gObjectFullbrightWaterProgram.mFeatures.mIndexedTextureChannels = 0;
 		gObjectFullbrightWaterProgram.mShaderFiles.clear();
@@ -3345,7 +3192,8 @@ BOOL LLViewerShaderMgr::loadShadersObject()
 		gObjectFullbrightWaterProgram.mShaderFiles.push_back(make_pair("objects/fullbrightWaterF.glsl", GL_FRAGMENT_SHADER_ARB));
 		gObjectFullbrightWaterProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
 		gObjectFullbrightWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER;
-		success = gObjectFullbrightWaterProgram.createShader(NULL, NULL);
+        success = make_rigged_variant(gObjectFullbrightWaterProgram, gSkinnedObjectFullbrightWaterProgram);
+		success = success && gObjectFullbrightWaterProgram.createShader(NULL, NULL);
 	}
 
 	if (success)
@@ -3361,7 +3209,8 @@ BOOL LLViewerShaderMgr::loadShadersObject()
 		gObjectEmissiveProgram.mShaderFiles.push_back(make_pair("objects/emissiveV.glsl", GL_VERTEX_SHADER_ARB));
 		gObjectEmissiveProgram.mShaderFiles.push_back(make_pair("objects/fullbrightF.glsl", GL_FRAGMENT_SHADER_ARB));
 		gObjectEmissiveProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-		success = gObjectEmissiveProgram.createShader(NULL, NULL);
+        success = make_rigged_variant(gObjectEmissiveProgram, gSkinnedObjectEmissiveProgram);
+		success = success && gObjectEmissiveProgram.createShader(NULL, NULL);
 	}
 
 	if (success)
@@ -3377,7 +3226,8 @@ BOOL LLViewerShaderMgr::loadShadersObject()
 		gObjectEmissiveWaterProgram.mShaderFiles.push_back(make_pair("objects/fullbrightWaterF.glsl", GL_FRAGMENT_SHADER_ARB));
 		gObjectEmissiveWaterProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
 		gObjectEmissiveWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER;
-		success = gObjectEmissiveWaterProgram.createShader(NULL, NULL);
+        success = make_rigged_variant(gObjectEmissiveWaterProgram, gSkinnedObjectEmissiveWaterProgram);
+		success = success && gObjectEmissiveWaterProgram.createShader(NULL, NULL);
 	}
 
 	if (success)
@@ -3394,12 +3244,13 @@ BOOL LLViewerShaderMgr::loadShadersObject()
 		gObjectFullbrightAlphaMaskProgram.mShaderFiles.push_back(make_pair("objects/fullbrightV.glsl", GL_VERTEX_SHADER_ARB));
 		gObjectFullbrightAlphaMaskProgram.mShaderFiles.push_back(make_pair("objects/fullbrightF.glsl", GL_FRAGMENT_SHADER_ARB));
 		gObjectFullbrightAlphaMaskProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-		success = gObjectFullbrightAlphaMaskProgram.createShader(NULL, NULL);
+        success = make_rigged_variant(gObjectFullbrightAlphaMaskProgram, gSkinnedObjectFullbrightAlphaMaskProgram);
+		success = success && gObjectFullbrightAlphaMaskProgram.createShader(NULL, NULL);
 	}
 
 	if (success)
 	{
-		gObjectFullbrightWaterAlphaMaskProgram.mName = "Fullbright Water Shader";
+		gObjectFullbrightWaterAlphaMaskProgram.mName = "Fullbright Water Alpha Mask Shader";
 		gObjectFullbrightWaterAlphaMaskProgram.mFeatures.calculatesAtmospherics = true;
 		gObjectFullbrightWaterAlphaMaskProgram.mFeatures.isFullbright = true;
 		gObjectFullbrightWaterAlphaMaskProgram.mFeatures.hasWaterFog = true;		
@@ -3411,7 +3262,8 @@ BOOL LLViewerShaderMgr::loadShadersObject()
 		gObjectFullbrightWaterAlphaMaskProgram.mShaderFiles.push_back(make_pair("objects/fullbrightWaterF.glsl", GL_FRAGMENT_SHADER_ARB));
 		gObjectFullbrightWaterAlphaMaskProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
 		gObjectFullbrightWaterAlphaMaskProgram.mShaderGroup = LLGLSLShader::SG_WATER;
-		success = gObjectFullbrightWaterAlphaMaskProgram.createShader(NULL, NULL);
+        success = make_rigged_variant(gObjectFullbrightWaterAlphaMaskProgram, gSkinnedObjectFullbrightWaterAlphaMaskProgram);
+		success = success && gObjectFullbrightWaterAlphaMaskProgram.createShader(NULL, NULL);
 	}
 
 	if (success)
@@ -3427,7 +3279,8 @@ BOOL LLViewerShaderMgr::loadShadersObject()
 		gObjectShinyProgram.mShaderFiles.push_back(make_pair("objects/shinyV.glsl", GL_VERTEX_SHADER_ARB));
 		gObjectShinyProgram.mShaderFiles.push_back(make_pair("objects/shinyF.glsl", GL_FRAGMENT_SHADER_ARB));		
 		gObjectShinyProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-		success = gObjectShinyProgram.createShader(NULL, NULL);
+        success = make_rigged_variant(gObjectShinyProgram, gSkinnedObjectShinyProgram);
+		success = success && gObjectShinyProgram.createShader(NULL, NULL);
 	}
 
 	if (success)
@@ -3444,7 +3297,8 @@ BOOL LLViewerShaderMgr::loadShadersObject()
 		gObjectShinyWaterProgram.mShaderFiles.push_back(make_pair("objects/shinyV.glsl", GL_VERTEX_SHADER_ARB));
 		gObjectShinyWaterProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
 		gObjectShinyWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER;
-		success = gObjectShinyWaterProgram.createShader(NULL, NULL);
+        success = make_rigged_variant(gObjectShinyWaterProgram, gSkinnedObjectShinyWaterProgram);
+		success = success && gObjectShinyWaterProgram.createShader(NULL, NULL);
 	}
 	
 	if (success)
@@ -3460,7 +3314,8 @@ BOOL LLViewerShaderMgr::loadShadersObject()
 		gObjectFullbrightShinyProgram.mShaderFiles.push_back(make_pair("objects/fullbrightShinyV.glsl", GL_VERTEX_SHADER_ARB));
 		gObjectFullbrightShinyProgram.mShaderFiles.push_back(make_pair("objects/fullbrightShinyF.glsl", GL_FRAGMENT_SHADER_ARB));
 		gObjectFullbrightShinyProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-		success = gObjectFullbrightShinyProgram.createShader(NULL, NULL);
+        success = make_rigged_variant(gObjectFullbrightShinyProgram, gSkinnedObjectFullbrightShinyProgram);
+		success = success && gObjectFullbrightShinyProgram.createShader(NULL, NULL);
 	}
 
 	if (success)
@@ -3478,196 +3333,8 @@ BOOL LLViewerShaderMgr::loadShadersObject()
 		gObjectFullbrightShinyWaterProgram.mShaderFiles.push_back(make_pair("objects/fullbrightShinyWaterF.glsl", GL_FRAGMENT_SHADER_ARB));
 		gObjectFullbrightShinyWaterProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
 		gObjectFullbrightShinyWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER;
-		success = gObjectFullbrightShinyWaterProgram.createShader(NULL, NULL);
-	}
-
-	if (mShaderLevel[SHADER_AVATAR] > 0)
-	{ //load hardware skinned attachment shaders
-		if (success)
-		{
-			gSkinnedObjectSimpleProgram.mName = "Skinned Simple Shader";
-			gSkinnedObjectSimpleProgram.mFeatures.calculatesLighting = true;
-			gSkinnedObjectSimpleProgram.mFeatures.calculatesAtmospherics = true;
-			gSkinnedObjectSimpleProgram.mFeatures.hasGamma = true;
-			gSkinnedObjectSimpleProgram.mFeatures.hasAtmospherics = true;
-			gSkinnedObjectSimpleProgram.mFeatures.hasLighting = true;
-			gSkinnedObjectSimpleProgram.mFeatures.hasObjectSkinning = true;
-			gSkinnedObjectSimpleProgram.mFeatures.hasAlphaMask = true;
-			gSkinnedObjectSimpleProgram.mFeatures.disableTextureIndex = true;
-			gSkinnedObjectSimpleProgram.mShaderFiles.clear();
-			gSkinnedObjectSimpleProgram.mShaderFiles.push_back(make_pair("objects/simpleSkinnedV.glsl", GL_VERTEX_SHADER_ARB));
-			gSkinnedObjectSimpleProgram.mShaderFiles.push_back(make_pair("objects/simpleF.glsl", GL_FRAGMENT_SHADER_ARB));
-			gSkinnedObjectSimpleProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-			success = gSkinnedObjectSimpleProgram.createShader(NULL, NULL);
-		}
-
-		if (success)
-		{
-			gSkinnedObjectFullbrightProgram.mName = "Skinned Fullbright Shader";
-			gSkinnedObjectFullbrightProgram.mFeatures.calculatesAtmospherics = true;
-			gSkinnedObjectFullbrightProgram.mFeatures.hasGamma = true;
-			gSkinnedObjectFullbrightProgram.mFeatures.hasTransport = true;
-			gSkinnedObjectFullbrightProgram.mFeatures.isFullbright = true;
-			gSkinnedObjectFullbrightProgram.mFeatures.hasObjectSkinning = true;
-			gSkinnedObjectFullbrightProgram.mFeatures.hasAlphaMask = true;			
-			gSkinnedObjectFullbrightProgram.mFeatures.disableTextureIndex = true;
-			gSkinnedObjectFullbrightProgram.mFeatures.hasSrgb = true;
-			gSkinnedObjectFullbrightProgram.mShaderFiles.clear();
-			gSkinnedObjectFullbrightProgram.mShaderFiles.push_back(make_pair("objects/fullbrightSkinnedV.glsl", GL_VERTEX_SHADER_ARB));
-			gSkinnedObjectFullbrightProgram.mShaderFiles.push_back(make_pair("objects/fullbrightF.glsl", GL_FRAGMENT_SHADER_ARB));
-			gSkinnedObjectFullbrightProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-			success = gSkinnedObjectFullbrightProgram.createShader(NULL, NULL);
-		}
-
-		if (success)
-		{
-			gSkinnedObjectEmissiveProgram.mName = "Skinned Emissive Shader";
-			gSkinnedObjectEmissiveProgram.mFeatures.calculatesAtmospherics = true;
-			gSkinnedObjectEmissiveProgram.mFeatures.hasGamma = true;
-			gSkinnedObjectEmissiveProgram.mFeatures.hasTransport = true;
-			gSkinnedObjectEmissiveProgram.mFeatures.isFullbright = true;
-			gSkinnedObjectEmissiveProgram.mFeatures.hasObjectSkinning = true;
-			gSkinnedObjectEmissiveProgram.mFeatures.disableTextureIndex = true;
-            gSkinnedObjectEmissiveProgram.mFeatures.hasSrgb = true;
-			gSkinnedObjectEmissiveProgram.mShaderFiles.clear();
-			gSkinnedObjectEmissiveProgram.mShaderFiles.push_back(make_pair("objects/emissiveSkinnedV.glsl", GL_VERTEX_SHADER_ARB));
-			gSkinnedObjectEmissiveProgram.mShaderFiles.push_back(make_pair("objects/fullbrightF.glsl", GL_FRAGMENT_SHADER_ARB));
-			gSkinnedObjectEmissiveProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-			success = gSkinnedObjectEmissiveProgram.createShader(NULL, NULL);
-		}
-
-		if (success)
-		{
-			gSkinnedObjectEmissiveWaterProgram.mName = "Skinned Emissive Water Shader";
-			gSkinnedObjectEmissiveWaterProgram.mFeatures.calculatesAtmospherics = true;
-			gSkinnedObjectEmissiveWaterProgram.mFeatures.hasGamma = true;
-			gSkinnedObjectEmissiveWaterProgram.mFeatures.hasTransport = true;
-			gSkinnedObjectEmissiveWaterProgram.mFeatures.isFullbright = true;
-			gSkinnedObjectEmissiveWaterProgram.mFeatures.hasObjectSkinning = true;
-			gSkinnedObjectEmissiveWaterProgram.mFeatures.disableTextureIndex = true;
-			gSkinnedObjectEmissiveWaterProgram.mFeatures.hasWaterFog = true;
-			gSkinnedObjectEmissiveWaterProgram.mShaderFiles.clear();
-			gSkinnedObjectEmissiveWaterProgram.mShaderFiles.push_back(make_pair("objects/emissiveSkinnedV.glsl", GL_VERTEX_SHADER_ARB));
-			gSkinnedObjectEmissiveWaterProgram.mShaderFiles.push_back(make_pair("objects/fullbrightWaterF.glsl", GL_FRAGMENT_SHADER_ARB));
-			gSkinnedObjectEmissiveWaterProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-			success = gSkinnedObjectEmissiveWaterProgram.createShader(NULL, NULL);
-		}
-
-		if (success)
-		{
-			gSkinnedObjectFullbrightShinyProgram.mName = "Skinned Fullbright Shiny Shader";
-			gSkinnedObjectFullbrightShinyProgram.mFeatures.calculatesAtmospherics = true;
-			gSkinnedObjectFullbrightShinyProgram.mFeatures.hasGamma = true;
-			gSkinnedObjectFullbrightShinyProgram.mFeatures.hasTransport = true;
-			gSkinnedObjectFullbrightShinyProgram.mFeatures.isShiny = true;
-			gSkinnedObjectFullbrightShinyProgram.mFeatures.isFullbright = true;
-			gSkinnedObjectFullbrightShinyProgram.mFeatures.hasObjectSkinning = true;
-			gSkinnedObjectFullbrightShinyProgram.mFeatures.hasAlphaMask = true;
-			gSkinnedObjectFullbrightShinyProgram.mFeatures.disableTextureIndex = true;
-			gSkinnedObjectFullbrightShinyProgram.mShaderFiles.clear();
-			gSkinnedObjectFullbrightShinyProgram.mShaderFiles.push_back(make_pair("objects/fullbrightShinySkinnedV.glsl", GL_VERTEX_SHADER_ARB));
-			gSkinnedObjectFullbrightShinyProgram.mShaderFiles.push_back(make_pair("objects/fullbrightShinyF.glsl", GL_FRAGMENT_SHADER_ARB));
-			gSkinnedObjectFullbrightShinyProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-			success = gSkinnedObjectFullbrightShinyProgram.createShader(NULL, NULL);
-		}
-
-		if (success)
-		{
-			gSkinnedObjectShinySimpleProgram.mName = "Skinned Shiny Simple Shader";
-			gSkinnedObjectShinySimpleProgram.mFeatures.calculatesLighting = true;
-			gSkinnedObjectShinySimpleProgram.mFeatures.calculatesAtmospherics = true;
-			gSkinnedObjectShinySimpleProgram.mFeatures.hasGamma = true;
-			gSkinnedObjectShinySimpleProgram.mFeatures.hasAtmospherics = true;
-			gSkinnedObjectShinySimpleProgram.mFeatures.hasObjectSkinning = true;
-			gSkinnedObjectShinySimpleProgram.mFeatures.hasAlphaMask = true;
-			gSkinnedObjectShinySimpleProgram.mFeatures.isShiny = true;
-			gSkinnedObjectShinySimpleProgram.mFeatures.disableTextureIndex = true;
-			gSkinnedObjectShinySimpleProgram.mShaderFiles.clear();
-			gSkinnedObjectShinySimpleProgram.mShaderFiles.push_back(make_pair("objects/shinySimpleSkinnedV.glsl", GL_VERTEX_SHADER_ARB));
-			gSkinnedObjectShinySimpleProgram.mShaderFiles.push_back(make_pair("objects/shinyF.glsl", GL_FRAGMENT_SHADER_ARB));
-			gSkinnedObjectShinySimpleProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-			success = gSkinnedObjectShinySimpleProgram.createShader(NULL, NULL);
-		}
-
-		if (success)
-		{
-			gSkinnedObjectSimpleWaterProgram.mName = "Skinned Simple Water Shader";
-			gSkinnedObjectSimpleWaterProgram.mFeatures.calculatesLighting = true;
-			gSkinnedObjectSimpleWaterProgram.mFeatures.calculatesAtmospherics = true;
-			gSkinnedObjectSimpleWaterProgram.mFeatures.hasGamma = true;
-			gSkinnedObjectSimpleWaterProgram.mFeatures.hasAtmospherics = true;
-			gSkinnedObjectSimpleWaterProgram.mFeatures.hasLighting = true;
-			gSkinnedObjectSimpleWaterProgram.mFeatures.disableTextureIndex = true;
-			gSkinnedObjectSimpleWaterProgram.mFeatures.hasWaterFog = true;
-			gSkinnedObjectSimpleWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER;
-			gSkinnedObjectSimpleWaterProgram.mFeatures.hasObjectSkinning = true;
-			gSkinnedObjectSimpleWaterProgram.mFeatures.disableTextureIndex = true;
-			gSkinnedObjectSimpleWaterProgram.mFeatures.hasAlphaMask = true;
-			gSkinnedObjectSimpleWaterProgram.mShaderFiles.clear();
-			gSkinnedObjectSimpleWaterProgram.mShaderFiles.push_back(make_pair("objects/simpleSkinnedV.glsl", GL_VERTEX_SHADER_ARB));
-			gSkinnedObjectSimpleWaterProgram.mShaderFiles.push_back(make_pair("objects/simpleWaterF.glsl", GL_FRAGMENT_SHADER_ARB));
-			gSkinnedObjectSimpleWaterProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-			success = gSkinnedObjectSimpleWaterProgram.createShader(NULL, NULL);
-		}
-
-		if (success)
-		{
-			gSkinnedObjectFullbrightWaterProgram.mName = "Skinned Fullbright Water Shader";
-			gSkinnedObjectFullbrightWaterProgram.mFeatures.calculatesAtmospherics = true;
-			gSkinnedObjectFullbrightWaterProgram.mFeatures.hasGamma = true;
-			gSkinnedObjectFullbrightWaterProgram.mFeatures.hasTransport = true;
-			gSkinnedObjectFullbrightWaterProgram.mFeatures.isFullbright = true;
-			gSkinnedObjectFullbrightWaterProgram.mFeatures.hasObjectSkinning = true;
-			gSkinnedObjectFullbrightWaterProgram.mFeatures.hasAlphaMask = true;
-			gSkinnedObjectFullbrightWaterProgram.mFeatures.hasWaterFog = true;
-			gSkinnedObjectFullbrightWaterProgram.mFeatures.disableTextureIndex = true;
-			gSkinnedObjectFullbrightWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER;
-			gSkinnedObjectFullbrightWaterProgram.mShaderFiles.clear();
-			gSkinnedObjectFullbrightWaterProgram.mShaderFiles.push_back(make_pair("objects/fullbrightSkinnedV.glsl", GL_VERTEX_SHADER_ARB));
-			gSkinnedObjectFullbrightWaterProgram.mShaderFiles.push_back(make_pair("objects/fullbrightWaterF.glsl", GL_FRAGMENT_SHADER_ARB));
-			gSkinnedObjectFullbrightWaterProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-			success = gSkinnedObjectFullbrightWaterProgram.createShader(NULL, NULL);
-		}
-
-		if (success)
-		{
-			gSkinnedObjectFullbrightShinyWaterProgram.mName = "Skinned Fullbright Shiny Water Shader";
-			gSkinnedObjectFullbrightShinyWaterProgram.mFeatures.calculatesAtmospherics = true;
-			gSkinnedObjectFullbrightShinyWaterProgram.mFeatures.hasGamma = true;
-			gSkinnedObjectFullbrightShinyWaterProgram.mFeatures.hasTransport = true;
-			gSkinnedObjectFullbrightShinyWaterProgram.mFeatures.isShiny = true;
-			gSkinnedObjectFullbrightShinyWaterProgram.mFeatures.isFullbright = true;
-			gSkinnedObjectFullbrightShinyWaterProgram.mFeatures.hasObjectSkinning = true;
-			gSkinnedObjectFullbrightShinyWaterProgram.mFeatures.hasAlphaMask = true;
-			gSkinnedObjectFullbrightShinyWaterProgram.mFeatures.hasWaterFog = true;
-			gSkinnedObjectFullbrightShinyWaterProgram.mFeatures.disableTextureIndex = true;
-			gSkinnedObjectFullbrightShinyWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER;
-			gSkinnedObjectFullbrightShinyWaterProgram.mShaderFiles.clear();
-			gSkinnedObjectFullbrightShinyWaterProgram.mShaderFiles.push_back(make_pair("objects/fullbrightShinySkinnedV.glsl", GL_VERTEX_SHADER_ARB));
-			gSkinnedObjectFullbrightShinyWaterProgram.mShaderFiles.push_back(make_pair("objects/fullbrightShinyWaterF.glsl", GL_FRAGMENT_SHADER_ARB));
-			gSkinnedObjectFullbrightShinyWaterProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-			success = gSkinnedObjectFullbrightShinyWaterProgram.createShader(NULL, NULL);
-		}
-
-		if (success)
-		{
-			gSkinnedObjectShinySimpleWaterProgram.mName = "Skinned Shiny Simple Water Shader";
-			gSkinnedObjectShinySimpleWaterProgram.mFeatures.calculatesLighting = true;
-			gSkinnedObjectShinySimpleWaterProgram.mFeatures.calculatesAtmospherics = true;
-			gSkinnedObjectShinySimpleWaterProgram.mFeatures.hasGamma = true;
-			gSkinnedObjectShinySimpleWaterProgram.mFeatures.hasAtmospherics = true;
-			gSkinnedObjectShinySimpleWaterProgram.mFeatures.hasObjectSkinning = true;
-			gSkinnedObjectShinySimpleWaterProgram.mFeatures.hasAlphaMask = true;
-			gSkinnedObjectShinySimpleWaterProgram.mFeatures.isShiny = true;
-			gSkinnedObjectShinySimpleWaterProgram.mFeatures.hasWaterFog = true;
-			gSkinnedObjectShinySimpleWaterProgram.mFeatures.disableTextureIndex = true;
-			gSkinnedObjectShinySimpleWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER;
-			gSkinnedObjectShinySimpleWaterProgram.mShaderFiles.clear();
-			gSkinnedObjectShinySimpleWaterProgram.mShaderFiles.push_back(make_pair("objects/shinySimpleSkinnedV.glsl", GL_VERTEX_SHADER_ARB));
-			gSkinnedObjectShinySimpleWaterProgram.mShaderFiles.push_back(make_pair("objects/shinyWaterF.glsl", GL_FRAGMENT_SHADER_ARB));
-			gSkinnedObjectShinySimpleWaterProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
-			success = gSkinnedObjectShinySimpleWaterProgram.createShader(NULL, NULL);
-		}
+        success = make_rigged_variant(gObjectFullbrightShinyWaterProgram, gSkinnedObjectFullbrightShinyWaterProgram);
+		success = success && gObjectFullbrightShinyWaterProgram.createShader(NULL, NULL);
 	}
 
 	if( !success )
@@ -3995,9 +3662,21 @@ BOOL LLViewerShaderMgr::loadShadersInterface()
 		gOcclusionProgram.mShaderFiles.push_back(make_pair("interface/occlusionV.glsl", GL_VERTEX_SHADER_ARB));
 		gOcclusionProgram.mShaderFiles.push_back(make_pair("interface/occlusionF.glsl", GL_FRAGMENT_SHADER_ARB));
 		gOcclusionProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
+        gOcclusionProgram.mRiggedVariant = &gSkinnedOcclusionProgram;
 		success = gOcclusionProgram.createShader(NULL, NULL);
 	}
 
+    if (success)
+    {
+        gSkinnedOcclusionProgram.mName = "Skinned Occlusion Shader";
+        gSkinnedOcclusionProgram.mFeatures.hasObjectSkinning = true;
+        gSkinnedOcclusionProgram.mShaderFiles.clear();
+        gSkinnedOcclusionProgram.mShaderFiles.push_back(make_pair("interface/occlusionSkinnedV.glsl", GL_VERTEX_SHADER_ARB));
+        gSkinnedOcclusionProgram.mShaderFiles.push_back(make_pair("interface/occlusionF.glsl", GL_FRAGMENT_SHADER_ARB));
+        gSkinnedOcclusionProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
+        success = gSkinnedOcclusionProgram.createShader(NULL, NULL);
+    }
+
 	if (success)
 	{
 		gOcclusionCubeProgram.mName = "Occlusion Cube Shader";
@@ -4014,10 +3693,22 @@ BOOL LLViewerShaderMgr::loadShadersInterface()
 		gDebugProgram.mShaderFiles.clear();
 		gDebugProgram.mShaderFiles.push_back(make_pair("interface/debugV.glsl", GL_VERTEX_SHADER_ARB));
 		gDebugProgram.mShaderFiles.push_back(make_pair("interface/debugF.glsl", GL_FRAGMENT_SHADER_ARB));
+        gDebugProgram.mRiggedVariant = &gSkinnedDebugProgram;
 		gDebugProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
 		success = gDebugProgram.createShader(NULL, NULL);
 	}
 
+    if (success)
+    {
+        gSkinnedDebugProgram.mName = "Skinned Debug Shader";
+        gSkinnedDebugProgram.mFeatures.hasObjectSkinning = true;
+        gSkinnedDebugProgram.mShaderFiles.clear();
+        gSkinnedDebugProgram.mShaderFiles.push_back(make_pair("interface/debugSkinnedV.glsl", GL_VERTEX_SHADER_ARB));
+        gSkinnedDebugProgram.mShaderFiles.push_back(make_pair("interface/debugF.glsl", GL_FRAGMENT_SHADER_ARB));
+        gSkinnedDebugProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
+        success = gSkinnedDebugProgram.createShader(NULL, NULL);
+    }
+
 	if (success)
 	{
 		gClipProgram.mName = "Clip Shader";
diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h
index 081221f15be..297cfb6e68b 100644
--- a/indra/newview/llviewershadermgr.h
+++ b/indra/newview/llviewershadermgr.h
@@ -184,10 +184,8 @@ extern LLGLSLShader			gObjectPreviewProgram;
 extern LLGLSLShader			gObjectSimpleAlphaMaskProgram;
 extern LLGLSLShader			gObjectSimpleWaterProgram;
 extern LLGLSLShader			gObjectSimpleWaterAlphaMaskProgram;
-extern LLGLSLShader			gObjectSimpleNonIndexedProgram;
 extern LLGLSLShader			gObjectSimpleNonIndexedTexGenProgram;
 extern LLGLSLShader			gObjectSimpleNonIndexedTexGenWaterProgram;
-extern LLGLSLShader			gObjectSimpleNonIndexedWaterProgram;
 extern LLGLSLShader			gObjectAlphaMaskNonIndexedProgram;
 extern LLGLSLShader			gObjectAlphaMaskNonIndexedWaterProgram;
 extern LLGLSLShader			gObjectAlphaMaskNoColorProgram;
@@ -200,8 +198,6 @@ extern LLGLSLShader			gObjectEmissiveProgram;
 extern LLGLSLShader			gObjectEmissiveWaterProgram;
 extern LLGLSLShader			gObjectFullbrightAlphaMaskProgram;
 extern LLGLSLShader			gObjectFullbrightWaterAlphaMaskProgram;
-extern LLGLSLShader			gObjectFullbrightNonIndexedProgram;
-extern LLGLSLShader			gObjectFullbrightNonIndexedWaterProgram;
 extern LLGLSLShader			gObjectEmissiveNonIndexedProgram;
 extern LLGLSLShader			gObjectEmissiveNonIndexedWaterProgram;
 extern LLGLSLShader			gObjectBumpProgram;
@@ -213,25 +209,9 @@ extern LLGLSLShader			gObjectFullbrightLODProgram;
 
 extern LLGLSLShader			gObjectFullbrightShinyProgram;
 extern LLGLSLShader			gObjectFullbrightShinyWaterProgram;
-extern LLGLSLShader			gObjectFullbrightShinyNonIndexedProgram;
-extern LLGLSLShader			gObjectFullbrightShinyNonIndexedWaterProgram;
 
 extern LLGLSLShader			gObjectShinyProgram;
 extern LLGLSLShader			gObjectShinyWaterProgram;
-extern LLGLSLShader			gObjectShinyNonIndexedProgram;
-extern LLGLSLShader			gObjectShinyNonIndexedWaterProgram;
-
-extern LLGLSLShader			gSkinnedObjectSimpleProgram;
-extern LLGLSLShader			gSkinnedObjectFullbrightProgram;
-extern LLGLSLShader			gSkinnedObjectEmissiveProgram;
-extern LLGLSLShader			gSkinnedObjectFullbrightShinyProgram;
-extern LLGLSLShader			gSkinnedObjectShinySimpleProgram;
-
-extern LLGLSLShader			gSkinnedObjectSimpleWaterProgram;
-extern LLGLSLShader			gSkinnedObjectFullbrightWaterProgram;
-extern LLGLSLShader			gSkinnedObjectEmissiveWaterProgram;
-extern LLGLSLShader			gSkinnedObjectFullbrightShinyWaterProgram;
-extern LLGLSLShader			gSkinnedObjectShinySimpleWaterProgram;
 
 //environment shaders
 extern LLGLSLShader			gTerrainProgram;
@@ -281,9 +261,6 @@ extern LLGLSLShader			gDeferredDiffuseAlphaMaskProgram;
 extern LLGLSLShader			gDeferredNonIndexedDiffuseAlphaMaskProgram;
 extern LLGLSLShader			gDeferredNonIndexedDiffuseAlphaMaskNoColorProgram;
 extern LLGLSLShader			gDeferredNonIndexedDiffuseProgram;
-extern LLGLSLShader			gDeferredSkinnedDiffuseProgram;
-extern LLGLSLShader			gDeferredSkinnedBumpProgram;
-extern LLGLSLShader			gDeferredSkinnedAlphaProgram;
 extern LLGLSLShader			gDeferredBumpProgram;
 extern LLGLSLShader			gDeferredTerrainProgram;
 extern LLGLSLShader			gDeferredTerrainWaterProgram;
@@ -330,8 +307,6 @@ extern LLGLSLShader			gDeferredWLSunProgram;
 extern LLGLSLShader			gDeferredWLMoonProgram;
 extern LLGLSLShader			gDeferredStarProgram;
 extern LLGLSLShader			gDeferredFullbrightShinyProgram;
-extern LLGLSLShader			gDeferredSkinnedFullbrightShinyProgram;
-extern LLGLSLShader			gDeferredSkinnedFullbrightProgram;
 extern LLGLSLShader			gNormalMapGenProgram;
 
 // Deferred materials shaders
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 310a6a2adb9..84bb67a03d1 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -111,6 +111,7 @@
 #include "llsdserialize.h"
 #include "llcallstack.h"
 #include "llrendersphere.h"
+#include "llskinningutil.h"
 
 #include <boost/lexical_cast.hpp>
 
@@ -9445,6 +9446,54 @@ LLViewerTexture* LLVOAvatar::getBakedTexture(const U8 te)
 	
 }
 
+const LLVOAvatar::MatrixPaletteCache& LLVOAvatar::updateSkinInfoMatrixPalette(const LLMeshSkinInfo* skin, LLVOVolume* requesting_obj)
+{
+    U64 hash = skin->mHash;
+    MatrixPaletteCache& entry = mMatrixPaletteCache[hash];
+
+    if (entry.mFrame != gFrameCount)
+    {
+        LL_PROFILE_ZONE_SCOPED;
+
+        entry.mFrame = gFrameCount;
+
+        //build matrix palette
+        U32 count = LLSkinningUtil::getMeshJointCount(skin);
+        entry.mMatrixPalette.resize(count);
+        LLSkinningUtil::initSkinningMatrixPalette(&(entry.mMatrixPalette[0]), count, skin, this);
+
+        const LLMatrix4a* mat = &(entry.mMatrixPalette[0]);
+
+        entry.mGLMp.resize(count * 12);
+
+        F32* mp = &(entry.mGLMp[0]);
+
+        for (U32 i = 0; i < count; ++i)
+        {
+            F32* m = (F32*)mat[i].mMatrix[0].getF32ptr();
+
+            U32 idx = i * 12;
+
+            mp[idx + 0] = m[0];
+            mp[idx + 1] = m[1];
+            mp[idx + 2] = m[2];
+            mp[idx + 3] = m[12];
+
+            mp[idx + 4] = m[4];
+            mp[idx + 5] = m[5];
+            mp[idx + 6] = m[6];
+            mp[idx + 7] = m[13];
+
+            mp[idx + 8] = m[8];
+            mp[idx + 9] = m[9];
+            mp[idx + 10] = m[10];
+            mp[idx + 11] = m[14];
+        }
+    }
+
+    return entry;
+}
+
 // static
 void LLVOAvatar::getAnimLabels( std::vector<std::string>* labels )
 {
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index aeac23ad922..b85400866ee 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -53,6 +53,8 @@
 #include "llviewerstats.h"
 #include "llvovolume.h"
 #include "llavatarrendernotifier.h"
+#include "llmodel.h"
+
 
 extern const LLUUID ANIM_AGENT_BODY_NOISE;
 extern const LLUUID ANIM_AGENT_BREATHE_ROT;
@@ -77,6 +79,7 @@ class LLViewerJointMesh;
 
 const F32 MAX_AVATAR_LOD_FACTOR = 1.0f;
 
+extern U32 gFrameCount;
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // LLVOAvatar
@@ -746,6 +749,26 @@ class LLVOAvatar :
 	void			updateMeshVisibility();
 	LLViewerTexture*		getBakedTexture(const U8 te);
 
+    class alignas(16) MatrixPaletteCache
+    {
+    public:
+        U32 mFrame;
+        LLMeshSkinInfo::matrix_list_t mMatrixPalette;
+
+        // Float array ready to be sent to GL
+        std::vector<F32> mGLMp;
+
+        MatrixPaletteCache() :
+            mFrame(gFrameCount - 1)
+        {
+        }
+    };
+
+    const MatrixPaletteCache& updateSkinInfoMatrixPalette(const LLMeshSkinInfo* skinInfo, LLVOVolume* requesting_obj = nullptr);
+
+    typedef std::unordered_map<U64, MatrixPaletteCache> matrix_palette_cache_t;
+    matrix_palette_cache_t mMatrixPaletteCache;
+
 protected:
 	void 			releaseMeshData();
 	virtual void restoreMeshData();
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index 755a70599a8..f4f9154fed3 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -1740,7 +1740,17 @@ BOOL LLVOVolume::genBBoxes(BOOL force_global)
 		}
 	}
 
-    if (any_valid_boxes)
+    if (isRiggedMesh())
+    {
+        min.set(-1, -1, -1, 0);
+        max.set(1, 1, 1, 0);
+
+        mDrawable->setSpatialExtents(min, max);
+        mDrawable->setPositionGroup(LLVector4a(0, 0, 0));
+        updateRadius();
+        mDrawable->movePartition();
+    }
+    else if (any_valid_boxes)
     {
         if (rebuild)
         {
@@ -5010,13 +5020,13 @@ bool can_batch_texture(LLFace* facep)
 
 const static U32 MAX_FACE_COUNT = 4096U;
 int32_t LLVolumeGeometryManager::sInstanceCount = 0;
-LLFace** LLVolumeGeometryManager::sFullbrightFaces = NULL;
-LLFace** LLVolumeGeometryManager::sBumpFaces = NULL;
-LLFace** LLVolumeGeometryManager::sSimpleFaces = NULL;
-LLFace** LLVolumeGeometryManager::sNormFaces = NULL;
-LLFace** LLVolumeGeometryManager::sSpecFaces = NULL;
-LLFace** LLVolumeGeometryManager::sNormSpecFaces = NULL;
-LLFace** LLVolumeGeometryManager::sAlphaFaces = NULL;
+LLFace** LLVolumeGeometryManager::sFullbrightFaces[2] = { NULL };
+LLFace** LLVolumeGeometryManager::sBumpFaces[2] = { NULL };
+LLFace** LLVolumeGeometryManager::sSimpleFaces[2] = { NULL };
+LLFace** LLVolumeGeometryManager::sNormFaces[2] = { NULL };
+LLFace** LLVolumeGeometryManager::sSpecFaces[2] = { NULL };
+LLFace** LLVolumeGeometryManager::sNormSpecFaces[2] = { NULL };
+LLFace** LLVolumeGeometryManager::sAlphaFaces[2] = { NULL };
 
 LLVolumeGeometryManager::LLVolumeGeometryManager()
 	: LLGeometryManager()
@@ -5044,32 +5054,38 @@ LLVolumeGeometryManager::~LLVolumeGeometryManager()
 
 void LLVolumeGeometryManager::allocateFaces(U32 pMaxFaceCount)
 {
-	sFullbrightFaces = static_cast<LLFace**>(ll_aligned_malloc<64>(pMaxFaceCount*sizeof(LLFace*)));
-	sBumpFaces = static_cast<LLFace**>(ll_aligned_malloc<64>(pMaxFaceCount*sizeof(LLFace*)));
-	sSimpleFaces = static_cast<LLFace**>(ll_aligned_malloc<64>(pMaxFaceCount*sizeof(LLFace*)));
-	sNormFaces = static_cast<LLFace**>(ll_aligned_malloc<64>(pMaxFaceCount*sizeof(LLFace*)));
-	sSpecFaces = static_cast<LLFace**>(ll_aligned_malloc<64>(pMaxFaceCount*sizeof(LLFace*)));
-	sNormSpecFaces = static_cast<LLFace**>(ll_aligned_malloc<64>(pMaxFaceCount*sizeof(LLFace*)));
-	sAlphaFaces = static_cast<LLFace**>(ll_aligned_malloc<64>(pMaxFaceCount*sizeof(LLFace*)));
+    for (int i = 0; i < 2; ++i)
+    {
+        sFullbrightFaces[i] = static_cast<LLFace**>(ll_aligned_malloc<64>(pMaxFaceCount * sizeof(LLFace*)));
+        sBumpFaces[i] = static_cast<LLFace**>(ll_aligned_malloc<64>(pMaxFaceCount * sizeof(LLFace*)));
+        sSimpleFaces[i] = static_cast<LLFace**>(ll_aligned_malloc<64>(pMaxFaceCount * sizeof(LLFace*)));
+        sNormFaces[i] = static_cast<LLFace**>(ll_aligned_malloc<64>(pMaxFaceCount * sizeof(LLFace*)));
+        sSpecFaces[i] = static_cast<LLFace**>(ll_aligned_malloc<64>(pMaxFaceCount * sizeof(LLFace*)));
+        sNormSpecFaces[i] = static_cast<LLFace**>(ll_aligned_malloc<64>(pMaxFaceCount * sizeof(LLFace*)));
+        sAlphaFaces[i] = static_cast<LLFace**>(ll_aligned_malloc<64>(pMaxFaceCount * sizeof(LLFace*)));
+    }
 }
 
 void LLVolumeGeometryManager::freeFaces()
 {
-	ll_aligned_free<64>(sFullbrightFaces);
-	ll_aligned_free<64>(sBumpFaces);
-	ll_aligned_free<64>(sSimpleFaces);
-	ll_aligned_free<64>(sNormFaces);
-	ll_aligned_free<64>(sSpecFaces);
-	ll_aligned_free<64>(sNormSpecFaces);
-	ll_aligned_free<64>(sAlphaFaces);
-
-	sFullbrightFaces = NULL;
-	sBumpFaces = NULL;
-	sSimpleFaces = NULL;
-	sNormFaces = NULL;
-	sSpecFaces = NULL;
-	sNormSpecFaces = NULL;
-	sAlphaFaces = NULL;
+    for (int i = 0; i < 2; ++i)
+    {
+        ll_aligned_free<64>(sFullbrightFaces[i]);
+        ll_aligned_free<64>(sBumpFaces[i]);
+        ll_aligned_free<64>(sSimpleFaces[i]);
+        ll_aligned_free<64>(sNormFaces[i]);
+        ll_aligned_free<64>(sSpecFaces[i]);
+        ll_aligned_free<64>(sNormSpecFaces[i]);
+        ll_aligned_free<64>(sAlphaFaces[i]);
+
+        sFullbrightFaces[i] = NULL;
+        sBumpFaces[i] = NULL;
+        sSimpleFaces[i] = NULL;
+        sNormFaces[i] = NULL;
+        sSpecFaces[i] = NULL;
+        sNormSpecFaces[i] = NULL;
+        sAlphaFaces[i] = NULL;
+    }
 }
 
 void LLVolumeGeometryManager::registerFace(LLSpatialGroup* group, LLFace* facep, U32 type)
@@ -5090,8 +5106,18 @@ void LLVolumeGeometryManager::registerFace(LLSpatialGroup* group, LLFace* facep,
 		return;
 	}
 
+    U32 passType = type;
+
+    bool rigged = facep->isState(LLFace::RIGGED);
+
+    if (rigged && type != LLRenderPass::PASS_ALPHA)
+    {
+        // hacky, should probably clean up -- if this face is rigged, put it in "type + 1"
+        // See LLRenderPass PASS_foo enum
+        passType += 1;
+    }
 	//add face to drawmap
-	LLSpatialGroup::drawmap_elem_t& draw_vec = group->mDrawMap[type];	
+	LLSpatialGroup::drawmap_elem_t& draw_vec = group->mDrawMap[passType];
 
 	S32 idx = draw_vec.size()-1;
 
@@ -5117,7 +5143,12 @@ void LLVolumeGeometryManager::registerFace(LLSpatialGroup* group, LLFace* facep,
 
 	LLDrawable* drawable = facep->getDrawable();
 	
-	if (drawable->isState(LLDrawable::ANIMATED_CHILD))
+    if (rigged)
+    {
+        // rigged meshes ignore their model matrix
+        model_mat = nullptr;
+    }
+	else if (drawable->isState(LLDrawable::ANIMATED_CHILD))
 	{
 		model_mat = &drawable->getWorldMatrix();
 	}
@@ -5191,7 +5222,7 @@ void LLVolumeGeometryManager::registerFace(LLSpatialGroup* group, LLFace* facep,
 		draw_vec[idx]->mEnd - draw_vec[idx]->mStart + facep->getGeomCount() <= (U32) gGLManager.mGLMaxVertexRange &&
 		draw_vec[idx]->mCount + facep->getIndicesCount() <= (U32) gGLManager.mGLMaxIndexRange &&
 #endif
-		draw_vec[idx]->mMaterial == mat &&
+		//draw_vec[idx]->mMaterial == mat &&
 		draw_vec[idx]->mMaterialID == mat_id &&
 		draw_vec[idx]->mFullbright == fullbright &&
 		draw_vec[idx]->mBump == bump &&
@@ -5199,7 +5230,9 @@ void LLVolumeGeometryManager::registerFace(LLSpatialGroup* group, LLFace* facep,
 		draw_vec[idx]->mTextureMatrix == tex_mat &&
 		draw_vec[idx]->mModelMatrix == model_mat &&
 		draw_vec[idx]->mShaderMask == shader_mask &&
-		draw_vec[idx]->mSelected == selected)
+		draw_vec[idx]->mSelected == selected &&
+        draw_vec[idx]->mAvatar == facep->mAvatar &&
+        draw_vec[idx]->getSkinHash() == facep->getSkinHash())
 	{
 		draw_vec[idx]->mCount += facep->getIndicesCount();
 		draw_vec[idx]->mEnd += facep->getGeomCount();
@@ -5245,6 +5278,8 @@ void LLVolumeGeometryManager::registerFace(LLSpatialGroup* group, LLFace* facep,
 		draw_info->mSpecularMap = NULL;
 		draw_info->mMaterial = mat;
 		draw_info->mShaderMask = shader_mask;
+        draw_info->mAvatar = facep->mAvatar;
+        draw_info->mSkinInfo = facep->mSkinInfo;
 
 		if (mat)
 		{
@@ -5411,11 +5446,21 @@ void handleRenderAutoMuteByteLimitChanged(const LLSD& new_value)
 
 // add a face pointer to a list of face pointers without going over MAX_COUNT faces
 template<typename T>
-static inline void add_face(T** list, U32& count, T* face)
+static inline void add_face(T*** list, U32* count, T* face)
 {
-    if (count < MAX_FACE_COUNT)
+    if (face->isState(LLFace::RIGGED))
+    {
+        if (count[1] < MAX_FACE_COUNT)
+        {
+            list[1][count[1]++] = face;
+        }
+    }
+    else
     {
-        list[count++] = face;
+        if (count[0] < MAX_FACE_COUNT)
+        {
+            list[0][count[0]++] = face;
+        }
     }
 }
 
@@ -5465,14 +5510,13 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 
 	mFaceList.clear();
 
-	U32 fullbright_count = 0;
-	U32 bump_count = 0;
-	U32 simple_count = 0;
-	U32 alpha_count = 0;
-	U32 norm_count = 0;
-	U32 spec_count = 0;
-	U32 normspec_count = 0;
-
+    U32 fullbright_count[2] = { 0 };
+	U32 bump_count[2] = { 0 };
+	U32 simple_count[2] = { 0 };
+	U32 alpha_count[2] = { 0 };
+	U32 norm_count[2] = { 0 };
+	U32 spec_count[2] = { 0 };
+	U32 normspec_count[2] = { 0 };
 
 	U32 useage = group->getSpatialPartition()->mBufferUsage;
 
@@ -5521,7 +5565,8 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 
             std::string vobj_name = llformat("Vol%p", vobj);
 
-			if (vobj->isMesh() &&
+            bool is_mesh = vobj->isMesh();
+			if (is_mesh &&
 				((vobj->getVolume() && !vobj->getVolume()->isMeshAssetLoaded()) || !gMeshRepo.meshRezEnabled()))
 			{
 				continue;
@@ -5534,7 +5579,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 				group->mSurfaceArea += volume->getSurfaceArea() * llmax(llmax(scale.mV[0], scale.mV[1]), scale.mV[2]);
 			}
 
-            bool is_mesh = vobj->isMesh();
+            
             F32 est_tris = vobj->getEstTrianglesMax();
 
             vobj->updateControlAvatar();
@@ -5558,15 +5603,32 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 
 			drawablep->clearState(LLDrawable::HAS_ALPHA);
 
-            if (vobj->isRiggedMesh() &&
-                ((vobj->isAnimatedObject() && vobj->getControlAvatar()) ||
-                 (!vobj->isAnimatedObject() && vobj->getAvatar())))
+            LLVOAvatar* avatar = nullptr;
+            const LLMeshSkinInfo* skinInfo = nullptr;
+            if (is_mesh)
+            {
+                skinInfo = vobj->getSkinInfo();
+            }
+
+            if (skinInfo)
+            {
+                if (vobj->isAnimatedObject())
+                {
+                    avatar = vobj->getControlAvatar();
+                }
+                else
+                {
+                    avatar = vobj->getAvatar();
+                }
+            }
+
+            if (avatar != nullptr)
             {
-                vobj->getAvatar()->addAttachmentOverridesForObject(vobj, NULL, false);
+                avatar->addAttachmentOverridesForObject(vobj, NULL, false);
             }
             
             // Standard rigged mesh attachments: 
-			bool rigged = !vobj->isAnimatedObject() && vobj->isRiggedMesh() && vobj->isAttachment();
+			bool rigged = !vobj->isAnimatedObject() && skinInfo && vobj->isAttachment();
             // Animated objects. Have to check for isRiggedMesh() to
             // exclude static objects in animated object linksets.
 			rigged = rigged || (vobj->isAnimatedObject() && vobj->isRiggedMesh() &&
@@ -5590,183 +5652,19 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 			
 				//sum up face verts and indices
 				drawablep->updateFaceSize(i);
-			
-				if (rigged)
-				{
-					if (!facep->isState(LLFace::RIGGED))
-					{ //completely reset vertex buffer
-						facep->clearVertexBuffer();
-					}
-		
-					facep->setState(LLFace::RIGGED);
-					any_rigged_face = true;
-				
-					//get drawpool of avatar with rigged face
-					LLDrawPoolAvatar* pool = get_avatar_drawpool(vobj);				
-					
-					if (pool)
-					{
-						const LLTextureEntry* te = facep->getTextureEntry();
-
-						//remove face from old pool if it exists
-						LLDrawPool* old_pool = facep->getPool();
-						if (old_pool
-							&& (old_pool->getType() == LLDrawPool::POOL_AVATAR || old_pool->getType() == LLDrawPool::POOL_CONTROL_AV))
-						{
-							((LLDrawPoolAvatar*) old_pool)->removeRiggedFace(facep);
-						}
-
-						//add face to new pool
-						LLViewerTexture* tex = facep->getTexture();
-						U32 type = gPipeline.getPoolTypeFromTE(te, tex);
-
-                        F32 te_alpha = te->getColor().mV[3];
-
-						if (te->getGlow())
-						{
-							pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_GLOW);
-						}
-
-						LLMaterial* mat = te->getMaterialParams().get();
-                        bool fullbright = te->getFullbright();
-
-						if (mat && LLPipeline::sRenderDeferred)
-						{
-							U8 alpha_mode = mat->getDiffuseAlphaMode();
-
-							bool is_alpha = type == LLDrawPool::POOL_ALPHA &&
-								(alpha_mode == LLMaterial::DIFFUSE_ALPHA_MODE_BLEND ||
-								te_alpha < 0.999f);
-
-							if (is_alpha)
-							{ //this face needs alpha blending, override alpha mode
-								alpha_mode = LLMaterial::DIFFUSE_ALPHA_MODE_BLEND;
-							}
-
-                            if (fullbright && (alpha_mode == LLMaterial::DIFFUSE_ALPHA_MODE_NONE))
-                            {
-                                pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_FULLBRIGHT);
-                            }
-							else if (!is_alpha || te_alpha > 0.f)  // //only add the face if it will actually be visible
-							{ 
-								U32 mask = mat->getShaderMask(alpha_mode);
-								pool->addRiggedFace(facep, mask);
-							}
-
-							if(vobj->isAnimatedObject() && vobj->isRiggedMesh())
-							{
-								pool->updateRiggedVertexBuffers(vobj->getAvatar());
-							}
-						}
-						else if (mat)
-						{							
-							bool is_alpha = type == LLDrawPool::POOL_ALPHA;
-							U8 mode = mat->getDiffuseAlphaMode();
-							bool can_be_shiny = mode == LLMaterial::DIFFUSE_ALPHA_MODE_NONE ||
-												mode == LLMaterial::DIFFUSE_ALPHA_MODE_EMISSIVE;
-							
-							if (mode == LLMaterial::DIFFUSE_ALPHA_MODE_MASK && te->getColor().mV[3] >= 0.999f)
-							{
-								pool->addRiggedFace(facep, fullbright ? LLDrawPoolAvatar::RIGGED_FULLBRIGHT : LLDrawPoolAvatar::RIGGED_SIMPLE);
-							}
-							else if (is_alpha || (te->getColor().mV[3] < 0.999f))
-							{
-								if (te->getColor().mV[3] > 0.f)
-								{
-									pool->addRiggedFace(facep, fullbright ? LLDrawPoolAvatar::RIGGED_FULLBRIGHT_ALPHA : LLDrawPoolAvatar::RIGGED_ALPHA);
-								}
-							}
-							else if (gPipeline.canUseVertexShaders()
-								&& LLPipeline::sRenderBump 
-								&& te->getShiny() 
-								&& can_be_shiny)
-							{
-								pool->addRiggedFace(facep, fullbright ? LLDrawPoolAvatar::RIGGED_FULLBRIGHT_SHINY : LLDrawPoolAvatar::RIGGED_SHINY);
-							}
-							else
-							{
-								pool->addRiggedFace(facep, fullbright ? LLDrawPoolAvatar::RIGGED_FULLBRIGHT : LLDrawPoolAvatar::RIGGED_SIMPLE);
-							}
-						}
-						else
-						{
-						if (type == LLDrawPool::POOL_ALPHA)
-						{
-							if (te->getColor().mV[3] > 0.f)
-							{
-								if (te->getFullbright())
-								{
-									pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_FULLBRIGHT_ALPHA);
-								}
-								else
-								{
-									pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_ALPHA);
-								}
-							}
-						}
-						else if (te->getShiny())
-						{
-							if (te->getFullbright())
-							{
-								pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_FULLBRIGHT_SHINY);
-							}
-							else
-							{
-								if (LLPipeline::sRenderDeferred)
-								{
-									pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_SIMPLE);
-								}
-								else
-								{
-									pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_SHINY);
-								}
-							}
-						}
-						else
-						{
-							if (te->getFullbright())
-							{
-								pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_FULLBRIGHT);
-							}
-							else
-							{
-								pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_SIMPLE);
-							}
-						}
-
-
-						if (LLPipeline::sRenderDeferred)
-						{
-							if (type != LLDrawPool::POOL_ALPHA && !te->getFullbright())
-							{
-								if (te->getBumpmap())
-								{
-									pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_DEFERRED_BUMP);
-								}
-								else
-								{
-									pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_DEFERRED_SIMPLE);
-								}
-							}
-						}
-					}
-					}
 
-					continue;
-				}
-				else
-				{
-					if (facep->isState(LLFace::RIGGED))
-					{ //face is not rigged but used to be, remove from rigged face pool
-						LLDrawPoolAvatar* pool = (LLDrawPoolAvatar*) facep->getPool();
-						if (pool)
-						{
-							pool->removeRiggedFace(facep);
-						}
-						facep->clearState(LLFace::RIGGED);
-					}
-				}
+                if (rigged)
+                {
+                    if (!facep->isState(LLFace::RIGGED))
+                    { //completely reset vertex buffer
+                        facep->clearVertexBuffer();
+                    }
 
+                    facep->setState(LLFace::RIGGED);
+                    facep->mSkinInfo = (LLMeshSkinInfo*) skinInfo; // TODO -- fix ugly de-consting here
+                    facep->mAvatar = avatar;
+                    any_rigged_face = true;
+                }
 
 				if (cur_total > max_total || facep->getIndicesCount() <= 0 || facep->getGeomCount() <= 0)
 				{
@@ -5776,7 +5674,9 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 
 				cur_total += facep->getGeomCount();
 
-				if (facep->hasGeometry() && facep->getPixelArea() > FORCE_CULL_AREA)
+				if (facep->hasGeometry() && 
+                    (rigged ||  // <-- HACK FIXME -- getPixelArea might be incorrect for rigged objects
+                        facep->getPixelArea() > FORCE_CULL_AREA)) // <-- don't render tiny faces
 				{
 					const LLTextureEntry* te = facep->getTextureEntry();
 					LLViewerTexture* tex = facep->getTexture();
@@ -5966,6 +5866,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 
 	BOOL batch_textures = LLViewerShaderMgr::instance()->getShaderLevel(LLViewerShaderMgr::SHADER_OBJECT) > 1;
 
+    // add extra vertex data for deferred rendering (not necessarily for batching textures)
 	if (batch_textures)
 	{
 		bump_mask = bump_mask | LLVertexBuffer::MAP_TANGENT;
@@ -5978,13 +5879,23 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 
 	U32 geometryBytes = 0;
 
-	geometryBytes += genDrawInfo(group, simple_mask | LLVertexBuffer::MAP_TEXTURE_INDEX, sSimpleFaces, simple_count, FALSE, batch_textures, FALSE);
-	geometryBytes += genDrawInfo(group, fullbright_mask | LLVertexBuffer::MAP_TEXTURE_INDEX, sFullbrightFaces, fullbright_count, FALSE, batch_textures);
-	geometryBytes += genDrawInfo(group, alpha_mask | LLVertexBuffer::MAP_TEXTURE_INDEX, sAlphaFaces, alpha_count, TRUE, batch_textures);
-	geometryBytes += genDrawInfo(group, bump_mask | LLVertexBuffer::MAP_TEXTURE_INDEX, sBumpFaces, bump_count, FALSE, FALSE);
-	geometryBytes += genDrawInfo(group, norm_mask | LLVertexBuffer::MAP_TEXTURE_INDEX, sNormFaces, norm_count, FALSE, FALSE);
-	geometryBytes += genDrawInfo(group, spec_mask | LLVertexBuffer::MAP_TEXTURE_INDEX, sSpecFaces, spec_count, FALSE, FALSE);
-	geometryBytes += genDrawInfo(group, normspec_mask | LLVertexBuffer::MAP_TEXTURE_INDEX, sNormSpecFaces, normspec_count, FALSE, FALSE);
+    U32 extra_mask = LLVertexBuffer::MAP_TEXTURE_INDEX;
+	geometryBytes += genDrawInfo(group, simple_mask | extra_mask, sSimpleFaces[0], simple_count[0], FALSE, batch_textures);
+	geometryBytes += genDrawInfo(group, fullbright_mask | extra_mask, sFullbrightFaces[0], fullbright_count[0], FALSE, batch_textures);
+	geometryBytes += genDrawInfo(group, alpha_mask | extra_mask, sAlphaFaces[0], alpha_count[0], TRUE, batch_textures);
+	geometryBytes += genDrawInfo(group, bump_mask | extra_mask, sBumpFaces[0], bump_count[0], FALSE, FALSE);
+	geometryBytes += genDrawInfo(group, norm_mask | extra_mask, sNormFaces[0], norm_count[0], FALSE, FALSE);
+	geometryBytes += genDrawInfo(group, spec_mask | extra_mask, sSpecFaces[0], spec_count[0], FALSE, FALSE);
+	geometryBytes += genDrawInfo(group, normspec_mask | extra_mask, sNormSpecFaces[0], normspec_count[0], FALSE, FALSE);
+
+    extra_mask |= LLVertexBuffer::MAP_WEIGHT4;
+    geometryBytes += genDrawInfo(group, simple_mask | extra_mask, sSimpleFaces[1], simple_count[1], FALSE, batch_textures, TRUE);
+    geometryBytes += genDrawInfo(group, fullbright_mask | extra_mask, sFullbrightFaces[1], fullbright_count[1], FALSE, batch_textures, TRUE);
+    geometryBytes += genDrawInfo(group, alpha_mask | extra_mask, sAlphaFaces[1], alpha_count[1], TRUE, batch_textures, TRUE);
+    geometryBytes += genDrawInfo(group, bump_mask | extra_mask, sBumpFaces[1], bump_count[1], FALSE, TRUE);
+    geometryBytes += genDrawInfo(group, norm_mask | extra_mask, sNormFaces[1], norm_count[1], FALSE, TRUE);
+    geometryBytes += genDrawInfo(group, spec_mask | extra_mask, sSpecFaces[1], spec_count[1], FALSE, TRUE);
+    geometryBytes += genDrawInfo(group, normspec_mask | extra_mask, sNormSpecFaces[1], normspec_count[1], FALSE, TRUE);
 
 	group->mGeometryBytes = geometryBytes;
 
@@ -6146,7 +6057,7 @@ struct CompareBatchBreakerModified
 		const LLTextureEntry* lte = lhs->getTextureEntry();
 		const LLTextureEntry* rte = rhs->getTextureEntry();
 
-		if (lte->getBumpmap() != rte->getBumpmap())
+        if (lte->getBumpmap() != rte->getBumpmap())
 		{
 			return lte->getBumpmap() < rte->getBumpmap();
 		}
@@ -6169,8 +6080,43 @@ struct CompareBatchBreakerModified
 	}
 };
 
+struct CompareBatchBreakerRigged
+{
+    bool operator()(const LLFace* const& lhs, const LLFace* const& rhs)
+    {
+        const LLTextureEntry* lte = lhs->getTextureEntry();
+        const LLTextureEntry* rte = rhs->getTextureEntry();
+
+        if (lhs->mAvatar != rhs->mAvatar)
+        {
+            return lhs->mAvatar < rhs->mAvatar;
+        }
+        else if (lhs->mSkinInfo->mHash != rhs->mSkinInfo->mHash)
+        {
+            return lhs->mSkinInfo->mHash < rhs->mSkinInfo->mHash;
+        }
+        else if (lhs->getTexture() != rhs->getTexture())
+        {
+            return lhs->getTexture() < rhs->getTexture();
+        }
+        else if (lte->getBumpmap() != rte->getBumpmap())
+        {
+            return lte->getBumpmap() < rte->getBumpmap();
+        }
+        else if (LLPipeline::sRenderDeferred && lte->getMaterialID() != rte->getMaterialID())
+        {
+            return lte->getMaterialID() < rte->getMaterialID();
+        }
+        else // if (LLPipeline::sRenderDeferred && (lte->getMaterialParams() == rte->getMaterialParams()) && (lte->getShiny() != rte->getShiny()))
+        {
+            return lte->getShiny() < rte->getShiny();
+        }
+        
+    }
+};
+
 
-U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace** faces, U32 face_count, BOOL distance_sort, BOOL batch_textures, BOOL no_materials)
+U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace** faces, U32 face_count, BOOL distance_sort, BOOL batch_textures, BOOL rigged)
 {
     LL_PROFILE_ZONE_SCOPED;
 
@@ -6205,11 +6151,17 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace
 
 	{
         LL_PROFILE_ZONE_NAMED("genDrawInfo - sort");
-		if (!distance_sort)
-		{
-			//sort faces by things that break batches
-			std::sort(faces, faces+face_count, CompareBatchBreakerModified());
-		}
+        
+        if (rigged)
+        {
+            //sort faces by things that break batches, including avatar and mesh id
+            std::sort(faces, faces + face_count, CompareBatchBreakerRigged());
+        }
+        else if (!distance_sort)
+        {
+            //sort faces by things that break batches, not including avatar and mesh id
+            std::sort(faces, faces + face_count, CompareBatchBreakerModified());
+        }
 		else
 		{
 			//sort faces by distance
@@ -6226,11 +6178,6 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace
 	LLViewerTexture* last_tex = NULL;
 	S32 buffer_index = 0;
 
-	if (distance_sort)
-	{
-		buffer_index = -1;
-	}
-
 	S32 texture_index_channels = 1;
 	
 	if (gGLManager.mGLSLVersionMajor > 1 || gGLManager.mGLSLVersionMinor >= 30)
@@ -6242,6 +6189,16 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace
 	{
 		texture_index_channels = gDeferredAlphaProgram.mFeatures.mIndexedTextureChannels;
 	}
+    
+    if (rigged)
+    { //don't attempt distance sorting on rigged meshes, not likely to succeed and breaks batches
+        distance_sort = FALSE;
+    }
+
+    if (distance_sort)
+    {
+        buffer_index = -1;
+    }
 
 	static LLCachedControl<U32> max_texture_index(gSavedSettings, "RenderMaxTextureIndex", 16);
 	texture_index_channels = llmin(texture_index_channels, (S32) max_texture_index);
@@ -6256,7 +6213,9 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace
 		//pull off next face
 		LLFace* facep = *face_iter;
 		LLViewerTexture* tex = facep->getTexture();
-		LLMaterialPtr mat = facep->getTextureEntry()->getMaterialParams();
+        const LLTextureEntry* te = facep->getTextureEntry();
+		LLMaterialPtr mat = te->getMaterialParams();
+        LLMaterialID matId = te->getMaterialID();
 
 		if (distance_sort)
 		{
@@ -6379,11 +6338,14 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace
 				while (i != end_faces && 
 					(LLPipeline::sTextureBindTest || 
 						(distance_sort || 
-							((*i)->getTexture() == tex &&
-							((*i)->getTextureEntry()->getMaterialParams() == mat)))))
+							((*i)->getTexture() == tex))))
 				{
 					facep = *i;
-			
+                    const LLTextureEntry* nextTe = facep->getTextureEntry();
+                    if (nextTe->getMaterialID() != matId)
+                    {
+                        break;
+                    }
 
 					//face has no texture index
 					facep->mDrawInfo = NULL;
@@ -6473,8 +6435,6 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace
 
 					U32 te_idx = facep->getTEOffset();
 
-					llassert(!facep->isState(LLFace::RIGGED));
-
 					if (!facep->getGeometryVolume(*volume, te_idx, 
 						vobj->getRelativeXform(), vobj->getRelativeXformInvTrans(), index_offset,true))
 					{
@@ -6571,10 +6531,6 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace
 						}
 					}
 				}
-				else if (no_materials)
-				{
-					registerFace(group, facep, LLRenderPass::PASS_SIMPLE);
-				}
 				else if (transparent)
 				{
 					registerFace(group, facep, LLRenderPass::PASS_ALPHA);
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index cb54b1eaed9..4710fdb0851 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -7408,32 +7408,91 @@ void LLPipeline::doResetVertexBuffers(bool forced)
 	LLVOPartGroup::restoreGL();
 }
 
-void LLPipeline::renderObjects(U32 type, U32 mask, bool texture, bool batch_texture)
+void LLPipeline::renderObjects(U32 type, U32 mask, bool texture, bool batch_texture, bool rigged)
 {
 	assertInitialized();
 	gGL.loadMatrix(gGLModelView);
 	gGLLastMatrix = NULL;
-	mSimplePool->pushBatches(type, mask, texture, batch_texture);
+    if (rigged)
+    {
+        mSimplePool->pushRiggedBatches(type + 1, mask, texture, batch_texture);
+    }
+    else
+    {
+        mSimplePool->pushBatches(type, mask, texture, batch_texture);
+    }
 	gGL.loadMatrix(gGLModelView);
 	gGLLastMatrix = NULL;		
 }
 
-void LLPipeline::renderMaskedObjects(U32 type, U32 mask, bool texture, bool batch_texture)
+void LLPipeline::renderAlphaObjects(U32 mask, bool texture, bool batch_texture, bool rigged)
+{
+    LL_PROFILE_ZONE_SCOPED;
+    assertInitialized();
+    gGL.loadMatrix(gGLModelView);
+    gGLLastMatrix = NULL;
+    U32 type = LLRenderPass::PASS_ALPHA;
+    LLVOAvatar* lastAvatar = nullptr;
+    U64 lastMeshId = 0;
+    for (LLCullResult::drawinfo_iterator i = gPipeline.beginRenderMap(type); i != gPipeline.endRenderMap(type); ++i)
+    {
+        LLDrawInfo* pparams = *i;
+        if (pparams)
+        {
+            if (rigged)
+            {
+                if (pparams->mAvatar != nullptr)
+                {
+                    if (lastAvatar != pparams->mAvatar || lastMeshId != pparams->mSkinInfo->mHash)
+                    {
+                        mSimplePool->uploadMatrixPalette(*pparams);
+                        lastAvatar = pparams->mAvatar;
+                        lastMeshId = pparams->mSkinInfo->mHash;
+                    }
+
+                    mSimplePool->pushBatch(*pparams, mask | LLVertexBuffer::MAP_WEIGHT4, texture, batch_texture);
+                }
+            }
+            else if (pparams->mAvatar == nullptr)
+            {
+                mSimplePool->pushBatch(*pparams, mask, texture, batch_texture);
+            }
+        }
+    }
+    gGL.loadMatrix(gGLModelView);
+    gGLLastMatrix = NULL;
+}
+
+void LLPipeline::renderMaskedObjects(U32 type, U32 mask, bool texture, bool batch_texture, bool rigged)
 {
 	assertInitialized();
 	gGL.loadMatrix(gGLModelView);
 	gGLLastMatrix = NULL;
-	mAlphaMaskPool->pushMaskBatches(type, mask, texture, batch_texture);
+    if (rigged)
+    {
+        mAlphaMaskPool->pushRiggedMaskBatches(type+1, mask, texture, batch_texture);
+    }
+    else
+    {
+        mAlphaMaskPool->pushMaskBatches(type, mask, texture, batch_texture);
+    }
 	gGL.loadMatrix(gGLModelView);
 	gGLLastMatrix = NULL;		
 }
 
-void LLPipeline::renderFullbrightMaskedObjects(U32 type, U32 mask, bool texture, bool batch_texture)
+void LLPipeline::renderFullbrightMaskedObjects(U32 type, U32 mask, bool texture, bool batch_texture, bool rigged)
 {
 	assertInitialized();
 	gGL.loadMatrix(gGLModelView);
 	gGLLastMatrix = NULL;
-	mFullbrightAlphaMaskPool->pushMaskBatches(type, mask, texture, batch_texture);
+    if (rigged)
+    {
+        mFullbrightAlphaMaskPool->pushRiggedMaskBatches(type+1, mask, texture, batch_texture);
+    }
+    else
+    {
+        mFullbrightAlphaMaskPool->pushMaskBatches(type, mask, texture, batch_texture);
+    }
 	gGL.loadMatrix(gGLModelView);
 	gGLLastMatrix = NULL;		
 }
@@ -9587,198 +9646,207 @@ static LLTrace::BlockTimerStatHandle FTM_SHADOW_ALPHA_TREE("Alpha Tree");
 static LLTrace::BlockTimerStatHandle FTM_SHADOW_ALPHA_GRASS("Alpha Grass");
 static LLTrace::BlockTimerStatHandle FTM_SHADOW_FULLBRIGHT_ALPHA_MASKED("Fullbright Alpha Masked");
 
-void LLPipeline::renderShadow(glh::matrix4f& view, glh::matrix4f& proj, LLCamera& shadow_cam, LLCullResult &result, bool use_shader, bool use_occlusion, U32 target_width)
+void LLPipeline::renderShadow(glh::matrix4f& view, glh::matrix4f& proj, LLCamera& shadow_cam, LLCullResult& result, bool use_shader, bool use_occlusion, U32 target_width)
 {
-	LL_RECORD_BLOCK_TIME(FTM_SHADOW_RENDER);
+    LL_RECORD_BLOCK_TIME(FTM_SHADOW_RENDER);
 
-	//clip out geometry on the same side of water as the camera
-	S32 occlude = LLPipeline::sUseOcclusion;
-	if (!use_occlusion)
-	{
-		LLPipeline::sUseOcclusion = 0;
-	}
-	LLPipeline::sShadowRender = true;
-	
-	static const U32 types[] = { 
-		LLRenderPass::PASS_SIMPLE, 
-		LLRenderPass::PASS_FULLBRIGHT, 
-		LLRenderPass::PASS_SHINY, 
-		LLRenderPass::PASS_BUMP, 
-		LLRenderPass::PASS_FULLBRIGHT_SHINY ,
-		LLRenderPass::PASS_MATERIAL,
-		LLRenderPass::PASS_MATERIAL_ALPHA_EMISSIVE,
-		LLRenderPass::PASS_SPECMAP,
-		LLRenderPass::PASS_SPECMAP_EMISSIVE,
-		LLRenderPass::PASS_NORMMAP,
-		LLRenderPass::PASS_NORMMAP_EMISSIVE,
-		LLRenderPass::PASS_NORMSPEC,
-		LLRenderPass::PASS_NORMSPEC_EMISSIVE,
-	};
+    //clip out geometry on the same side of water as the camera
+    S32 occlude = LLPipeline::sUseOcclusion;
+    if (!use_occlusion)
+    {
+        LLPipeline::sUseOcclusion = 0;
+    }
+    LLPipeline::sShadowRender = true;
+
+    static const U32 types[] = {
+        LLRenderPass::PASS_SIMPLE,
+        LLRenderPass::PASS_FULLBRIGHT,
+        LLRenderPass::PASS_SHINY,
+        LLRenderPass::PASS_BUMP,
+        LLRenderPass::PASS_FULLBRIGHT_SHINY ,
+        LLRenderPass::PASS_MATERIAL,
+        LLRenderPass::PASS_MATERIAL_ALPHA_EMISSIVE,
+        LLRenderPass::PASS_SPECMAP,
+        LLRenderPass::PASS_SPECMAP_EMISSIVE,
+        LLRenderPass::PASS_NORMMAP,
+        LLRenderPass::PASS_NORMMAP_EMISSIVE,
+        LLRenderPass::PASS_NORMSPEC,
+        LLRenderPass::PASS_NORMSPEC_EMISSIVE,
+    };
+
+    LLGLEnable cull(GL_CULL_FACE);
+
+    //enable depth clamping if available
+    LLGLEnable depth_clamp(gGLManager.mHasDepthClamp ? GL_DEPTH_CLAMP : 0);
+
+    if (use_shader)
+    {
+        gDeferredShadowCubeProgram.bind();
+    }
 
-	LLGLEnable cull(GL_CULL_FACE);
+    LLRenderTarget& occlusion_target = mShadowOcclusion[LLViewerCamera::sCurCameraID - 1];
 
-	//enable depth clamping if available
-	LLGLEnable depth_clamp(gGLManager.mHasDepthClamp ? GL_DEPTH_CLAMP : 0);
+    occlusion_target.bindTarget();
+    updateCull(shadow_cam, result);
+    occlusion_target.flush();
 
-	if (use_shader)
-	{
-		gDeferredShadowCubeProgram.bind();
-	}
+    stateSort(shadow_cam, result);
 
-	LLRenderTarget& occlusion_target = mShadowOcclusion[LLViewerCamera::sCurCameraID-1];
 
-	occlusion_target.bindTarget();
-	updateCull(shadow_cam, result);
-	occlusion_target.flush();
+    //generate shadow map
+    gGL.matrixMode(LLRender::MM_PROJECTION);
+    gGL.pushMatrix();
+    gGL.loadMatrix(proj.m);
+    gGL.matrixMode(LLRender::MM_MODELVIEW);
+    gGL.pushMatrix();
+    gGL.loadMatrix(view.m);
 
-	stateSort(shadow_cam, result);
-	
-	
-	//generate shadow map
-	gGL.matrixMode(LLRender::MM_PROJECTION);
-	gGL.pushMatrix();
-	gGL.loadMatrix(proj.m);
-	gGL.matrixMode(LLRender::MM_MODELVIEW);
-	gGL.pushMatrix();
-	gGL.loadMatrix(view.m);
+    stop_glerror();
+    gGLLastMatrix = NULL;
 
-	stop_glerror();
-	gGLLastMatrix = NULL;
+    gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+    stop_glerror();
 
-	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-	
-	stop_glerror();
-	
     LLEnvironment& environment = LLEnvironment::instance();
 
-	LLVertexBuffer::unbind();
+    LLVertexBuffer::unbind();
 
-	{
-		if (!use_shader)
-		{ //occlusion program is general purpose depth-only no-textures
-			gOcclusionProgram.bind();
-		}
-		else
-		{
-			gDeferredShadowProgram.bind();
-            gDeferredShadowProgram.uniform1i(LLShaderMgr::SUN_UP_FACTOR, environment.getIsSunUp() ? 1 : 0);
-		}
+    for (int j = 0; j < 2; ++j) // 0 -- static, 1 -- rigged
+    {
+        bool rigged = j == 1;
+        if (!use_shader)
+        { //occlusion program is general purpose depth-only no-textures
+            gOcclusionProgram.bind(rigged);
+        }
+        else
+        {
+            gDeferredShadowProgram.bind(rigged);
+            LLGLSLShader::sCurBoundShaderPtr->uniform1i(LLShaderMgr::SUN_UP_FACTOR, environment.getIsSunUp() ? 1 : 0);
+        }
 
-		gGL.diffuseColor4f(1,1,1,1);
+        gGL.diffuseColor4f(1, 1, 1, 1);
 
         S32 shadow_detail = gSavedSettings.getS32("RenderShadowDetail");
 
         // if not using VSM, disable color writes
         if (shadow_detail <= 2)
         {
-		gGL.setColorMask(false, false);
+            gGL.setColorMask(false, false);
         }
-	
-		LL_RECORD_BLOCK_TIME(FTM_SHADOW_SIMPLE);
-		
-		gGL.getTexUnit(0)->disable();
-		for (U32 i = 0; i < sizeof(types)/sizeof(U32); ++i)
-		{
-			renderObjects(types[i], LLVertexBuffer::MAP_VERTEX, FALSE);
-		}
-		gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE);
-		if (!use_shader)
-		{
-			gOcclusionProgram.unbind();
-		}
-	}
-	
-	if (use_shader)
-	{
+
+        LL_RECORD_BLOCK_TIME(FTM_SHADOW_SIMPLE);
+
+        gGL.getTexUnit(0)->disable();
+        for (U32 i = 0; i < sizeof(types) / sizeof(U32); ++i)
+        {
+            renderObjects(types[i], LLVertexBuffer::MAP_VERTEX, FALSE, FALSE, rigged);
+        }
+        gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE);
+        if (!use_shader)
+        {
+            gOcclusionProgram.unbind();
+        }
+
+
+    }
+
+    if (use_shader)
+    {
         LL_RECORD_BLOCK_TIME(FTM_SHADOW_GEOM);
 
-		gDeferredShadowProgram.unbind();
-		renderGeomShadow(shadow_cam);
-		gDeferredShadowProgram.bind();
+        gDeferredShadowProgram.unbind();
+        renderGeomShadow(shadow_cam);
+        gDeferredShadowProgram.bind();
         gDeferredShadowProgram.uniform1i(LLShaderMgr::SUN_UP_FACTOR, environment.getIsSunUp() ? 1 : 0);
-	}
-	else
-	{
+    }
+    else
+    {
         LL_RECORD_BLOCK_TIME(FTM_SHADOW_GEOM);
 
-		renderGeomShadow(shadow_cam);
-	}
+        renderGeomShadow(shadow_cam);
+    }
 
-	{
-		LL_RECORD_BLOCK_TIME(FTM_SHADOW_ALPHA);
+    {
+        LL_RECORD_BLOCK_TIME(FTM_SHADOW_ALPHA);
 
-		gDeferredShadowAlphaMaskProgram.bind();
-		gDeferredShadowAlphaMaskProgram.uniform1f(LLShaderMgr::DEFERRED_SHADOW_TARGET_WIDTH, (float)target_width);
-        gDeferredShadowAlphaMaskProgram.uniform1i(LLShaderMgr::SUN_UP_FACTOR, environment.getIsSunUp() ? 1 : 0);
+        for (int i = 0; i < 2; ++i)
+        {
+            bool rigged = i == 1;
 
-		U32 mask =	LLVertexBuffer::MAP_VERTEX | 
-					LLVertexBuffer::MAP_TEXCOORD0 | 
-					LLVertexBuffer::MAP_COLOR | 
-					LLVertexBuffer::MAP_TEXTURE_INDEX;
+            gDeferredShadowAlphaMaskProgram.bind(rigged);
+            LLGLSLShader::sCurBoundShaderPtr->uniform1f(LLShaderMgr::DEFERRED_SHADOW_TARGET_WIDTH, (float)target_width);
+            LLGLSLShader::sCurBoundShaderPtr->uniform1i(LLShaderMgr::SUN_UP_FACTOR, environment.getIsSunUp() ? 1 : 0);
 
-        {
-            LL_RECORD_BLOCK_TIME(FTM_SHADOW_ALPHA_MASKED);
-		renderMaskedObjects(LLRenderPass::PASS_ALPHA_MASK, mask, TRUE, TRUE);
-        }
+            U32 mask = LLVertexBuffer::MAP_VERTEX |
+                LLVertexBuffer::MAP_TEXCOORD0 |
+                LLVertexBuffer::MAP_COLOR |
+                LLVertexBuffer::MAP_TEXTURE_INDEX;
 
-        {
-            LL_RECORD_BLOCK_TIME(FTM_SHADOW_ALPHA_BLEND);
-		gDeferredShadowAlphaMaskProgram.setMinimumAlpha(0.598f);
-		renderObjects(LLRenderPass::PASS_ALPHA, mask, TRUE, TRUE);
-        }
+            {
+                LL_RECORD_BLOCK_TIME(FTM_SHADOW_ALPHA_MASKED);
+                renderMaskedObjects(LLRenderPass::PASS_ALPHA_MASK, mask, TRUE, TRUE, rigged);
+            }
 
-        {
-            LL_RECORD_BLOCK_TIME(FTM_SHADOW_FULLBRIGHT_ALPHA_MASKED);
-            gDeferredShadowFullbrightAlphaMaskProgram.bind();
-            gDeferredShadowFullbrightAlphaMaskProgram.uniform1f(LLShaderMgr::DEFERRED_SHADOW_TARGET_WIDTH, (float)target_width);
-            gDeferredShadowFullbrightAlphaMaskProgram.uniform1i(LLShaderMgr::SUN_UP_FACTOR, environment.getIsSunUp() ? 1 : 0);
-            renderFullbrightMaskedObjects(LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK, mask, TRUE, TRUE);
-        }
+            {
+                LL_RECORD_BLOCK_TIME(FTM_SHADOW_ALPHA_BLEND);
+                LLGLSLShader::sCurBoundShaderPtr->setMinimumAlpha(0.598f);
+                renderAlphaObjects(mask, TRUE, TRUE, rigged);
+            }
 
-		mask = mask & ~LLVertexBuffer::MAP_TEXTURE_INDEX;
 
-        {
-            LL_RECORD_BLOCK_TIME(FTM_SHADOW_ALPHA_TREE);
-		gDeferredTreeShadowProgram.bind();
-            gDeferredTreeShadowProgram.uniform1i(LLShaderMgr::SUN_UP_FACTOR, environment.getIsSunUp() ? 1 : 0);
-		renderMaskedObjects(LLRenderPass::PASS_NORMSPEC_MASK, mask);
-		renderMaskedObjects(LLRenderPass::PASS_MATERIAL_ALPHA_MASK, mask);
-		renderMaskedObjects(LLRenderPass::PASS_SPECMAP_MASK, mask);
-		renderMaskedObjects(LLRenderPass::PASS_NORMMAP_MASK, mask);
+            {
+                LL_RECORD_BLOCK_TIME(FTM_SHADOW_FULLBRIGHT_ALPHA_MASKED);
+                gDeferredShadowFullbrightAlphaMaskProgram.bind(rigged);
+                LLGLSLShader::sCurBoundShaderPtr->uniform1f(LLShaderMgr::DEFERRED_SHADOW_TARGET_WIDTH, (float)target_width);
+                LLGLSLShader::sCurBoundShaderPtr->uniform1i(LLShaderMgr::SUN_UP_FACTOR, environment.getIsSunUp() ? 1 : 0);
+                renderFullbrightMaskedObjects(LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK, mask, TRUE, TRUE, rigged);
+            }
+
+
+            {
+                LL_RECORD_BLOCK_TIME(FTM_SHADOW_ALPHA_GRASS);
+                gDeferredTreeShadowProgram.bind(rigged);
+                if (i == 0)
+                {
+                    LLGLSLShader::sCurBoundShaderPtr->setMinimumAlpha(0.598f);
+                    renderObjects(LLRenderPass::PASS_GRASS, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, TRUE);
+                }
+
+                U32 no_idx_mask = mask & ~LLVertexBuffer::MAP_TEXTURE_INDEX;
+                renderMaskedObjects(LLRenderPass::PASS_NORMSPEC_MASK, no_idx_mask, true, false, rigged);
+                renderMaskedObjects(LLRenderPass::PASS_MATERIAL_ALPHA_MASK, no_idx_mask, true, false, rigged);
+                renderMaskedObjects(LLRenderPass::PASS_SPECMAP_MASK, no_idx_mask, true, false, rigged);
+                renderMaskedObjects(LLRenderPass::PASS_NORMMAP_MASK, no_idx_mask, true, false, rigged);
+            }
         }
-		
-        {
-            LL_RECORD_BLOCK_TIME(FTM_SHADOW_ALPHA_GRASS);
-		gDeferredTreeShadowProgram.setMinimumAlpha(0.598f);
-		renderObjects(LLRenderPass::PASS_GRASS, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, TRUE);
-	}
     }
 
-	//glCullFace(GL_BACK);
+    //glCullFace(GL_BACK);
 
-	gDeferredShadowCubeProgram.bind();
-	gGLLastMatrix = NULL;
-	gGL.loadMatrix(gGLModelView);
+    gDeferredShadowCubeProgram.bind();
+    gGLLastMatrix = NULL;
+    gGL.loadMatrix(gGLModelView);
 
-	LLRenderTarget& occlusion_source = mShadow[LLViewerCamera::sCurCameraID-1];
+    LLRenderTarget& occlusion_source = mShadow[LLViewerCamera::sCurCameraID - 1];
 
-	doOcclusion(shadow_cam, occlusion_source, occlusion_target);
+    doOcclusion(shadow_cam, occlusion_source, occlusion_target);
 
-	if (use_shader)
-	{
-		gDeferredShadowProgram.unbind();
-	}
-	
-	gGL.setColorMask(true, true);
-			
-	gGL.matrixMode(LLRender::MM_PROJECTION);
-	gGL.popMatrix();
-	gGL.matrixMode(LLRender::MM_MODELVIEW);
-	gGL.popMatrix();
-	gGLLastMatrix = NULL;
+    if (use_shader)
+    {
+        gDeferredShadowProgram.unbind();
+    }
 
-	LLPipeline::sUseOcclusion = occlude;
-	LLPipeline::sShadowRender = false;
+    gGL.setColorMask(true, true);
+
+    gGL.matrixMode(LLRender::MM_PROJECTION);
+    gGL.popMatrix();
+    gGL.matrixMode(LLRender::MM_MODELVIEW);
+    gGL.popMatrix();
+    gGLLastMatrix = NULL;
+
+    LLPipeline::sUseOcclusion = occlude;
+    LLPipeline::sShadowRender = false;
 }
 
 bool LLPipeline::getVisiblePointCloud(LLCamera& camera, LLVector3& min, LLVector3& max, std::vector<LLVector3>& fp, LLVector3 light_dir)
@@ -10109,6 +10177,29 @@ void LLPipeline::generateSunShadow(LLCamera& camera)
 					LLPipeline::RENDER_TYPE_PASS_NORMSPEC_BLEND,
 					LLPipeline::RENDER_TYPE_PASS_NORMSPEC_MASK,
 					LLPipeline::RENDER_TYPE_PASS_NORMSPEC_EMISSIVE,
+                    LLPipeline::RENDER_TYPE_PASS_ALPHA_MASK_RIGGED,
+                    LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT_ALPHA_MASK_RIGGED,
+                    LLPipeline::RENDER_TYPE_PASS_SIMPLE_RIGGED,
+                    LLPipeline::RENDER_TYPE_PASS_BUMP_RIGGED,
+                    LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT_RIGGED,
+                    LLPipeline::RENDER_TYPE_PASS_SHINY_RIGGED,
+                    LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT_SHINY_RIGGED,
+                    LLPipeline::RENDER_TYPE_PASS_MATERIAL_RIGGED,
+                    LLPipeline::RENDER_TYPE_PASS_MATERIAL_ALPHA_RIGGED,
+                    LLPipeline::RENDER_TYPE_PASS_MATERIAL_ALPHA_MASK_RIGGED,
+                    LLPipeline::RENDER_TYPE_PASS_MATERIAL_ALPHA_EMISSIVE_RIGGED,
+                    LLPipeline::RENDER_TYPE_PASS_SPECMAP_RIGGED,
+                    LLPipeline::RENDER_TYPE_PASS_SPECMAP_BLEND_RIGGED,
+                    LLPipeline::RENDER_TYPE_PASS_SPECMAP_MASK_RIGGED,
+                    LLPipeline::RENDER_TYPE_PASS_SPECMAP_EMISSIVE_RIGGED,
+                    LLPipeline::RENDER_TYPE_PASS_NORMMAP_RIGGED,
+                    LLPipeline::RENDER_TYPE_PASS_NORMMAP_BLEND_RIGGED,
+                    LLPipeline::RENDER_TYPE_PASS_NORMMAP_MASK_RIGGED,
+                    LLPipeline::RENDER_TYPE_PASS_NORMMAP_EMISSIVE_RIGGED,
+                    LLPipeline::RENDER_TYPE_PASS_NORMSPEC_RIGGED,
+                    LLPipeline::RENDER_TYPE_PASS_NORMSPEC_BLEND_RIGGED,
+                    LLPipeline::RENDER_TYPE_PASS_NORMSPEC_MASK_RIGGED,
+                    LLPipeline::RENDER_TYPE_PASS_NORMSPEC_EMISSIVE_RIGGED,
 					END_RENDER_TYPES);
 
 	gGL.setColorMask(false, false);
@@ -10835,6 +10926,21 @@ void LLPipeline::renderGroups(LLRenderPass* pass, U32 type, U32 mask, bool textu
 	}
 }
 
+void LLPipeline::renderRiggedGroups(LLRenderPass* pass, U32 type, U32 mask, bool texture)
+{
+    for (LLCullResult::sg_iterator i = sCull->beginVisibleGroups(); i != sCull->endVisibleGroups(); ++i)
+    {
+        LLSpatialGroup* group = *i;
+        if (!group->isDead() &&
+            (!sUseOcclusion || !group->isOcclusionState(LLSpatialGroup::OCCLUDED)) &&
+            gPipeline.hasRenderType(group->getSpatialPartition()->mDrawableType) &&
+            group->mDrawMap.find(type) != group->mDrawMap.end())
+        {
+            pass->renderRiggedGroup(group, type, mask, texture);
+        }
+    }
+}
+
 static LLTrace::BlockTimerStatHandle FTM_GENERATE_IMPOSTOR("Generate Impostor");
 
 void LLPipeline::generateImpostor(LLVOAvatar* avatar)
@@ -10870,37 +10976,31 @@ void LLPipeline::generateImpostor(LLVOAvatar* avatar)
 	
 	if (visually_muted || too_complex)
 	{
+        // only show jelly doll geometry
 		andRenderTypeMask(LLPipeline::RENDER_TYPE_AVATAR,
 							LLPipeline::RENDER_TYPE_CONTROL_AV,
 							END_RENDER_TYPES);
 	}
 	else
 	{
-		andRenderTypeMask(LLPipeline::RENDER_TYPE_ALPHA,
-			LLPipeline::RENDER_TYPE_FULLBRIGHT,
-			LLPipeline::RENDER_TYPE_VOLUME,
-			LLPipeline::RENDER_TYPE_GLOW,
-						LLPipeline::RENDER_TYPE_BUMP,
-						LLPipeline::RENDER_TYPE_PASS_SIMPLE,
-						LLPipeline::RENDER_TYPE_PASS_ALPHA,
-						LLPipeline::RENDER_TYPE_PASS_ALPHA_MASK,
-			LLPipeline::RENDER_TYPE_PASS_BUMP,
-			LLPipeline::RENDER_TYPE_PASS_POST_BUMP,
-						LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT,
-						LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT_ALPHA_MASK,
-						LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT_SHINY,
-			LLPipeline::RENDER_TYPE_PASS_GLOW,
-			LLPipeline::RENDER_TYPE_PASS_GRASS,
-						LLPipeline::RENDER_TYPE_PASS_SHINY,
-						LLPipeline::RENDER_TYPE_PASS_INVISIBLE,
-						LLPipeline::RENDER_TYPE_PASS_INVISI_SHINY,
-			LLPipeline::RENDER_TYPE_AVATAR,
-			LLPipeline::RENDER_TYPE_CONTROL_AV,
-			LLPipeline::RENDER_TYPE_ALPHA_MASK,
-			LLPipeline::RENDER_TYPE_FULLBRIGHT_ALPHA_MASK,
-			LLPipeline::RENDER_TYPE_INVISIBLE,
-			LLPipeline::RENDER_TYPE_SIMPLE,
-						END_RENDER_TYPES);
+        //hide world geometry
+        clearRenderTypeMask(
+            RENDER_TYPE_SKY,
+            RENDER_TYPE_WL_SKY,
+            RENDER_TYPE_GROUND,
+            RENDER_TYPE_TERRAIN,
+            RENDER_TYPE_GRASS,
+            RENDER_TYPE_CONTROL_AV, // Animesh
+            RENDER_TYPE_TREE,
+            RENDER_TYPE_VOIDWATER,
+            RENDER_TYPE_WATER,
+            RENDER_TYPE_PASS_GRASS,
+            RENDER_TYPE_HUD,
+            RENDER_TYPE_PARTICLES,
+            RENDER_TYPE_CLOUDS,
+            RENDER_TYPE_HUD_PARTICLES,
+            END_RENDER_TYPES
+         );
 	}
 	
 	S32 occlusion = sUseOcclusion;
diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h
index b87a726647b..110df8c979d 100644
--- a/indra/newview/pipeline.h
+++ b/indra/newview/pipeline.h
@@ -266,11 +266,13 @@ class LLPipeline
     void touchTextures(LLDrawInfo* info);
 	void forAllVisibleDrawables(void (*func)(LLDrawable*));
 
-	void renderObjects(U32 type, U32 mask, bool texture = true, bool batch_texture = false);
-	void renderMaskedObjects(U32 type, U32 mask, bool texture = true, bool batch_texture = false);
-    void renderFullbrightMaskedObjects(U32 type, U32 mask, bool texture = true, bool batch_texture = false);
+    void renderObjects(U32 type, U32 mask, bool texture = true, bool batch_texture = false, bool rigged = false);
+    void renderAlphaObjects(U32 mask, bool texture = true, bool batch_texture = false, bool rigged = false);
+	void renderMaskedObjects(U32 type, U32 mask, bool texture = true, bool batch_texture = false, bool rigged = false);
+    void renderFullbrightMaskedObjects(U32 type, U32 mask, bool texture = true, bool batch_texture = false, bool rigged = false);
 
 	void renderGroups(LLRenderPass* pass, U32 type, U32 mask, bool texture);
+    void renderRiggedGroups(LLRenderPass* pass, U32 type, U32 mask, bool texture);
 
 	void grabReferences(LLCullResult& result);
 	void clearReferences();
@@ -457,34 +459,61 @@ class LLPipeline
  		RENDER_TYPE_ALPHA						= LLDrawPool::POOL_ALPHA,
 		RENDER_TYPE_GLOW						= LLDrawPool::POOL_GLOW,
 		RENDER_TYPE_PASS_SIMPLE 				= LLRenderPass::PASS_SIMPLE,
+        RENDER_TYPE_PASS_SIMPLE_RIGGED = LLRenderPass::PASS_SIMPLE_RIGGED,
 		RENDER_TYPE_PASS_GRASS					= LLRenderPass::PASS_GRASS,
 		RENDER_TYPE_PASS_FULLBRIGHT				= LLRenderPass::PASS_FULLBRIGHT,
+        RENDER_TYPE_PASS_FULLBRIGHT_RIGGED = LLRenderPass::PASS_FULLBRIGHT,
 		RENDER_TYPE_PASS_INVISIBLE				= LLRenderPass::PASS_INVISIBLE,
+        RENDER_TYPE_PASS_INVISIBLE_RIGGED = LLRenderPass::PASS_INVISIBLE_RIGGED,
 		RENDER_TYPE_PASS_INVISI_SHINY			= LLRenderPass::PASS_INVISI_SHINY,
+        RENDER_TYPE_PASS_INVISI_SHINY_RIGGED = LLRenderPass::PASS_INVISI_SHINY_RIGGED,
 		RENDER_TYPE_PASS_FULLBRIGHT_SHINY		= LLRenderPass::PASS_FULLBRIGHT_SHINY,
+        RENDER_TYPE_PASS_FULLBRIGHT_SHINY_RIGGED = LLRenderPass::PASS_FULLBRIGHT_SHINY_RIGGED,
 		RENDER_TYPE_PASS_SHINY					= LLRenderPass::PASS_SHINY,
+        RENDER_TYPE_PASS_SHINY_RIGGED = LLRenderPass::PASS_SHINY_RIGGED,
 		RENDER_TYPE_PASS_BUMP					= LLRenderPass::PASS_BUMP,
+        RENDER_TYPE_PASS_BUMP_RIGGED = LLRenderPass::PASS_BUMP_RIGGED,
 		RENDER_TYPE_PASS_POST_BUMP				= LLRenderPass::PASS_POST_BUMP,
+        RENDER_TYPE_PASS_POST_BUMP_RIGGED = LLRenderPass::PASS_POST_BUMP_RIGGED,
 		RENDER_TYPE_PASS_GLOW					= LLRenderPass::PASS_GLOW,
+        RENDER_TYPE_PASS_GLOW_RIGGED = LLRenderPass::PASS_GLOW_RIGGED,
 		RENDER_TYPE_PASS_ALPHA					= LLRenderPass::PASS_ALPHA,
 		RENDER_TYPE_PASS_ALPHA_MASK				= LLRenderPass::PASS_ALPHA_MASK,
+        RENDER_TYPE_PASS_ALPHA_MASK_RIGGED = LLRenderPass::PASS_ALPHA_MASK_RIGGED,
 		RENDER_TYPE_PASS_FULLBRIGHT_ALPHA_MASK	= LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK,
+        RENDER_TYPE_PASS_FULLBRIGHT_ALPHA_MASK_RIGGED = LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK_RIGGED,
 		RENDER_TYPE_PASS_MATERIAL				= LLRenderPass::PASS_MATERIAL,
+        RENDER_TYPE_PASS_MATERIAL_RIGGED = LLRenderPass::PASS_MATERIAL_RIGGED,
 		RENDER_TYPE_PASS_MATERIAL_ALPHA			= LLRenderPass::PASS_MATERIAL_ALPHA,
+        RENDER_TYPE_PASS_MATERIAL_ALPHA_RIGGED = LLRenderPass::PASS_MATERIAL_ALPHA_RIGGED,
 		RENDER_TYPE_PASS_MATERIAL_ALPHA_MASK	= LLRenderPass::PASS_MATERIAL_ALPHA_MASK,
+        RENDER_TYPE_PASS_MATERIAL_ALPHA_MASK_RIGGED = LLRenderPass::PASS_MATERIAL_ALPHA_MASK_RIGGED,
 		RENDER_TYPE_PASS_MATERIAL_ALPHA_EMISSIVE= LLRenderPass::PASS_MATERIAL_ALPHA_EMISSIVE,
+        RENDER_TYPE_PASS_MATERIAL_ALPHA_EMISSIVE_RIGGED = LLRenderPass::PASS_MATERIAL_ALPHA_EMISSIVE_RIGGED,
 		RENDER_TYPE_PASS_SPECMAP				= LLRenderPass::PASS_SPECMAP,
+        RENDER_TYPE_PASS_SPECMAP_RIGGED = LLRenderPass::PASS_SPECMAP_RIGGED,
 		RENDER_TYPE_PASS_SPECMAP_BLEND			= LLRenderPass::PASS_SPECMAP_BLEND,
+        RENDER_TYPE_PASS_SPECMAP_BLEND_RIGGED = LLRenderPass::PASS_SPECMAP_BLEND_RIGGED,
 		RENDER_TYPE_PASS_SPECMAP_MASK			= LLRenderPass::PASS_SPECMAP_MASK,
+        RENDER_TYPE_PASS_SPECMAP_MASK_RIGGED = LLRenderPass::PASS_SPECMAP_MASK_RIGGED,
 		RENDER_TYPE_PASS_SPECMAP_EMISSIVE		= LLRenderPass::PASS_SPECMAP_EMISSIVE,
+        RENDER_TYPE_PASS_SPECMAP_EMISSIVE_RIGGED = LLRenderPass::PASS_SPECMAP_EMISSIVE_RIGGED,
 		RENDER_TYPE_PASS_NORMMAP				= LLRenderPass::PASS_NORMMAP,
+        RENDER_TYPE_PASS_NORMMAP_RIGGED = LLRenderPass::PASS_NORMMAP_RIGGED,
 		RENDER_TYPE_PASS_NORMMAP_BLEND			= LLRenderPass::PASS_NORMMAP_BLEND,
+        RENDER_TYPE_PASS_NORMMAP_BLEND_RIGGED = LLRenderPass::PASS_NORMMAP_BLEND_RIGGED,
 		RENDER_TYPE_PASS_NORMMAP_MASK			= LLRenderPass::PASS_NORMMAP_MASK,
+        RENDER_TYPE_PASS_NORMMAP_MASK_RIGGED = LLRenderPass::PASS_NORMMAP_MASK_RIGGED,
 		RENDER_TYPE_PASS_NORMMAP_EMISSIVE		= LLRenderPass::PASS_NORMMAP_EMISSIVE,
+        RENDER_TYPE_PASS_NORMMAP_EMISSIVE_RIGGED = LLRenderPass::PASS_NORMMAP_EMISSIVE_RIGGED,
 		RENDER_TYPE_PASS_NORMSPEC				= LLRenderPass::PASS_NORMSPEC,
+        RENDER_TYPE_PASS_NORMSPEC_RIGGED = LLRenderPass::PASS_NORMSPEC_RIGGED,
 		RENDER_TYPE_PASS_NORMSPEC_BLEND			= LLRenderPass::PASS_NORMSPEC_BLEND,
+        RENDER_TYPE_PASS_NORMSPEC_BLEND_RIGGED = LLRenderPass::PASS_NORMSPEC_BLEND_RIGGED,
 		RENDER_TYPE_PASS_NORMSPEC_MASK			= LLRenderPass::PASS_NORMSPEC_MASK,
+        RENDER_TYPE_PASS_NORMSPEC_MASK_RIGGED = LLRenderPass::PASS_NORMSPEC_MASK_RIGGED,
 		RENDER_TYPE_PASS_NORMSPEC_EMISSIVE		= LLRenderPass::PASS_NORMSPEC_EMISSIVE,
+        RENDER_TYPE_PASS_NORMSPEC_EMISSIVE_RIGGED = LLRenderPass::PASS_NORMSPEC_EMISSIVE_RIGGED,
 		// Following are object types (only used in drawable mRenderType)
 		RENDER_TYPE_HUD = LLRenderPass::NUM_RENDER_TYPES,
 		RENDER_TYPE_VOLUME,
-- 
GitLab