diff --git a/indra/llcharacter/lljoint.cpp b/indra/llcharacter/lljoint.cpp
index 07fcd9970124662d46c0377c21508aac4d530fb3..68355cbbdcfeee9a58d60af9586d812ce5fe6a50 100644
--- a/indra/llcharacter/lljoint.cpp
+++ b/indra/llcharacter/lljoint.cpp
@@ -96,6 +96,46 @@ void LLVector3OverrideMap::clear()
 	m_map.clear();
 }
 
+//-----------------------------------------------------------------------------
+// LLJointRiggingInfo
+//-----------------------------------------------------------------------------
+LLJointRiggingInfo::LLJointRiggingInfo()
+{
+    mRiggedExtents[0].clear();
+    mRiggedExtents[1].clear();
+    mIsRiggedTo = false;
+}
+
+bool LLJointRiggingInfo::isRiggedTo() const
+{
+    return mIsRiggedTo;
+}
+
+void LLJointRiggingInfo::setIsRiggedTo(bool val)
+{
+    mIsRiggedTo = val;
+}
+    
+LLVector4a *LLJointRiggingInfo::getRiggedExtents()
+{
+    return mRiggedExtents;
+}
+
+const LLVector4a *LLJointRiggingInfo::getRiggedExtents() const
+{
+    return mRiggedExtents;
+}
+
+// Combine two rigging info states.
+// - isRiggedTo if either of the source infos are rigged to
+// - box is union of the two sources
+void LLJointRiggingInfo::merge(const LLJointRiggingInfo& other)
+{
+    mIsRiggedTo = mIsRiggedTo || other.mIsRiggedTo;
+    update_min_max(mRiggedExtents[0], mRiggedExtents[1], other.mRiggedExtents[0]);
+    update_min_max(mRiggedExtents[0], mRiggedExtents[1], other.mRiggedExtents[1]);
+}
+
 //-----------------------------------------------------------------------------
 // LLJoint()
 // Class Constructor
@@ -597,6 +637,19 @@ void LLJoint::showAttachmentPosOverrides(const std::string& av_info) const
 	}
 }
 
+//--------------------------------------------------------------------
+// getRiggingInfo()
+//--------------------------------------------------------------------
+LLJointRiggingInfo& LLJoint::getRiggingInfo()
+{
+    return mRiggingInfo;
+}
+
+const LLJointRiggingInfo& LLJoint::getRiggingInfo() const
+{
+    return mRiggingInfo;
+}
+
 //--------------------------------------------------------------------
 // updatePos()
 //--------------------------------------------------------------------
diff --git a/indra/llcharacter/lljoint.h b/indra/llcharacter/lljoint.h
index 8112d246f28b89d3b2b7a0f11daf44fb35a1cf89..ff1e96718899a78785ab0450ba5a4f5359165a63 100644
--- a/indra/llcharacter/lljoint.h
+++ b/indra/llcharacter/lljoint.h
@@ -70,6 +70,23 @@ class LLVector3OverrideMap
 	map_type m_map;
 };
 
