diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 16877c345e82b94ab7725f1f083cd02505b50729..4ba81047f54eb06aa1cab51930ae01a65b488220 100755
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -541,6 +541,7 @@ set(viewer_SOURCE_FILES
     llsidepaneliteminfo.cpp
     llsidepaneltaskinfo.cpp
     llsidetraypanelcontainer.cpp
+    llskinningutil.cpp
     llsky.cpp
     llslurl.cpp
     llsnapshotlivepreview.cpp
@@ -1147,6 +1148,7 @@ set(viewer_HEADER_FILES
     llsidepaneliteminfo.h
     llsidepaneltaskinfo.h
     llsidetraypanelcontainer.h
+    llskinningutil.h
     llsky.h
     llslurl.h
     llsnapshotlivepreview.h
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index fbf2a04bcc82c732f8008d2d9c6946f07c47040d..04758ef839fcebf640d81d998b4a2fcdef30e573 100755
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -101,6 +101,7 @@
 #include "llscenemonitor.h"
 #include "llavatarrenderinfoaccountant.h"
 #include "lllocalbitmaps.h"
+#include "llskinningutil.h"
 
 // Linden library includes
 #include "llavatarnamecache.h"
@@ -794,6 +795,9 @@ bool LLAppViewer::init()
 
 	LL_INFOS("InitInfo") << "Configuration initialized." << LL_ENDL ;
 
+	// initialize skinning util
+	LLSkinningUtil::initClass();
+
 	//set the max heap size.
 	initMaxHeapSize() ;
 	LLCoros::instance().setStackSize(gSavedSettings.getS32("CoroutineStackSize"));
diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp
index dff6cada9abea4a9fe251d66939525cedbf07da3..89233b8e325c23ad40826eea195edec2a7f26a9e 100755
--- a/indra/newview/lldrawpoolavatar.cpp
+++ b/indra/newview/lldrawpoolavatar.cpp
@@ -27,6 +27,7 @@
 #include "llviewerprecompiledheaders.h"
 
 #include "lldrawpoolavatar.h"
+#include "llskinningutil.h"
 #include "llrender.h"
 
 #include "llvoavatar.h"
@@ -1537,282 +1538,6 @@ void LLDrawPoolAvatar::getRiggedGeometry(
 	buffer->flush();
 }
 
-// static
-U32 LLDrawPoolAvatar::getMaxJointCount()
-{
-    return llmin(LL_MAX_JOINTS_PER_MESH_OBJECT, gSavedSettings.getU32("MaxJointsPerMeshObject"));
-}
-
-// static
-U32 LLDrawPoolAvatar::getMeshJointCount(const LLMeshSkinInfo *skin)
-{
-	return llmin((U32)getMaxJointCount(), (U32)skin->mJointNames.size());
-}
-
-bool getNameIndex(const std::string& name, std::vector<std::string>& names, U32& result)
-{
-    std::vector<std::string>::const_iterator find_it =
-        std::find(names.begin(), names.end(), name);
-    if (find_it != names.end())
-    {
-        result = find_it - names.begin();
-        return true;
-    }
-    else
-    {
-        return false;
-    }
-}
-
-// Find a name table index that is also a valid joint on the
-// avatar. Order of preference is: requested name, mPelvis, first
-// valid match in names table.
-U32 getValidJointIndex(const std::string& name, LLVOAvatar *avatar, std::vector<std::string>& joint_names)
-{
-    U32 result;
-    if (avatar->getJoint(name) && getNameIndex(name,joint_names,result))
-    {
-        return result;
-    }
-    if (getNameIndex("mPelvis",joint_names,result))
-    {
-        return result;
-    }
-    for (U32 j=0; j<joint_names.size(); j++)
-    {
-        if (avatar->getJoint(joint_names[j]))
-        {
-            return j;
-        }
-    }
-    // BENTO how to handle?
-    LL_ERRS() << "no valid joints in joint_names" << LL_ENDL;
-    return 0;
-}
-
-// Which joint will stand in for this joint? 
-U32 getProxyJointIndex(U32 joint_index, LLVOAvatar *avatar, std::vector<std::string>& joint_names)
-{
-    bool include_enhanced = gSavedSettings.getBOOL("IncludeEnhancedSkeleton");
-    U32 j_proxy = getValidJointIndex(joint_names[joint_index], avatar, joint_names);
-    LLJoint *joint = avatar->getJoint(joint_names[j_proxy]);
-    llassert(joint);
-    // BENTO - test of simple push-to-base-ancestor
-    // complexity reduction scheme.  Find the first
-    // ancestor that's not flagged as extended, or the
-    // last ancestor that's rigged in this mesh, whichever
-    // comes first.
-    while (1)
-    {
-        if (include_enhanced || 
-            joint->getSupport()==LLJoint::SUPPORT_BASE)
-            break;
-        LLJoint *parent = joint->getParent();
-        if (!parent)
-            break;
-        if (!getNameIndex(parent->getName(), joint_names, j_proxy))
-        {
-            break;
-        }
-        joint = parent;
-    }
-    return j_proxy;
-}
-
-// static
-
-// Destructively remap the joints in skin info based on what joints
-// are known in the avatar, and which are currently supported.  This
-// will also populate mJointRemap[] in the skin, which can be used to
-// make the corresponding changes to the integer part of vertex
-// weights.
-//
-// This will throw away joint info for any joints that are not known
-// in the avatar, or not currently flagged to support based on the
-// debug setting for IncludeEnhancedSkeleton.
-//
-// static
-void LLDrawPoolAvatar::remapSkinInfoJoints(LLVOAvatar *avatar, LLMeshSkinInfo* skin)
-{
-	// skip if already done.
-    if (!skin->mJointRemap.empty())
-    {
-        return; 
-    }
-
-    // Compute the remap
-    std::vector<U32> j_proxy(skin->mJointNames.size());
-    for (U32 j = 0; j < skin->mJointNames.size(); ++j)
-    {
-        U32 j_rep = getProxyJointIndex(j, avatar, skin->mJointNames);
-        j_proxy[j] = j_rep;
-    }
-    S32 top = 0;
-    std::vector<U32> j_remap(skin->mJointNames.size());
-    // Fill in j_remap for all joints that will make the cut.
-    for (U32 j = 0; j < skin->mJointNames.size(); ++j)
-    {
-        if (j_proxy[j] == j)
-        {
-            // Joint will be included
-            j_remap[j] = top++;
-        }
-    }
-    // Then use j_proxy to fill in j_remap for the joints that will be discarded
-    for (U32 j = 0; j < skin->mJointNames.size(); ++j)
-    {
-        if (j_proxy[j] != j)
-        {
-            j_remap[j] = j_remap[j_proxy[j]];
-        }
-    }
-    
-    
-    // Apply the remap to mJointNames, mInvBindMatrix, and mAlternateBindMatrix
-    std::vector<std::string> new_joint_names;
-    std::vector<LLMatrix4> new_inv_bind_matrix;
-    std::vector<LLMatrix4> new_alternate_bind_matrix;
-
-    for (U32 j = 0; j < skin->mJointNames.size(); ++j)
-    {
-        if (j_proxy[j] == j)
-        {
-            new_joint_names.push_back(skin->mJointNames[j]);
-            new_inv_bind_matrix.push_back(skin->mInvBindMatrix[j]);
-            if (!skin->mAlternateBindMatrix.empty())
-            {
-                new_alternate_bind_matrix.push_back(skin->mAlternateBindMatrix[j]);
-            }
-        }
-    }
-
-    for (U32 j = 0; j < skin->mJointNames.size(); ++j)
-    {
-        LL_INFOS() << "Starting joint[" << j << "] = " << skin->mJointNames[j] << " j_remap " << j_remap[j] << " ==> " << new_joint_names[j_remap[j]] << LL_ENDL;
-    }
-
-    skin->mJointNames = new_joint_names;
-    skin->mInvBindMatrix = new_inv_bind_matrix;
-    skin->mAlternateBindMatrix = new_alternate_bind_matrix;
-    skin->mJointRemap = j_remap;
-}
-
-// static
-void LLDrawPoolAvatar::initSkinningMatrixPalette(
-    LLMatrix4* mat,
-    S32 count, 
-    const LLMeshSkinInfo* skin,
-    LLVOAvatar *avatar)
-{
-    // BENTO - switching to use Matrix4a and SSE might speed this up.
-    // Note that we are mostly passing Matrix4a's to this routine anyway, just dubiously casted.
-    for (U32 j = 0; j < count; ++j)
-    {
-        LLJoint* joint = avatar->getJoint(skin->mJointNames[j]);
-        mat[j] = skin->mInvBindMatrix[j];
-        mat[j] *= joint->getWorldMatrix();
-    }
-}
-
-// Transform the weights based on the remap info stored in skin. Note
-// that this is destructive and non-idempotent, so we need to keep
-// track of whether we've done it already. If the desired remapping
-// changes, the viewer must be restarted.
-//
-// static
-void LLDrawPoolAvatar::remapSkinWeights(LLVector4a* weights, U32 num_vertices, const LLMeshSkinInfo* skin)
-{
-    llassert(skin->mJointRemap.size()>0); // Must call remapSkinInfoJoints() first, which this checks for.
-    const U32* remap = &skin->mJointRemap[0];
-    const S32 max_joints = skin->mJointNames.size();
-    for (U32 j=0; j<num_vertices; j++)
-    {
-        F32 *w = weights[j].getF32ptr();
-
-        for (U32 k=0; k<4; ++k)
-        {
-            S32 i = llfloor(w[k]);
-            F32 f = w[k]-i;
-            i = llclamp(i,0,max_joints-1);
-            w[k] = remap[i] + f;
-        }
-    }
-}
-
-// static
-void LLDrawPoolAvatar::checkSkinWeights(LLVector4a* weights, U32 num_vertices, const LLMeshSkinInfo* skin)
-{
-    if (skin->mJointRemap.size()>0)
-    {
-        // Check the weights are consistent with the current remap.
-        const S32 max_joints = skin->mJointNames.size();
-        for (U32 j=0; j<num_vertices; j++)
-        {
-            F32 *w = weights[j].getF32ptr();
-
-            for (U32 k=0; k<4; ++k)
-            {
-                S32 i = llfloor(w[k]);
-                llassert(i>=0);
-                llassert(i<max_joints);
-            }
-    }
-    }
-}
-
-// static
-void LLDrawPoolAvatar::getPerVertexSkinMatrix(
-    F32* weights,
-    LLMatrix4a* mat,
-    bool handle_bad_scale,
-    LLMatrix4a& final_mat,
-    U32 max_joints)
-{
-
-    final_mat.clear();
-
-    S32 idx[4];
-
-    LLVector4 wght;
-
-    F32 scale = 0.f;
-    for (U32 k = 0; k < 4; k++)
-    {
-        F32 w = weights[k];
-
-        // BENTO potential optimizations
-        // - Do clamping in unpackVolumeFaces() (once instead of every time)
-        // - int vs floor: if we know w is
-        // >= 0.0, we can use int instead of floorf; the latter
-        // allegedly has a lot of overhead due to ieeefp error
-        // checking which we should not need.
-        idx[k] = llclamp((S32) floorf(w), (S32)0, (S32)max_joints-1);
-
-        wght[k] = w - floorf(w);
-        scale += wght[k];
-    }
-    if (handle_bad_scale && scale <= 0.f)
-    {
-        wght = LLVector4(1.0f, 0.0f, 0.0f, 0.0f);
-    }
-    else
-    {
-        // This is enforced  in unpackVolumeFaces()
-        llassert(scale>0.f);
-        wght *= 1.f/scale;
-    }
-
-    for (U32 k = 0; k < 4; k++)
-    {
-        F32 w = wght[k];
-
-        LLMatrix4a src;
-        src.setMul(mat[idx[k]], w);
-
-        final_mat.add(src);
-    }
-}
-
 void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(
     LLVOAvatar* avatar,
     LLFace* face,
@@ -1826,7 +1551,7 @@ void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(
 		return;
 	}
     // BENTO ugly const cast
-    remapSkinInfoJoints(avatar, const_cast<LLMeshSkinInfo*>(skin));
+    LLSkinningUtil::remapSkinInfoJoints(avatar, const_cast<LLMeshSkinInfo*>(skin));
 
 	LLPointer<LLVertexBuffer> buffer = face->getVertexBuffer();
 	LLDrawable* drawable = face->getDrawable();
@@ -1835,7 +1560,7 @@ void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(
 	
     if (!vol_face.mWeightsRemapped)
     {
-        remapSkinWeights(weight, vol_face.mNumVertices, skin); 
+        LLSkinningUtil::remapSkinWeights(weight, vol_face.mNumVertices, skin); 
         vol_face.mWeightsRemapped = TRUE;
     }
 
@@ -1890,18 +1615,18 @@ void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(
 		
 		//build matrix palette
 		LLMatrix4a mat[LL_MAX_JOINTS_PER_MESH_OBJECT];
-        U32 count = getMeshJointCount(skin);
-        initSkinningMatrixPalette((LLMatrix4*)mat, count, skin, avatar);
-        checkSkinWeights(weight, buffer->getNumVerts(), skin);
+        U32 count = LLSkinningUtil::getMeshJointCount(skin);
+        LLSkinningUtil::initSkinningMatrixPalette((LLMatrix4*)mat, count, skin, avatar);
+        LLSkinningUtil::checkSkinWeights(weight, buffer->getNumVerts(), skin);
 
 		LLMatrix4a bind_shape_matrix;
 		bind_shape_matrix.loadu(skin->mBindShapeMatrix);
 
-        const U32 max_joints = getMaxJointCount();
+        const U32 max_joints = LLSkinningUtil::getMaxJointCount();
 		for (U32 j = 0; j < buffer->getNumVerts(); ++j)
 		{
 			LLMatrix4a final_mat;
-            getPerVertexSkinMatrix(weight[j].getF32ptr(), mat, false, final_mat, max_joints);
+            LLSkinningUtil::getPerVertexSkinMatrix(weight[j].getF32ptr(), mat, false, final_mat, max_joints);
 			
 			LLVector4a& v = vol_face.mPositions[j];
 			LLVector4a t;
@@ -1984,8 +1709,8 @@ void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, bool glow)
 			{
                 // upload matrix palette to shader
 				LLMatrix4a mat[LL_MAX_JOINTS_PER_MESH_OBJECT];
-				U32 count = getMeshJointCount(skin);
-                initSkinningMatrixPalette((LLMatrix4*)mat, count, skin, avatar);
+				U32 count = LLSkinningUtil::getMeshJointCount(skin);
+                LLSkinningUtil::initSkinningMatrixPalette((LLMatrix4*)mat, count, skin, avatar);
 
 				stop_glerror();
 
diff --git a/indra/newview/lldrawpoolavatar.h b/indra/newview/lldrawpoolavatar.h
index 8d6e95ba1ab90be1d9b22819cca3c44d6ddc4ab9..b9d220405211c0a8119bde65932ebf652dc434f3 100755
--- a/indra/newview/lldrawpoolavatar.h
+++ b/indra/newview/lldrawpoolavatar.h
@@ -134,13 +134,6 @@ class LLDrawPoolAvatar : public LLFacePool
 	void endDeferredRiggedBump();
 		
 	void getRiggedGeometry(LLFace* face, LLPointer<LLVertexBuffer>& buffer, U32 data_mask, const LLMeshSkinInfo* skin, LLVolume* volume, const LLVolumeFace& vol_face);
-    static U32 getMaxJointCount();
-    static U32 getMeshJointCount(const LLMeshSkinInfo *skin);
-    static void remapSkinInfoJoints(LLVOAvatar *avatar, LLMeshSkinInfo* skin);
-    static void initSkinningMatrixPalette(LLMatrix4* mat, S32 count, const LLMeshSkinInfo* skin, LLVOAvatar *avatar);
-    static void checkSkinWeights(LLVector4a* weights, U32 num_vertices, const LLMeshSkinInfo* skin);
-    static void remapSkinWeights(LLVector4a* weights, U32 num_vertices, const LLMeshSkinInfo* skin);
-    static void getPerVertexSkinMatrix(F32* weights, LLMatrix4a* mat, bool handle_bad_scale, LLMatrix4a& final_mat, U32 max_joints);
 	void updateRiggedFaceVertexBuffer(LLVOAvatar* avatar,
 									  LLFace* facep, 
 									  const LLMeshSkinInfo* skin, 
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index c971faac5f9df6282113f9bfcc3623a943b5efb9..3cad7badadfe568d800ee048ebbfd87f1ae33f7b 100755
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -42,7 +42,6 @@
 #include "llcombobox.h"
 #include "lldatapacker.h"
 #include "lldrawable.h"
-#include "lldrawpoolavatar.h"
 #include "llrender.h"
 #include "llface.h"
 #include "lleconomy.h"
@@ -54,6 +53,7 @@
 #include "llmeshrepository.h"
 #include "llnotificationsutil.h"
 #include "llsdutil_math.h"
+#include "llskinningutil.h"
 #include "lltextbox.h"
 #include "lltoolmgr.h"
 #include "llui.h"
@@ -4031,17 +4031,17 @@ BOOL LLModelPreview::render()
 
 							LLMatrix4a mat[LL_MAX_JOINTS_PER_MESH_OBJECT];
                             const LLMeshSkinInfo *skin = &model->mSkinInfo;
-							U32 count = LLDrawPoolAvatar::getMeshJointCount(skin);
-                            LLDrawPoolAvatar::initSkinningMatrixPalette((LLMatrix4*)mat, count,
+							U32 count = LLSkinningUtil::getMeshJointCount(skin);
+                            LLSkinningUtil::initSkinningMatrixPalette((LLMatrix4*)mat, count,
                                                                         skin, getPreviewAvatar());
                             LLMatrix4a bind_shape_matrix;
                             bind_shape_matrix.loadu(skin->mBindShapeMatrix);
-                            U32 max_joints = LLDrawPoolAvatar::getMaxJointCount();
+                            U32 max_joints = LLSkinningUtil::getMaxJointCount();
 							for (U32 j = 0; j < buffer->getNumVerts(); ++j)
 							{
                                 LLMatrix4a final_mat;
                                 F32 *wptr = weight[j].mV;
-                                LLDrawPoolAvatar::getPerVertexSkinMatrix(wptr, mat, true, final_mat, max_joints);
+                                LLSkinningUtil::getPerVertexSkinMatrix(wptr, mat, true, final_mat, max_joints);
 
 								//VECTORIZE THIS
                                 LLVector4a& v = face.mPositions[j];
diff --git a/indra/newview/llskinningutil.cpp b/indra/newview/llskinningutil.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..23bbbdcf90fc6fe4a03bc4b137213591623fdee3
--- /dev/null
+++ b/indra/newview/llskinningutil.cpp
@@ -0,0 +1,329 @@
+/** 
+* @file llskinningutil.cpp
+* @brief  Functions for mesh object skinning
+* @author vir@lindenlab.com
+*
+* $LicenseInfo:firstyear=2015&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2015, 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$
+*/
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llskinningutil.h"
+#include "llvoavatar.h"
+#include "llviewercontrol.h"
+#include "llmeshrepository.h"
+
+bool LLSkinningUtil::sIncludeEnhancedSkeleton = true;
+U32 LLSkinningUtil::sMaxJointsPerMeshObject = LL_MAX_JOINTS_PER_MESH_OBJECT;
+
+namespace {
+
+bool get_name_index(const std::string& name, std::vector<std::string>& names, U32& result)
+{
+    std::vector<std::string>::const_iterator find_it =
+        std::find(names.begin(), names.end(), name);
+    if (find_it != names.end())
+    {
+        result = find_it - names.begin();
+        return true;
+    }
+    else
+    {
+        return false;
+    }
+}
+
+// Find a name table index that is also a valid joint on the
+// avatar. Order of preference is: requested name, mPelvis, first
+// valid match in names table.
+U32 get_valid_joint_index(const std::string& name, LLVOAvatar *avatar, std::vector<std::string>& joint_names)
+{
+    U32 result;
+    if (avatar->getJoint(name) && get_name_index(name,joint_names,result))
+    {
+        return result;
+    }
+    if (get_name_index("mPelvis",joint_names,result))
+    {
+        return result;
+    }
+    for (U32 j=0; j<joint_names.size(); j++)
+    {
+        if (avatar->getJoint(joint_names[j]))
+        {
+            return j;
+        }
+    }
+    // BENTO how to handle?
+    LL_ERRS() << "no valid joints in joint_names" << LL_ENDL;
+    return 0;
+}
+
+// Which joint will stand in for this joint? 
+U32 get_proxy_joint_index(U32 joint_index, LLVOAvatar *avatar, std::vector<std::string>& joint_names)
+{
+	bool include_enhanced = LLSkinningUtil::sIncludeEnhancedSkeleton;
+    U32 j_proxy = get_valid_joint_index(joint_names[joint_index], avatar, joint_names);
+    LLJoint *joint = avatar->getJoint(joint_names[j_proxy]);
+    llassert(joint);
+    // BENTO - test of simple push-to-base-ancestor
+    // complexity reduction scheme.  Find the first
+    // ancestor that's not flagged as extended, or the
+    // last ancestor that's rigged in this mesh, whichever
+    // comes first.
+    while (1)
+    {
+        if (include_enhanced || 
+            joint->getSupport()==LLJoint::SUPPORT_BASE)
+            break;
+        LLJoint *parent = joint->getParent();
+        if (!parent)
+            break;
+        if (!get_name_index(parent->getName(), joint_names, j_proxy))
+        {
+            break;
+        }
+        joint = parent;
+    }
+    return j_proxy;
+}
+
+}
+
+// static
+void LLSkinningUtil::initClass()
+{
+    sIncludeEnhancedSkeleton = gSavedSettings.getBOOL("IncludeEnhancedSkeleton");
+    sMaxJointsPerMeshObject = gSavedSettings.getU32("MaxJointsPerMeshObject");
+}
+
+// static
+U32 LLSkinningUtil::getMaxJointCount()
+{
+    U32 result = llmin(LL_MAX_JOINTS_PER_MESH_OBJECT, sMaxJointsPerMeshObject);
+    if (!sIncludeEnhancedSkeleton)
+    {
+        result = llmin(result,(U32)52); // BENTO replace with LLAvatarAppearance::getBaseJointCount()) or equivalent 
+    }
+	return result;
+}
+
+// static
+U32 LLSkinningUtil::getMeshJointCount(const LLMeshSkinInfo *skin)
+{
+	return llmin((U32)getMaxJointCount(), (U32)skin->mJointNames.size());
+}
+
+// static
+
+// Destructively remap the joints in skin info based on what joints
+// are known in the avatar, and which are currently supported.  This
+// will also populate mJointRemap[] in the skin, which can be used to
+// make the corresponding changes to the integer part of vertex
+// weights.
+//
+// This will throw away joint info for any joints that are not known
+// in the avatar, or not currently flagged to support based on the
+// debug setting for IncludeEnhancedSkeleton.
+//
+// static
+void LLSkinningUtil::remapSkinInfoJoints(LLVOAvatar *avatar, LLMeshSkinInfo* skin)
+{
+	// skip if already done.
+    if (!skin->mJointRemap.empty())
+    {
+        return; 
+    }
+
+    // Compute the remap
+    std::vector<U32> j_proxy(skin->mJointNames.size());
+    for (U32 j = 0; j < skin->mJointNames.size(); ++j)
+    {
+        U32 j_rep = get_proxy_joint_index(j, avatar, skin->mJointNames);
+        j_proxy[j] = j_rep;
+    }
+    S32 top = 0;
+    std::vector<U32> j_remap(skin->mJointNames.size());
+    // Fill in j_remap for all joints that will make the cut.
+    for (U32 j = 0; j < skin->mJointNames.size(); ++j)
+    {
+        if (j_proxy[j] == j)
+        {
+            // Joint will be included
+            j_remap[j] = top++;
+        }
+    }
+    // Then use j_proxy to fill in j_remap for the joints that will be discarded
+    for (U32 j = 0; j < skin->mJointNames.size(); ++j)
+    {
+        if (j_proxy[j] != j)
+        {
+            j_remap[j] = j_remap[j_proxy[j]];
+        }
+    }
+    
+    
+    // Apply the remap to mJointNames, mInvBindMatrix, and mAlternateBindMatrix
+    std::vector<std::string> new_joint_names;
+    std::vector<LLMatrix4> new_inv_bind_matrix;
+    std::vector<LLMatrix4> new_alternate_bind_matrix;
+
+    for (U32 j = 0; j < skin->mJointNames.size(); ++j)
+    {
+        if (j_proxy[j] == j)
+        {
+            new_joint_names.push_back(skin->mJointNames[j]);
+            new_inv_bind_matrix.push_back(skin->mInvBindMatrix[j]);
+            if (!skin->mAlternateBindMatrix.empty())
+            {
+                new_alternate_bind_matrix.push_back(skin->mAlternateBindMatrix[j]);
+            }
+        }
+    }
+
+    for (U32 j = 0; j < skin->mJointNames.size(); ++j)
+    {
+        LL_DEBUGS("Avatar") << "Starting joint[" << j << "] = " << skin->mJointNames[j] << " j_remap " << j_remap[j] << " ==> " << new_joint_names[j_remap[j]] << LL_ENDL;
+    }
+
+    skin->mJointNames = new_joint_names;
+    skin->mInvBindMatrix = new_inv_bind_matrix;
+    skin->mAlternateBindMatrix = new_alternate_bind_matrix;
+    skin->mJointRemap = j_remap;
+}
+
+// static
+void LLSkinningUtil::initSkinningMatrixPalette(
+    LLMatrix4* mat,
+    S32 count, 
+    const LLMeshSkinInfo* skin,
+    LLVOAvatar *avatar)
+{
+    // BENTO - switching to use Matrix4a and SSE might speed this up.
+    // Note that we are mostly passing Matrix4a's to this routine anyway, just dubiously casted.
+    for (U32 j = 0; j < count; ++j)
+    {
+        LLJoint* joint = avatar->getJoint(skin->mJointNames[j]);
+        mat[j] = skin->mInvBindMatrix[j];
+        mat[j] *= joint->getWorldMatrix();
+    }
+}
+
+// Transform the weights based on the remap info stored in skin. Note
+// that this is destructive and non-idempotent, so we need to keep
+// track of whether we've done it already. If the desired remapping
+// changes, the viewer must be restarted.
+//
+// static
+void LLSkinningUtil::remapSkinWeights(LLVector4a* weights, U32 num_vertices, const LLMeshSkinInfo* skin)
+{
+    llassert(skin->mJointRemap.size()>0); // Must call remapSkinInfoJoints() first, which this checks for.
+    const U32* remap = &skin->mJointRemap[0];
+    const S32 max_joints = skin->mJointNames.size();
+    for (U32 j=0; j<num_vertices; j++)
+    {
+        F32 *w = weights[j].getF32ptr();
+
+        for (U32 k=0; k<4; ++k)
+        {
+            S32 i = llfloor(w[k]);
+            F32 f = w[k]-i;
+            i = llclamp(i,0,max_joints-1);
+            w[k] = remap[i] + f;
+        }
+    }
+}
+
+// static
+void LLSkinningUtil::checkSkinWeights(LLVector4a* weights, U32 num_vertices, const LLMeshSkinInfo* skin)
+{
+    if (skin->mJointRemap.size()>0)
+    {
+        // Check the weights are consistent with the current remap.
+        const S32 max_joints = skin->mJointNames.size();
+        for (U32 j=0; j<num_vertices; j++)
+        {
+            F32 *w = weights[j].getF32ptr();
+
+            for (U32 k=0; k<4; ++k)
+            {
+                S32 i = llfloor(w[k]);
+                llassert(i>=0);
+                llassert(i<max_joints);
+            }
+    }
+    }
+}
+
+// static
+void LLSkinningUtil::getPerVertexSkinMatrix(
+    F32* weights,
+    LLMatrix4a* mat,
+    bool handle_bad_scale,
+    LLMatrix4a& final_mat,
+    U32 max_joints)
+{
+
+    final_mat.clear();
+
+    S32 idx[4];
+
+    LLVector4 wght;
+
+    F32 scale = 0.f;
+    for (U32 k = 0; k < 4; k++)
+    {
+        F32 w = weights[k];
+
+        // BENTO potential optimizations
+        // - Do clamping in unpackVolumeFaces() (once instead of every time)
+        // - int vs floor: if we know w is
+        // >= 0.0, we can use int instead of floorf; the latter
+        // allegedly has a lot of overhead due to ieeefp error
+        // checking which we should not need.
+        idx[k] = llclamp((S32) floorf(w), (S32)0, (S32)max_joints-1);
+
+        wght[k] = w - floorf(w);
+        scale += wght[k];
+    }
+    if (handle_bad_scale && scale <= 0.f)
+    {
+        wght = LLVector4(1.0f, 0.0f, 0.0f, 0.0f);
+    }
+    else
+    {
+        // This is enforced  in unpackVolumeFaces()
+        llassert(scale>0.f);
+        wght *= 1.f/scale;
+    }
+
+    for (U32 k = 0; k < 4; k++)
+    {
+        F32 w = wght[k];
+
+        LLMatrix4a src;
+        src.setMul(mat[idx[k]], w);
+
+        final_mat.add(src);
+    }
+}
+
diff --git a/indra/newview/llskinningutil.h b/indra/newview/llskinningutil.h
new file mode 100644
index 0000000000000000000000000000000000000000..813d4015355663ac41af720c3a0b7ab534cc7e14
--- /dev/null
+++ b/indra/newview/llskinningutil.h
@@ -0,0 +1,51 @@
+/** 
+* @file   llskinningutil.h
+* @brief  Functions for mesh object skinning
+* @author vir@lindenlab.com
+*
+* $LicenseInfo:firstyear=2015&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2015, 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$
+*/
+#ifndef LLSKINNINGUTIL_H
+#define LLSKINNINGUTIL_H
+
+class LLVOAvatar;
+class LLMeshSkinInfo;
+class LLMatrix4a;
+
+class LLSkinningUtil
+{
+public:
+    static void initClass();
+    static U32 getMaxJointCount();
+    static U32 getMeshJointCount(const LLMeshSkinInfo *skin);
+    static void remapSkinInfoJoints(LLVOAvatar *avatar, LLMeshSkinInfo* skin);
+    static void initSkinningMatrixPalette(LLMatrix4* mat, S32 count, const LLMeshSkinInfo* skin, LLVOAvatar *avatar);
+    static void checkSkinWeights(LLVector4a* weights, U32 num_vertices, const LLMeshSkinInfo* skin);
+    static void remapSkinWeights(LLVector4a* weights, U32 num_vertices, const LLMeshSkinInfo* skin);
+    static void getPerVertexSkinMatrix(F32* weights, LLMatrix4a* mat, bool handle_bad_scale, LLMatrix4a& final_mat, U32 max_joints);
+
+    // This is initialized from gSavedSettings at startup and then left alone.
+    static bool sIncludeEnhancedSkeleton;
+    static U32 sMaxJointsPerMeshObject;
+};
+
+#endif
diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
index 466edb19b25bee8cb73f150828c433e616b04cc7..4e4aaf5f8ee382e96de1c6d8e9bf27123a96b250 100755
--- a/indra/newview/llviewercontrol.cpp
+++ b/indra/newview/llviewercontrol.cpp
@@ -67,6 +67,7 @@
 #include "llvowlsky.h"
 #include "llrender.h"
 #include "llnavigationbar.h"
+#include "llnotificationsutil.h"
 #include "llfloatertools.h"
 #include "llpaneloutfitsinventory.h"
 #include "llpanellogin.h"
@@ -119,6 +120,12 @@ static bool handleTerrainDetailChanged(const LLSD& newvalue)
 }
 
 
+static bool handleDeferredDebugSettingChanged(const LLSD& newvalue)
+{
+    LLNotificationsUtil::add("ChangeDeferredDebugSetting");
+    return true;
+}
+
 static bool handleSetShaderChanged(const LLSD& newvalue)
 {
 	// changing shader level may invalidate existing cached bump maps, as the shader type determines the format of the bump map it expects - clear and repopulate the bump cache
@@ -761,7 +768,8 @@ void settings_setup_listeners()
 	gSavedSettings.getControl("SpellCheck")->getSignal()->connect(boost::bind(&handleSpellCheckChanged));
 	gSavedSettings.getControl("SpellCheckDictionary")->getSignal()->connect(boost::bind(&handleSpellCheckChanged));
 	gSavedSettings.getControl("LoginLocation")->getSignal()->connect(boost::bind(&handleLoginLocationChanged));
-    gSavedSettings.getControl("MaxJointsPerMeshObject")->getCommitSignal()->connect(boost::bind(&handleSetShaderChanged, _2));
+    gSavedSettings.getControl("MaxJointsPerMeshObject")->getCommitSignal()->connect(boost::bind(&handleDeferredDebugSettingChanged, _2));
+	gSavedSettings.getControl("IncludeEnhancedSkeleton")->getCommitSignal()->connect(boost::bind(&handleDeferredDebugSettingChanged, _2));
 }
 
 #if TEST_CACHED_CONTROL
diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp
index b1e521f193cc8d348a20e43afd79e653f5a18c27..3e0cec0f0999e7fb19b05ae4141f28bd03789070 100755
--- a/indra/newview/llviewershadermgr.cpp
+++ b/indra/newview/llviewershadermgr.cpp
@@ -44,7 +44,7 @@
 #include "llvosky.h"
 #include "llrender.h"
 #include "lljoint.h"
-#include "lldrawpoolavatar.h"
+#include "llskinningutil.h"
 
 #ifdef LL_RELEASE_FOR_DOWNLOAD
 #define UNIFORM_ERRS LL_WARNS_ONCE("Shader")
@@ -876,7 +876,7 @@ BOOL LLViewerShaderMgr::loadBasicShaders()
 
 	boost::unordered_map<std::string, std::string> attribs;
 	attribs["MAX_JOINTS_PER_MESH_OBJECT"] = 
-		boost::lexical_cast<std::string>(LLDrawPoolAvatar::getMaxJointCount());
+		boost::lexical_cast<std::string>(LLSkinningUtil::getMaxJointCount());
 
 	// We no longer have to bind the shaders to global glhandles, they are automatically added to a map now.
 	for (U32 i = 0; i < shaders.size(); i++)
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index 5d8558cb4630a2ed66e77959d495f2a2810a619d..9b2e9db59a26b10b22eaece6b2bfaa4c5d316a9f 100755
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -54,6 +54,7 @@
 #include "llspatialpartition.h"
 #include "llhudmanager.h"
 #include "llflexibleobject.h"
+#include "llskinningutil.h"
 #include "llsky.h"
 #include "lltexturefetch.h"
 #include "llvector4a.h"
@@ -4179,8 +4180,8 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons
 	static const size_t kMaxJoints = LL_MAX_JOINTS_PER_MESH_OBJECT;
 
 	LLMatrix4a mat[kMaxJoints];
-	U32 maxJoints = LLDrawPoolAvatar::getMeshJointCount(skin);
-    LLDrawPoolAvatar::initSkinningMatrixPalette((LLMatrix4*)mat, maxJoints, skin, avatar);
+	U32 maxJoints = LLSkinningUtil::getMeshJointCount(skin);
+    LLSkinningUtil::initSkinningMatrixPalette((LLMatrix4*)mat, maxJoints, skin, avatar);
 
 	for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
 	{
@@ -4192,7 +4193,7 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons
 
 		if ( weight )
 		{
-            LLDrawPoolAvatar::checkSkinWeights(weight, dst_face.mNumVertices, skin);
+            LLSkinningUtil::checkSkinWeights(weight, dst_face.mNumVertices, skin);
 			LLMatrix4a bind_shape_matrix;
 			bind_shape_matrix.loadu(skin->mBindShapeMatrix);
 
@@ -4202,11 +4203,11 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons
 			{
 				LL_RECORD_BLOCK_TIME(FTM_SKIN_RIGGED);
 
-                U32 max_joints = LLDrawPoolAvatar::getMaxJointCount();
+                U32 max_joints = LLSkinningUtil::getMaxJointCount();
 				for (U32 j = 0; j < dst_face.mNumVertices; ++j)
 				{
 					LLMatrix4a final_mat;
-                    LLDrawPoolAvatar::getPerVertexSkinMatrix(weight[j].getF32ptr(), mat, false, final_mat, max_joints);
+                    LLSkinningUtil::getPerVertexSkinMatrix(weight[j].getF32ptr(), mat, false, final_mat, max_joints);
 				
 					LLVector4a& v = vol_face.mPositions[j];
 					LLVector4a t;
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 70ba4d5077580d39bb4bf95d8b0a6051b2c18cea..ab027ac6006ede120c597452afff579ac0a48208 100755
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -1422,6 +1422,13 @@ Note: This will clear the cache.
 Port settings take effect after you restart [APP_NAME].
   </notification>
 
+  <notification
+   icon="alertmodal.tga"
+   name="ChangeDeferredDebugSetting"
+   type="alertmodal">
+This debug setting change will take effect after you restart [APP_NAME].
+  </notification>
+
   <notification
    icon="alertmodal.tga"
    name="ChangeSkin"