diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl
index 15e02c9551c012714594fae00453f4100de83599..d5b4e278bc574290d817c217daa6eaa97f3c96da 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl
@@ -53,8 +53,11 @@ VARYING vec3 vary_position;
 VARYING vec4 vertex_color;
 VARYING vec2 vary_texcoord0;
 #ifdef HAS_NORMAL_MAP
-    VARYING vec3 vary_normal;
-    VARYING vec2 vary_texcoord1;
+VARYING vec3 vary_normal;
+VARYING vec3 vary_mat0;
+VARYING vec3 vary_mat1;
+VARYING vec3 vary_mat2;
+VARYING vec2 vary_texcoord1;
 #endif
 
 #ifdef HAS_SPECULAR_MAP
@@ -77,24 +80,31 @@ void main()
 
 #ifdef HAS_NORMAL_MAP
     vec4 norm = texture2D(bumpMap, vary_texcoord1.xy);
-    vec3 tnorm = norm.xyz * 2 - 1;
+    norm.xyz = norm.xyz * 2 - 1;
+
+	vec3 tnorm = vec3(dot(norm.xyz,vary_mat0),
+			  dot(norm.xyz,vary_mat1),
+			  dot(norm.xyz,vary_mat2));
 #else
     vec4 norm = vec4(0,0,0,1.0);
 //    vec3 tnorm = vary_normal;
     vec3 tnorm = vec3(0,0,1);
 #endif
 
+    tnorm = normalize(tnorm.xyz);
+
+    norm.xyz = normalize(tnorm.xyz);
     // RGB = Occlusion, Roughness, Metal
     // default values
     //   occlusion ?
     //   roughness 1.0
     //   metal     1.0
 #ifdef HAS_SPECULAR_MAP
-    vec3 spec = texture2D(specularMap, vary_texcoord0.xy).rgb; // TODO: FIXME: vary_texcoord2
+    vec3 spec = texture2D(specularMap, vary_texcoord2.xy).rgb;
 #else
-    vec3 spec = vec3(0,1,1);
+    vec3 spec = vec3(1,1,1);
 #endif
-    norm.xyz = normalize(tnorm.xyz);
+    
 
 #if DEBUG_BASIC
     col.rgb = vec3( 1, 0, 1 );
@@ -111,6 +121,6 @@ void main()
 
     frag_data[0] = vec4(col, 0.0);
     frag_data[1] = vec4(spec.rgb, vertex_color.a);                                      // Occlusion, Roughness, Metal
-    frag_data[2] = vec4(encode_normal(norm.xyz), vertex_color.a, GBUFFER_FLAG_HAS_PBR); //
+    frag_data[2] = vec4(encode_normal(tnorm), vertex_color.a, GBUFFER_FLAG_HAS_PBR); //
     frag_data[3] = vec4(emissive,0);                                                    // Emissive
 }
diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbropaqueV.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbropaqueV.glsl
index 58048687a5d13fec991e649bfceb67b45caddf6f..a2606ed771ce8b4cb223fe12964372ccc73afa76 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/pbropaqueV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/pbropaqueV.glsl
@@ -23,59 +23,122 @@
  * $/LicenseInfo$
  */
 
-uniform mat3 normal_matrix;
-uniform mat4 texture_matrix0;
+#define DIFFUSE_ALPHA_MODE_IGNORE 0
+#define DIFFUSE_ALPHA_MODE_BLEND 1
+#define DIFFUSE_ALPHA_MODE_MASK 2
+#define DIFFUSE_ALPHA_MODE_EMISSIVE 3
+
+#ifdef HAS_SKIN
 uniform mat4 modelview_matrix;
+uniform mat4 projection_matrix;
+mat4 getObjectSkinnedTransform();
+#else
+uniform mat3 normal_matrix;
 uniform mat4 modelview_projection_matrix;