+// Stores information related to associated rigged mesh vertices
+// Extents are in joint space
+// isRiggedTo is based on the state of all currently associated rigged meshes
+class LLJointRiggingInfo
+{
+public:
+    LLJointRiggingInfo();
+    bool isRiggedTo() const;
+    void setIsRiggedTo(bool val);
+    LLVector4a *getRiggedExtents();
+    const LLVector4a *getRiggedExtents() const;
+    void merge(const LLJointRiggingInfo& other);
+private:
+	LL_ALIGN_16(LLVector4a mRiggedExtents[2]);
+    bool mIsRiggedTo;
+};
+
 inline bool operator==(const LLVector3OverrideMap& a, const LLVector3OverrideMap& b)
 {
     return a.getMap() == b.getMap();
@@ -158,6 +175,11 @@ class LLJoint
 	LLVector3OverrideMap m_attachmentScaleOverrides;
 	LLVector3 m_scaleBeforeOverrides;
 
+    // Rigging Info
+    LLJointRiggingInfo mRiggingInfo;
+    LLJointRiggingInfo& getRiggingInfo();
+    const LLJointRiggingInfo& getRiggingInfo() const;
+    
 	void updatePos(const std::string& av_info);
 	void updateScale(const std::string& av_info);
 
diff --git a/indra/llmath/CMakeLists.txt b/indra/llmath/CMakeLists.txt
index 4c8bcdac917eb856022ac9626e33dc0cb74ce0bc..dab566da56436586c399767f29f7c48987f03022 100644
--- a/indra/llmath/CMakeLists.txt
+++ b/indra/llmath/CMakeLists.txt
@@ -20,6 +20,7 @@ set(llmath_SOURCE_FILES
     llcoordframe.cpp
     llline.cpp
     llmatrix3a.cpp
+    llmatrix4a.cpp
     llmodularmath.cpp
     lloctree.cpp
     llperlin.cpp
diff --git a/indra/llmath/llmatrix4a.cpp b/indra/llmath/llmatrix4a.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fe8f0b98f3604a8c1df602c84d49d38e123d3780
--- /dev/null
+++ b/indra/llmath/llmatrix4a.cpp
@@ -0,0 +1,80 @@
+/**
+* @file llmatrix4a.cpp
+* @brief  Functions for vectorized matrix/vector operations
+*
+* $LicenseInfo:firstyear=2018&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2018, 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 "llmath.h"
+#include "llmatrix4a.h"
+
+// Convert a bounding box into other coordinate system. Should give
+// the same results as transforming every corner of the bounding box
+// and extracting the bounding box of that, although that's not
+// necessarily the fastest way to implement.
+void matMulBoundBox(const LLMatrix4a &mat, const LLVector4a *in_extents, LLVector4a *out_extents)
+{
+		//get 8 corners of bounding box
+		LLVector4Logical mask[6];
+
+		for (U32 i = 0; i < 6; ++i)
+		{
+			mask[i].clear();
+		}
+
+		mask[0].setElement<2>(); //001
+		mask[1].setElement<1>(); //010
+		mask[2].setElement<1>(); //011
+		mask[2].setElement<2>();
+		mask[3].setElement<0>(); //100
+		mask[4].setElement<0>(); //101
+		mask[4].setElement<2>();
+		mask[5].setElement<0>(); //110
+		mask[5].setElement<1>();
+
+		LLVector4a v[8];
+
+		v[6] = in_extents[0];
+		v[7] = in_extents[1];
+
+		for (U32 i = 0; i < 6; ++i)
+		{
+			v[i].setSelectWithMask(mask[i], in_extents[0], in_extents[1]);
+		}
+
+		LLVector4a tv[8];
+
+		//transform bounding box into drawable space
+		for (U32 i = 0; i < 8; ++i)
+		{
+			mat.affineTransform(v[i], tv[i]);
+		}
+	
+		//find bounding box
+		out_extents[0] = out_extents[1] = tv[0];
+
+		for (U32 i = 1; i < 8; ++i)
+		{
+			out_extents[0].setMin(out_extents[0], tv[i]);
+			out_extents[1].setMax(out_extents[1], tv[i]);
+		}
+}
diff --git a/indra/llmath/llmatrix4a.h b/indra/llmath/llmatrix4a.h
index baa3a891760a49926b45130d664e4e8dce971c4a..7ba347062f724c99b8df1589cb5bee169daa8fa1 100644
--- a/indra/llmath/llmatrix4a.h
+++ b/indra/llmath/llmatrix4a.h
@@ -121,7 +121,7 @@ class LLMatrix4a
 		res.add(z);
 	}
 
-	inline void affineTransformSSE(const LLVector4a& v, LLVector4a& res)
+	inline void affineTransformSSE(const LLVector4a& v, LLVector4a& res) const
 	{
 		LLVector4a x,y,z;
 
@@ -138,7 +138,7 @@ class LLMatrix4a
 		res.setAdd(x,z);
 	}
 
-    inline void affineTransformNonSSE(const LLVector4a& v, LLVector4a& res)
+    inline void affineTransformNonSSE(const LLVector4a& v, LLVector4a& res) const
     {
         F32 x = v[0] * mMatrix[0][0] + v[1] * mMatrix[1][0] + v[2] * mMatrix[2][0] + mMatrix[3][0];
         F32 y = v[0] * mMatrix[0][1] + v[1] * mMatrix[1][1] + v[2] * mMatrix[2][1] + mMatrix[3][1];
@@ -147,7 +147,7 @@ class LLMatrix4a
         res.set(x,y,z,w);
     }
 
-	inline void affineTransform(const LLVector4a& v, LLVector4a& res)
+	inline void affineTransform(const LLVector4a& v, LLVector4a& res) const
     {
         affineTransformSSE(v,res);
     }
@@ -182,4 +182,6 @@ inline std::ostream& operator<<(std::ostream& s, const LLMatrix4a& m)
     return s;
 } 
 
+void matMulBoundBox(const LLMatrix4a &a, const LLVector4a *in_extents, LLVector4a *out_extents);
+
 #endif
diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h
index 097558ef679a83b50c3bfc33f897439ac9a18910..53585d2e0418567f578ad529eb03c9663bbd2321 100644
--- a/indra/llprimitive/llmodel.h
+++ b/indra/llprimitive/llmodel.h
@@ -38,7 +38,6 @@ class domMesh;
 
 #define MAX_MODEL_FACES 8
 
-
 class LLMeshSkinInfo 
 {
 public:
diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp
index efd57ec39f38991403913a65d6b2b434d0e582cf..7bc78143926b87ffc368d514285070424687f62a 100644
--- a/indra/newview/llface.cpp
+++ b/indra/newview/llface.cpp
@@ -810,17 +810,11 @@ bool less_than_max_mag(const LLVector4a& vec)
 }
 
 BOOL LLFace::genVolumeBBoxes(const LLVolume &volume, S32 f,
-								const LLMatrix4& mat_vert_in, BOOL global_volume)
+                             const LLMatrix4& mat_vert_in, BOOL global_volume)
 {
 	//get bounding box
 	if (mDrawablep->isState(LLDrawable::REBUILD_VOLUME | LLDrawable::REBUILD_POSITION | LLDrawable::REBUILD_RIGGED))
 	{
-		//VECTORIZE THIS
-		LLMatrix4a mat_vert;
-		mat_vert.loadu(mat_vert_in);
-
-		LLVector4a min,max;
-	
 		if (f >= volume.getNumVolumeFaces())
 		{
 			LL_WARNS() << "Generating bounding box for invalid face index!" << LL_ENDL;
@@ -828,39 +822,11 @@ BOOL LLFace::genVolumeBBoxes(const LLVolume &volume, S32 f,
 		}
 
 		const LLVolumeFace &face = volume.getVolumeFace(f);
-		min = face.mExtents[0];
-		max = face.mExtents[1];
 		
-        LL_DEBUGS("RiggedBox") << "updating extents for face " << f << " starting extents " << mExtents[0] << ", " << mExtents[1] << LL_ENDL;
-        LL_DEBUGS("RiggedBox") << "updating extents for face " << f << " starting vf extents " << face.mExtents[0] << ", " << face.mExtents[1] << " num verts " << face.mNumVertices << LL_ENDL;
-
-		llassert(less_than_max_mag(min));
-		llassert(less_than_max_mag(max));
-
-		//min, max are in volume space, convert to drawable render space
-
-		//get 8 corners of bounding box
-		LLVector4Logical mask[6];
-
-		for (U32 i = 0; i < 6; ++i)
-		{
-			mask[i].clear();
-		}
-
-		mask[0].setElement<2>(); //001
-		mask[1].setElement<1>(); //010
-		mask[2].setElement<1>(); //011
-		mask[2].setElement<2>();
-		mask[3].setElement<0>(); //100
-		mask[4].setElement<0>(); //101
-		mask[4].setElement<2>();
-		mask[5].setElement<0>(); //110
-		mask[5].setElement<1>();
-
-		LLVector4a v[8];
-
-		v[6] = min;
-		v[7] = max;
+        LL_DEBUGS("RiggedBox") << "updating extents for face " << f 
+                               << " starting extents " << mExtents[0] << ", " << mExtents[1] 
+                               << " starting vf extents " << face.mExtents[0] << ", " << face.mExtents[1] 
+                               << " num verts " << face.mNumVertices << LL_ENDL;
 
         // MAINT-8264 - stray vertices, especially in low LODs, cause bounding box errors.
 		if (face.mNumVertices < 3) 
@@ -870,52 +836,38 @@ BOOL LLFace::genVolumeBBoxes(const LLVolume &volume, S32 f,
             return FALSE;
         }
         
-		for (U32 i = 0; i < 6; ++i)
-		{
-			v[i].setSelectWithMask(mask[i], min, max);
-		}
-
-		LLVector4a tv[8];
-
-        LL_DEBUGS("RiggedBox") << "updating extents for face " << f << " mat is " << mat_vert << LL_ENDL;
+		//VECTORIZE THIS
+		LLMatrix4a mat_vert;
+		mat_vert.loadu(mat_vert_in);
+        LLVector4a new_extents[2];
 
-		//transform bounding box into drawable space
-		for (U32 i = 0; i < 8; ++i)
-		{
-			mat_vert.affineTransform(v[i], tv[i]);
-            LL_DEBUGS("RiggedBox") << "updating extents for face " << f << " i " << i << " v and tv " << v[i] << ", " << tv[i] << LL_ENDL;
-		}
-	
-		//find bounding box
-		LLVector4a& newMin = mExtents[0];
-		LLVector4a& newMax = mExtents[1];
+		llassert(less_than_max_mag(face.mExtents[0]));
+		llassert(less_than_max_mag(face.mExtents[1]));
 
-		newMin = newMax = tv[0];
+		matMulBoundBox(mat_vert, face.mExtents, mExtents);
 
-		for (U32 i = 1; i < 8; ++i)
-		{
-			newMin.setMin(newMin, tv[i]);
-			newMax.setMax(newMax, tv[i]);
-		}
-        LL_DEBUGS("RiggedBox") << "updating extents for face " << f << " bbox gave extents " << mExtents[0] << ", " << mExtents[1] << LL_ENDL;
+        LL_DEBUGS("RiggedBox") << "updated extents for face " << f 
+                               << " bbox gave extents " << mExtents[0] << ", " << mExtents[1] << LL_ENDL;
 
 		if (!mDrawablep->isActive())
 		{	// Shift position for region
 			LLVector4a offset;
 			offset.load3(mDrawablep->getRegion()->getOriginAgent().mV);
-			newMin.add(offset);
-			newMax.add(offset);
-            LL_DEBUGS("RiggedBox") << "updating extents for face " << f << " not active, added offset " << offset << LL_ENDL;
+			mExtents[0].add(offset);
+			mExtents[1].add(offset);
+            LL_DEBUGS("RiggedBox") << "updating extents for face " << f 
+                                   << " not active, added offset " << offset << LL_ENDL;
 		}
 
-        LL_DEBUGS("RiggedBox") << "updated extents for face " << f << " to " << mExtents[0] << ", " << mExtents[1] << LL_ENDL;
+        LL_DEBUGS("RiggedBox") << "updated extents for face " << f 
+                               << " to " << mExtents[0] << ", " << mExtents[1] << LL_ENDL;
 		LLVector4a t;
-		t.setAdd(newMin,newMax);
+		t.setAdd(mExtents[0],mExtents[1]);
 		t.mul(0.5f);
 
 		mCenterLocal.set(t.getF32ptr());
 
-		t.setSub(newMax,newMin);
+		t.setSub(mExtents[1],mExtents[0]);
 		mBoundingSphereRadius = t.getLength3().getF32()*0.5f;
 
 		updateCenterAgent();
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 0ea29bae58d17dce34581a84585e12b69495e533..277f8e453325f34533cfb1640d8e4741bf8fd371 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -1277,13 +1277,11 @@ void LLVOAvatar::updateSpatialExtents(LLVector4a& newMin, LLVector4a &newMax)
 		mImpostorOffset = LLVector3(pos_group.getF32ptr())-getRenderPosition();
 		mDrawable->setPositionGroup(pos_group);
 	}
-	
-	
 }
 
 void LLVOAvatar::getSpatialExtents(LLVector4a& newMin, LLVector4a& newMax)
 {
-	LLVector4a buffer(0.25f);
+	LLVector4a buffer(0.0);
 	LLVector4a pos;
 	pos.load3(getRenderPosition().mV);
 	newMin.setSub(pos, buffer);
@@ -1312,7 +1310,7 @@ void LLVOAvatar::getSpatialExtents(LLVector4a& newMin, LLVector4a& newMax)
 
 	mPixelArea = LLPipeline::calcPixelArea(center, size, *LLViewerCamera::getInstance());
 
-	//stretch bounding box by attachments
+	//stretch bounding box by static attachments
 	for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); 
 		 iter != mAttachmentPoints.end();
 		 ++iter)
@@ -1356,6 +1354,39 @@ void LLVOAvatar::getSpatialExtents(LLVector4a& newMin, LLVector4a& newMax)
 		}
 	}
 
+    // Stretch bounding box by rigged mesh joint boxes
+    for (S32 joint_num = 0; joint_num < LL_CHARACTER_MAX_ANIMATED_JOINTS; joint_num++)
+    {
+        LLJoint *joint = getJoint(joint_num);
+
+        // FIXME TEMP HACK FOR TESTING
+        if (joint)
+        {
+            joint->getRiggingInfo().setIsRiggedTo(true);
+        }
+
+        if (joint && joint->getRiggingInfo().isRiggedTo())
+        {
+            LLViewerJointAttachment *as_joint_attach = dynamic_cast<LLViewerJointAttachment*>(joint);
+            if (as_joint_attach && as_joint_attach->getIsHUDAttachment())
+            {
+                // Ignore bounding box of HUD joints
+                continue;
+            }
+            LLMatrix4a mat;
+            LLVector4a new_extents[2];
+            mat.loadu(joint->getWorldMatrix());
+            matMulBoundBox(mat, joint->getRiggingInfo().getRiggedExtents(), new_extents);
+            update_min_max(newMin, newMax, new_extents[0]);
+            update_min_max(newMin, newMax, new_extents[1]);
+            //if (isSelf())
+            //{
+            //    LL_INFOS() << joint->getName() << " extents " << new_extents[0] << "," << new_extents[1] << LL_ENDL;
+            //    LL_INFOS() << joint->getName() << " av box is " << newMin << "," << newMax << LL_ENDL;
+            //}
+        }
+    }
+
 	//pad bounding box	
 
 	newMin.sub(buffer);