diff --git a/indra/llmath/llmatrix4a.h b/indra/llmath/llmatrix4a.h
index 216334752a170c5364c1f675ba7e63ab9b9fb956..baa3a891760a49926b45130d664e4e8dce971c4a 100644
--- a/indra/llmath/llmatrix4a.h
+++ b/indra/llmath/llmatrix4a.h
@@ -176,4 +176,10 @@ inline void matMul(const LLMatrix4a &a, const LLMatrix4a &b, LLMatrix4a &res)
     res.mMatrix[3] = row3;
 }
 
+inline std::ostream& operator<<(std::ostream& s, const LLMatrix4a& m)
+{
+    s << "[" << m.mMatrix[0] << ", " << m.mMatrix[1] << ", " << m.mMatrix[2] << ", " << m.mMatrix[3] << "]";
+    return s;
+} 
+
 #endif
diff --git a/indra/llmath/llvector4a.h b/indra/llmath/llvector4a.h
index 79d0a44551a607f9ca0d8edcfb39dc0054761999..222f3cf235199347813b67c6351cd3a1d74aa97a 100644
--- a/indra/llmath/llvector4a.h
+++ b/indra/llmath/llvector4a.h
@@ -320,7 +320,7 @@ class LLVector4a
 	inline const LLVector4a& operator= ( const LLQuad& rhs );
 
 	inline operator LLQuad() const;	
-
+    
 private:
 	LLQuad mQ;
 } LL_ALIGN_POSTFIX(16);
@@ -331,4 +331,9 @@ inline void update_min_max(LLVector4a& min, LLVector4a& max, const LLVector4a& p
 	max.setMax(max, p);
 }
 
+inline std::ostream& operator<<(std::ostream& s, const LLVector4a& v)
+{
+    s << "(" << v[0] << ", " << v[1] << ", " << v[2] << ", " << v[3] << ")";
+    return s;
+}
 #endif
diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
index f1e9817cb2e3309159c98fbbb99a423bfb2fac2b..8556e5294c58f1e5d30d386e4318efd716fb6553 100644
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -2636,6 +2636,7 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)
 			}
 
 			//calculate bounding box
+			// VFExtents change
 			LLVector4a& min = face.mExtents[0];
 			LLVector4a& max = face.mExtents[1];
 
@@ -4721,6 +4722,7 @@ LLVolumeFace::~LLVolumeFace()
 {
 	ll_aligned_free_16(mExtents);
 	mExtents = NULL;
+	mCenter = NULL;
 
 	freeData();
 }
@@ -5496,7 +5498,7 @@ BOOL LLVolumeFace::createUnCutCubeCap(LLVolume* volume, BOOL partial_build)
 
 	// S32 i;
 	S32	grid_size = (profile.size()-1)/4;
-
+	// VFExtents change
 	LLVector4a& min = mExtents[0];
 	LLVector4a& max = mExtents[1];
 
@@ -5773,7 +5775,7 @@ BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build)
 	
 	LLVector2 cuv;
 	LLVector2 min_uv, max_uv;
-
+	// VFExtents change
 	LLVector4a& min = mExtents[0];
 	LLVector4a& max = mExtents[1];
 
@@ -6398,14 +6400,17 @@ void LLVolumeFace::appendFace(const LLVolumeFace& face, LLMatrix4& mat_in, LLMat
 
 		if (offset == 0 && i == 0)
 		{ //initialize bounding box
+			// VFExtents change
 			mExtents[0] = mExtents[1] = dst_pos[i];
 		}
 		else
 		{
 			//stretch bounding box
+			// VFExtents change
 			update_min_max(mExtents[0], mExtents[1], dst_pos[i]);
 		}
 	}
+    LL_DEBUGS("RiggedBox") << "appendFace got extents " << mExtents[0] << ", " << mExtents[1] << " from dst_pos " << LL_ENDL;
 
 
 	new_count = mNumIndices + face.mNumIndices;
@@ -6568,7 +6573,7 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)
 	{
 		update_min_max(face_min, face_max, *cur_pos++);
 	}
-
+	// VFExtents change
 	mExtents[0] = face_min;
 	mExtents[1] = face_max;
 
diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
index 8401cb976e3c77e7b068fc89ad4c25d615467afc..8f75d89e5a797b17c6b1a780cc4cf2a93c75be8c 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -192,7 +192,7 @@ LLModel::EModelStatus load_face_from_dom_triangles(std::vector<LLVolumeFace>& fa
 		{
 			return LLModel::BAD_ELEMENT;
 		}
-
+		// VFExtents change
 		face.mExtents[0].set(v[0], v[1], v[2]);
 		face.mExtents[1].set(v[0], v[1], v[2]);
 	}