+#endif
+
+#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_BLEND)
+
+#if !defined(HAS_SKIN)
+uniform mat4 modelview_matrix;
+#endif
+
+VARYING vec3 vary_position;
+
+#endif
+
+uniform mat4 texture_matrix0;
 
 ATTRIBUTE vec3 position;
 ATTRIBUTE vec4 diffuse_color;
 ATTRIBUTE vec3 normal;
 ATTRIBUTE vec2 texcoord0;
 
+
 #ifdef HAS_NORMAL_MAP
-    ATTRIBUTE vec4 tangent;
-    ATTRIBUTE vec2 texcoord1;
-    VARYING   vec3 vary_mat0;
-    VARYING   vec3 vary_mat1;
-    VARYING   vec3 vary_mat2;
-#endif
+ATTRIBUTE vec4 tangent;
+ATTRIBUTE vec2 texcoord1;
 
-#if HAS_SPECULAR_MAP
-    ATTRIBUTE vec2 texcoord2;
-#endif
+VARYING vec3 vary_mat0;
+VARYING vec3 vary_mat1;
+VARYING vec3 vary_mat2;
 
-VARYING vec3 vary_position;
+VARYING vec2 vary_texcoord1;
+#else
 VARYING vec3 vary_normal;
+#endif
+
+#ifdef HAS_SPECULAR_MAP
+ATTRIBUTE vec2 texcoord2;
+VARYING vec2 vary_texcoord2;
+#endif
+ 
 VARYING vec4 vertex_color;
 VARYING vec2 vary_texcoord0;
-VARYING vec2 vary_texcoord1; // normal map
-VARYING vec2 vary_texcoord2; // specular map
-
-void passTextureIndex();
 
 void main()
 {
-    //transform vertex
-    gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0);
-    vary_position = (modelview_matrix * vec4(position.xyz,1.0)).xyz;
-    vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy;
+#ifdef HAS_SKIN
+	mat4 mat = getObjectSkinnedTransform();
+
+	mat = modelview_matrix * mat;
 
-    passTextureIndex();
-    vary_normal = normalize(normal_matrix * normal);
+	vec3 pos = (mat*vec4(position.xyz,1.0)).xyz;
+
+#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_BLEND)
+	vary_position = pos;
+#endif
 
+	gl_Position = projection_matrix*vec4(pos,1.0);
+
+#else
+	//transform vertex
+	gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0); 
+
+#endif
+	
+	vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy;
+	
 #ifdef HAS_NORMAL_MAP
-    //vary_texcoord1 = (texture_matrix0 * vec4(texcoord1,0,1)).xy;
-    vary_texcoord1 = texcoord1;
-//  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);
+	vary_texcoord1 = (texture_matrix0 * vec4(texcoord1,0,1)).xy;
 #endif
 
 #ifdef HAS_SPECULAR_MAP
-    //vary_texcoord2 = (texture_matrix0 * vec4(texcoord2,0,1)).xy;
-    vary_texcoord2 = texcoord2;
+	vary_texcoord2 = (texture_matrix0 * vec4(texcoord2,0,1)).xy;
 #endif
 
-    vertex_color = diffuse_color;
+#ifdef HAS_SKIN
+	vec3 n = normalize((mat*vec4(normal.xyz+position.xyz,1.0)).xyz-pos.xyz);
+#ifdef HAS_NORMAL_MAP
+	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);
+#else //HAS_NORMAL_MAP
+vary_normal  = n;
+#endif //HAS_NORMAL_MAP
+#else //HAS_SKIN
+	vec3 n = normalize(normal_matrix * normal);
+#ifdef HAS_NORMAL_MAP
+	vec3 t = normalize(normal_matrix * tangent.xyz);
+	vec3 b = cross(n,t)*tangent.w;
+	//vec3 t = cross(b,n) * binormal.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);
+#else //HAS_NORMAL_MAP
+	vary_normal = n;
+#endif //HAS_NORMAL_MAP
+#endif //HAS_SKIN
+	
+	vertex_color = diffuse_color;
+
+#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_BLEND)
+#if !defined(HAS_SKIN)
+	vary_position = (modelview_matrix*vec4(position.xyz, 1.0)).xyz;
+#endif
+#endif
 }
