diff --git a/indra/llmath/CMakeLists.txt b/indra/llmath/CMakeLists.txt
index 075d3b3af0e601d0d89d5234cb51c83c3b7ec7f0..d287a5063a36313fccf62dc403cb5ea64d6e4318 100644
--- a/indra/llmath/CMakeLists.txt
+++ b/indra/llmath/CMakeLists.txt
@@ -10,6 +10,7 @@ include_directories(
     )
 
 set(llmath_SOURCE_FILES
+    llbbox.cpp
     llbboxlocal.cpp
     llcamera.cpp
     llcoordframe.cpp
@@ -39,6 +40,7 @@ set(llmath_HEADER_FILES
 
     camera.h
     coordframe.h
+    llbbox.h
     llbboxlocal.h
     llcamera.h
     llcoord.h
diff --git a/indra/llmath/llbbox.cpp b/indra/llmath/llbbox.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..acf93a2a38fc2e0efbc2cb144af5bece154362ab
--- /dev/null
+++ b/indra/llmath/llbbox.cpp
@@ -0,0 +1,160 @@
+/** 
+ * @file llbbox.cpp
+ * @brief General purpose bounding box class (Not axis aligned)
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ * 
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+// self include
+#include "llbbox.h"
+
+// library includes
+#include "m4math.h"
+
+void LLBBox::addPointLocal(const LLVector3& p)
+{
+	if (mEmpty)
+	{
+		mMinLocal = p;
+		mMaxLocal = p;
+		mEmpty = FALSE;
+	}
+	else
+	{
+		mMinLocal.mV[VX] = llmin( p.mV[VX], mMinLocal.mV[VX] );
+		mMinLocal.mV[VY] = llmin( p.mV[VY], mMinLocal.mV[VY] );
+		mMinLocal.mV[VZ] = llmin( p.mV[VZ], mMinLocal.mV[VZ] );
+		mMaxLocal.mV[VX] = llmax( p.mV[VX], mMaxLocal.mV[VX] );
+		mMaxLocal.mV[VY] = llmax( p.mV[VY], mMaxLocal.mV[VY] );
+		mMaxLocal.mV[VZ] = llmax( p.mV[VZ], mMaxLocal.mV[VZ] );
+	}
+}
+
+void LLBBox::addPointAgent( LLVector3 p)
+{
+	p -= mPosAgent;
+	p.rotVec( ~mRotation );
+	addPointLocal( p );
+}
+
+
+void LLBBox::addBBoxAgent(const LLBBox& b)
+{
+	if (mEmpty)
+	{
+		mPosAgent = b.mPosAgent;
+		mRotation = b.mRotation;
+		mMinLocal.clearVec();
+		mMaxLocal.clearVec();
+	}
+	LLVector3 vertex[8];
+	vertex[0].setVec( b.mMinLocal.mV[VX], b.mMinLocal.mV[VY], b.mMinLocal.mV[VZ] );
+	vertex[1].setVec( b.mMinLocal.mV[VX], b.mMinLocal.mV[VY], b.mMaxLocal.mV[VZ] );
+	vertex[2].setVec( b.mMinLocal.mV[VX], b.mMaxLocal.mV[VY], b.mMinLocal.mV[VZ] );
+	vertex[3].setVec( b.mMinLocal.mV[VX], b.mMaxLocal.mV[VY], b.mMaxLocal.mV[VZ] );
+	vertex[4].setVec( b.mMaxLocal.mV[VX], b.mMinLocal.mV[VY], b.mMinLocal.mV[VZ] );
+	vertex[5].setVec( b.mMaxLocal.mV[VX], b.mMinLocal.mV[VY], b.mMaxLocal.mV[VZ] );
+	vertex[6].setVec( b.mMaxLocal.mV[VX], b.mMaxLocal.mV[VY], b.mMinLocal.mV[VZ] );
+	vertex[7].setVec( b.mMaxLocal.mV[VX], b.mMaxLocal.mV[VY], b.mMaxLocal.mV[VZ] );
+
+	LLMatrix4 m( b.mRotation );
+	m.translate( b.mPosAgent );
+	m.translate( -mPosAgent );
+	m.rotate( ~mRotation );
+
+	for( S32 i=0; i<8; i++ )
+	{
+		addPointLocal( vertex[i] * m );
+	}
+}
+
+
+void LLBBox::expand( F32 delta )
+{
+	mMinLocal.mV[VX] -= delta;
+	mMinLocal.mV[VY] -= delta;
+	mMinLocal.mV[VZ] -= delta;
+	mMaxLocal.mV[VX] += delta;
+	mMaxLocal.mV[VY] += delta;
+	mMaxLocal.mV[VZ] += delta;
+}
+
+LLVector3 LLBBox::localToAgent(const LLVector3& v) const
+{
+	LLMatrix4 m( mRotation );
+	m.translate( mPosAgent );
+	return v * m;
+}
+
+LLVector3 LLBBox::agentToLocal(const LLVector3& v) const
+{
+	LLMatrix4 m;
+	m.translate( -mPosAgent );
+	m.rotate( ~mRotation );  // inverse rotation
+	return v * m;
+}
+
+LLVector3 LLBBox::localToAgentBasis(const LLVector3& v) const
+{
+	LLMatrix4 m( mRotation );
+	return v * m;
+}
+
+LLVector3 LLBBox::agentToLocalBasis(const LLVector3& v) const
+{
+	LLMatrix4 m( ~mRotation );  // inverse rotation
+	return v * m;
+}
+
+BOOL LLBBox::containsPointLocal(const LLVector3& p) const
+{
+	if (  (p.mV[VX] < mMinLocal.mV[VX])
+		||(p.mV[VX] > mMaxLocal.mV[VX])
+		||(p.mV[VY] < mMinLocal.mV[VY])
+		||(p.mV[VY] > mMaxLocal.mV[VY])
+		||(p.mV[VZ] < mMinLocal.mV[VZ])
+		||(p.mV[VZ] > mMaxLocal.mV[VZ]))
+	{
+		return FALSE;
+	}
+	return TRUE;
+}
+
+BOOL LLBBox::containsPointAgent(const LLVector3& p) const
+{
+	LLVector3 point_local = agentToLocal(p);
+	return containsPointLocal(point_local);
+}
+
+
+/*
+LLBBox operator*(const LLBBox &a, const LLMatrix4 &b)
+{
+	return LLBBox( a.mMin * b, a.mMax * b );
+}
+*/
diff --git a/indra/llmath/llbbox.h b/indra/llmath/llbbox.h
new file mode 100644
index 0000000000000000000000000000000000000000..cd29551b0139c7f50ba4b0a693f7809084b373de
--- /dev/null
+++ b/indra/llmath/llbbox.h
@@ -0,0 +1,103 @@
+/** 
+ * @file llbbox.h
+ * @brief General purpose bounding box class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ * 
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_BBOX_H
+#define LL_BBOX_H
+
+#include "v3math.h"
+#include "llquaternion.h"
+
+// Note: "local space" for an LLBBox is defined relative to agent space in terms of
+// a translation followed by a rotation.  There is no scale term since the LLBBox's min and 
+// max are not necessarily symetrical and define their own extents.
+
+class LLBBox
+{
+public:
+	LLBBox() {mEmpty = TRUE;}
+	LLBBox( const LLVector3& pos_agent,
+		const LLQuaternion& rot,
+		const LLVector3& min_local,
+		const LLVector3& max_local )
+		:
+		mMinLocal( min_local ), mMaxLocal( max_local ), mPosAgent(pos_agent), mRotation( rot), mEmpty( TRUE )
+		{}
+
+	// Default copy constructor is OK.
+
+	const LLVector3&	getPositionAgent() const			{ return mPosAgent; }
+	const LLQuaternion&	getRotation() const					{ return mRotation; }
+
+	const LLVector3&	getMinLocal() const					{ return mMinLocal; }
+	void				setMinLocal( const LLVector3& min )	{ mMinLocal = min; }
+
+	const LLVector3&	getMaxLocal() const					{ return mMaxLocal; }
+	void				setMaxLocal( const LLVector3& max )	{ mMaxLocal = max; }
+
+	LLVector3			getCenterLocal() const				{ return (mMaxLocal - mMinLocal) * 0.5f + mMinLocal; }
+	LLVector3			getCenterAgent() const				{ return localToAgent( getCenterLocal() ); }
+
+	LLVector3			getExtentLocal() const				{ return mMaxLocal - mMinLocal; }
+
+	BOOL				containsPointLocal(const LLVector3& p) const;
+	BOOL				containsPointAgent(const LLVector3& p) const;
+
+	void				addPointAgent(LLVector3 p);
+	void				addBBoxAgent(const LLBBox& b);
+	
+	void				addPointLocal(const LLVector3& p);
+	void				addBBoxLocal(const LLBBox& b) {	addPointLocal( b.mMinLocal ); addPointLocal( b.mMaxLocal ); }
+
+	void				expand( F32 delta );
+
+	LLVector3			localToAgent( const LLVector3& v ) const;
+	LLVector3			agentToLocal( const LLVector3& v ) const;
+
+	// Changes rotation but not position
+	LLVector3			localToAgentBasis(const LLVector3& v) const;
+	LLVector3			agentToLocalBasis(const LLVector3& v) const;
+
+
+//	friend LLBBox operator*(const LLBBox& a, const LLMatrix4& b);
+
+private:
+	LLVector3			mMinLocal;
+	LLVector3			mMaxLocal;
+	LLVector3			mPosAgent;  // Position relative to Agent's Region
+	LLQuaternion		mRotation;
+	BOOL				mEmpty;		// Nothing has been added to this bbox yet
+};
+
+//LLBBox operator*(const LLBBox &a, const LLMatrix4 &b);
+
+
+#endif  // LL_BBOX_H
diff --git a/indra/llmath/llbboxlocal.h b/indra/llmath/llbboxlocal.h
index d69028e3aae26e48dbc5ce972836250a64a7e5f3..0d1e5a3ae5e87aebb7178a2137bea87374746dfa 100644
--- a/indra/llmath/llbboxlocal.h
+++ b/indra/llmath/llbboxlocal.h
@@ -53,9 +53,6 @@ class LLBBoxLocal
 	LLVector3			getCenter() const				{ return (mMax - mMin) * 0.5f + mMin; }
 	LLVector3			getExtent() const				{ return mMax - mMin; }
 
-	BOOL				containsPoint(const LLVector3& p) const;
-	BOOL				intersects(const LLBBoxLocal& b) const;
-
 	void				addPoint(const LLVector3& p);
 	void				addBBox(const LLBBoxLocal& b) {	addPoint( b.mMin );	addPoint( b.mMax ); }
 
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 8becd814e7067c7243931cb5356fe92a3b29e5c0..d9d2f6f7327807fd6061e096f94a61b16d323acc 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -79,7 +79,6 @@ set(viewer_SOURCE_FILES
     llavatarlist.cpp
     llavatarlistitem.cpp
     llavatarpropertiesprocessor.cpp
-    llbbox.cpp
     llbottomtray.cpp
     llbox.cpp
     llbreadcrumbview.cpp
@@ -501,7 +500,6 @@ set(viewer_HEADER_FILES
     llavatarlist.h
     llavatarlistitem.h
     llavatarpropertiesprocessor.h
-    llbbox.h
     llbottomtray.h
     llbox.h
     llbreadcrumbview.h
diff --git a/indra/test/CMakeLists.txt b/indra/test/CMakeLists.txt
index 9ea8d5e5fb1adb121437d1f47591bf638a87a76f..463eedb4fd2fb50117f03898d9d0a1f8444c34f6 100644
--- a/indra/test/CMakeLists.txt
+++ b/indra/test/CMakeLists.txt
@@ -32,6 +32,8 @@ set(test_SOURCE_FILES
     io.cpp
 #    llapp_tut.cpp						# Temporarily removed until thread issues can be solved
     llbase64_tut.cpp
+    llbbox_tut.cpp
+    llbboxlocal_tut.cpp
     llblowfish_tut.cpp
     llbuffer_tut.cpp
     lldate_tut.cpp
@@ -54,6 +56,7 @@ set(test_SOURCE_FILES
     llpipeutil.cpp
     llquaternion_tut.cpp
     llrandom_tut.cpp
+    llrect_tut.cpp
     llsaleinfo_tut.cpp
     llscriptresource_tut.cpp
     llsdmessagebuilder_tut.cpp