@@ -254,6 +254,7 @@ LLModel::EModelStatus load_face_from_dom_triangles(std::vector<LLVolumeFace>& fa
 
 		if (!found)
 		{
+			// VFExtents change
 			update_min_max(face.mExtents[0], face.mExtents[1], cv.getPosition());
 			verts.push_back(cv);
 			if (verts.size() >= 65535)
@@ -305,6 +306,7 @@ LLModel::EModelStatus load_face_from_dom_triangles(std::vector<LLVolumeFace>& fa
 			}
 
 			face = LLVolumeFace();
+			// VFExtents change
 			face.mExtents[0].set(v[0], v[1], v[2]);
 			face.mExtents[1].set(v[0], v[1], v[2]);
 			point_map.clear();
@@ -383,6 +385,7 @@ LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& fac
 	if (pos_source)
 	{
 		v = pos_source->getFloat_array()->getValue();
+		// VFExtents change
 		face.mExtents[0].set(v[0], v[1], v[2]);
 		face.mExtents[1].set(v[0], v[1], v[2]);
 	}
@@ -482,6 +485,7 @@ LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& fac
 
 			if (!found)
 			{
+				// VFExtents change
 				update_min_max(face.mExtents[0], face.mExtents[1], cv.getPosition());
 				verts.push_back(cv);
 				if (verts.size() >= 65535)
@@ -551,6 +555,7 @@ LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& fac
 				}
 
 				face = LLVolumeFace();
+				// VFExtents change
 				face.mExtents[0].set(v[0], v[1], v[2]);
 				face.mExtents[1].set(v[0], v[1], v[2]);
 				verts.clear();
@@ -734,7 +739,7 @@ LLModel::EModelStatus load_face_from_dom_polygons(std::vector<LLVolumeFace>& fac
 	{
 		return LLModel::NO_ERRORS;
 	}
-
+	// VFExtents change
 	face.mExtents[0] = verts[0].getPosition();
 	face.mExtents[1] = verts[0].getPosition();
 	
@@ -758,6 +763,7 @@ LLModel::EModelStatus load_face_from_dom_polygons(std::vector<LLVolumeFace>& fac
 	for (std::map<LLVolumeFace::VertexData, U32>::iterator iter = vert_idx.begin(); iter != vert_idx.end(); ++iter)
 	{
 		new_verts[iter->second] = iter->first;
+		// VFExtents change
 		update_min_max(face.mExtents[0], face.mExtents[1], iter->first.getPosition());
 	}
 
diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp
index db6d00bc2cfc9b749e0ca7617ac6fdea0d166322..597cc66088f09e65c46f7acf8a526a86583800e0 100644
--- a/indra/llprimitive/llmodel.cpp
+++ b/indra/llprimitive/llmodel.cpp
@@ -276,6 +276,7 @@ void LLModel::normalizeVolumeFaces()
 			// We shrink the extents so
 			// that they fall within
 			// the unit cube.
+			// VFExtents change
 			face.mExtents[0].add(trans);
 			face.mExtents[0].mul(scale);
 
diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp
index 6799c3f86204705cc44def536050e8fdbfdd1bc4..7e513896551b1bd7ada9d6786bd92e9bfb92afc5 100644
--- a/indra/newview/lldrawable.cpp
+++ b/indra/newview/lldrawable.cpp
@@ -51,6 +51,7 @@
 #include "llviewerwindow.h"
 #include "llvocache.h"
 #include "llcontrolavatar.h"
+#include "llcallstack.h"
 
 const F32 MIN_INTERPOLATE_DISTANCE_SQUARED = 0.001f * 0.001f;
 const F32 MAX_INTERPOLATE_DISTANCE_SQUARED = 10.f * 10.f;
@@ -1086,7 +1087,8 @@ void LLDrawable::setGroup(LLViewerOctreeGroup *groupp)
 	llassert(!groupp || (LLSpatialGroup*)groupp->hasElement(this));
 
 	if (cur_groupp != groupp && getVOVolume())
-	{ //NULL out vertex buffer references for volumes on spatial group change to maintain
+	{
+		//NULL out vertex buffer references for volumes on spatial group change to maintain
 		//requirement that every face vertex buffer is either NULL or points to a vertex buffer
 		//contained by its drawable's spatial group
 		for (S32 i = 0; i < getNumFaces(); ++i)
diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp
index 0a7e0c92be7644431e053724c9a2e6cc0563b71c..efd57ec39f38991403913a65d6b2b434d0e582cf 100644
--- a/indra/newview/llface.cpp
+++ b/indra/newview/llface.cpp
@@ -831,6 +831,9 @@ BOOL LLFace::genVolumeBBoxes(const LLVolume &volume, S32 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));
 
@@ -859,6 +862,14 @@ BOOL LLFace::genVolumeBBoxes(const LLVolume &volume, S32 f,
 		v[6] = min;
 		v[7] = max;
 
+        // MAINT-8264 - stray vertices, especially in low LODs, cause bounding box errors.
+		if (face.mNumVertices < 3) 
+        {
+            LL_DEBUGS("RiggedBox") << "skipping face " << f << ", bad num vertices " 
+                                   << face.mNumVertices << " " << face.mNumIndices << " " << face.mWeights << LL_ENDL;
+            return FALSE;
+        }
+        
 		for (U32 i = 0; i < 6; ++i)
 		{
 			v[i].setSelectWithMask(mask[i], min, max);
@@ -866,10 +877,13 @@ BOOL LLFace::genVolumeBBoxes(const LLVolume &volume, S32 f,
 
 		LLVector4a tv[8];
 
+        LL_DEBUGS("RiggedBox") << "updating extents for face " << f << " mat is " << mat_vert << LL_ENDL;
+
 		//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
@@ -883,6 +897,7 @@ BOOL LLFace::genVolumeBBoxes(const LLVolume &volume, S32 f,
 			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;
 
 		if (!mDrawablep->isActive())
 		{	// Shift position for region
@@ -890,8 +905,10 @@ BOOL LLFace::genVolumeBBoxes(const LLVolume &volume, S32 f,
 			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;
 		}
 
+        LL_DEBUGS("RiggedBox") << "updated extents for face " << f << " to " << mExtents[0] << ", " << mExtents[1] << LL_ENDL;
 		LLVector4a t;
 		t.setAdd(newMin,newMax);
 		t.mul(0.5f);
diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp
index d0328a75394612fd617f88fde0e5e0a663ace452..8b1a23fe89c566e3dbb4b0729972e599ea4874c4 100644
--- a/indra/newview/llspatialpartition.cpp
+++ b/indra/newview/llspatialpartition.cpp
@@ -29,6 +29,7 @@
 #include "llspatialpartition.h"
 
 #include "llappviewer.h"
+#include "llcallstack.h"
 #include "lltexturecache.h"
 #include "lltexturefetch.h"
 #include "llimageworker.h"
@@ -777,6 +778,10 @@ F32 LLSpatialPartition::calcDistance(LLSpatialGroup* group, LLCamera& camera)
 		dist = eye.getLength3().getF32();
 	}
 
+    LL_DEBUGS("RiggedBox") << "calcDistance, group " << group << " camera " << origin << " obj bounds " 
+                           << group->mObjectBounds[0] << ", " << group->mObjectBounds[1] 
+                           << " dist " << dist << " radius " << group->mRadius << LL_ENDL;
+
 	if (dist < 16.f)
 	{
 		dist /= 16.f;
@@ -808,7 +813,8 @@ F32 LLSpatialGroup::getUpdateUrgency() const
 BOOL LLSpatialGroup::changeLOD()
 {
 	if (hasState(ALPHA_DIRTY | OBJECT_DIRTY))
-	{ ///a rebuild is going to happen, update distance and LoD
+	{
+		//a rebuild is going to happen, update distance and LoD
 		return TRUE;
 	}
 
@@ -816,8 +822,28 @@ BOOL LLSpatialGroup::changeLOD()
 	{
 		F32 ratio = (mDistance - mLastUpdateDistance)/(llmax(mLastUpdateDistance, mRadius));
 
+        // MAINT-8264 - this check is not robust if it needs to work
+        // for bounding boxes much larger than the actual enclosed
+        // objects, and using distance to box center is also
+        // problematic. Consider the case that you have a large box
+        // where the enclosed object is in one corner. As you zoom in
+        // on the corner, the object gets much closer to the camera,
+        // but the distance to the box center changes very little, and
+        // an LOD change will not trigger, so object LOD gets "stuck"
+        // at a too-low value. In the case of the above JIRA, the box
+        // was large only due to another error, so this logic did not
+        // need to be changed.
+
 		if (fabsf(ratio) >= getSpatialPartition()->mSlopRatio)
 		{
+            LL_DEBUGS("RiggedBox") << "changeLOD true because of ratio compare "
+                                   << fabsf(ratio) << " " << getSpatialPartition()->mSlopRatio << LL_ENDL;
+            LL_DEBUGS("RiggedBox") << "sg " << this << "\nmDistance " << mDistance
+                                   << " mLastUpdateDistance " << mLastUpdateDistance
+                                   << " mRadius " << mRadius
+                                   << " fab ratio " << fabsf(ratio) 
+                                   << " slop " << getSpatialPartition()->mSlopRatio << LL_ENDL;
+       
 			return TRUE;
 		}
 
@@ -2109,17 +2135,17 @@ void renderBoundingBox(LLDrawable* drawable, BOOL set_color = TRUE)
 	{
 		if (drawable->isSpatialBridge())
 		{
-			gGL.diffuseColor4f(1,0.5f,0,1);
+			gGL.diffuseColor4f(1,0.5f,0,1); // orange
 		}
 		else if (drawable->getVOVolume())
-		{
-			if (drawable->isRoot())
+		{ 
+            if (drawable->isRoot())
 			{
-				gGL.diffuseColor4f(1,1,0,1);
+				gGL.diffuseColor4f(1,1,0,1); // yellow
 			}
 			else
 			{
-				gGL.diffuseColor4f(0,1,0,1);
+				gGL.diffuseColor4f(0,1,0,1); // green
 			}
 		}
 		else if (drawable->getVObj())
@@ -2127,24 +2153,24 @@ void renderBoundingBox(LLDrawable* drawable, BOOL set_color = TRUE)
 			switch (drawable->getVObj()->getPCode())
 			{
 				case LLViewerObject::LL_VO_SURFACE_PATCH:
-						gGL.diffuseColor4f(0,1,1,1);
+                    	gGL.diffuseColor4f(0,1,1,1); // cyan
 						break;
 				case LLViewerObject::LL_VO_CLOUDS:
 						// no longer used
 						break;
 				case LLViewerObject::LL_VO_PART_GROUP:
 				case LLViewerObject::LL_VO_HUD_PART_GROUP:
-						gGL.diffuseColor4f(0,0,1,1);
+                    	gGL.diffuseColor4f(0,0,1,1); // blue
 						break;
 				case LLViewerObject::LL_VO_VOID_WATER:
 				case LLViewerObject::LL_VO_WATER:
-						gGL.diffuseColor4f(0,0.5f,1,1);
+                    	gGL.diffuseColor4f(0,0.5f,1,1); // medium blue
 						break;
 				case LL_PCODE_LEGACY_TREE:
-						gGL.diffuseColor4f(0,0.5f,0,1);
+                    	gGL.diffuseColor4f(0,0.5f,0,1); // dark green
 						break;
 				default:
-						gGL.diffuseColor4f(1,0,1,1);
+                    	gGL.diffuseColor4f(1,0,1,1); // magenta
 						break;
 			}
 		}
diff --git a/indra/newview/llvieweroctree.cpp b/indra/newview/llvieweroctree.cpp
index 023f1b92ba5ff92a6a11abfceb9d25e929c9b943..e7916ebd3b67f7a8a26f8bd49a5e3968afb4eefe 100644
--- a/indra/newview/llvieweroctree.cpp
+++ b/indra/newview/llvieweroctree.cpp
@@ -731,7 +731,7 @@ bool LLViewerOctreeGroup::boundObjects(BOOL empty, LLVector4a& minOut, LLVector4
 			update_min_max(newMin, newMax, minMax[0]);
 			update_min_max(newMin, newMax, minMax[1]);
 		}
-		
+
 		mObjectBounds[0].setAdd(newMin, newMax);
 		mObjectBounds[0].mul(0.5f);
 		mObjectBounds[1].setSub(newMax, newMin);
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index c007b446f7d2b3e59456db421a0a4003d19a6bf3..a07e3f8be119cacbe106f4da0ff21843959b5443 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -1515,14 +1515,22 @@ BOOL LLVOVolume::genBBoxes(BOOL force_global)
 
 	BOOL rebuild = mDrawable->isState(LLDrawable::REBUILD_VOLUME | LLDrawable::REBUILD_POSITION | LLDrawable::REBUILD_RIGGED);
 
-	//	bool rigged = false;
+    if (getRiggedVolume())
+    {
+        updateRiggedVolume(TRUE);
+    }
+    
 	LLVolume* volume = mRiggedVolume;
 	if (!volume)
 	{
 		volume = getVolume();
 	}
 
-	// There's no guarantee that getVolume()->getNumFaces() == mDrawable->getNumFaces()
+    if (getRiggedVolume())
+    {
+        LL_DEBUGS("RiggedBox") << "rebuilding box, volume face count " << getVolume()->getNumVolumeFaces() << " drawable face count " << mDrawable->getNumFaces() << LL_ENDL;
+    }
+    // There's no guarantee that getVolume()->getNumFaces() == mDrawable->getNumFaces()
 	for (S32 i = 0;
 		 i < getVolume()->getNumVolumeFaces() && i < mDrawable->getNumFaces() && i < getNumTEs();
 		 i++)
@@ -1532,12 +1540,18 @@ BOOL LLVOVolume::genBBoxes(BOOL force_global)
 		{
 			continue;
 		}
-		res &= face->genVolumeBBoxes(*volume, i,
-										mRelativeXform, 
-										(mVolumeImpl && mVolumeImpl->isVolumeGlobal()) || force_global);
+
+        BOOL face_res = face->genVolumeBBoxes(*volume, i,
+                                              mRelativeXform, 
+                                              (mVolumeImpl && mVolumeImpl->isVolumeGlobal()) || force_global);
+        res &= face_res; // note that this result is never used
 		
 		if (rebuild)
 		{
+            if (getRiggedVolume())
+            {
+                LL_DEBUGS("RiggedBox") << "rebuilding box, face " << i << " extents " << face->mExtents[0] << ", " << face->mExtents[1] << LL_ENDL;
+            }
 			if (i == 0)
 			{
 				min = face->mExtents[0];
@@ -1545,6 +1559,11 @@ BOOL LLVOVolume::genBBoxes(BOOL force_global)
 			}
 			else
 			{
+                if (!face_res)
+                {
+                    // MAINT-8264 - ignore bboxes of ill-formed faces.
+                    continue;
+                }
 				min.setMin(min, face->mExtents[0]);
 				max.setMax(max, face->mExtents[1]);
 			}
@@ -1553,6 +1572,10 @@ BOOL LLVOVolume::genBBoxes(BOOL force_global)
 	
 	if (rebuild)
 	{
+        if (getRiggedVolume())
+        {
+            LL_DEBUGS("RiggedBox") << "rebuilding got extents " << min << ", " << max << LL_ENDL;
+        }
 		mDrawable->setSpatialExtents(min,max);
 		min.add(max);
 		min.mul(0.5f);
@@ -4512,6 +4535,7 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons
 				}
 
 				//update bounding box
+				// VFExtents change
 				LLVector4a& min = dst_face.mExtents[0];
 				LLVector4a& max = dst_face.mExtents[1];
 
@@ -4528,6 +4552,7 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons
 					min.setMin(min, pos[j]);
 					max.setMax(max, pos[j]);
 				}
+
                 box_min.setMin(min,box_min);
                 box_max.setMax(max,box_max);
 
@@ -5065,6 +5090,9 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
                                                 << " is_animated " << vobj->isAnimatedObject()
                                                 << " can_animate " << vobj->canBeAnimatedObject() 
                                                 << " cav " << vobj->getControlAvatar() 
+                                                << " lod " << vobj->getLOD()
+                                                << " drawable rigged " << (drawablep->isState(LLDrawable::RIGGED))
+                                                << " drawable state " << drawablep->getState()
                                                 << " playing " << (U32) (vobj->getControlAvatar() ? vobj->getControlAvatar()->mPlaying : false)
                                                 << " frame " << LLFrameTimer::getFrameCount()
                                                 << LL_ENDL;
diff --git a/indra/newview/skins/default/xui/en/menu_object.xml b/indra/newview/skins/default/xui/en/menu_object.xml
index 91e7328979904ef1416c678e339e4af95b47705b..ce34508303b34122fdf9ea5e4e8ee04f1791c7f4 100644
--- a/indra/newview/skins/default/xui/en/menu_object.xml
+++ b/indra/newview/skins/default/xui/en/menu_object.xml
@@ -228,6 +228,6 @@
             <menu_item_call.on_click
                 function="Avatar.ResetSkeleton" />
             <menu_item_call.on_visible
-             function="Advanced.EnableResetSkeleton"/>
+             function="Avatar.EnableResetSkeleton"/>
     </menu_item_call>
 </context_menu>