diff --git a/indra/newview/lldrawpoolpbropaque.cpp b/indra/newview/lldrawpoolpbropaque.cpp
index f23d2d04a8e19b0960e7b117d5af79a05eb873cf..86b3ac0d46e3aaaf97aef106134a27d7913e991b 100644
--- a/indra/newview/lldrawpoolpbropaque.cpp
+++ b/indra/newview/lldrawpoolpbropaque.cpp
@@ -105,7 +105,7 @@ void LLDrawPoolPBROpaque::renderDeferred(S32 pass)
         {
             if (params.mTexture.notNull())
             {
-                gGL.getTexUnit(-1)->bindFast(params.mTexture); // diffuse
+                gGL.getTexUnit(0)->bindFast(params.mTexture); // diffuse
             }
         }
 
@@ -120,9 +120,9 @@ void LLDrawPoolPBROpaque::renderDeferred(S32 pass)
         }
 
         // Similar to LLDrawPooLMaterials::pushMaterialsBatch(params, getVertexDataMask(), false);
-
-        LLRenderPass::applyModelMatrix(params);
-        params.mVertexBuffer->setBufferFast(getVertexDataMask());
-        params.mVertexBuffer->drawRangeFast(params.mDrawMode, params.mStart, params.mEnd, params.mCount, params.mOffset);
+        LLRenderPass::pushBatch(params, getVertexDataMask(), FALSE, FALSE);
+        //LLRenderPass::applyModelMatrix(params);
+        //params.mVertexBuffer->setBufferFast(getVertexDataMask());
+        //params.mVertexBuffer->drawRangeFast(params.mDrawMode, params.mStart, params.mEnd, params.mCount, params.mOffset);
     }
 }
diff --git a/indra/newview/lldrawpoolpbropaque.h b/indra/newview/lldrawpoolpbropaque.h
index 413bcb800cd58ec85a21f9400df7b2a0e88b2702..478d4e1bd44ecf28dcc9d3e7b26baeb97a91ad86 100644
--- a/indra/newview/lldrawpoolpbropaque.h
+++ b/indra/newview/lldrawpoolpbropaque.h
@@ -41,6 +41,7 @@ class LLDrawPoolPBROpaque : public LLRenderPass
                          | LLVertexBuffer::MAP_TEXCOORD0 // Diffuse
                          | LLVertexBuffer::MAP_TEXCOORD1 // Normal
                          | LLVertexBuffer::MAP_TEXCOORD2 // Spec <-- ORM Occlusion Roughness Metal
+                         | LLVertexBuffer::MAP_TANGENT
                          | LLVertexBuffer::MAP_COLOR
     };
     virtual U32 getVertexDataMask() { return VERTEX_DATA_MASK; }
diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp
index 562042f8bc882871fdcbb9fddd43e49bd70e7b81..d1ea5409edf603bb7790f757568e2596e76dc9e2 100644
--- a/indra/newview/llface.cpp
+++ b/indra/newview/llface.cpp
@@ -2024,10 +2024,12 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 					mVertexBuffer->flush();
 				}
 
-				if (!mat && do_bump)
+				if ((!mat && !gltf_mat) && do_bump)
 				{
 					mVertexBuffer->getTexCoord1Strider(tex_coords1, mGeomIndex, mGeomCount, map_range);
 		
+                    mVObjp->getVolume()->genTangents(f);
+
 					for (S32 i = 0; i < num_vertices; i++)
 					{
 						LLVector4a tangent = vf.mTangents[i];
diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp
index d0f7f70c81ec74579edff7c282a53f2ef1606213..49eba9856ce70aead47e59d283011c256b001578 100644
--- a/indra/newview/llviewershadermgr.cpp
+++ b/indra/newview/llviewershadermgr.cpp
@@ -264,6 +264,7 @@ LLGLSLShader			gNormalMapGenProgram;
 LLGLSLShader			gDeferredMaterialProgram[LLMaterial::SHADER_COUNT*2];
 LLGLSLShader			gDeferredMaterialWaterProgram[LLMaterial::SHADER_COUNT*2];
 LLGLSLShader			gDeferredPBROpaqueProgram;
+LLGLSLShader            gDeferredSkinnedPBROpaqueProgram;
 
 //helper for making a rigged variant of a given shader
 bool make_rigged_variant(LLGLSLShader& shader, LLGLSLShader& riggedShader)
@@ -1325,6 +1326,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 		}
 
         gDeferredPBROpaqueProgram.unload();
+        gDeferredSkinnedPBROpaqueProgram.unload();
 
 		return TRUE;
 	}
@@ -1616,22 +1618,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
             success = gDeferredMaterialWaterProgram[i].createShader(NULL, NULL);//&mWLUniforms);
             llassert(success);
 		}
-
-        if (success)
-        {
-            gDeferredPBROpaqueProgram.mName = "Deferred PBR Opaque Shader";
-            gDeferredPBROpaqueProgram.mFeatures.encodesNormal = true;
-            gDeferredPBROpaqueProgram.mFeatures.hasSrgb = true;
-
-            gDeferredPBROpaqueProgram.mShaderFiles.clear();
-            gDeferredPBROpaqueProgram.mShaderFiles.push_back(make_pair("deferred/pbropaqueV.glsl", GL_VERTEX_SHADER_ARB));
-            gDeferredPBROpaqueProgram.mShaderFiles.push_back(make_pair("deferred/pbropaqueF.glsl", GL_FRAGMENT_SHADER_ARB));
-            gDeferredPBROpaqueProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
-            gDeferredPBROpaqueProgram.addPermutation("HAS_NORMAL_MAP", "1");
-            gDeferredPBROpaqueProgram.addPermutation("HAS_SPECULAR_MAP", "1");
-            success = gDeferredPBROpaqueProgram.createShader(NULL, NULL);
-            llassert(success);
-        }
 	}
 
 	gDeferredMaterialProgram[1].mFeatures.hasLighting = true;
@@ -1652,6 +1638,27 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 	gDeferredMaterialWaterProgram[9+LLMaterial::SHADER_COUNT].mFeatures.hasLighting = true;
 	gDeferredMaterialWaterProgram[13+LLMaterial::SHADER_COUNT].mFeatures.hasLighting = true;
 
+    if (success)
+    {
+        gDeferredPBROpaqueProgram.mName = "Deferred PBR Opaque Shader";
+        gDeferredPBROpaqueProgram.mFeatures.encodesNormal = true;
+        gDeferredPBROpaqueProgram.mFeatures.hasSrgb = true;
+
+        gDeferredPBROpaqueProgram.mShaderFiles.clear();
+        gDeferredPBROpaqueProgram.mShaderFiles.push_back(make_pair("deferred/pbropaqueV.glsl", GL_VERTEX_SHADER_ARB));
+        gDeferredPBROpaqueProgram.mShaderFiles.push_back(make_pair("deferred/pbropaqueF.glsl", GL_FRAGMENT_SHADER_ARB));
+        gDeferredPBROpaqueProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
+        gDeferredPBROpaqueProgram.addPermutation("HAS_NORMAL_MAP", "1");
+        gDeferredPBROpaqueProgram.addPermutation("HAS_SPECULAR_MAP", "1");
+        gDeferredPBROpaqueProgram.addPermutation("DIFFUSE_ALPHA_MODE", "0");
+
+        success = make_rigged_variant(gDeferredPBROpaqueProgram, gDeferredSkinnedPBROpaqueProgram);
+        if (success)
+        {
+            success = gDeferredPBROpaqueProgram.createShader(NULL, NULL);
+        }
+        llassert(success);
+    }
 	
 	if (success)
 	{